如何将旧 JSP 的片段重构为某些 JSF 等价物?
How to refactor snippet of old JSP to some JSF equivalent?
原创 JSP (WorkItem.jsp)
<c:forEach var="actionItem" items="${workItem.work_action_list}">
<c:if test="${actionItem.workActionClass.work_action_type_id == '1'}" >
<%@ include file="inc_done_button.jsp" %>
</c:if>
<c:if test="${actionItem.workActionClass.work_action_type_id == '2'}" >
<c:set var="actionItem" value="${actionItem}" scope="request" />
<c:set var="checklist" value="${actionItem.meat}" scope="request" />
<jsp:include page="inc_dynamic_checklist_v.jsp" flush="true" />
</c:if>
etc...
</c:forEach>
原版Java
for (ListIterator<WorkflowInstanceWorkItemAction> actionIter = wfiwi.getWork_action_list().listIterator(); actionIter.hasNext();) {
if ("2".equals(work_action_type_id)) {
ChecklistInstanceForm ciForm = new ChecklistInstanceForm(this, authenticatedUser);
ChecklistInstance ci = null;
ci = (ChecklistInstance) ciForm.getChkLstInstanceByWfiWiaOwner(wfiWorkItemAction, authenticatedUser);
// Get the meat details for this action and inject it into the object
wfiWorkItemAction.setMeat(ci);
}
}
request.setAttribute("workItem", wfiwi);
request.setAttribute("workFlowInstance", wfi);
新 JSF (WorkItem.xhtml)
<f:metadata>
<o:viewParam name="wfi_wid" value="#{workItemController.wfiwi}" converter="#{workItemConverter}"
<f:event type="preRenderView" listener="#{workItemController.preRender}" />
</f:metadata>
<ui:repeat var="actionItem" value="#{workItemController.wfiwi.work_action_list}">
<ui:fragment rendered="#{actionItem.workActionClass.workActionType.action_type_id == '1'}">
<stk:done_button actionItem="#{actionItem}" /> <!-- Here I chose custom c -->
</ui:fragment>
<ui:fragment rendered="#{actionItem.workActionClass.workActionType.action_type_id == '2'}">
<ui:include src="inc_dynamic_checklist.xhtml">
<ui:param name="checklist" value="#{actionItem.meat}" />
</ui:include>
</ui:fragment>
我的新 backing bean 的材料
public class WorkItemController implements Serializable {
private static final long serialVersionUID = 1L;
private WorkflowInstanceWorkItem wfiwi;
public void preRender() {
if (wfiwi.getWork_action_list() != null) {
//loop through and add real model to meat attribute
我所追求的是一种更优雅的方式来将模型(我称之为肉)注入到我对每个动作的视图中。在一个工作项(单页视图)下,有多个操作。作为清单的操作可以有多种类型(yes/no/na、数量 major/minor、yes/no/na/已解决等)。
复合组件 done_button
很简单,因为我只访问基础 action
模型而没有 meat
。例如 done_button.xhtml
复合组件
的片段
<ui:fragment rendered="#{cc.attrs.actionItem.is_active != '1'}">
Action is not active for you until the following has been completed:
<h:outputText value="#{cc.attrs.actionItem.prerequisite_work_action_list}" escapeXml="false" />
</ui:fragment>
但是 dynamic_checklist facelet 代码的包含让我感到困惑,因为我将各种 Objects
注入到这个通用属性 meat
中的方法似乎是错误的。在我原来的 JSP 中,我使用了 <c:set var="checklist" value="${actionItem.meat}" scope="request" />
,然后 inc_dynamic_checklist_v.jsp
的原始 JSP 看起来像
inc_dynamic_checklist_v.jsp
<form method="post" >
<c:out value="${actionItem.workActionClass.name}" />
<c:if test="${checklist.checkListClass.type == '1'}" >
<%@ include file="inc_yes_no_na_resolved_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '2'}" >
<%@ include file="inc_major_minor_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '3'}" >
<%@ include file="inc_quantity_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '4'}" >
<%@ include file="inc_yes_no_na_checklist.jsp" %>
</c:if>
这些包括还需要访问 actionItem.meat,这是使用 WorkItem.jsp
中的 c:set 设置的
我正在寻找关于我应该将所有这些包含转换为复合组件的指导,即使我有嵌套的包含。或者我应该使用基本的 ui:includes?我知道我可以使用 include 或 cc 发送 param
但我是否仍然在我的模型中使用通用字段 private Object meat
还是有更好的方法来检索这些单独的动作模型。
也许是这样,但没用
<ui:include src="inc_dynamic_checklist.xhtml" >
<ui:param name="wfi_id" value="#{actionItem.workflowInstance.workflow_instance_id}" />
<ui:param name="wfi_aid" value="#{actionItem.wfi_work_item_action_id}" />
</ui:include>
然后在 inc_dynamic_checklist.xhtml
<f:metadata>
<o:viewParam name="wfi_id" value="#{checklistInstanceView.ci}" converter="#{checklistInstanceConverter}">
<f:attribute name="wfi_id" value="#{param.wfi_id}" />
<f:attribute name="wfi_aid" value="#{param.wfi_aid}" />
</o:viewParam>
</f:metadata>
更新
工作项支持 bean。工作项包含一组操作。可以完成操作按钮(操作类型 id=1)清单(操作类型 id=2),以及其他不能 implemented/shown 的东西。我现在的方法有效,但它是正确的方法吗?
public void preRender() {
if (wfiwi.getWork_action_list() != null) {
for (ListIterator<WorkflowInstanceWorkItemAction> actionIter = wfiwi.getWork_action_list().listIterator(); actionIter.hasNext();) {
WorkflowInstanceWorkItemAction wfiWorkItemAction = new WorkflowInstanceWorkItemAction();
wfiWorkItemAction = actionIter.next();
Long work_action_type_id = wfiWorkItemAction.getWorkActionClass().getWorkActionType().getAction_type_id();
updatePrerequisites(wfiWorkItemAction, wfiwi.getWorkflowInstance(), wfiwi);
if (work_action_type_id == 2) {
System.out.println("Action Type 2 is Dynamic Checklist Type");
ci = ciRepository.retrieveLatestByWfiWiai(wfiwi.getWorkflowInstance().getWorkflow_instance_id(), wfiWorkItemAction.getWfi_work_item_action_id());
if (ci != null) {
if ("1".equals(ci.getCheckListClass().getType())) {
List<YesNoNaResolvedAnswer> answer_attribute_list = yesNoNaResolvedDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("2".equals(ci.getCheckListClass().getType())) {
List<MajorMinorAnswer> answer_attribute_list = majorMinorAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("3".equals(ci.getCheckListClass().getType())) {
List<QuantityAnswer> answer_attribute_list = quantityAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("4".equals(ci.getCheckListClass().getType())) {
List<YesNoNaAnswer> answer_attribute_list = yesNoNaAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
wfiWorkItemAction.setMeat(ci);
} else {
Messages.addFlashErrorMessage("Could not find checklist Instance");
}
// wfi_action_list.add(ci);
} else {
wfiWorkItemAction.setMeat("meat pie");
}
}
}
}
inc_dynamic_checklist.xhtml(参见上面的 WorkItem.xhtm 以了解如何包含)这显示了 "meat"
<ui:fragment rendered="#{checklist.checkListClass.type == '1'}">
<ui:include src="inc_yes_no_na_resolved_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{checklist.checkListClass.type == '2'}">
<ui:include src="inc_major_minor_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="${checklist.checkListClass.type == '3'}">
<ui:include src="inc_quantity_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="${checklist.checkListClass.type == '4'}">
<ui:include src="inc_yes_no_na_checklist.xhtml" />
</ui:fragment>
型号
@Entity
public class WorkflowInstanceWorkItemAction implements Serializable {
private static final long serialVersionUID = 1L;
private String status;
private String is_active;
@Transient
private Object meat;
and various mappings
一步一个脚印。
在进入下一步之前,一切都按预期运行很重要。
继续使用JSTL动态构建视图
只要继续使用 JSTL 并且只用 <ui:include>
替换 JSP 包含,直到你让它全部工作。不要改变太多。首先让它全部工作,然后重构为标记文件或复合文件。
在您最初的 JSP 方法中,您基本上是在 JSTL 的帮助下动态构建视图。您可以在 JSF 2.x 中继续执行相同的操作,前提是您使用更新的 JSF impl 版本来防止破坏视图范围 bean (Mojarra 2.1.18+)。您可以在 JSF 中继续以这种方式使用 <c:forEach>
、<c:if>
和 <c:set>
。您只需要将 @include
和 <jsp:include>
替换为 <ui:include>
。请注意 <ui:include>
与 JSTL 具有相同的生命周期。它也是一个标签处理器而不是一个组件。另见 JSTL in JSF2 Facelets... makes sense?
然而,<ui:fragment>
是一个 UI 组件。它不会有条件地构建视图。无论其 rendered
属性的结果如何,它及其所有子项仍将最终出现在 JSF 组件树中。他们只会在渲染响应阶段有条件地渲染他们的 HTML 输出。与 <c:if>
相比的回报是 JSF 组件树的大小将针对每种情况增长。鉴于您在该 inc_dynamic_checklist_v
文件中有 4 个条件包含,它至少会增长 4 倍。只需继续使用 JSTL 动态构建视图即可。这是一个非常好的工具。另见 a.o。 How to make a grid of JSF composite component? 另一种方法是通过 binding
、findComponent()
、createComponent()
、new SomeComponent()
、getChildren().add()
在支持 bean 中手动创建组件,等等这只会导致难以维护的冗长和脆弱的代码。绝对不要那样做。
您失败尝试中显示的 <f|o:viewParam>
有不同的用途。他们无法像您预期的那样对 <ui:include>
中的 <ui:param>
值采取行动。它们仅作用于 HTTP 请求参数。另见 What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for? 你可以为你的 <ui:include>
继续使用 <ui:param>
而不是 <c:set>
,但你应该直接访问它们,就像你对 <c:set>
所做的那样.唯一的区别是这些变量只在 include 本身内部可用,而不是在整个请求中可用(即因此也在 include 外部)。 <ui:param>
的 JSP 顺便说一下 <jsp:param>
,你实际上应该首先使用它。
至于backing bean逻辑,只需要将预处理Java代码放在backing bean的@PostConstruct
和post-processing Java代码中即可支持 bean 的方法,绑定到 <h:commandXxx>
组件。 <f:viewAction>
和 preRenderView
不合适,因为它们 运行 远远晚于视图构建时间,因此 JSTL 无法获得它期望的模型。仅使用那些来处理用户提交的 HTTP 请求参数。
如果您被旧版 Mojarra 中的先有鸡还是先有蛋的视图状态错误困扰,您绝对无法升级,也无法通过将 javax.faces.PARTIAL_STATE_SAVING
设置为 [=44= 来禁用部分状态保存],则无法附加 JSTL 标记属性来查看范围内的 bean 属性。如果您确实在此处有一个视图范围的 bean,并且不能在此处使用请求范围的 bean,则您需要删除 JSTL 并专门使用 <ui:repeat>
和 <ui:fragment>
而不是 <c:forEach>
和 <c:if>
。但是,您可以继续使用 <c:set>
(如果适用)。您还应该保留上述支持 bean 逻辑的准则。
将重复的 include-with-params 重构为标记文件
一旦一切正常,您就可以开始查看重复的 include-with-params(即 <ui:include><ui:param>
多次使用的块)并通过简单地将它们注册到标签文件中来重构它们your.taglib.xml
文件。这实际上并没有改变任何逻辑和流程,而是使代码更加简洁明了。另请参阅 How to create a custom Facelets tag? 了解完整的 *.taglib.xml
示例和在 web.xml
中的注册。
这个虚构的例子包括 "yes/no/na checklist"
<ui:include src="/WEB-INF/includes/tristateChecklist.xhtml">
<ui:param name="value" value="#{actionItem}" />
</ui:include>
...可以如下使用
<my:tristateChecklist value="#{actionItem}" />
...将物理文件移动到 /WEB-INF/tags/tristateChecklist.xhtml
并将其注册到 /WEB-INF/your.taglib.xml
中,如下所示,所有包含参数作为标记属性。
<tag>
<tag-name>tristateChecklist</tag-name>
<source>tags/tristateChecklist.xhtml</source>
<attribute>
<name>value</name>
<type>java.lang.Object</type><!-- TODO: fix type -->
</attribute>
</tag>
(你没有展示你的模型,所以我只是指定了一个过于通用的类型)
将重复模型pre/post-processing重构为复合材料
一旦一切恢复正常,您就可以开始查看重复模型 pre/post-processing 并将它们重构为具有 "backing component" 的复合材料,以及相关的 XHTML里面 <cc:implementation>
。
基本上,当您在 @PostConstruct
中有相当多的 Java 代码将 service/DB 返回的 "external" 模型转换为 "internal" 模型时正如视图所预期的那样,and/or 当您在操作方法中有相当多的 Java 代码将 "internal" 模型转换回 "external" 模型作为 service/DB 期望,那么你可以考虑将其重构为可重用的复合组件。这样当您想在不同的视图中重用相同的功能时,您不需要 copypaste/repeat 这个 pre/post-processing 任务到不同的辅助 bean 中。而且,您最终会得到一个视图,该视图完全引用 "external" 模型类型而不是 "internal" 模型类型,可能由多个属性组成。
如果不全面了解您的所有模型 pre/post-processing,这部分很难用针对您的具体案例的示例来回答。下面的答案包含示例,这些示例应该对复合组件的意义和意义提供足够的洞察力:
- Split java.util.Date over two h:inputText fields representing hour and minute with f:convertDateTime
- Initialize a composite component based on the provided attributes
- #{cc.clientId} evalutated in wrong composite after upgrading to JSF 2.2
至少,我的印象是您的 "meat" 可能是一个接口。如果您有不同的 objects/classes 具有相同的共同行为,那么您应该创建一个定义该共同行为的接口并让那些 类 实现该接口。这部分又不是严格的 JSF 相关,而只是 "basic" Java.
不要忘记:一步一个脚印。
使用标记文件和复合文件作为重构工具来最大程度地减少代码重复。您应该已经有了完整的工作代码。
原创 JSP (WorkItem.jsp)
<c:forEach var="actionItem" items="${workItem.work_action_list}">
<c:if test="${actionItem.workActionClass.work_action_type_id == '1'}" >
<%@ include file="inc_done_button.jsp" %>
</c:if>
<c:if test="${actionItem.workActionClass.work_action_type_id == '2'}" >
<c:set var="actionItem" value="${actionItem}" scope="request" />
<c:set var="checklist" value="${actionItem.meat}" scope="request" />
<jsp:include page="inc_dynamic_checklist_v.jsp" flush="true" />
</c:if>
etc...
</c:forEach>
原版Java
for (ListIterator<WorkflowInstanceWorkItemAction> actionIter = wfiwi.getWork_action_list().listIterator(); actionIter.hasNext();) {
if ("2".equals(work_action_type_id)) {
ChecklistInstanceForm ciForm = new ChecklistInstanceForm(this, authenticatedUser);
ChecklistInstance ci = null;
ci = (ChecklistInstance) ciForm.getChkLstInstanceByWfiWiaOwner(wfiWorkItemAction, authenticatedUser);
// Get the meat details for this action and inject it into the object
wfiWorkItemAction.setMeat(ci);
}
}
request.setAttribute("workItem", wfiwi);
request.setAttribute("workFlowInstance", wfi);
新 JSF (WorkItem.xhtml)
<f:metadata>
<o:viewParam name="wfi_wid" value="#{workItemController.wfiwi}" converter="#{workItemConverter}"
<f:event type="preRenderView" listener="#{workItemController.preRender}" />
</f:metadata>
<ui:repeat var="actionItem" value="#{workItemController.wfiwi.work_action_list}">
<ui:fragment rendered="#{actionItem.workActionClass.workActionType.action_type_id == '1'}">
<stk:done_button actionItem="#{actionItem}" /> <!-- Here I chose custom c -->
</ui:fragment>
<ui:fragment rendered="#{actionItem.workActionClass.workActionType.action_type_id == '2'}">
<ui:include src="inc_dynamic_checklist.xhtml">
<ui:param name="checklist" value="#{actionItem.meat}" />
</ui:include>
</ui:fragment>
我的新 backing bean 的材料
public class WorkItemController implements Serializable {
private static final long serialVersionUID = 1L;
private WorkflowInstanceWorkItem wfiwi;
public void preRender() {
if (wfiwi.getWork_action_list() != null) {
//loop through and add real model to meat attribute
我所追求的是一种更优雅的方式来将模型(我称之为肉)注入到我对每个动作的视图中。在一个工作项(单页视图)下,有多个操作。作为清单的操作可以有多种类型(yes/no/na、数量 major/minor、yes/no/na/已解决等)。
复合组件 done_button
很简单,因为我只访问基础 action
模型而没有 meat
。例如 done_button.xhtml
复合组件
<ui:fragment rendered="#{cc.attrs.actionItem.is_active != '1'}">
Action is not active for you until the following has been completed:
<h:outputText value="#{cc.attrs.actionItem.prerequisite_work_action_list}" escapeXml="false" />
</ui:fragment>
但是 dynamic_checklist facelet 代码的包含让我感到困惑,因为我将各种 Objects
注入到这个通用属性 meat
中的方法似乎是错误的。在我原来的 JSP 中,我使用了 <c:set var="checklist" value="${actionItem.meat}" scope="request" />
,然后 inc_dynamic_checklist_v.jsp
的原始 JSP 看起来像
inc_dynamic_checklist_v.jsp
<form method="post" >
<c:out value="${actionItem.workActionClass.name}" />
<c:if test="${checklist.checkListClass.type == '1'}" >
<%@ include file="inc_yes_no_na_resolved_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '2'}" >
<%@ include file="inc_major_minor_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '3'}" >
<%@ include file="inc_quantity_checklist.jsp" %>
</c:if>
<c:if test="${checklist.checkListClass.type == '4'}" >
<%@ include file="inc_yes_no_na_checklist.jsp" %>
</c:if>
这些包括还需要访问 actionItem.meat,这是使用 WorkItem.jsp
中的 c:set 设置的我正在寻找关于我应该将所有这些包含转换为复合组件的指导,即使我有嵌套的包含。或者我应该使用基本的 ui:includes?我知道我可以使用 include 或 cc 发送 param
但我是否仍然在我的模型中使用通用字段 private Object meat
还是有更好的方法来检索这些单独的动作模型。
也许是这样,但没用
<ui:include src="inc_dynamic_checklist.xhtml" >
<ui:param name="wfi_id" value="#{actionItem.workflowInstance.workflow_instance_id}" />
<ui:param name="wfi_aid" value="#{actionItem.wfi_work_item_action_id}" />
</ui:include>
然后在 inc_dynamic_checklist.xhtml
<f:metadata>
<o:viewParam name="wfi_id" value="#{checklistInstanceView.ci}" converter="#{checklistInstanceConverter}">
<f:attribute name="wfi_id" value="#{param.wfi_id}" />
<f:attribute name="wfi_aid" value="#{param.wfi_aid}" />
</o:viewParam>
</f:metadata>
更新
工作项支持 bean。工作项包含一组操作。可以完成操作按钮(操作类型 id=1)清单(操作类型 id=2),以及其他不能 implemented/shown 的东西。我现在的方法有效,但它是正确的方法吗?
public void preRender() {
if (wfiwi.getWork_action_list() != null) {
for (ListIterator<WorkflowInstanceWorkItemAction> actionIter = wfiwi.getWork_action_list().listIterator(); actionIter.hasNext();) {
WorkflowInstanceWorkItemAction wfiWorkItemAction = new WorkflowInstanceWorkItemAction();
wfiWorkItemAction = actionIter.next();
Long work_action_type_id = wfiWorkItemAction.getWorkActionClass().getWorkActionType().getAction_type_id();
updatePrerequisites(wfiWorkItemAction, wfiwi.getWorkflowInstance(), wfiwi);
if (work_action_type_id == 2) {
System.out.println("Action Type 2 is Dynamic Checklist Type");
ci = ciRepository.retrieveLatestByWfiWiai(wfiwi.getWorkflowInstance().getWorkflow_instance_id(), wfiWorkItemAction.getWfi_work_item_action_id());
if (ci != null) {
if ("1".equals(ci.getCheckListClass().getType())) {
List<YesNoNaResolvedAnswer> answer_attribute_list = yesNoNaResolvedDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("2".equals(ci.getCheckListClass().getType())) {
List<MajorMinorAnswer> answer_attribute_list = majorMinorAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("3".equals(ci.getCheckListClass().getType())) {
List<QuantityAnswer> answer_attribute_list = quantityAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
if ("4".equals(ci.getCheckListClass().getType())) {
List<YesNoNaAnswer> answer_attribute_list = yesNoNaAnsDao.retrieveByCiWfi(ci.getChecklist_instance_id(), ci.getWorkflowInstance().getWorkflow_instance_id());
ci.setAnswer_attribute_list(answer_attribute_list);
}
wfiWorkItemAction.setMeat(ci);
} else {
Messages.addFlashErrorMessage("Could not find checklist Instance");
}
// wfi_action_list.add(ci);
} else {
wfiWorkItemAction.setMeat("meat pie");
}
}
}
}
inc_dynamic_checklist.xhtml(参见上面的 WorkItem.xhtm 以了解如何包含)这显示了 "meat"
<ui:fragment rendered="#{checklist.checkListClass.type == '1'}">
<ui:include src="inc_yes_no_na_resolved_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="#{checklist.checkListClass.type == '2'}">
<ui:include src="inc_major_minor_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="${checklist.checkListClass.type == '3'}">
<ui:include src="inc_quantity_checklist.xhtml" />
</ui:fragment>
<ui:fragment rendered="${checklist.checkListClass.type == '4'}">
<ui:include src="inc_yes_no_na_checklist.xhtml" />
</ui:fragment>
型号
@Entity
public class WorkflowInstanceWorkItemAction implements Serializable {
private static final long serialVersionUID = 1L;
private String status;
private String is_active;
@Transient
private Object meat;
and various mappings
一步一个脚印。
在进入下一步之前,一切都按预期运行很重要。
继续使用JSTL动态构建视图
只要继续使用 JSTL 并且只用 <ui:include>
替换 JSP 包含,直到你让它全部工作。不要改变太多。首先让它全部工作,然后重构为标记文件或复合文件。
在您最初的 JSP 方法中,您基本上是在 JSTL 的帮助下动态构建视图。您可以在 JSF 2.x 中继续执行相同的操作,前提是您使用更新的 JSF impl 版本来防止破坏视图范围 bean (Mojarra 2.1.18+)。您可以在 JSF 中继续以这种方式使用 <c:forEach>
、<c:if>
和 <c:set>
。您只需要将 @include
和 <jsp:include>
替换为 <ui:include>
。请注意 <ui:include>
与 JSTL 具有相同的生命周期。它也是一个标签处理器而不是一个组件。另见 JSTL in JSF2 Facelets... makes sense?
然而,<ui:fragment>
是一个 UI 组件。它不会有条件地构建视图。无论其 rendered
属性的结果如何,它及其所有子项仍将最终出现在 JSF 组件树中。他们只会在渲染响应阶段有条件地渲染他们的 HTML 输出。与 <c:if>
相比的回报是 JSF 组件树的大小将针对每种情况增长。鉴于您在该 inc_dynamic_checklist_v
文件中有 4 个条件包含,它至少会增长 4 倍。只需继续使用 JSTL 动态构建视图即可。这是一个非常好的工具。另见 a.o。 How to make a grid of JSF composite component? 另一种方法是通过 binding
、findComponent()
、createComponent()
、new SomeComponent()
、getChildren().add()
在支持 bean 中手动创建组件,等等这只会导致难以维护的冗长和脆弱的代码。绝对不要那样做。
您失败尝试中显示的 <f|o:viewParam>
有不同的用途。他们无法像您预期的那样对 <ui:include>
中的 <ui:param>
值采取行动。它们仅作用于 HTTP 请求参数。另见 What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for? 你可以为你的 <ui:include>
继续使用 <ui:param>
而不是 <c:set>
,但你应该直接访问它们,就像你对 <c:set>
所做的那样.唯一的区别是这些变量只在 include 本身内部可用,而不是在整个请求中可用(即因此也在 include 外部)。 <ui:param>
的 JSP 顺便说一下 <jsp:param>
,你实际上应该首先使用它。
至于backing bean逻辑,只需要将预处理Java代码放在backing bean的@PostConstruct
和post-processing Java代码中即可支持 bean 的方法,绑定到 <h:commandXxx>
组件。 <f:viewAction>
和 preRenderView
不合适,因为它们 运行 远远晚于视图构建时间,因此 JSTL 无法获得它期望的模型。仅使用那些来处理用户提交的 HTTP 请求参数。
如果您被旧版 Mojarra 中的先有鸡还是先有蛋的视图状态错误困扰,您绝对无法升级,也无法通过将 javax.faces.PARTIAL_STATE_SAVING
设置为 [=44= 来禁用部分状态保存],则无法附加 JSTL 标记属性来查看范围内的 bean 属性。如果您确实在此处有一个视图范围的 bean,并且不能在此处使用请求范围的 bean,则您需要删除 JSTL 并专门使用 <ui:repeat>
和 <ui:fragment>
而不是 <c:forEach>
和 <c:if>
。但是,您可以继续使用 <c:set>
(如果适用)。您还应该保留上述支持 bean 逻辑的准则。
将重复的 include-with-params 重构为标记文件
一旦一切正常,您就可以开始查看重复的 include-with-params(即 <ui:include><ui:param>
多次使用的块)并通过简单地将它们注册到标签文件中来重构它们your.taglib.xml
文件。这实际上并没有改变任何逻辑和流程,而是使代码更加简洁明了。另请参阅 How to create a custom Facelets tag? 了解完整的 *.taglib.xml
示例和在 web.xml
中的注册。
这个虚构的例子包括 "yes/no/na checklist"
<ui:include src="/WEB-INF/includes/tristateChecklist.xhtml">
<ui:param name="value" value="#{actionItem}" />
</ui:include>
...可以如下使用
<my:tristateChecklist value="#{actionItem}" />
...将物理文件移动到 /WEB-INF/tags/tristateChecklist.xhtml
并将其注册到 /WEB-INF/your.taglib.xml
中,如下所示,所有包含参数作为标记属性。
<tag>
<tag-name>tristateChecklist</tag-name>
<source>tags/tristateChecklist.xhtml</source>
<attribute>
<name>value</name>
<type>java.lang.Object</type><!-- TODO: fix type -->
</attribute>
</tag>
(你没有展示你的模型,所以我只是指定了一个过于通用的类型)
将重复模型pre/post-processing重构为复合材料
一旦一切恢复正常,您就可以开始查看重复模型 pre/post-processing 并将它们重构为具有 "backing component" 的复合材料,以及相关的 XHTML里面 <cc:implementation>
。
基本上,当您在 @PostConstruct
中有相当多的 Java 代码将 service/DB 返回的 "external" 模型转换为 "internal" 模型时正如视图所预期的那样,and/or 当您在操作方法中有相当多的 Java 代码将 "internal" 模型转换回 "external" 模型作为 service/DB 期望,那么你可以考虑将其重构为可重用的复合组件。这样当您想在不同的视图中重用相同的功能时,您不需要 copypaste/repeat 这个 pre/post-processing 任务到不同的辅助 bean 中。而且,您最终会得到一个视图,该视图完全引用 "external" 模型类型而不是 "internal" 模型类型,可能由多个属性组成。
如果不全面了解您的所有模型 pre/post-processing,这部分很难用针对您的具体案例的示例来回答。下面的答案包含示例,这些示例应该对复合组件的意义和意义提供足够的洞察力:
- Split java.util.Date over two h:inputText fields representing hour and minute with f:convertDateTime
- Initialize a composite component based on the provided attributes
- #{cc.clientId} evalutated in wrong composite after upgrading to JSF 2.2
至少,我的印象是您的 "meat" 可能是一个接口。如果您有不同的 objects/classes 具有相同的共同行为,那么您应该创建一个定义该共同行为的接口并让那些 类 实现该接口。这部分又不是严格的 JSF 相关,而只是 "basic" Java.
不要忘记:一步一个脚印。
使用标记文件和复合文件作为重构工具来最大程度地减少代码重复。您应该已经有了完整的工作代码。