【问题标题】:Problems navigating in a JSF Single Page Application在 JSF 单页应用程序中导航的问题
【发布时间】:2019-01-26 22:04:28
【问题描述】:

我正在学习 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 部分。我将提供您可能需要的任何其他信息,以便深入了解这个问题。提前感谢任何花时间解决此问题的人。

【问题讨论】:

  • 在我看来,JSF 不太适合单页应用程序。如果您真的想要一个 SPA,那么众多前端框架(Angular、React、Vue 等数百个)中的一个很可能是更好的选择。 JSF 是在 SPA 还没有真正成为事物的时候发明的。所以这可能是可能的,但这并不是 JSF 的真正设计目的,这意味着这并不容易。
  • @Jesper 这就是我害怕的。不幸的是,我不得不在 JSF 中这样做。首先是因为这是我的任务,其次是因为 Java 是我唯一合理知道的语言。我刚刚开始学习 JavaScript,现在学习框架对我来说还有很长的路要走。
  • JSF 可用于 SPA 和 afaics,您采用的方法是正确的。看不到导致您所经历的事情的原因,但我也无法运行它。你能减少代码并使其成为真正的minimal reproducible example。然后我很乐意进一步调查。哦,请发布 jsf 版本和实现
  • 题外话:如果您从头开始,我建议使用 cdi 托管 bean (@Named) 而不是已弃用的 @ManagedBean
  • @Kukeltje 谢谢你的建议。你能告诉我你所说的实施是什么意思吗?

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


【解决方案1】:

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

非 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)以保持在原来的页面上,但它不会更新任何内容,实际上保持在原来的页面上。
  • 您可以返回一个空字符串,这将使您停留在同一页面上,但会刷新整个页面。但是如果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>

你会得到你想要的行为。对其他页面/控制器也这样做。

【讨论】:

  • 哇哦!非常感谢您花时间做这一切。我真的很重要。我想问你一些非常离题的问题。我正在寻找一份工作,这是继续使用 JSF 的明智途径,还是我应该开始学习 Spring 和 Angular/React?我对 JSF 了解得越多,我就越觉得它不再被使用,人们也不喜欢它。我需要从头开始学习 JS,但在这一点上,它比坚持没有人寻找的东西要好。
  • @Des 对于一个没人再使用的框架,你得到了很多答案,而且很快;)
  • @MitchBroadhead 是的,我是一只快乐的熊猫 :D,除了“没人再使用的框架”的可怕部分。我这样做是为了找到一份工作,我担心我被引导到了一条次优的道路上。我正在考虑重新开始,但我不确定路径。一方面,由于我对 Java 有一定的了解,所以我想使用 Spring 和 Hibernate。但我一直很沮丧,因为我不懂 JavaScript。你有什么建议?
  • Angular 将被另一个不向后兼容的版本取代的可怕部分?或者那个反应会不如淘汰赛6或者?对于严肃的业务应用程序,JSF 仍然是伟大和现代的(html5、css3+、ajax、'SPA'(炒作!)。JSF 应该与其中一个 javascript 框架 + A + B + C + 进行比较,因为它不仅仅是前端('OWASP' 之类的东西是内置的),例如 AngularFaces,你可以两全其美。但是基于 jquery 的 PrimeFaces 组件框架有很多组件和很多认真的用户。
  • 查看这里(或互联网上任何地方)的新问题数量并不能很好地表明用户数量。也可能是框架不清楚、不稳定、不... 较少的新问题也可能表明稳定性,因为所有问题都已得到解答(在 JSF 中有许多“重复”)。这不是一个建议,因为我有偏见,但许多“流行”博客网站通过发布引起混乱或兴奋的东西来赚钱,这并不总是比较事物的最佳理由。
猜你喜欢
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-28
  • 2012-06-01
  • 2015-11-08
  • 1970-01-01
相关资源
最近更新 更多