我正在学习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页面。这是创建用户的控制器类:
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部分。我将提供您可能需要的所有其他信息,以便深入了解此问题。预先感谢您抽出宝贵时间解决此问题。
答案 0 :(得分:1)
我尝试通过添加User
和UsersDataBaseSimulator
使您的应用程序运行,并注意到以下内容:
对于菜单部分,您似乎正在执行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,并且不返回真实页面或结果(稍后再介绍)
现在,如果您执行了所有这些操作,您仍然会看到“其他页面在首次导航到它们时不起作用”部分(在RegisterController#register()
方法中设置断点,您将发现它永远不会被调用)。这样做的原因是在Mojara 2.2.x there is a bug中导致此行为,并且与具有多种形式有关(本身就是很好)。当我将其应用于您的项目时,其他页面也开始工作,除了现在也给出了上面导航部分中提到的错误。
如果在您的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>
您将获得所需的行为。只需对其他页面/控制器执行此操作。