如何将旧JSP的片段重构为某些JSF等价物?

时间:2015-07-30 19:36:17

标签: jsp jsf facelets composite-component

ORIGINAL 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>

ORIGINAL 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>

我的新支持豆的材料

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

我所追求的是一种更优雅的方式将模型(我称之为肉)注入每个动作的视图中。在工作项(单页面视图)下,有多个操作。作为清单的操作可以是各种类型(是/否/ na,数量主要/次要,是/否/ 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,它是使用c:set in WorkItem.jsp

设置的

我正在寻找关于是的指导我应该将所有这些包含转换为复合组件,即使我有嵌套包含。或者我应该使用基本的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),以及未实现/显示的其他内容。我现在有什么用,但这是正确的方法吗?

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了解如何包含它)这是显示&#34;肉&#34;

    <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

1 个答案:

答案 0 :(得分:7)

一次一步。

重要的是,在进入下一步之前,一切都要按照预期的方式运作。

继续使用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?另一种方法是通过bindingfindComponent()createComponent()new SomeComponent()getChildren().add()手动创建支持bean中的组件这只会导致难以维护的冗长而脆弱的代码。绝对不要这样做。

失败尝试中显示的<f|o:viewParam>有不同的用途。正如您所期望的那样,他们无法对<ui:param>的{​​{1}}值采取行动。它们仅对HTTP请求参数起作用。另请参阅What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?您可以<ui:include>继续使用<ui:include>代替<ui:param>,但您应该直接访问它们,就像使用<c:set>一样。唯一的区别是这些变量仅在包含本身内而不是在整个请求中可用(即因此也在包含之外)。相当于<c:set>的JSP等同于<ui:param>,您实际应该首先使用它。

关于支持bean逻辑,只需将预处理Java代码放在支持bean的<jsp:param>中,并将后处理Java代码放在支持bean的操作方法中,绑定到@PostConstruct组件。 <h:commandXxx><f:viewAction>是可以保留的,因为它们在视图构建时间之后运行很远,因此JSTL无法获得它期望的模型。仅用于处理用户提交的HTTP请求参数。

如果您在较旧的Mojarra版本中被鸡蛋视图状态错误所困,并且您绝对无法升级,也无法通过将preRenderView设置为{{}来禁用部分状态保存1}},然后您无法附加JSTL标记属性来查看范围内的bean属性。如果您确实在此处使用了视图范围的bean,并且此处不能使用请求范围的bean,那么您需要删除JSTL并专门使用javax.faces.PARTIAL_STATE_SAVING和{{ 1}}而不是false<ui:repeat>。但是,您可以继续使用<ui:fragment>(如果适用)。您还应该遵循上面描述的支持bean逻辑的准则。

重复包含-s-params到tagfiles

一旦你完成所有工作,那么你可以开始查看重复的include-with-params(即多次使用的<c:forEach>块)并通过简单地在{{{{}}中重新定义它们来重构它们。 1}}文件。这实际上并没有改变逻辑和流程的任何内容,但使代码更加简洁明了。另请参阅How to create a custom Facelets tag?以获取完整的<c:if>示例以及<c:set>中的注册。

这个虚构的例子包括&#34;是/否/ na清单&#34;

<ui:include><ui:param>

...可以使用如下

your.taglib.xml

...将物理文件移动到*.taglib.xml并将其注册到web.xml,如下所示,并将所有包含参数作为标记属性。

<ui:include src="/WEB-INF/includes/tristateChecklist.xhtml">
    <ui:param name="value" value="#{actionItem}" />
</ui:include>

(你没有展示你的模型,所以我只指定了一种过于通用的类型)

重构模型前/后处理到复合材料

一旦你再次完成所有工作,你就可以开始查看重复的模型前/后处理,并将它们重构为带有&#34;支持组件&#34;的复合体,以及内部相关的XHTML {{ 1}}。

基本上,当你在<my:tristateChecklist value="#{actionItem}" /> 中有相当多的Java代码来转换&#34;外部&#34;服务/ DB返回的模型到&#34;内部&#34;模型正如视图所期望的那样,和/或当你在动作方法中有相当多的Java代码来转换&#34;内部&#34;模特回到&#34;外部&#34;作为服务/ DB期望的模型,您可以考虑将其重构为可重用的复合组件。这样,当您想要在不同视图中重用相同的功能时,您不需要将此前/后处理任务复制/重复到不同的支持bean中。而且,你最终会得到一个完全引用&#34;外部&#34;模型类型而不是&#34;内部&#34;一个,可能由多个属性组成。

如果没有对所有模型前/后处理的完整概述,您可以通过一个针对特定案例的示例来回答这部分内容。以下答案包含的示例应该能够充分了解复合组件的含义和无意义:

至少,我的印象是你的&#34;肉&#34;可以是一个界面。如果您有不同的具有相同公共行为的对象/类,那么您应该创建一个定义该公共行为的接口,并让这些类实现该接口。这部分反过来并不是严格的JSF相关,而只是&#34;基本&#34; Java的。

不要忘记:一次一步。

使用tagfiles和composites作为重构工具,以最大限度地减少代码重复。你应该已经有了完整的代码。