我正在学习 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')}"/>
<h:commandLink value="Create user" action="#{navController.setToPage('create-user')}"/>
<h:commandLink value="Reset password" action="#{navController.setToPage('reset-password')}"/>
</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 部分。我将提供您可能需要的任何其他信息,以便深入了解这个问题。预先感谢所有花时间解决此问题的人。
最佳答案
我尝试通过添加 User
和 UsersDataBaseSimulator
来运行您的应用程序,并注意到以下内容:
非 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/