视图是JSF中的核心概念,它允许开发人员使用组件化的方式构建Web用户界面。前面章节我们已经编写过很多视图了,这篇笔记我们继续学习JSF视图的开发。
一个典型的XHTML视图页面通常包含以下内容。
<?xml version="1.0" encoding="UTF-8"?>
<!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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>JSF Demo</title>
</h:head>
<h:body>
</h:body>
</html>
我们这里主要关注引入的XML命名空间,其中h
是JSF的HTML控件标签,f
是JSF的核心标签,ui
则是Facelets标签。核心标签库用于设置AJAX、设置属性监听器等核心功能;HTML标签则对应于输入框、按钮等控件,用于构建Web界面;Facelets标签则包含了模板嵌套、循环渲染逻辑等标签,它使得JSF视图成为一个完整的模板引擎。
JSF提供了大量的HTML控件标签并封装了很多实用的功能,我们这里不能逐一介绍,具体可以参考相关文档。我们这里主要展示一些例子,以帮助大家理解这些标签的基本使用方法。
面板控件类似于布局容器,它们常用于将一些控件分组管理,这样便于控制这一组控件的位置、显示与隐藏等,此外还方便控制内部控件的布局,常用的有<h:panelGrid>
和<h:panelGroup>
等。
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="username" value="用户名"/>
<h:inputText id="username"/>
<h:outputLabel for="password" value="密码"/>
<h:inputSecret id="password"/>
</h:panelGrid>
</h:form>
上面代码我们使用了<h:panelGrid>
,它指定了布局样式为2列,显示效果如下图。
<h:panelGroup rendered="#{demo.renderFlag}">
<h:outputText value="Hello JSF!"/>
<h:outputText value="Hello JSF!"/>
<h:outputText value="Hello JSF!"/>
</h:panelGroup>
上面代码我们使用了<h:panelGroup>
,其rendered
属性用于控制该面板是否显示。
输入控件包括文本输入框、密码输入框、单选框、复选框等,输出控件包括文本输出框、图片输出框等。下面例子展示了文本输出控件和文本框控件。
<h:outputText value="#{demo.text}" escape="true"/>
<h:form>
<h:inputText id="username" value="#{demo.username}"/>
</h:form>
代码中,对于<h:outputText>
我们设置了escape
属性为true
,表示转义特殊字符,这对于防止XSS有重要意义,不过如果是在诸如富文本显示等场景,则可能需要将其设置为false
。此外,我们还设置了value
属性将它和托管Bean中的字段相关联。对于<h:inputText>
文本输出框,其value
属性将它和托管Bean中的字段相关联,我们通常都需要使用<h:form>
表单控件将其包裹,否则JSF会抛出一个警告信息。
实际上,如果不需要太多复杂的设置,仅仅需要展示一些文本,我们更习惯于在XHTML中直接像如下方式编写输出文本:
#{demo.text}
对于日期等需要格式化展示的内容,我们可以使用转换器标签。
<h:outputText value="#{demo.pageTime}">
<f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss" />
</h:outputText>
对于输入控件类是类似的,例如时间类型,我们同样可以使用转换器标签,将其转换为字符串提交。
操作控件用于接收用户的输入或触发特定的操作,这些控件允许用户与应用程序进行交互,例如提交表单数据、执行动作等。典型的操作控件包括按钮、命令链接(类似按钮,但渲染为超链接)等。
<h:form>
<h:panelGrid columns="2">
<h:outputLabel for="username" value="用户名"/>
<h:inputText id="username" value="#{demo.username}"/>
<h:outputLabel for="password" value="密码"/>
<h:inputSecret id="password" value="#{demo.password}"/>
</h:panelGrid>
<h:commandButton value="登录" action="#{demo.login()}" />
<h:commandButton type="reset" value="重置" />
</h:form>
上面代码中我们设置了两个按钮,一个通过触发demo.login()
方法提交表单信息,另一个则用于重置表单。
在各种管理系统中数据表格非常常用,它用于按行列展示数据,JSF对表格做了一些封装,下面是一个例子。
<h:dataTable value="#{student.studentList}" var="s">
<h:column>
<f:facet name="header">姓名</f:facet>
#{s.name}
</h:column>
<h:column>
<f:facet name="header">年级</f:facet>
#{s.grade}
</h:column>
<h:column>
<f:facet name="header">分数</f:facet>
#{s.score}
</h:column>
</h:dataTable>
代码中student.studentList
是包含一组实体类的列表,它绑定到了表格<h:dataTable>
上,<h:column>
则用于定义每一列的显示内容,其中包括表头和具体的数据字段。
实际开发中,表格的处理可能非常复杂,涉及分页、排序、筛选、行或列锁定等诸多功能,原生HTML的表格控件通常是难以满足的,这一般需要使用组件库来实现。
前面我们介绍过,条件渲染通常使用控件标签的rendered
属性控制,循环渲染则需要使用<ui:repeat>
标签。
<ui:repeat value="#{student.studentList}" var="s">
#{s.name} #{s.grade} #{s.score}
</ui:repeat>
我们可以看到,循环渲染的使用方式有些类似数据表格<h:dataTable>
,其中value
属性用于指定循环渲染的数据源,var
属性用于指定循环渲染的变量名。
Facelets提供了一种模板机制,使得我们能够更好的复用视图。Facelets主要包括以下标签:
<ui:insert>
:用于插入由<ui:define>
定义的子视图。<ui:define>
:用于定义子视图。<ui:include>
:类似JSP的<jsp:include>
,用于引入其他视图。<ui:composition>
:用于定义模板页面或一组用于填充模板的子视图。我们这里直接看一个例子。
page.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<head>
<title><ui:insert name="title">Default Title</ui:insert></title>
</head>
<body>
<ui:insert name="content">Default Content</ui:insert>
</body>
</html>
</ui:composition>
template.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
template="/page.xhtml">
<ui:define name="title">Page Title</ui:define>
<ui:define name="content">
<h1>Welcome to My Page</h1>
</ui:define>
</ui:composition>
上面代码中,template.xhtml
中使用了<ui:composition>
和一系列的<ui:define>
定义了一组模板,这些模板被逐一在page.xhtml
中使用<ui:insert>
引入。
视图的CSS、JavaScript、图片等在JSF中被统一视为资源,JSF在此基础上还提供了资源库的概念,这样我们的视图就可以在不同的资源库之间切换,实现换肤效果。我们的资源库需要遵循如下目录结构,resources
文件夹必须放置在webapp
下。
|_ webapp
|_ resources # 包含所有资源库的文件夹
|_ mytheme # 资源库,文件夹名就是资源库名
|_ app.css # 资源库内容
|_ WEB-INF
<h:outputStylesheet library="mytheme" name="app.css" />
代码中,我们使用<h:outputStylesheet>
引入资源库中的CSS文件,library
为资源库名,name
为资源库中的文件名,引入文件时的相对路径起始点是资源库文件夹。类似的,我们也可以使用<h:outputScript>
引入资源库的JavaScript文件。
上面我们介绍的组件都是HTML标准组件,这些标准组件对于实际项目来说是远远不够用的,实际开发中我们通常需要用到组件库。PrimeFaces是当前比较流行的一个JSF组件库,功能强大而且组件极为丰富(媲美Antd),而且它是基于MIT协议开源的。
引入Maven依赖如下。
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>12.0.0</version>
</dependency>
编写测试页面,我们这里需要引入PrimeFaces的XML命名空间。
<?xml version="1.0" encoding="UTF-8"?>
<!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://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
<title>JSF Demo</title>
</h:head>
<h:body>
<p:button value="提交" />
</h:body>
</html>
如果一切正常,我们可以看到一个默认的蓝色按钮。相比于那些搭建工程就繁琐到炸裂的前端项目,JSF的组件库使用实在是太方便了。