События onChange не запускаются в элементе управления повторением XPage

У меня есть большая "форма заказа" XPage, которая отображает 99 строк с 3 текстовыми полями ввода в каждой строке. Чтобы зафиксировать изменения, я поместил вызов функции SSJS в событие onchange каждого поля ввода. Вызов просто отправляет идентификатор продукта, тип изменения (какой столбец) и количество. Затем функция SSJS сохраняет эти изменения в переменной sessionScope (java.util.HashMap). Обновление не связано с изменением.

Изменения обрабатываются в массовом порядке, когда пользователь нажимает кнопку «Отправить». Это еще одна функция SSJS, которая просто записывает все изменения во внутреннюю базу данных Domino.

Вроде бы все работает нормально и работает уже пару лет. Тем не менее, похоже, что мои пользователи стали слишком эффективно работать с приложением и печатают быстрее, чем оно успевает.

Мой код отладки записывает каждое изменение в консоль сервера, и я вижу, где некоторые изменения просто игнорируются, если пользователь вносит изменения в быстрой последовательности (они просто перемещаются между полями ввода). Как будто сервер слишком занят обработкой предыдущего изменения и пропускает одно, чтобы перейти к другому. Иногда пропускаются целые блоки изменений, а затем приложение возвращается, когда может.

Использую ли я неправильную технику для фиксации изменений? Есть ли что-то, что я могу сделать, чтобы приложение инициировало событие onchange каждый раз?

Я тестировал это с помощью IE8/9 и FF24. Я просмотрел другие подобные сообщения, в которых вместо этого предлагается использовать событие onkeyup. Я не думаю, что это сработает в моем случае, так как пользователи могут заказывать двузначные количества.

Любые / все предложения будут с благодарностью оценены!


person Terry Boyd    schedule 06.11.2013    source источник
comment
Одновременно может выполняться только одно событие, поэтому, если событие запускается, когда предыдущее событие все еще находится в ожидании, оно автоматически блокирует новое событие. Поскольку с этими данными ничего не происходит до отправки, зачем инициировать отдельное событие изменения для каждого компонента?   -  person Tim Tripcony    schedule 07.11.2013
comment
Спасибо за ответ Тим! Извините, я пропустил ключевую часть своего объяснения... Мне нужно сохранить изменения, внесенные пользователями до отправки заказа, потому что в форме заказа есть несколько страниц (а не только начальные 99 строк). Сохраняя каждое отдельное изменение в переменной sessionScope, я могу отправить их вместе в тот момент, когда пользователь нажимает кнопку отправки.   -  person Terry Boyd    schedule 07.11.2013
comment
Извините, еще один момент, чтобы поддержать мою причину «пакетной обработки» всех изменений во время отправки — бизнес-логика требует, чтобы все изменения обрабатывались вместе. Логика правил покупки (скидки при покупке нескольких товаров, товары, которые должны быть куплены вместе и т. д.) содержится в библиотеке LotusScript, совместно используемой клиентами Notes, и эта библиотека зависит от полного пакета представленных изменений.   -  person Terry Boyd    schedule 08.11.2013
comment
На самом деле я не пытаюсь убедить вас отказаться от пакетной обработки; скорее, отказаться от индивидуальной обработки. Ваши существующие события размещают информацию в области сеанса... вместо этого привяжите весь интерфейс (или, по крайней мере, повторение) к объекту в области, и при нажатии кнопки «Отправить» ваша область автоматически обновляется, независимо от того, сколько строк вовлечены. Это в основном то, что иллюстрирует ответ Стефана.   -  person Tim Tripcony    schedule 08.11.2013
comment
Спасибо Тим - я получаю. Я принял во внимание комментарии/код Стефана и начал изучать/разрабатывать решение для управляемого компонента. Очень ценю, что вы нашли время объяснить, и с нетерпением жду возможности угостить вас выпивкой в ​​Орландо!   -  person Terry Boyd    schedule 08.11.2013


Ответы (1)


Терри, тебе нужно пересмотреть архитектуру. Если обновления обрабатываются при отправке, зачем отправлять их на сервер по отдельности, как прекрасно заметил Тим. Что бы я сделал:

  • создайте 2 класса Java: один "Заказ" один "LineItem"
  • Пусть класс Order реализует карту интерфейса Map
  • Используйте класс Order для управления повторением (он даст вам ключ каждого LineItem в качестве переменной повторения)
  • Привяжите поля внутри повторения к Order[RepeatKey].fieldName
  • Использование Order в объектном источнике данных
  • Реализуйте метод сохранения в классе Order и вызовите его в методе сохранения объекта-источника данных.

Очень грубый план, дайте мне знать, если вам нужно, чтобы я уточнил. Java Collections Framework — ваш друг.

Это проще, чем кажется:

   public class LineItem {

private String unid;
private String partno;
private int quantity;
private long unitprice;

/**
 * Constructor for new items
 */
public LineItem() {
    this.unid = null;
}

/**
 * Constructor for existing items
 */
public LineItem(Document doc) {
    this.unid = doc.getUniversalId();
    // more here
}


/**
 * @return the unid
 */
public String getUnid() {
    return this.unid;
}

/**
 * @return the partno
 */
public String getPartno() {
    return this.partno;
}
/**
 * @param partno the partno to set
 */
public void setPartno(String partno) {
    this.partno = partno;
}
/**
 * @return the quantity
 */
public int getQuantity() {
    return this.quantity;
}
/**
 * @param quantity the quantity to set
 */
public void setQuantity(int quantity) {
    this.quantity = quantity;
}
/**
 * @return the unitprice
 */
public long getUnitprice() {
    return this.unitprice;
}
/**
 * @param unitprice the unitprice to set
 */
public void setUnitprice(long unitprice) {
    this.unitprice = unitprice;
}

public void save(Database db) {
    Document doc = null;
    if (this.unid == null) {
        doc = db.createDocument();
        doc.replaceItem("Form", "LineItem");
    }
    doc.replaceItem("PartNo", this.partno);
    // More here
    doc.save();
}
}

а для Заказа - при условии, что вы загружаете из коллекции документов.

public class Order implements Map<String, LineItem> {

// You might want to have a stack here to keep order
private final Map<String, LineItem> backingMap          = new LinkedHashMap<String, LineItem>();
private final Set<String>           deletedItemKeys     = new HashSet<String>();

// The key we use for new items when unid is null
private int                         lastNewItemNumber   = 0;

@Override
public int size() {
    return this.backingMap.size();
}

@Override
public boolean isEmpty() {
    return this.backingMap.isEmpty();
}

@Override
public boolean containsKey(Object key) {
    return this.backingMap.containsKey(key);
}

@Override
public boolean containsValue(Object value) {
    return this.backingMap.containsValue(value);
}

@Override
public LineItem get(Object key) {
    return this.backingMap.get(key);
}

@Override
public LineItem put(String key, LineItem value) {
    // Here it gets a little special
    // We need to prevent null keys
    if (key == null) {
        key = String.valueOf(this.lastNewItemNumber);
        lastNewItemNumber++;
    }
    this.deletedItemKeys.remove(key);
    return this.backingMap.put(key, value);
}

@Override
public LineItem remove(Object key) {
    this.deletedItemKeys.add(key.toString());
    return this.backingMap.remove(key);
}

@Override
public void putAll(Map<? extends String, ? extends LineItem> m) {
    for (Map.Entry<? extends String, ? extends LineItem> me : m.entrySet()) {
        this.put(me.getKey(), me.getValue());
    }
}

@Override
public void clear() {
    this.deletedItemKeys.addAll(this.backingMap.keySet());
    this.backingMap.clear();
}

@Override
public Set<String> keySet() {
    return this.backingMap.keySet();
}

@Override
public Collection<LineItem> values() {
    return this.backingMap.values();
}

@Override
public Set<java.util.Map.Entry<String, LineItem>> entrySet() {
    return this.backingMap.entrySet();
}

public void load(NotesDocumentCollection dc) throws NotesException {
    Document doc = dc.getFirstDocument();
    Document nextDoc;
    while (doc != null) {
        nextDoc = dc.getNextDocument(doc);
        LineItem li = new LineItem(doc);
        this.put(doc.getUniversalId(), li);
        doc.recycle();
        doc = nextDoc;
    }

    doc.recyle();
}

public void save(Database db) {
    for (LineItem item : this.backingMap.values()) {
        item.save(db);
    }

    // Now kill the left overs - needs error handling
    for (String morituri : this.deletedItemKeys) {
        Document delDoc = db.getDocumentByUnid(morituri);
        if (delDoc != null) {
            delDoc.remove(true);
        }
    }       
}
}
person stwissel    schedule 07.11.2013
comment
Спасибо Стефан! Я знал, что ты вернешься с решением, которое было на дрожжах выше моей головы. Блин, хотел бы я знать Java лучше!! Я рассмотрю это (вы предложили этот подход еще в феврале 2011 года, и я до сих пор недостаточно развил свои навыки Java!), но, возможно, мне придется найти кого-то более знающего, чем я, чтобы помочь. - person Terry Boyd; 07.11.2013
comment
Терри - это проще, чем кажется. Взгляните на примеры классов выше - person stwissel; 07.11.2013
comment
Ты легенда Стефан! Я попробую сегодня вечером и дам вам знать, как я справедлив. Напомни мне купить тебе пару напитков в Орландо! Вы идете? Не забудьте взять с собой «Желтого парня» (Мэта)!! - person Terry Boyd; 07.11.2013
comment
Ты сможешь, Терри! Удачи. Мы здесь, чтобы помочь, на всякий случай :-) - person Per Henrik Lausten; 07.11.2013
comment
Все выглядит очень хорошо за одним исключением. Я собираю детали с помощью NotesViewEntries, чтобы убедиться, что я собираю их в том порядке, в котором я хочу, чтобы они отображались в моем элементе управления повторением. Однако когда они отображаются, они находятся в совершенно другом порядке, чем я предполагал. Я отмечу ваш комментарий. Стефан: Возможно, вы захотите иметь здесь стопку для поддержания порядка. Не могли бы вы уточнить этот момент, чтобы я мог попытаться убедиться, что порядок entrySet представляет порядок, в котором собираются детали? Еще раз спасибо за всю вашу помощь, софер - я могу просто закончить это дело вовремя !! - person Terry Boyd; 19.11.2013
comment
Вам нужно изменить backingMap = HashMap‹String, LineItem›(); to backingMap = new LinkedHashMap‹String, LineItem›(); - Обновлен код выше. LinkedHashMap сохраняет порядок вставки - person stwissel; 19.11.2013
comment
Хорошо, я почти у цели! У меня есть последнее препятствие для навигации... поскольку в заказе так много строк, элемент управления повтором "выгружается". Всякий раз, когда я перехожу на другую страницу, а затем возвращаюсь, новые детали заказа, которые я ввожу в поля, теряются из поля зрения. Пейджер настроен на частичное обновление, и мои тесты показывают, что заказы загружаются только один раз во время загрузки страницы, так что все в порядке. Должно быть, я упускаю что-то явно очевидное, но я не могу понять это правильно. Любые предложения будут ценны!! - person Terry Boyd; 27.11.2013
comment
Вам нужно отправить значения, иначе они не обновятся. Я бы не листать заказ. Прокрутка быстрее, чем нажатие. И у вас есть все в фасоли, поэтому у вас нет преимущества в скорости от пейджинга. Мы прошли 80x25 - person stwissel; 27.11.2013
comment
Спасибо Стефан - я подозревал, что это был ответ, который я собирался получить. Форма заказа может содержать более 1000 позиций, только некоторые из которых пользователи будут заказывать одновременно. Бизнес диктует, что все строки должны быть доступны в любое время, и я реализовал пейджинг, чтобы обеспечить наилучшую производительность. Я реализовал несколько представлений, с помощью которых пользователи могут изолировать различные категории продуктов и минимизировать количество отображаемых строк заказа. Я полагаю, что это то направление, которое мне нужно будет выбрать тогда. Ваше здоровье!! - person Terry Boyd; 28.11.2013
comment
Вам нужно изменить некоторую логику... вместо того, чтобы использовать пейджер для отправки значений обратно, тогда ваш bean-компонент будет обновлен - person stwissel; 28.11.2013