系列简介本系列 的三篇文章演示了一个独立的 Web 应用程序,每篇文章都对其添加一些增强。本节将对这个示例应用程序进行简要回顾。 第 1 部分 首先给出一个典型的 JSF 表单 在 第 2 部分 中,您了解了如何将当前 JSF 视图数据保存到数据存储库中。 developerWorks Ajax 资源中心请访问 Ajax 资源中心,这里几乎囊括了关于 Ajax 编程模型的所有信息,包括各种文章、教程、论坛、博客、wikis、活动和新闻。 本文(本系列的第三篇也是最后一篇)将对 为恢复请求构建处理程序本节将提供用于恢复 JSF 表单数据的 Java 方法。这些方法将与一些 JavaScript 功能和 JSF 组件相结合,本文稍后会介绍这些内容。 恢复当前视图的数据第 2 部分 展示了如何通过遍历 JSF 组件树并获取输入组件的值来保存当前视图的表单数据。这个操作是在 清单 1. 恢复输入组件的值package autosave; ... import javax.faces.component.EditableValueHolder; import javax.faces.component.UIComponent; ... public class DataMapRepository ... { ... public static void restoreValues(UIComponent comp, Map<String, Object> dataMap) { if (comp == null || dataMap == null) return; if (comp instanceof EditableValueHolder) { // Input component. Get its value from the data map // and clear any submitted value EditableValueHolder evh = (EditableValueHolder) comp; evh.setValue(dataMap.get(comp.getId())); evh.setSubmittedValue(null); } // Iterate over the children of the current component Iterator children = comp.getChildren().iterator(); while (children.hasNext()) { UIComponent child = (UIComponent) children.next(); // Recursive call restoreValues(child, dataMap); } } } 第 2 部分 还演示了 清单 2 展示了 清单 2. 恢复当前 JSF 视图的数据package autosave; import java.util.Map; ... import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; public class ViewRestorer implements java.io.Serializable { public void restoreCurrentView() { // Get the faces context of the current request. FacesContext ctx = FacesContext.getCurrentInstance(); // Get the root component of the current view. UIViewRoot root = ctx.getViewRoot(); // Get the managed bean instance wrapping the data repository. RepositoryWrapper wrapper = RepositoryWrapper.getManagedBean(ctx); // Get the data map for the current context. Map<String, Object> dataMap = wrapper.getDataMap(ctx); // Use the data map to restore the values of the JSF components. DataMapRepository.restoreValues(root, dataMap); // Signal the JSF framework to go to the Render Response phase. ctx.renderResponse(); } ... } 实现 JSF 侦听程序
清单 3. 可以恢复事件的 JSF 侦听器package autosave; ... import javax.faces.event.ActionEvent; import javax.faces.event.ValueChangeEvent; public class ViewRestorer implements java.io.Serializable { ... public void actionListener(ActionEvent e) { restoreCurrentView(); } public void valueChangeListener(ValueChangeEvent e) { restoreCurrentView(); } ... } 如果存储库包含当前视图和当前用户的数据地图, 清单 4 . 检验是否可以恢复当前视图的数据package autosave; ... public class ViewRestorer implements java.io.Serializable { ... public boolean isCurrentViewRestorable() { FacesContext ctx = FacesContext.getCurrentInstance(); RepositoryWrapper wrapper = RepositoryWrapper.getManagedBean(ctx); return wrapper.hasDataMap(ctx); } } 清单 5 演示如何将 清单 5. 配置视图恢复程序<faces-config> ... <managed-bean> <managed-bean-name>viewRestorer</managed-bean-name> <managed-bean-class>autosave.ViewRestorer</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> ... </faces-config> 发送恢复请求
对恢复请求使用隐藏的触发器对 清单 6. 对恢复请求使用隐藏的触发器<h:form id="supportForm"> <h:inputHidden id="restoreTrigger" value="default" valueChangeListener="#{viewRestorer.valueChangeListener}" immediate="true"/> ... </h:form> 您可能想知道我们为什么使用隐藏的组件触发表单恢复。为什么不在呈现时直接恢复表单?要理解这些问题的答案,您必须了解 JSF 框架构建组件树的方式,这些组件树的值必须进行恢复。请记住,用户曾经离开了应用程序(或者浏览器崩溃),现在用户使用一个 GET 请求返回到表单页面。 在上面描述的场景中处理 GET 请求时,JSF 框架很可能不能在请求处理生命周期的第 1 阶段(Restore View)恢复 JSF 组件树。即使 JSF 实现可以这样做,请求将不具备参数,因为用户刚刚返回到应用程序中。 根据 JSF 规范,如果视图不能被恢复或者请求没有包含查询参数或 POST 数据,JSF 实现必须在 Restore View 阶段调用 faces 上下文的 发送 POST 请求以恢复 JSF 表单上述问题的解决方法就是让 JSF 框架处理 GET 请求并随后发送新的 POST 请求,这将允许应用程序处理 JSF 组件树,恢复组件值。POST 请求可通过表单对象的 JavaScript 如果查看表单页面的 HTML 输出,您将注意到 JSF 为隐藏的元素生成了 清单 7. 查找使用 JSF 组件呈现的表单元素function getFormElement(formId, elemId) { return document.getElementById(formId + ":" + elemId); } 清单 8 展示了另一个 JavaScript 函数,它将发送恢复表单数据的请求。在调用 清单 8. 提交请求以恢复表单数据function submitRestoreRequest() { var restoreTrigger = getFormElement("supportForm", "restoreTrigger"); restoreTrigger.value = "restore"; var supportForm = document.getElementById("supportForm"); supportForm.submit(); } 检验表单是否可恢复使用 清单 9. 验证是否可以发送恢复请求function isRestorable() { return "${pageContext.request.method}".toUpperCase() == "GET" && <h:outputText value="#{viewRestorer.currentViewRestorable}"/>; } 假设在发送 GET 请求后生成了页面,并且 清单 10. 发出 GET 请求后生成的 JavaScript 代码function isRestorable() { return "GET".toUpperCase() == "GET" && true; } 如果使用 清单 11. 发出 GET 请求后生成的 JavaScript 代码function isRestorable() { return "POST".toUpperCase() == "GET" && true; } 了解如何处理恢复请求本节将解释 使用 JSF 组件的 immediate 属性前面一节中使用的 清单 12. 恢复表单数据的命令按钮<h:form id="supportForm"> ... <h:commandButton id="restoreButton" value="Restore" actionListener="#{viewRestorer.actionListener}" immediate="true"/> ... </h:form> 最后,将在 Apply Request Values 阶段调用 跳过某些 JSF 请求处理阶段前面一节讨论的
让我们分析一下 使用 允许用户控制表单保存和恢复隐藏的 然而,您可能希望对真实的应用程序扩展自动保存功能,以便您的用户可以像桌面程序保存/加载文档一样保存/加载 Web 表单。在本例中,用户可以恢复 Web 表单、修改一些数据并重新向 Web 服务器提交表单。假设您拥有一个可以提交每月发票的表单。其中一些输入 — 例如发票日期、发票额以及发票行 — 每月都会改变,但是可以重用输入的供应商和购买者信息,从而减少输入表单数据所需的时间。 添加一个 Save 按钮通过上节的介绍,您已经了解了如何添加 Restore 按钮。表单还可以包含一个 Save 按钮,其代码如 清单 13 所示。Save 按钮不是通过 清单 13. 保存表单数据的命令按钮<h:form id="supportForm"> ... <h:commandButton id="saveButton" value="Save" onclick="return submitSaveRequest()"/> ... </h:form> 清单 14 展示了 清单 14. 提交当前用户输入以保存在服务器中function submitSaveRequest() { submitAllForms(); return false; } 如果从 添加一个 Auto-Save 复选框表单页面还可以包含一个复选框,它允许用户对当前页面启用或禁用自动保存功能(参见 清单 15)。 清单 15. 启用和禁用自动保存功能的复选框<h:form id="supportForm"> ... <h:selectBooleanCheckbox id="autoSaveCheckbox" onclick="setAutoSaving(this.checked ? autoSaveInterval : 0)"/> <h:outputLabel value="Auto-Save Form" for="autoSaveCheckbox"/> ... </h:form>
客户端初始化在 Web 浏览器加载页面后, 清单 16. 获得用户同意恢复表单数据var autoSaveInterval = 10000; function init() { if (isRestorable()) if (confirm("Do you want to restore the auto-saved form?")) { submitRestoreRequest(); return; } var autoSaveCheckbox = getFormElement("supportForm", "autoSaveCheckbox"); if (autoSaveCheckbox.checked) setAutoSaving(autoSaveInterval); } 结束语在这份 共分三部分的系列文章 中,您完成了以下功能:
您学习了大量 JavaScript 和 JSF 技巧,比如:
如果您希望实现表单自动保存功能或类似功能,尽可在您自己的 Ajax/JSF 应用程序中重用本系列附带的示例代码。 下载
|
|