设计和实现 Web 应用程序界面

设计优秀的Web应用程序界面是一项挑战。本文探讨了Web界面设计的关键因素,并提出选择合适技术的建议,重点关注JavaServer Faces(JSF)在定制错误消息方面的应用。

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

注:本文转自网络 作者不明

设计好的用户界面从来都不是一件容易的事情,而设计 Web 应用程序界面尤其富有挑战性。在本文中,我首先概括地谈一谈关于 web 应用程序界面的设计考虑因素以及关于如何选择合适的实现技术。然后我将深入介绍使用 JavaServer Faces 的 web 应用程序界面的一个方面,特别是如何定制标准的错误消息。

 

设计考虑因素

为了最终获得实用且易用的 web 应用程序界面,需要正确处理许多事情:

  • 导航 。因为 web 应用程序在浏览器中运行,因此用户希望能够使用浏览器的按钮进行导航(例如,使用 Back 按钮返回上一页面)。然而,正确处理这种类型的导航非常棘手,因此设计鼓励用户使用其他方式进行导航的界面非常重要。建议将用户界面设计得看起来尽可能像 传统的图形用户界面 (GUI) — 使用通用 GUI 小部件,如用于选择选项的树、显示所选项不同方面的选项卡以及顶部的菜单栏链接。此外,一定要记住 web 应用程序是由面向任务的页面组成的,这些页面必须以特定的顺序进入,而不像传统 web 站点中那样包含一组形式自由的链接。必须只允许用户直接跳转到特定页面,例如不同任务的主页面。
  • 书签 。书签与导航相关,并且也可能难以在 web 应用程序中支持;例如,您不希望用户收藏仅是提交表单的结果的页面。虽然我不喜欢普通 Web 站点上的 HTML 框架,但框架在应用程序中可能非常有用,因为它们可以防止用户收藏个别页面。
  • Web 应用程序限制 。 让我们来面对这种情况:web 应用程序永远不能拥有和传统的 GUI 应用程序一样的交互性,至少利用当前的浏览器技术无法做到。在 GUI 中,可以容易地让用户选中表中的多行,然后单击一次全部删除它们。相反,在 web 应用程序中,您必须以不同的方式来处理这种情况,例如在每一行放一个复选框,用户可以选中它来选择一行。传统的 GUI 还能轻松地根据用户输入来动态启用或禁用输入小部件,例如当单击了一个单选钮或复选框时。在 web 应用程序中实现动态界面组件一般需要 JavaScript 代码,因此在浏览器中禁用了 JavaScript 的用户将无法使用这种应用程序。除非您对您的用户群有完全的控制权,否则您应当以其他方式将不同选项提供给用户(例如,将单选钮与按钮组合来激活新的选 项,或使用链接来激活选项)。
  • 页面大小 。尽管人们习惯滚动页面来在线阅读完整的文章,但当与每个任务相关的所有信息都包含在一个易读的页面中,或者如果仅在页面的一部分中包含滚动(利用内部框架的帮助)或通过将 Next/Previous 按钮用于大型表,用户将发现 Web 应用程序更易于使用。

 

“尽早验证用户界面是否有意义的一种很好的方式是使用模型和用户指南草稿。”

以我的经验,尽早验证用户界面是否有意义的一种很好的方式是使用模型和用户指南草稿。在编写代码之 前,我会创建纯 HTML 页面,并用它们制作用户指南早期版本中的屏幕截图,然后我请同事(或者在理想情况下请最终用户)来评阅这种早期版本。如果您以前没有试过这种策略,那么您 将吃惊地发现它不仅能够有效地揭示纯界面设计问题,还将揭示被错误理解的需求、缺少的功能和许多其他问题。

实现考虑因素

一旦您对用户界面设计感到满意,就必须决定如何实现该用户界面。使用 Java,您有许多选择。

如果您的应用程序需要高度交互的界面,那么您可能希望开发丰富 GUI 应用程序,而不是 Web 应用程序。得益于 Sun 的 Java Web Start,部署和维护功能全面的 GUI 应用程序几乎与部署和维护 web 应用程序一样容易。然而,许多互联网用户仍然认为 Java Web Start 的入门门槛太高,因此 web 应用程序无疑仍有一席之地。

直到目前为止,大多数 Java web 应用程序是使用 JavaServer Pages (JSP) 或类似的开放源代码框架(例如 Apache Velocity)实现的。这些技术是那些用户输入非常有限、动态创建内容的 web 站点的理想选择。

然而,对于具有非常复杂的用户交互的用户界面,类似这些的模板页面技术就开始显露它们的局限了。例 如,即使用复选标记来显示一组复选框以表示当前选择的简单动作都需要在模板页面自身中使用大量的条件逻辑 — 无论用 Java scriptlet、JSP 标准标记库 (JSTL) 操作和表达式语言 (EL) 或 Velocity 模板语言 (VTL) 来实现均是如此。然而,在许多情况下,JSP 或类似的技术却是正确的选择。

2004 年 3 月发布的 JavaServer Faces (JSF) 规范对于复杂的 web 应用程序界面是一种更好的解决方案。JSF 定义了一种基于组件的 web 应用程序开发模型,使供应商和开放源代码项目能够创建复杂的用户界面小部件,开发人员随后即用这些小部件创建易于使用的、在工具和应用服务器之间可移植的 web 应用程序。(Oracle 是 JSF 规范的一个积极贡献者,并通过提供基于该规范的产品(例如 ADF Faces 组件套件的早期试用版)来继续创新。此外,Oracle 承诺在 Oracle JDeveloper IDE 的一个即将推出的版本中提供全面的 JSF 支持。)

利用 JSF 组件模型,所有的逻辑(例如选中复选框来表示当前的选择所需的条件代码)都用组件来实现,而不是用页面内部的代码来实现。JSF 事件模型鼓励将组件和动作分离,并使您能够以类似开发 GUI 应用程序的方式开发 web 应用程序界面。ADF Faces 组件套件和许多其他组件套件(开发源代码和商业套件)为您提供了您所需的大部分组件,但如果您无法找到非常适合您应用程序的组件,那么您可以通过扩展 JSF 类和实现 JSF 接口来实现您自己的组件。

如果您决定使用 JSF,那么您应当了解这些并不那么显而易见的问题:

  • 命令按钮或命令链接 。JSF 提供提交表单的两个标准组件 — 一个命令按钮和一个命令链接。命令链接使用 JavaScript 代码提交表单,因此如果您不能确保您的所有用户将始终启用 JavaScript,那么您应当坚持使用命令按钮而非链接。
  • 使用页面片断 。对于复杂的用户界面,您应当为各个部分使用单独的文件并用一个主文件将它们组合起来。这种方法使开发和维护应用程序变得更容易,还令您可以在多个页面中重用相同的部分。例如,如果您使用 JSP 开发 JSF 应用程序,那么您可以使用动态的 <jsp:include><c:import> 操作,或者静态的 <%@ include %> 指令来创建主文件,以获得这些片断的内容。(建议您尽可能使用静态 include 以避免产生与使用动态 include 相关的额外需求和问题。)
  • 是否使用 JSP 。 虽然 JSP 是用该规范中描述的全部 JSF 组件组建 Web 应用程序界面的唯一技术,但并不是您的唯一选择。JSF API 非常灵活,足以使您(或其他人)使用其他技术(例如纯 XML 文件)。JSP 对许多 web 应用程序开发人员而言非常熟悉,但当和 JSF 一起使用时,它将给您带来一些麻烦。(关于这些问题的更多信息,请参考我的文章“通过转储 JSP 改善 JSF ”(http://www.onjava.com/pub/a/onjava/2004/06/09/jsf.html))。

在简要讨论了在设计和实现 web 应用程序界面时需要考虑的一些事情后,我将深入讨论 web 应用程序界面在使用 JavaServer Faces 时的一个方面,特别是如何定制标准的错误消息。让我们看看通用属性和 PhaseListener 实现可以如何帮助您定制由 JSF 标准转换器和验证器创建的错误消息。

添加有意义的域引用

JSF 定义了许多转换器和验证器,您可以将它们与组件相连以验证用户输入。当输入无效时,它们将错误消息入队,然后消息组件把消息显示给用户。例如,这个 JSP 页面创建了一个需要 1 到 10 之间的一个数字值的输入组件,以及用于提供详细消息和摘要消息的两种消息组件:

 

<%@ page contentType="text/html" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

<f:view>
<h:messages layout="table" showDetail="true" showSummary="false" />
<h:form>
<h:panelGrid columns="3">
<h:outputText value="Number of passengers:"/>
<h:inputText id="noOfPass" size="8" required="true">
<f:convertNumber integerOnly="true" />
<f:validateLongRange minimum="1" maximum="10"/>
</h:inputText>
<h:message for="noOfPass" showDetail="false" showSummary="true"
style="color:red" />

</h:panelGrid>
<h:commandButton value="Submit"/>
</h:form>
</f:view>

 

验证错误消息通常一起显示在页面顶部,但这只有在用户能够轻松地将每一条消息与相应的无效域匹配时 才起作用。标准的 JSF 错误消息仅提供问题的描述,而没有与消息引用的域相关的任何信息 — 正如向上面例子中的验证器提供一个超出范围的值时 Sun 的 JSF 1.0 引用实现 (RI) 所返回的消息那样:

 

Validation error:Specified attribute is not between the expected values of 1 and 10

 

要解决这个问题,我们需要两个东西:一种将用户友好的域引用与组件关联的方式以及一种将这种引用添加到消息文本中的方式。我们来使用一个通用组件属性来指定组件的域引用:

 

  ...
<f:view>
<h:messages layout="table" showDetail="true" showSummary="false" />
<h:form>
<h:panelGrid columns="3">
<h:outputText value="Number of passengers:"/>
<h:inputText id="noOfPass" size="8" required="true">
<f:convertNumber integerOnly="true" />
<f:validateLongRange minimum="1" maximum="10"/>
<f:attribute name="fieldRef" value="Number of passengers" />
</h:inputText>
...
</h:panelGrid>
<h:commandButton value="Submit"/>
</h:form>
</f:view>

 

通用属性是组件的一个命名定制值,对该组件有访问权的其他代码段可以使用这个值。 <f:attribute> 动作元素将一个名为 fieldRef 的通用属性设置为与最终用户看到的输入域的标签匹配的一个值。

接下来我们需要能够获取通用属性并将其插入消息中的东西。最适合于这个任务的是定制的 PhaseListener 。应用程序可以注册一个或多个 PhaseListener 实现:

 

<faces-config>
<lifecycle>
<phase-listener>
com.mycompany.jsf.listeners.MessageListener1
</phase-listener>

</lifecycle>
</faces-config>

 

JSF 在它感兴趣的请求处理生命周期阶段之前或之后调用 PhaseListener 。这个 PhaseListener 实现处理我们的消息定制需求:

 

package com.mycompany.jsf.listeners;

import java.util.Iterator;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

public class MessageListener1 implements PhaseListener {

public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}

public void beforePhase(PhaseEvent e) {

FacesContext fc = e.getFacesContext();
UIViewRoot root = fc.getViewRoot();
Iterator i = fc.getClientIdsWithMessages();
while (i.hasNext()) {
String clientId = (String) i.next();
UIComponent c = root.findComponent(clientId);
String fieldRef =
(String) c.getAttributes().get("fieldRef");

if (value != null)
Iterator j = fc.getMessages(clientId);
while (j.hasNext()) {
FacesMessage fm = (FacesMessage) j.next();
fm.setDetail(fieldRef + ":" + fm.getDetail());
}
}
}
}

public void afterPhase(PhaseEvent e) {
}
}

 

MessageListener1 类从 getPhaseId() 方法返回 PhaseId.RENDER_RESPONSE ,因此 JSF 就在提交响应之前调用它的 beforePhase() 方法。 beforePhase() 方法首先获取所有要为其进行消息入队的组件的客户 ID。然后它借助 findComponent() 方法找到每一个这种组件,并获取分配给 JSP 页面中组件的 fieldRef 属性的值。最后,该方法获得组件的所有消息,并在开头添加对详细消息文本的域引用。当 JSF 继续输出响应时,JSP 页面顶部的 <h:messages> 组件输出修改后的详细消息,包括在图 1 中显示的用户友好的域引用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值