Использование a4j: поддержка для обновления модели и представления, готовность к следующей кнопке / действию отправки

Проблема

У нас есть интерфейс на основе Swing для корпоративного приложения, и теперь мы реализуем (пока более простой) интерфейс JSF / Seam / Richfaces для него.

Некоторые страницы содержат поля, которые при редактировании должны вызывать изменение других полей. Нам нужно, чтобы это изменение было показано пользователю немедленно (т.е. им не нужно было нажимать кнопку или что-то еще).

Я успешно реализовал это, используя h: commandButton и добавив onchange = "submit ()" в поля, которые вызывают изменение других полей. Таким образом, отправка формы происходит, когда они редактируют поле, и в результате обновляются другие поля.

Функционально это работает нормально, но особенно когда сервер находится под значительной нагрузкой (что случается часто), отправка формы может занять долгое время, и наши пользователи тем временем продолжали редактировать поля, которые затем возвращаются, когда отображаются ответы на запросы onchange = "submit ()" .

Чтобы решить эту проблему, я надеялся достичь чего-то, где:

  1. После редактирования поля, при необходимости обрабатывается только это поле , а только те поля, которые он изменяет , повторно отображаются (так что любые другие изменения, внесенные пользователем за это время, не потеряны).
  2. После нажатия кнопки все поля обрабатываются и повторно отображаются как обычно.

(Нестабильное) решение

Хорошо, я думаю, что проще всего показать часть моей страницы первый. Обратите внимание, что это только отрывок и что на некоторых страницах будет много полей и много кнопок.

<a4j:form id="mainForm">
    ...
    <a4j:commandButton id="calculateButton" value="Calculate" action="#{illustrationManager.calculatePremium()}" reRender="mainForm" />
    ...
    <h:outputLabel for="firstName" value=" First Name" />
    <h:inputText id="firstName" value="#{life.firstName}" />
    ...
    <h:outputLabel for="age" value=" Age" />
    <h:inputText id="age" value="#{life.age}">
        <f:convertNumber type="number" integerOnly="true" />
        <a4j:support event="onchange" ajaxSingle="true" reRender="dob" />
    </h:inputText>
    <h:outputLabel for="dob" value=" DOB" />
    <h:inputText id="dob" value="#{life.dateOfBirth}" styleClass="date">
        <f:convertDateTime pattern="dd/MM/yyyy" timeZone="#{userPreference.timeZone}" />
        <a4j:support event="onchange" ajaxSingle="true" reRender="age,dob" />
    </h:inputText>
    ...        
</a4j:form>

Изменение значения age приводит к изменению значения dob в модели и наоборот. Я использую reRender = "dob" и reRender = "age, dob" для отображения измененных значений из модели. Это прекрасно работает.

Я также использую глобальную очередь для обеспечения упорядочивания запросов AJAX.

Однако событие onchange не происходит, пока я не щелкну в другом месте страницы или не нажму вкладку или что-то в этом роде. Это вызывает проблемы, когда пользователь вводит значение, скажем, age , а затем нажимает calculateButton без щелчка в другом месте страницы или нажатия вкладки.

] onchange действительно происходит первым, поскольку я вижу изменение значения dob , но два значения затем возвращаются, когда выполняется запрос calculateButton .

Итак наконец, на вопрос: Есть ли способ гарантировать, что модель и представление полностью обновлены до выполнения запроса calculateButton , чтобы они не отменялись? Почему этого еще не происходит, поскольку я использую очередь AJAX?

Обходные пути

Есть две стратегии, чтобы обойти это ограничение , но обе они требуют раздувания кода лицевой панели, что может сбивать с толку другим разработчикам и вызвать другие проблемы.

Обходной путь 1: Использование a4j: support

Эта стратегия выглядит следующим образом:

  1. Добавьте атрибут ajaxSingle = "true" в calculateButton .
  2. Добавьте тег a4j: support с атрибутом ajaxSingle = "true" в firstName .

Первый шаг гарантирует, что calculateButton не перезапишет значения в age или dob , поскольку больше не обрабатывает их. К сожалению, у этого есть побочный эффект: он больше не обрабатывает firstName . Второй шаг добавлен для противодействия этому побочному эффекту путем обработки firstName до нажатия calculateButton .

Имейте в виду, что может быть более 20 полей, например firstName . Пользователь, заполняющий форму, может вызвать более 20 запросов на сервер! Как я уже упоминал ранее, это также беспорядок, который может сбить с толку других разработчиков.

Способ 2: Использование списка процессов

Благодаря @DaveMaple и @MaxKatz за предложение этой стратегии, он выглядит следующим образом:

  1. Добавьте ajaxSingle = "

    Установщики и получатели возраста и DOB (на случай, если они являются причиной проблемы)

    public Number getAge() {
        Long age = null;
    
        if (dateOfBirth != null) {
            Calendar epochCalendar = Calendar.getInstance();
            epochCalendar.setTimeInMillis(0L);
            Calendar dobCalendar = Calendar.getInstance();
            dobCalendar.setTimeInMillis(new Date().getTime() - dateOfBirth.getTime());
            dobCalendar.add(Calendar.YEAR, epochCalendar.get(Calendar.YEAR) * -1);
    
            age = new Long(dobCalendar.get(Calendar.YEAR));
        }
    
        return (age);
    }
    
    public void setAge(Number age) {
        if (age != null) {
            // This only gives a rough date of birth at 1/1/<this year minus <age> years>.
            Calendar calendar = Calendar.getInstance();
            calendar.set(calendar.get(Calendar.YEAR) - age.intValue(), Calendar.JANUARY, 1, 0, 0, 0);
    
            setDateOfBirth(calendar.getTime());
        }
    }
    
    public Date getDateOfBirth() {
        return dateOfBirth;
    }
    
    public void setDateOfBirth(Date dateOfBirth) {
        if (notEqual(this.dateOfBirth, dateOfBirth)) {
            // If only two digits were entered for the year, provide useful defaults for the decade.
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(dateOfBirth);
            if (calendar.get(Calendar.YEAR) < 50) {
                // If the two digits entered are in the range 0-49, default the decade 2000.
                calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 2000);
            } else if (calendar.get(Calendar.YEAR) < 100) {
                // If the two digits entered are in the range 50-99, default the decade 1900.
                calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 1900);
            }
            dateOfBirth = calendar.getTime();
    
            this.dateOfBirth = dateOfBirth;
            changed = true;
        }
    }
    
6
задан Gyan aka Gary Buyn 18 May 2011 в 21:35
поделиться