Приложение JSF, действия не работают, если ранее было вызвано другое действие

Я изучаю Java EE 6 и JSF 2.0 на JBoss6 и создал очень простое одностраничное приложение Todo, которое работает, но с очень странной ошибкой. Протестировано в Safari 5.0.5 и Firefox 5.

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

И пример использования может выглядеть так:

  1. попробуйте добавить todo = успех
  2. попробуйте добавить todo = успех
  3. попробуй проверить todo = не получится
  4. попытаться проверить задачу = успех
  5. попробуйте добавить todo = не получится
  6. попробуйте добавить todo = успех
  7. попробуй проверить todo = не получится

Приложение имеет следующие основные файлы (плюс другие фрагменты):

  • entity/Todo.java ‹- сущность JPA
  • manager/TodoManager.java ‹ — EJB для обработки сущностей Todo, @Stateless
  • controllers/TodoController.java ‹- управляемый компонент для страницы, @SessionScoped
  • todos.xhtml ‹ — страница JSF

Нет лиц-config.xml

Форма для добавления задачи выглядит так:

<h:panelGroup id="projects">
   <h:message for="newtitle" />
   <h:form id="newtodo">
    <h:panelGrid columns="5">
            <h:outputText value="New Todo: "/>
            <h:inputText id="newtitle" value="#{todoController.todo.title}" />
            <h:outputText value="Due: "/>
            <h:inputText id="newDueDate" value="#{todoController.todo.dueDate}">
                <f:convertDateTime pattern="dd/mm/yyyy"/>
            </h:inputText>
            <h:commandButton action="#{todoController.addTodo}" value="add">
                <f:ajax execute="@form" render=":projects"/>
            </h:commandButton>

форма для проверки/снятия отметки со статусом «готово» выглядит следующим образом:

<h:form>
    <h:dataTable id="todolist" var="t" value="#{todoController.todolist}">
        <h:column>
            <h:selectBooleanCheckbox id="rowCheckbox" value="#{t.done}" >
                <f:ajax event="click" listener="#{todoController.updateDone(t)}" render=":projects"/>
            </h:selectBooleanCheckbox>
        </h:column>

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

@ManagedBean(name="todoController")
@SessionScoped
public class TodoController 
{
    @EJB
    private TodoManager todoManager;
    private Todo todo = new Todo();
    private ArrayList<Todo> todolist = new ArrayList<Todo>();

    public String addTodo()
    {
        todo.setDone(false);
        todo.setUser(this.getLoggedInUser());
        todoManager.addTodo(todo);
        todo = null;
        return "todos.xhtml";
    }

    public String updateDone(Todo t)
    {
        t.setDone(!t.getDone());
        todoManager.updateTodo(t);
        return "todos.xhtml";
    }

Я добавил сообщения регистрации в addTodo() и updateDone(Todo t), чтобы проверять, когда они вызываются. Когда действия «не работают», кажется, что они вообще не вызываются. :-(


person Dana    schedule 25.06.2011    source источник
comment
Обнаружил, что это работает, если я обновляю страницу перед действием, которое, как я ожидаю, не удастся. Хотя это немного противоречит цели AJAX :-(   -  person Dana    schedule 25.06.2011


Ответы (1)


Это произойдет, когда у вас есть 2 формы, из которых вторая форма повторно отображается путем суммирования первой формы. Таким образом, JSF-состояние просмотра второй формы будет полностью потеряно (это можно определить самостоятельно по отсутствию скрытого поля ввода с именем javax.faces.ViewState в Ajax-ответе). Отправка второй формы после повторного рендеринга отправкой первой формы визуально не будет иметь никакого эффекта. Без состояния просмотра JSF не будет обрабатывать отправку формы. Только новая форма (с правильным состоянием просмотра!) вернется на место, и, следовательно, вторая отправка работает.

Вам нужно точно настроить атрибут render таким образом, чтобы повторно отображалось только содержимое второй формы, а не вся форма.

E.g.

<h:form id="form1">
    <h:panelGroup id="content">
        ...
        <h:commandButton value="Submit">
            <f:ajax execute="@form" render="@form :form2:content" />
        </h:commandButton>
    </h:panelGroup>
</h:form>
<h:form id="form2">
    <h:panelGroup id="content">
        ...
        <h:commandButton value="Submit">
            <f:ajax execute="@form" render="@form :form1:content" />
        </h:commandButton>
    </h:panelGroup>
</h:form>

В вашем конкретном случае дайте <h:panelGrid> первой формы и <h:dataTable> второй формы фиксированное id и перерисуйте только это из другой формы.

person BalusC    schedule 25.06.2011
comment
правильно, поэтому, если у вас есть несколько форм с вызовами AJAX, вам не следует повторно отображать сами компоненты ‹h:form›, а только дочерние компоненты. - person Dana; 26.06.2011
comment
Только когда это другое, чем вы подчиняетесь. - person BalusC; 26.06.2011