为了实现您的单页应用程序,您应该说明应该呈现页面的哪一部分。这可以通过使用诸如创建、编辑、列表等布尔标志来完成。例如,请参阅以下(只是相关代码)
<h:body>
<h:form rendered="#{userController.stateManager.create}">
<h:panelGroup rendered="#{not empty facesContext.messageList or userController.stateManager.failure}">
<!--render error message right here-->
</h:panelGroup>
<div>
<label>#{messages['br.com.spa.domain.model.User.name']}</label>
<h:inputText value="#{user.name}"/>
</div>
<h:commandButton action="#{userController.create}">
<f:ajax execute="@form" render="@all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
</form>
</h:body>
Run Code Online (Sandbox Code Playgroud)
请注意,我们的表单将在标志 create 为 true 时呈现 - 请参阅上面的第二行。为了包装我们的标志,我们创建了一个名为 StateManager 的类,如下所示
/**
* I am using lombok, which takes care of generating our getters and setters. For more info, please refer http://projectlombok.org/features/index.html
*/
@Setter @Getter
public class StateManager {
private boolean create;
private boolean edit;
private boolean list;
}
Run Code Online (Sandbox Code Playgroud)
现在,因为我们只使用一个页面,所以我们应该使用 ViewScoped 托管 bean,只要您在同一个视图上,它就会使我们的托管 bean 保持活动范围——它是一个单页面应用程序,对吗?所以,没有导航。考虑到这一点,让我们创建我们的托管 bean。
@ManagedBean
@ViewScoped
public class UserController implements StateManagerAwareManagedBean {
private @Inject UserService service;
private @Getter @Setter stateManager = new StateManager();
private @Getter @Setter List<User> userList = new ArrayList<User>();
private @Getter @Setter User user;
@PostConstruct
public void initialize() {
list();
}
public void create() {
service.persist(user);
stateManager.setCreate(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void edit() {
service.merge(user);
stateManager.setEdit(false);
stateManager.setList(true);
stateManager.setSuccess(true);
}
public void list() {
userList = service.list();
stateManager.setList(true);
}
}
Run Code Online (Sandbox Code Playgroud)
对于每个操作方法,我们定义应呈现页面的哪一部分。例如,考虑到我们的表单被处理,覆盖了所有 JSF lyfecycle,这意味着它们的值被成功转换和验证,并且我们的 action 方法被调用。以我们的创建操作方法为例- 见上文 - 我们将其创建标志设置为 false,因为我们的表单已被转换和验证,所以我们不需要再次显示它(除非你想要)。此外,我们将列表和成功标志都设置为 true,这表示应该呈现我们页面的列表并且我们的表单已成功处理 - 您可以使用此标志来显示诸如“用户创建”之类的内容,如下所示
<h:panelGroup rendered="#{userController.stateManager.success}">
#{messages['default.created.message']}
</h:panelGroup>
Run Code Online (Sandbox Code Playgroud)
现在,让我们讨论第一次调用时应该呈现页面的哪一部分。也许您不知道,但会首先调用带有 @PostConstruct 注释的 void 方法。所以我们定义了页面的哪一部分应该被渲染。在我们的示例中,我们调用 list 方法,该方法将其列表标志设置为 true 并填充后备列表。
@PostConstruct
public void initialize() {
list();
}
Run Code Online (Sandbox Code Playgroud)
最后,让我们回顾一下嵌套在 h:commandButton 中的以下顺序
<h:commandButton action="#{userController.create}">
<f:ajax execute="@form" render="@all"/>
<f:actionListener type="br.com.spa.web.faces.listener.StateManagerActionListener" />
<f:setPropertyActionListener target="#{userController.stateManager.create}" value="true"/>
<f:setPropertyActionListener target="#{userController.user}" value="#{user}" />
</h:commandButton>
Run Code Online (Sandbox Code Playgroud)
首先,您应该调用 ActionListener - 这里称为 StateManagerActionListener - 它负责重置任何 StateManager - 代码如下。它必须在任何其他旨在控制任何标志的 setPropertyActionListener 之前首先调用,因为 h:commandButton 中定义的顺序是它们将被调用的顺序。记住这一点。
public class StateManagerActionListener implements ActionListener {
public void processAction(ActionEvent e) throws AbortProcessingException {
Map<String,Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
for(Map.Entry<String,Object> entry: viewMap.entrySet()) {
if(entry.getValue() instanceof StateManagerAwareManagedBean) {
((StateManagerAwareManagedBean) entry.getValue()).setStateManager(new StateManager());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
StateManagerAwareManagedBean - 在我们的 ViewScoped Managed bean 中使用 - 允许我们重置任何 ManagedBean 的任何 StateManager而不是在我们的 ActionListener 中一一重置,定义如下
public interface StateManagerAwareManagedBean {
StateManager getStateManager();
void setStateManager(StateManager stateManager);
}
Run Code Online (Sandbox Code Playgroud)
其次,在定义我们的 ActionListener 之后,我们使用 setPropertyActionListener 将控制视图封闭部分的标志设置为 true。它是必需的,因为我们的表单不应该被转换和验证。因此,在我们的 action 方法中,我们如前所述将这个标志设置为 false。
一些注意事项