JavaServer Faces 1.2 入门,第 1 部分: 构建基本应用程序(2)

本文介绍如何使用JSF技术改进Calculator应用的外观和用户体验,包括使用CSS、国际化消息及简化错误提示。此外,还介绍了如何通过依赖注入和控制器类增强应用的可维护性和可测试性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

改进 Calculator 示例

在本节中,将用 JSF 技术改进 Calculator 应用程序的外观并简化它。您将学习如何使用 CSS、设置国际化(I18N)消息和以其他方式改进应用程序的外观和感觉。还要改进默认的错误消息,以便于用户理解。

使用面板单元格

在前一节中,使用了大量 HTML 控制页面布局。可以使用 HTML 代码精确地控制页面的布局。但是,Web 应用程序的布局可能并不太重要。为了简化 Calculator 应用程序的 GUI,现在使用一个 <h:panelGrid> 在一个单元格中布置元素。

清单 12 演示如何修改输入表单代码来使用 <h:panelGrid>


清单 12. 修改表单来使用 <h:panelGrid>

< h:form id ="calcForm" >
< h4 > Calculator </ h4 >
< h:panelGrid columns ="3" >
<% ... --FirstNumber-- %>
< h:outputLabel value ="FirstNumber" for ="firstNumber" />
< h:inputText id ="firstNumber"
value
="#{calculator.firstNumber}" required ="true" />
< h:message for ="firstNumber" />
<% ... --SecondNumber-- %>
< h:outputLabel value ="SecondNumber" for ="secondNumber" />
< h:inputText id ="secondNumber"
value
="#{calculator.secondNumber}" required ="true" />
< h:message for ="secondNumber" />
</ h:panelGrid >

清单 13 给出对结果部分的修改:


清单 13. 修改结果部分

< h:panelGroup rendered ="#{calculator.result!=0}" >
< h4 > Results </ h4 >
< h:panelGrid columns ="1" >
< h:outputText value ="FirstNumber#{calculator.firstNumber}" />
< h:outputText value ="SecondNumber#{calculator.secondNumber}" />
< h:outputText value ="Result#{calculator.result}" />
</ h:panelGrid >
</ h:panelGroup >

这就减少了大约 20 行代码(大约三分之一),使代码更容易阅读了。而且,现在这个应用程序的 JSF “味道” 更强了,有些人(例如大多数开发人员)喜欢这样,有些人(Web 设计人员)不喜欢。您应该根据自己项目的需要权衡考虑应该怎么做。项目的情况各不相同,所以要判断是需要绝对控制(纯 HTML 布局),还是需要容易维护的应用程序(更具 JSF 风格)。

这里有一个需要注意的问题。<h:panelGrid> 只能包含组件,而 <h:form><f:view><h:panelGroup> 可以包含 HTML 和组件。在这个表单中,第一个 <h:panelGrid> 有三列,当添加一个额外组件时,它会转入下一行。第二个 <h:panelGrid> 只包含一列,所以每个组件都会添加到下一行中。<h:panelGrid> 显示一个表格,所以输出几乎与以前相同。(对于用户,确实是相同的。)再次重申:不能在 <h:panelGrid> 中添加 HTML。它不会按照您的期望显示 HTML。它只接受组件。

用 CSS 修饰 GUI

如果您了解 HTML 和 CSS,就知道如何改进 Calculator 应用程序第一个版本的外观和感觉。<h:panelGrid> 也允许使用 CSS。可以导入一个 CSS 样式表,然后将它用于 <h:panelGrid>。我们要让 <h:panelGrid> 显示一个边框以及交替的银色和白色行。

首先导入样式表,见清单 14:


清单 14. 导入样式表

< head >
< title > CalculatorApplication </ title >
< link rel ="stylesheet" type ="text/css"
href
="<%=request.getContextPath()%>/css/main.css" />
</ head >

清单 15 是样式表:


清单 15. CSS 样式表

oddRow{
background-color:white;
}

evenRow{
background-color:silver;
}

formGrid{
border:solid#0003px;
width:400px;
}

resultGrid{
border:solid#0001px;
width:200px;
}

清单 15 定义了 oddRowevenRow 样式,让奇数行显示为白色,偶数行显示为银色。

现在将这些样式应用于 panelGrid。清单 16 将它们添加到表单 panelGrid


清单 16. 在表单 panelGrid 中使用样式类

< h:panelGrid columns ="3" rowClasses ="oddRow,evenRow"
styleClass
="formGrid" >
...
</ h:panelGrid >

清单 16 通过设置 rowClasses="oddRow, evenRow" 属性,将 oddRowevenRow 样式应用于表单。styleClass="formGrid" 会在表格周围显示边框。结果 <h:panelGrid> 是相似的,它显示比较小的边框,见清单 17:


清单 17. 在结果 panelGrid 中使用样式类

< h:panelGrid columns ="1" rowClasses ="oddRow,evenRow"
styleClass
="resultGrid" >
...
</ h:panelGrid >

图 6 显示 Calculator 应用程序现在的外观:


图 6. 使用一些样式之后的 Calculator
使用一些样式之后的 Calculator

我只触及了 <panelGrid> 支持的样式的皮毛。更多信息请参见 参考资料 中的标记库 API 链接。

改进错误消息

如果您的用户是技术专家,那么这些错误消息倒是很合适。否则,用户很难理解它们的意思。可以以几种方式改进它们。可以先添加一个标签,见清单 18:


清单 18. 添加一个标签

< h:inputText id ="firstNumber" label ="FirstNumber" ... />
...
< h:inputText id ="secondNumber" label ="SecondNumber" ... />
...

注意,在 h:inputText 字段中使用了 label="First Number" 属性。现在看到的错误文本像图 7 这样:


图 7. 带标签的消息
带标签的消息

标签名不再是属性名,对用户更友好了。但是,既然错误消息总是出现在字段旁边,那么可能根本不需要标签。另外,错误消息非常长。可以用清单 19 中的代码缩短它们:


清单 19. 显示简短的消息而不是细节

< h:outputLabel value ="FirstNumber" for ="firstNumber" />
< h:inputText id ="firstNumber" label ="FirstNumber"
value
="#{calculator.firstNumber}" required ="true" />
< h:message for ="firstNumber" showSummary ="true" showDetail ="false" />

注意,清单 19 将 h:message 组件的 showSummaryshowDetail 属性设置为 showSummary="true" showDetail="false"。对于转换和必需字段 firstNumbersecondNumber,这会产生 “First Number: 'aaa' must be a number consisting of one or more digits.” 和 “Second Number: Validation Error: Value is required.” 这样的消息。但是,这仍然不够好。下面讨论一种更好的替代方法。

覆盖消息文本

JSF 1.2 添加了 requiredMessageconversionMessage,所以我们可以根据不同的情况覆盖消息,见清单 20:


清单 20. 使用 requiredMessageconverterMessge 覆盖消息

<% ... --FirstNumber-- %>
< h:outputLabel value ="FirstNumber" for ="firstNumber" />
< h:inputText id ="firstNumber" label ="FirstNumber"
value
="#{calculator.firstNumber}" required ="true"
requiredMessage
="required" converterMessage ="notavalidnumber"
/>
< h:message for ="firstNumber" />
<% ... --SecondNumber-- %>
< h:outputLabel value ="SecondNumber" for ="secondNumber" />
< h:inputText id ="secondNumber" label ="SecondNumber"
value
="#{calculator.secondNumber}" required ="true"
requiredMessage
="required" converterMessage ="notavalidnumber"
/>
< h:message for ="secondNumber" />

注意,清单 20 中的 h:inputText 添加了 requiredMessage="required" converterMessage="not a valid number"。现在看起来不错了,而且消息在 <h:panelGrid> 的上下文中是有意义的:它们出现在字段的旁边,所以用户知道它们应用于哪个上下文(见图 8):


图 8. 更短的消息
更短的消息

这种方法的问题是,需要在每个 inputText 字段中添加 requiredMessageconverterMessage。对于这个简单的示例,这倒不是问题。但是对于真正的应用程序,就会在维护方面造成大问题,肯定会破坏 DRY(don't repeat yourself)原则。

以全局方式修改消息

为了以全局方式修改消息,需要在 faces-config.xml 文件中定义一个资源束并用它重新定义默认的消息,见清单 21:


清单 21. 在 faces-config.xml 中配置消息

<? xmlversion="1.0"encoding="UTF-8" ?>

< faces-config xmlns ="http://java.sun.com/xml/ns/javaee"
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"

version
="1.2" >

< application >
< message-bundle > messages </ message-bundle >
</ application >
...

message.properties 文件包含清单 22 所示的条目:


清单 22. 消息资源束(messages.properties)

javax.faces.component.UIInput.REQUIRED_detail=required
javax.faces.converter.IntegerConverter.INTEGER_detail=notavalidnumber

现在已经以全局方式修改了必需字段检验或整数转换失败时显示的消息。

注意:如果使用 Eclipse JEE,那么一定要将 src/main/resources/messages.properties 添加为源文件夹。

本节中所做的改进增加了应用程序的 GUI 逻辑,所以在下一节中将添加一个 CalculatorController 类,它会注入 Calculator 类。

添加控制器

现在要重构这个应用程序,不要把纯 Java Calculator 对象绑定到 JSF,以避免 JSF 和 POJO 之间有紧密联系。这需要创建一个控制器类并把纯模型对象注入这个控制器类。这个控制器类将能够感知 JSF,但是模型类不了解 JSF 的任何情况。

本节讨论:

  • 使用 JSF 的依赖性注入容器
  • 处理 JSF facesContext
  • 添加 FacesMessage
  • 使用 h:messages
  • 将组件绑定到控制器

下面依次执行每个步骤。然后,我会回过头来详细解释每个步骤。

Calculator 中添加一个 divide() 方法

首先,在 Calculator 中添加一个 divide() 方法(见清单 23),以便从 “被零除” 异常中恢复并添加一个 FacesMessage 向用户显示消息:


清单 23. 在 Calculator POJO 中添加一个 divide() 方法

package com.arcmind.jsfquickstart.model;

/***/ /**
*Calculator.SimplePOJO.
*
*
@authorRickHightower
*/

public class Calculator ... {

/***//**Firstnumberusedinoperation.*/
privateintfirstNumber=0;

/***//**Resultofoperationonfirstnumberandsecondnumber.*/
privateintresult=0;

...

/***//**Dividethetwonumbers.*/
publicvoiddivide()...{
this.result=this.firstNumber/this.secondNumber;
}


/***//**Cleartheresults.*/
publicvoidclear()...{
result
=0;
}


...

}

创建控制器类

接下来,添加一个称为 CalculatorController 的新类,它接收 Calculator POJO。

有三个 JSF 组件绑定到 CalculatorController。它能够感知 JSF。它还通过将 FacesMessages 放在 FacesContext 中,从异常中恢复。

绑定到 CalculatorController 的三个 JSF 组件是:

  • resultsPanel,这是一个 UIPanel
  • firstNumberInput,这是一个 UIInput
  • secondNumberInput,这是一个 UInput

图 9 显示 Calculator 应用程序如何处理错误:


图 9. “被零除” 异常
应用程序示例

图 10 显示应用程序如何报告状态:


图 10. 显示状态消息的结果
显示状态消息的结果

CalculatorController(见清单 24)避免了 JSF 相关代码混入 POJO 中。它能够感知 JSF,与组件绑定,并将错误和状态消息放到 facesContext 中。


清单 24. 感知 JSF 的 CalculatorController

package com.arcmind.jsfquickstart.controller;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIInput;
import javax.faces.component.UIPanel;
import javax.faces.context.FacesContext;
import com.arcmind.jsfquickstart.model.Calculator;

public class CalculatorController ... {

privateCalculatorcalculator;
privateUIPanelresultsPanel;
privateUIInputfirstNumberInput;
privateUIInputsecondNumberInput;

publicStringadd()...{
FacesContextfacesContext
=FacesContext.getCurrentInstance();

try...{
calculator.add();
resultsPanel.setRendered(
true);
facesContext.addMessage(
null,newFacesMessage(
FacesMessage.SEVERITY_INFO,
"Addedsuccessfully",null));

}
catch(Exceptionex)...{
resultsPanel.setRendered(
false);
facesContext.addMessage(
null,
newFacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),null));
}

returnnull;
}


publicStringmultiply()...{
FacesContextfacesContext
=FacesContext.getCurrentInstance();

try...{
calculator.multiply();
resultsPanel.setRendered(
true);
facesContext.addMessage(
null,newFacesMessage(
FacesMessage.SEVERITY_INFO,
"Multipliedsuccessfully",null));

}
catch(Exceptionex)...{
resultsPanel.setRendered(
false);
facesContext.addMessage(
null,
newFacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),null));
}

returnnull;
}


publicStringdivide()...{
FacesContextfacesContext
=FacesContext.getCurrentInstance();

try...{
calculator.divide();
resultsPanel.setRendered(
true);
facesContext.addMessage(
null,newFacesMessage(
FacesMessage.SEVERITY_INFO,
"Dividedsuccessfully",null));

}
catch(Exceptionex)...{
resultsPanel.setRendered(
false);
if(exinstanceofArithmeticException)...{
secondNumberInput.setValue(Integer.valueOf(
1));
}

facesContext.addMessage(
null,
newFacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),null));
}

returnnull;
}


publicStringclear()...{
FacesContextfacesContext
=FacesContext.getCurrentInstance();

try...{
calculator.clear();
resultsPanel.setRendered(
false);
facesContext.addMessage(
null,newFacesMessage(
FacesMessage.SEVERITY_INFO,
"Resultscleared",null));

}
catch(Exceptionex)...{
resultsPanel.setRendered(
false);
facesContext.addMessage(
null,
newFacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),null));
}

returnnull;
}


publicStringgetFirstNumberStyleClass()...{
if(firstNumberInput.isValid())...{
return"labelClass";
}
else...{
return"errorClass";
}

}

//removesimpleprops

更新 calculator.jsp

接下来,更新 calculator.jsp(见清单 25),让它显示错误消息并绑定到 calculatorController,而不是直接绑定到 Calculator POJO:


清单 25. 更新后的 calculator.jsp

<? xmlversion="1.0"encoding="ISO-8859-1" ?>
<% ... @tagliburi="http://java.sun.com/jsf/html"prefix="h" %>
<% ... @tagliburi="http://java.sun.com/jsf/core"prefix="f" %>

<! DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
< title > CalculatorApplication </ title >
< link rel ="stylesheet" type ="text/css"
href
="<%=request.getContextPath()%>/css/main.css" />
</ head >
< body >
< f:view >
< h:form id ="calcForm" >
< h4 > Calculator3 </ h4 >
< h:messages infoClass ="infoClass" errorClass ="errorClass"
layout
="table" globalOnly ="true" />
< h:panelGrid columns ="3" rowClasses ="oddRow,evenRow"
styleClass
="formGrid" >
<% ... --FirstNumber-- %>
< h:outputLabel value ="FirstNumber" for ="firstNumber"
styleClass
="#{calculatorController.firstNumberStyleClass}" />
< h:inputText id ="firstNumber" label ="FirstNumber"
value
="#{calculatorController.calculator.firstNumber}" required ="true"
binding
="#{calculatorController.firstNumberInput}" />
< h:message for ="firstNumber" errorClass ="errorClass" />

<% ... --SecondNumber-- %>
< h:outputLabel id ="snl" value ="SecondNumber" for ="secondNumber"
styleClass
="#{calculatorController.secondNumberStyleClass}" />
< h:inputText id ="secondNumber" label ="SecondNumber"
value
="#{calculatorController.calculator.secondNumber}" required ="true"
binding
="#{calculatorController.secondNumberInput}" />
< h:message for ="secondNumber" errorClass ="errorClass" />
</ h:panelGrid >
< div >
< h:commandButton action ="#{calculatorController.add}" value ="Add" />
< h:commandButton action ="#{calculatorController.multiply}" value ="Multiply" />
< h:commandButton action ="#{calculatorController.divide}" value ="Divide" />
< h:commandButton action ="#{calculatorController.clear}" value ="Clear"
immediate
="true" />
</ div >
</ h:form >


< h:panelGroup binding ="#{calculatorController.resultsPanel}" rendered ="false" >
< h4 > Results </ h4 >
< h:panelGrid columns ="1" rowClasses ="oddRow,evenRow"
styleClass
="resultGrid" >
< h:outputText value ="FirstNumber#{calculatorController.calculator.firstNumber}" />
< h:outputText value ="SecondNumber#{calculatorController.calculator.secondNumber}" />
< h:outputText value ="Result#{calculatorController.calculator.result}" />
</ h:panelGrid >
</ h:panelGroup >
</ f:view >

</ body >
</ html >

在 faces-config.xml 中映射控制器

接下来,需要在 faces-config.xml 中映射新的控制器并在其中注入 calculator,见清单 26:


清单 26. 更新后的 faces-config.xml


<? xmlversion="1.0"encoding="UTF-8" ?>

< faces-config xmlns ="http://java.sun.com/xml/ns/javaee"
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"

version
="1.2" >

< application >
< message-bundle > messages </ message-bundle >
</ application >

< managed-bean >
< managed-bean-name > calculatorController </ managed-bean-name >
< managed-bean-class >
com.arcmind.jsfquickstart.controller.CalculatorController
</ managed-bean-class >
< managed-bean-scope > request </ managed-bean-scope >
< managed-property >
< property-name > calculator </ property-name >
< value > #{calculator} </ value >
</ managed-property >
</ managed-bean >
< managed-bean >
< managed-bean-name > calculator </ managed-bean-name >
< managed-bean-class >
com.arcmind.jsfquickstart.model.Calculator
</ managed-bean-class >
< managed-bean-scope > none </ managed-bean-scope >
</ managed-bean >

</ faces-config >

既然已经修改了整个应用程序,现在就讨论一下细节。

用 JSF 进行依赖性注入

JSF 支持依赖性注入。可以将 bean 注入其他 bean 的属性。因为要将 calculator bean 注入 calculatorController,所以可以把它放到 none 范围中。none 意味着在创建它时不把它放到范围中。清单 27 给出 faces-config.xml 的部分代码,这些代码注入托管 calculator bean,并使用 none 范围:


清单 27. 托管的 calculatornone 范围

< managed-bean >
< managed-bean-name > calculator </ managed-bean-name >
< managed-bean-class >
com.arcmind.jsfquickstart.model.Calculator
</ managed-bean-class >
< managed-bean-scope > none </ managed-bean-scope >
</ managed-bean >

calculatorControllerrequest 范围下映射。将 calculator 注入 calculatorController 的方法是使用 <managed-property> 并传递表达式 #{calculator}。这会创建一个 Calculator 对象并使用 CalculatorControllersetCalculator 方法把它注入 CalculatorController,见清单 28:


清单 28. 托管的 calculatorControllerrequest 范围,用 managed-property 注入

< managed-bean >
< managed-bean-name > calculatorController </ managed-bean-name >
< managed-bean-class >
com.arcmind.jsfquickstart.controller.CalculatorController
</ managed-bean-class >
< managed-bean-scope > request </ managed-bean-scope >
< managed-property >
< property-name > calculator </ property-name >
< value > #{calculator} </ value >
</ managed-property >
</ managed-bean >

CalculatorController 要使用 calculator,所以注入了 calculator。这样就可以使用 calculator 并让它与 JSF 相互隔离,这是良好的模型对象应该具备的性质。JSF 相关代码只出现在 CalculatorController 中。这种良好的关注点隔离会使代码的可测试性和可重用性更好。

CalculatorController 的 JSF 绑定组件

根据设计,CalculatorController 了解 JSF 的许多情况。CalculatorController 绑定三个 JSF 组件,其中之一是 resultsPanel,它代表显示计算器结果的面板,见清单 29:


清单 29. CalculatorControllerresultsPanel

private UIPanelresultsPanel;

...

public UIPanelgetResultsPanel() ... {
returnresultsPanel;
}


public void setResultsPanel(UIPanelresultPanel) ... {
this.resultsPanel=resultPanel;
}

resultsPanel 通过 JSF 绑定到 CalculatorController,见清单 30 中的 binding 属性:


清单 30. 把组件绑定到控制器

< h:panelGroup binding ="#{calculatorController.resultsPanel}" rendered ="false" >
< h4 > Results </ h4 >
< h:panelGrid columns ="1" rowClasses ="oddRow,evenRow"
styleClass
="resultGrid" >
< h:outputText value ="FirstNumber#{calculatorController.calculator.firstNumber}" />
< h:outputText value ="SecondNumber#{calculatorController.calculator.secondNumber}" />
< h:outputText value ="Result#{calculatorController.calculator.result}" />
</ h:panelGrid >
/h:panelGroup>

在清单 30 中,binding="#{calculatorController.resultsPanel}" 通过绑定关联 resultsPanel 组件。实际上,JSF 会看到这个表达式,在装载页面时,它通过调用 calculateController.setResultsPanel 方法注入 resultsPanel 组件。这个方便的机制让我们能够以程序方式操作组件的状态,不需要在组件树中移动。

实际上,JSF 所做的操作是调用 calculateController.getResultsPanel。如果这个调用返回一个组件,JSF 视图就会使用这个组件。如果 calculateController.getResultsPanel 返回 null,JSF 就会创建 resultPanel 组件,然后用基于绑定表达式的新组件调用 calculateController.setResultPanel

清单 31 演示 CalculateControlleradd() 方法如何使用这种技术:


清单 31. CalculateControlleradd() 方法,关闭 resultsPanel

public Stringadd() ... {

...

try...{
calculator.add();
resultsPanel.setRendered(
true);
...

}
catch(Exceptionex)...{
...
resultsPanel.setRendered(
false);
}

returnnull;
}

在清单 31 中,如果对 calculator.add 方法的调用成功,CalculateController.add 方法会调用 resultsPanel.setRendered(true),这会打开结果面板。如果调用失败,那么 CalculateController.add 调用 resultsPanel.setRendered(false),这个面板不再显示。

现在稍微停一下。请注意:因为 JSF 是一个组件模型,而且组件是有状态的,所以组件会记住它们的状态。不需要像前面的 Calculator 示例那样包含逻辑。告诉组件不要显示其本身,它就不会再显示了。如果告诉组件禁用其本身,那么只要视图是活动的,每次装载视图时这个组件都会被禁用。与 Model 2 框架相比,JSF 更接近传统的 GUI 应用程序。需要编写的代码更少,就可以更快地开发 Web 应用程序。JSF 使Web 应用程序 真正体现了应用程序 的思想。

需要 Ajax 支持吗?不必担心!可以进行部分页面显示

如果使用 JBoss Ajax4Jsf 等框架(参见 参考资料),那么只需对 JSF Calculator 应用程序做很少几处修改,就可以支持部分页面显示。(JSF 2.0 将提供相同的支持。目前,可以对 JSF 1.1 和 JSF 1.2 使用 Ajax4JSF。)从用户的角度来看,应用程序看起来像 applet 或 Flex 应用程序。不需要编写任何 JavaScript!

CalculatorController 处理消息

JSF 提供一种向用户显示状态消息的机制。CalculateController 使用 FacesContext 将消息添加到 FacesContext 中,这样就可以用 <h:messages> 标记向用户显示这些消息。

JSF 在 ThreadLocal 变量中存储一个 FacesContext,可以通过调用 FacesContext.getCurrentInstance() 方法访问它。add() 方法使用当前的 FacesContext 添加消息,这些消息可供当前请求使用,见清单 32:


清单 32. CalculateControlleradd() 方法添加 JSF 消息

public Stringadd() ... {

FacesContextfacesContext
=FacesContext.getCurrentInstance();

try...{
calculator.add();
facesContext.addMessage(
null,newFacesMessage(
FacesMessage.SEVERITY_INFO,
"Addedsuccessfully",null));
...

}
catch(Exceptionex)...{
facesContext.addMessage(
null,
newFacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),null));
//Logtheexceptionaswell.
...
}

returnnull;
}

在清单 32 中,如果添加操作成功,就在 facesContext 中添加一个严重性级别为 INFOFacesMessage;如果添加操作抛出异常,就添加一个严重性级别为 ERRORFacesMessage

<h:messages> 标记向用户显示消息,见清单 33:


清单 33. 向最终用户显示错误和状态消息

< h:messages infoClass ="infoClass" errorClass ="errorClass"
layout
="table" globalOnly ="true" />

如果将 globalOnly 属性设置为 true,就只显示不与特定组件连接的消息,比如在清单 32 中添加的消息。注意,状态消息和错误消息使用不同的样式。

CalculatorController 纠正 “被零除” 异常

因为我们正在使用一个组件模型,所以可以根据显示逻辑修改组件的值并进行初始化。当新的除法方法抛出 “被零除” 异常时,可以通过将 secondNumberInput 值设置为 1 来恢复。

首先,需要将 secondNumberInput 绑定到 CalculatorController 类,见清单 34:


清单 34. 绑定输入组件:binding="#{calculatorController.resultsPanel}"

< h:inputText id ="secondNumber" label ="SecondNumber"
value
="#{calculatorController.calculator.secondNumber}" required ="true"
binding
="#{calculatorController.secondNumberInput}" />

接下来,使用 secondNumberInput 组件。如果遇到 “被零除” 异常,就将 secondNumberInput 值设置为 1,见清单 35:


清单 35. 新的 divide() 方法

public Stringdivide() ... {

FacesContextfacesContext
=FacesContext.getCurrentInstance();

try...{
calculator.divide();
facesContext.addMessage(
null,newFacesMessage(
FacesMessage.SEVERITY_INFO,
"Dividedsuccessfully",null));
resultsPanel.setRendered(
true);

}
catch(Exceptionex)...{
if(exinstanceofArithmeticException)...{
secondNumberInput.setValue(Integer.valueOf(
1));
}

facesContext.addMessage(
null,
newFacesMessage(FacesMessage.SEVERITY_ERROR,ex.getMessage(),null));
}

returnnull;
}

一定要认识到 JSF 更接近传统的 GUI 组件模型,而不是 Model 2 的特殊版本。如果您一直牢记 JSF 是一个组件模型,就会发现许多可能性。在清单 35 中,可以设置 secondNumberInput 的值,这是因为它是一个对象,而不是 JSP 中的 HTML 代码。您可以操作它,它会记住它的值。它是有状态的。

处理属性

大多数 JSF 属性接受表达式,所以如果在发生错误时希望将字段标签变成红色的,那么很容易实现,见清单 36:


清单 36. 将标签变成红色

<% ... --FirstNumber-- %>
< h:outputLabel value ="FirstNumber" for ="firstNumber"
styleClass
="#{calculatorController.firstNumberStyleClass}" />
...

注意,styleClass 属性被设置为表达式 #{calculatorController.firstNumberStyleClass},这与清单 37 中的方法绑定:


清单 37. 如果发生错误,就返回红色的样式类

public StringgetFirstNumberStyleClass() ... {
if(firstNumberInput.isValid())...{
return"labelClass";
}
else...{
return"errorClass";
}

}
清单 37 检查 firstNumbedInput 组件的输入是否有效,然后根据检查的结果修改返回的 styleClass
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值