在JSF单页应用程序中导航时遇到的问题

时间:2019-01-26 22:04:28

标签: java jsf single-page-application jsf-2.2

我正在学习JSF 2.2,并试图制作一个单页应用程序(SPA)。使用2.2.8版。我制作了一个仅显示文本和图片的简单SPA。我是通过使用f:ajax,ui:include和ui:composition做到的。

所有操作均始于index.xhtml文件:

<!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://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:jsf="http://xmlns.jcp.org/jsf">
<h:head>
<title>Project S - SPA</title>
<link href="./css/styles.css" rel="stylesheet" type="text/css"/>
</h:head>
<h:body>
<h1 class="title">Project S - Single Page Application</h1>

<h:form>
    <f:ajax render="content">
        <h:commandLink value="Login" action="#{navController.setToPage('login')}"/>&nbsp;&nbsp;
        <h:commandLink value="Create user" action="#{navController.setToPage('create-user')}"/>&nbsp;&nbsp;
        <h:commandLink value="Reset password" action="#{navController.setToPage('reset-password')}"/>&nbsp;&nbsp;
    </f:ajax>
</h:form>
<hr/>
    <div jsf:id="content">
        <ui:include src="#{navController.page}.xhtml"/>
    </div>

</h:body></html>

从这里我加载诸如login.xhtml之类的页面:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <h1>Login page</h1>
    <h:form>
    Please log in.<br/>
    <h:panelGrid columns="2">
        User name: <h:inputText value="#{loginController.name}"/>
        Password: <h:inputSecret value="#{loginController.password}"/>
    </h:panelGrid>
        <h:commandButton value="Login" action="#{loginController.login}"/>
</h:form>
</ui:composition>

和create-user.xhtml:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
    <h1>Create user page</h1>

    <h:form>
        <h:panelGrid columns="3" styleClass="formTable">
            User name:
            <h:inputText value="#{registerController.userName}"
                         id="uname"
                         required="true"
                         requiredMessage="Please enter your User Name">
            </h:inputText>
            <h:message for="uname"/>

            Password:
            <h:inputSecret value="#{registerController.password}"
                         id="pass"
                         required="true"
                         requiredMessage="Please enter your Password">
            </h:inputSecret>
            <h:message for="pass"/>

            Hint:
            <h:inputText value="#{registerController.hint}"
                         id="hint"
                         required="true"
                         requiredMessage="Please enter a hint">
            </h:inputText>
            <h:message for="hint"/>
        </h:panelGrid>
        <h:commandButton value="Register" action="#{registerController.register}"/>
    </h:form>
</ui:composition>

我使用NavController.java托管bean进行导航:

package controllers;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class NavController implements Serializable {
    private static final long serialVersionUID = 1L;
    private String page = "login";

    public String getPage() {
        return page;
    }

    public void setToPage(String page) {
        this.page = page;
    }
}

页面可以很好地显示和切换到它们。将加载的第一页(在这种情况下为登录名)可以正常工作。但是其他页面则没有。并不是我的意思是,无论我单击h:commandButton时输入了什么,都不会激活验证,也不会创建用户。我只是回到了index.jsf页面。这是创建用户的控制器类:

package controllers;

import javax.faces.bean.ManagedBean;

import database.UsersDataBaseSimulator;
import models.User;

@ManagedBean
public class RegisterController {

    private String userName, password, hint;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getHint() {
        return hint;
    }

    public void setHint(String hint) {
        this.hint = hint;
    }

    public String register() {
        if(UsersDataBaseSimulator.USERS.containsKey(userName)) {
            return "user-exists";
        }
        User user = new User(userName, password, hint);
        UsersDataBaseSimulator.USERS.put(userName, user);
        return "user-created-successfully";
    }
}

登录的控制器类:

package controllers;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

import database.UsersDataBaseSimulator;

@ManagedBean(name="loginController")
@SessionScoped
public class LoginController implements Serializable{

    private static final long serialVersionUID = 1L;
    private String name, password;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String login() {
        if(UsersDataBaseSimulator.USERS.get(name) != null) {
            if(UsersDataBaseSimulator.USERS.get(name).getPassword().equals(password)) {
                return "main-page?faces-redirect=true";
            }
        }
        return "error";
    }
}

我已将此页面创建为非SPA,并且可以正常运行。我试图将初始页面从登录切换为创建用户,在这种情况下,只有创建用户页面有效。在这一点上,我什至不知道这是否可能,以及我是否正以正确的方式进行操作。我正在从http://www.coreservlets.com/JSF-Tutorial/jsf2/这门课程中学习。到目前为止,我已经完成了复合组件的第1部分。我将提供您可能需要的所有其他信息,以便深入了解此问题。预先感谢您抽出宝贵时间解决此问题。

1 个答案:

答案 0 :(得分:1)

我尝试通过添加UserUsersDataBaseSimulator使您的应用程序运行,并注意到以下内容:

Non-SPA(甚至是非Ajax)

对于菜单部分,您似乎正在执行How to ajax-refresh dynamic include content by navigation menu? (JSF SPA)中所述的“ SPA”。

在“包含”中,您实际上没有在执行SPA。您无需在“子页面”中的commandButton上使用“ ajax”,而是返回一个在正常JSF导航中有效的“字符串”,这种情况在您的情况下是不存在的。通过在web.xml中设置javax.faces.PROJECT_STAGE并执行“失败的登录”,以开发模式运行应用程序,您将看到:

  

无法为结果为“错误”的动作“#{loginController.login}”找到具有from-view-id为“ /index.xhtml”的匹配导航案例。

,并在日志中记录

  

13:07:27,933警告[javax.enterprise.resource.webcontainer.jsf.application](默认任务20)JSF1064:无法查找或提供资源/error.xhtml。

有两个选项可以保留on the same page(在这里严格来说是index.jsf)

  • 您可以返回null(或返回void)以保持原样在同一页面上,但随后不会更新任何内容,而实际上保留在页面上。
  • li>
  • 您可以返回一个空字符串,该字符串将使您停留在同一页面上,但会刷新整个页面。但是,如果navigationController中的页面您将看到相同的内容。将NavigationController注入其他页面控制器并执行“ setPage(...)”将显示一个新页面。

第一个不是您想要的时间,第二个不是“ SPA”,因为它会刷新整个页面。

因此,您也需要在此处使用ajax,并且不返回真实页面或结果(稍后再介绍)

Ajax-Form-Navigation-Multiple-Forms错误

现在,如果您执行了所有这些操作,您仍然会看到“其他页面在首次导航到它们时不起作用”部分(在RegisterController#register()方法中设置断点,您将发现它永远不会被调用)。这样做的原因是在Mojara 2.2.x there is a bug中导致此行为,并且与具有多种形式有关(本身就是很好)。当我将其应用于您的项目时,其他页面也开始工作,除了现在也给出了上面导航部分中提到的错误。

子页面中的AJAX

如果在您的LoginController中“注入”了NavController,并在login()方法中设置了(部分)页面:

public class LoginController implements Serializable{

    @ManagedProperty
    NavController navController;

    ...

    public void login() {

       ... 

       navController.setPage('main-page');
    }

然后在xhtml中完成

<h:commandButton value="Login" action="#{loginController.login}">
    <f:ajax render=":content"/>
</h:commandButton>

您将获得所需的行为。只需对其他页面/控制器执行此操作。