java - 在 JSF 单页应用程序中导航时出现问题

标签 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 页面。这是创建用户的 Controller 类:

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";
    }
}

以及登录的 Controller 类:

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,并且运行良好。我尝试将初始页面从登录切换到创建用户,在这种情况下,只有创建用户页面有效。目前我不知道这是否可能,以及我是否以正确的方式进行。我正在学习这门类(class)http://www.coreservlets.com/JSF-Tutorial/jsf2/ 。到目前为止,我已经完成了复合组件第 1 部分。我将提供您可能需要的任何其他信息,以便深入了解这个问题。预先感谢所有花时间解决此问题的人。

最佳答案

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

非 SPA(甚至非 Ajax)

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

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

Unable to find matching navigation case with from-view-id '/index.xhtml' for action '#{loginController.login}' with outcome 'error'.

在日志记录中类似

13:07:27,933 WARNING [javax.enterprise.resource.webcontainer.jsf.application] (default task-20) JSF1064: Unable to find or serve resource, /error.xhtml.

有两种住宿选择 on the same page (技术上是这里的index.jsf)

  • 您可以返回null(或使返回void)保持在同一页面上,但它不会更新任何内容,实际上保持在页面上就这样。
  • 您可以返回一个空字符串,这将使您停留在同一页面上,但会进行整页刷新。但如果 navigationController 中的页面您会看到相同的内容。将 NavigationController 注入(inject)其他页面 Controller 并执行“setPage(...)”将显示一个新页面。

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

所以你也需要 ajax 并且不返回真实的页面或结果(稍后再说)

Ajax-Form-Navigation-Multiple-Forms 错误

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

子页面中的 AJAX

如果在您的 LoginController 中“注入(inject)”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>

你会得到你想要的行为。对其他页面/ Controller 也执行此操作。

关于java - 在 JSF 单页应用程序中导航时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54383229/

相关文章:

JSF <c :if> with different object type

reactjs - React.js 是否要求应​​用程序是单页面

java - java socket程序中的多线程

java - 如何从 Hibernate/JPA 流式传输结果并在处理后释放资源?

jsf - 启用选择时,DataModel 必须实现 org.primefaces.model.SelectableDataModel。

html - 我的 Basic jsf 标签没有呈现到 html 标签中

java - Typesafe Map 的多种键类型

java - 如何从其他 Activity 中完成主要 Activity

oauth-2.0 - 将 Google OIDC 与代码流和 PKCE 结合使用

angular - 如何将 CSS 文件添加到 webpack 以便它们可以在 Angular2 SPA 中使用