【问题标题】:Single page applications with ajax [duplicate]带有ajax的单页应用程序[重复]
【发布时间】:2012-10-18 23:16:28
【问题描述】:

我对 JSF 比较陌生,并试图了解当前的 JSF 2 应用程序是如何设计的。我见过对使用 ajax 的单页应用程序的引用。有人可以向我介绍一些使用的技术和/或指向我的模型或书籍吗?我看过的书(JSF Complete Reference 等)对基本技术问题很有帮助,但我找不到当前设计技术的来源。

谢谢 戴夫

【问题讨论】:

  • 在我看来,专门针对核心 JSF 的书籍(而且数量不多)在该主题上做得不够好。您最终将不得不合并书籍以获得所需的总和质量。试试 Guilio Zambon 的 this book 和 Richfaces JSF 库的创建者的 this。其余的,请上网。见balusc.blogspot.com
  • 我去看看 Richfaces 的书,谢谢。

标签: jsf jsf-2


【解决方案1】:

为了实现您的单页应用程序,您应该说明应该呈现页面的哪一部分。这可以通过使用布尔标志来完成,例如创建、编辑、列表等。例如,请参见以下(仅相关代码

<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>

请注意,当标志 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;

}

现在,因为我们只使用一个页面,所以我们应该使用 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);
    }

}

对于每个动作方法,我们定义应该呈现页面的哪一部分。例如,考虑到我们的表单已被处理,覆盖了所有 JSF lyfecycle,这意味着它们的值已成功转换和验证,并且我们的操作方法被调用。通过使用我们的 create 操作方法(见上文)作为示例,我们将其创建标志设置为 false,因为我们的表单已被转换和验证,因此我们不需要再次显示它(除非你想要)。此外,我们将 list 和 success 标志都设置为 true,这表明我们的页面列表应该被渲染并且我们的表单已成功处理 - 您可以使用此标志显示类似“用户创建”之类的内容,如下所示

<h:panelGroup rendered="#{userController.stateManager.success}">
    #{messages['default.created.message']}
</h:panelGroup>

现在,让我们讨论第一次调用时应该呈现页面的哪一部分。也许你不知道,但是一个用 @PostConstruct 注释的 void 方法将首先被调用。所以我们定义应该渲染页面的哪一部分。在我们的示例中,我们调用 list 方法,它将其列表标志设置为 true 并填充一个后备列表。

@PostConstruct
public void initialize() {
    list();
}

最后,让我们回顾一下嵌套在 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>

首先,您应该调用一个 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());
            }
        }
    }

}

StateManagerAwareManagedBean - 在我们的 ViewScoped Managed bean 中使用 - 允许我们重置任何 ManagedBean 的任何 StateManager而不是在我们的 ActionListener 中一一重置,定义如下

public interface StateManagerAwareManagedBean {

    StateManager getStateManager();
    void setStateManager(StateManager stateManager);

}

其次,在定义我们的 ActionListener 之后,我们使用 setPropertyActionListener 将控制视图封闭部分的标志设置为 true。它是必需的,因为我们的表单 应该 不会被转换和验证。因此,在我们的 action 方法中,我们将该标志设置为 false,如前所述。

一些注意事项

  • 用户被标记为 RequestScoped ManagedBean,因此不能使用 ManagedProperty 将其注入到 ViewScoped 中,因为它的范围是有限的。为了克服这个问题,我使用 来设置它的值 - 请参阅我们的表单
  • 我们的示例使用需要适当应用服务器的 JEE 功能。欲了解更多信息,请参阅http://docs.oracle.com/javaee/6/tutorial/doc/
  • ManagedBean 可以扮演不同的角色,例如Controller、DTO 等。当它扮演控制器的角色时,我更喜欢在它的名字后缀控制器。更多信息,请参考http://java.dzone.com/articles/making-distinctions-between

【讨论】:

  • 很遗憾看到如此详尽的答案没有投票或 cmets,甚至来自 OP。继续努力!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
相关资源
最近更新 更多