CDI @ConversationScoped с AJAX - PullRequest
       33

CDI @ConversationScoped с AJAX

4 голосов
/ 08 ноября 2011

У меня довольно типичная ситуация с CRUD, с которой я борюсь, поэтому я полагаю, что я что-то неправильно понимаю. Я собрал небольшое демонстрационное приложение, чтобы лучше объяснить мою проблему. Два представляющих интерес файла показаны ниже:

PersonView - Управляемый компонент CDI, поддерживающий страницу JSF

package example

import java.io.Serializable;
import java.util.List;
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;

@ConversationScoped @Named
public class PersonView implements Serializable {

private Person selectedPerson;
@Inject private PersonService personService;
@Inject private Conversation conversation;

public PersonView() {}

public List<Person> getPeople() { return personService.findAll(); }

public void beginConversation() { if( conversation.isTransient() ) {conversation.begin();} }

public void endConversation() { if( !conversation.isTransient() ) { conversation.end();} }

public void createPerson() {
    beginConversation();
    setSelectedPerson( new Person() );
}

public void addPerson() {
    personService.addPerson( getSelectedPerson() );
    endConversation();
}

public void updatePerson() { personService.updatePerson( getSelectedPerson() ); }

public Person getSelectedPerson() { return selectedPerson; }

public void setSelectedPerson(Person selectedPerson) { this.selectedPerson = selectedPerson; }
}

index.xhtml - JSF-страница для манипулирования людьми

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:p="http://primefaces.org/ui">
<h:head>
    <title>CRUD Example</title>
</h:head>
<h:body>
    <h:form prependId="false">
        <p:dataTable var="p" value="#{personView.people}" id="person_table" rowKey="#{p.id}" selection="#{personView.selectedPerson}">
            <p:column selectionMode="single"/>              
            <p:column><f:facet name="header">ID</f:facet>#{p.id}<p:column>
            <p:column><f:facet name="header">Name</f:facet>#{p.name}</p:column>
            <f:facet name="footer">
               <p:commandButton value="Create Person" onclick="create_dialog.show();" actionListener="#{personView.createPerson}"/>                 
               <p:commandButton value="Edit Person" onclick="edit_dialog.show();" update="edit_panel"/>
            </f:facet>
        </p:dataTable>
    </h:form>
    <p:dialog header="Create Person" id="create_dialog" widgetVar="create_dialog" modal="true" width="750" height="300">
        <h:form prependId="false">
            <p:panel id="create_panel">
                <p>Name: <p:inputText value="#{personView.selectedPerson.name}" required="true"/></p>
                <p><p:commandButton value="Add" actionListener="#{personView.addPerson}" oncomplete="create_dialog.hide();" update="person_table" /></p>
            </p:panel>
        </h:form>
    </p:dialog>
</h:body>

На странице индекса пользователю показана таблица данных, содержащая всех людей, о которых знает система. Затем они нажимают кнопку «Создать человека» в нижней части таблицы. Я проверил, что это правильно вызывает метод createPerson и разговор, очевидно, начинается. Затем отображается create_dialog, где пользователь может ввести имя. Проблема возникает, когда пользователь нажимает кнопку Добавить. JSF пытается сохранить имя пользователя, но переменная selectedPerson теперь имеет значение null, поэтому она завершается с ошибкой NullPointerException.

Я понимаю, что это не обычный способ создания объектов, но в моем текущем приложении это имеет смысл, так как я могу догадаться о некоторых значениях нового Персона. Это также хорошо согласуется с тем, как я хотел бы заниматься редактированием.

Так что мой вопрос (а): почему разговор не распространяется? Компонент PersonView, по-видимому, постоянно находится в области запроса. Я читал о @ViewScoped в JSF2, но я бы предпочел придерживаться CDI, если это возможно. Из того, что я прочитал, я думаю, что проблема в том, что мне не удалось передать имя CID с запросом, но я не могу понять, как я могу сделать это с AJAX.

Единственное решение, которое я придумала, - это перенести PersonView в сессию, но это похоже на огромный клочок. е

Ответы [ 3 ]

4 голосов
/ 09 ноября 2011

Единственный способ заставить это работать - использовать @ViewAccessScoped в MyFaces CODI .CDI обеспечивает расширяемость, поэтому все, что вам нужно сделать, это включить файлы jar CODI в ваше приложение.Это работает, даже если вы используете Mojarra, а не MyFaces.

Так что, если вы хотите использовать аннотации CDI, это моя рекомендация.Некоторое время я пытался использовать аннотацию ConversationScoped, но просто не мог заставить ее работать удобно.Как только я начал использовать CODI, все мои проблемы исчезли.

0 голосов
/ 01 июня 2018

Работа с @ConversationScoped в сочетании с тегами <f:ajax> делает необходимым вставку идентификатора диалога в элементы commandButton и commandLink. Смотрите этот пример:

<h:commandLink value="update"
    actionListener="#{myController.updateSomething(myData)}">
    <f:ajax render="...."/>
    <f:param name="cid" value="#{myController.getCID()}" />
</h:commandLink>
0 голосов
/ 23 марта 2013

Похоже, вы используете PrimeFaces, я не уверен, как сделать вызов ajax с Primefaces, поэтому я покажу вам, как это делается со стандартным JSF 2.0

Прежде всего при использовании AJAX вы ДОЛЖНЫ передавать cid с каждым запросом AJAX. Попробуйте добавить следующее в ваш управляемый компонент ConversationScope (вам нужно как-то передать cid в view - index.xhtml, тогда каждый последующий вызов AJAX будет передавать ту же самую cid обратно на сервер):

@Named
@ConversationScoped
public class PersonView implements Serializable {

public List<Person> getPeople() { 
 return personService.findAll(); 
}

 @Inject 
 private Conversation conversation;

 //Start the conversation once the 
 //bean is created and all injection is done on the bean
 //I typically use this in the case of AJAX
 @PostContruct 
public void beginConversation() {
 if( conversation.isTransient() ) {
   conversation.begin();
 }
}

public void endConversation() { 
  if( !conversation.isTransient() ) { 
    conversation.end();
  }
}
// This will be used in the view (index.xhtml)
public String getConversationId() {
  return conversation.getId();
}

public void createPerson() {
 setSelectedPerson( new Person() );
}

public void addPerson() {
 personService.addPerson( getSelectedPerson() );
 endConversation();
 // beginConversation(); //might need to start a new conversation once old one is done
}
}

Теперь, по вашему мнению, вы обычно делаете следующее:

<h:commandButton action="#{personView.createPerson}" value="Create Person">
   <!-- passing the cid -->
   <f:param name="cid" value="#{personView.conversationId}" />
   <f:ajax execute="@form" />
</h:commandButton>

<h:commandButton action="#{personView.addPerson}" value="Add">
   <!-- passing the cid -->
   <f:param name="cid" value="#{personView.conversationId}" />
   <f:ajax execute="@form" render=":person_table" />
</h:commandButton>

Это работает, если все последующие вызовы являются AJAX. Если вы начнете смешивать его с обычными вызовами, разговор теряется. С другой стороны, если бы это был не-AJAX список звонков, которые вы делаете, cid будет автоматически передан для вас

...