小编典典

如何将旧JSP的代码段重构为等效的JSF?

jsp

原始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>

我新的后菜豆的制作方法

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

我追求的是一种更优雅的方式,可以将模型(我称之为肉)注入到每次操作的视图中。在工作项(单页视图)下,有多个操作。作为检查清单的操作可以有多种类型(是/否/不适用,主要/次要数量,是/否/不存在/已解决等)。

复合组件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" />,然后原来的JSP用于inc_dynamic_checklist_v.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>

这些还包括需要访问在WorkItem.jsp中使用c:set设置的actionItem.meat

我正在寻找有关是的指导,即使我已嵌套包含,我也应将所有这些包含转换为复合组件。还是我应该使用基本的ui:includes?我知道我可以param使用include或cc
发送,但我仍在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)这显示了“肉”

    <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

阅读 275

收藏
2020-06-08

共1个答案

小编典典

一步一步来。

在进行下一步之前,一切都必须按预期进行,这一点很重要。


继续使用JSTL动态构建视图

只需继续使用JSTL,并仅替换JSP包含项,<ui:include>直到一切正常即可。不要改变太多。首先使其全部正常工作,然后将其重构为标记文件或复合文件。

在最初的JSP方法中,您基本上是在JSTL的帮助下动态构建视图。您可以继续在JSF 2.x中执行相同的操作,前提是您使用的是最新的JSF
impl版本,以防止损坏视图作用域的bean(Mojarra
2.1.18+)。您可以继续使用<c:forEach><c:if><c:set>这样的JSF。您只需要更换@include<jsp:include>通过<ui:include>。请注意,它<ui:include>具有与JSTL相同的生命周期。它也是标记处理程序,而不是组件。另请参见JSF2
Facelets中的JSTL …有意义吗?

<ui:fragment>,但是,是一个UI组件。它不会有条件地构建视图。无论其rendered属性的结果如何,它及其所有子代仍将最终出现在JSF组件树中。他们只会在渲染响应阶段有条件地渲染HTML输出。与之相比的收益<c:if>是,对于每种条件,JSF组件树的大小都会增加。假设您在该inc_dynamic_checklist_v文件中有4个条件包含,那么它的大小至少会增长4倍。只需继续使用JSTL即可动态构建视图。这是一个非常好的工具。另请参见ao
如何制作JSF复合组件的网格?另一种方法是通过手动创建在背衬bean组件bindingfindComponent()createComponent()new SomeComponent()getChildren().add()否则,这只会​​导致冗长而脆弱的代码,而这些代码很难维护。绝对不要那样做。

<f|o:viewParam>你失败的尝试如用于不同的用途。正如您所期望的那样,它们无法对中的<ui:param>值采取行动<ui:include>。它们仅对HTTP请求参数起作用。另请参见有什么用途?您可以<ui:include>继续使用<ui:param>来代替<c:set>,但是您应该像直接使用一样直接访问它们<c:set>。唯一的区别是,这些变量仅在包含本身内部可用,而不是在整个请求中可用(即,因此也可以在包含外部使用)。<ui:param>顺便说一句<jsp:param>,您实际上应该首先使用JSP的等效项。

对于支持bean逻辑,只需将支持Java的预处理Java代码放入@PostConstruct支持bean
中,将支持Java的后处理Java代码放入与<h:commandXxx>组件绑定的支持bean的操作方法中即可。在<f:viewAction>preRenderView因为他们认为建造时间后跑多远,因此JSTL不会得到它期望的模型是insuitable。仅使用这些参数来处理用户提交的HTTP请求参数。

如果您在较旧的Mojarra版本中被Chicken-
egg视图状态错误所咬伤,并且您绝对不能升级,也不能通过将设置javax.faces.PARTIAL_STATE_SAVING为来禁用部分状态保存false,那么您就不能将JSTL标记属性附加到查看作用域的bean中属性。如果您确实在这里有一个范围内的视图bean,并且不是在这里使用请求范围内的bean的选项,则需要删除JSTL并专门使用<ui:repeat>and
<ui:fragment>代替<c:forEach>and
<c:if>。但是,您可以继续使用<c:set>(如果适用)。您还应该保留上述支持bean逻辑的准则。


将重复的包含参数重构为标记文件

一旦一切<ui:include><ui:param>就绪,您就可以开始查看重复的包含参数(即,多次使用的块),并通过简单地将它们注册到your.taglib.xml文件中将它们重构为标记文件。实际上,这不会改变逻辑和流程,但会使代码更简洁明了。另请参阅如何创建自定义Facelets标签?有关完整的*.taglib.xml示例和在中的注册web.xml

这个虚构的示例包括“是/否/不适用检查表”

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

(您没有显示您的模型,所以我只指定了一个过于泛型的类型)


将重复的模型预处理/后处理重构为复合材料

一旦一切恢复正常,您就可以开始研究重复的模型预处理/后处理,并将它们重构为带有“后备组件”以及内部关联的XHTML的复合文件<cc:implementation>

基本上,当您有很多Java代码@PostConstruct可以将服务/数据库返回的“外部”模型转换为视图所期望的“内部”模型时,和/或当您有很多Java代码在使用方法时按照服务/数据库的期望将“内部”模型转换回“外部”模型,那么您可以考虑将其重构为可重用的复合组件。这样,当您想在不同的视图中重用相同的功能时,就无需将该粘贴/重复此预处理/后处理任务复制/粘贴到其他支持bean中。并且,您最终得到一个视图,该视图精确地引用了“外部”模型类型而不是“内部”模型类型,该模型类型可能包含多个属性。

如果没有全面了解所有模型的前/后处理,则很难用特定案例的示例来回答这一部分。以下答案包含一些示例,这些示例应该可以对复合组件的意义和废话提供足够的了解:

至少,我的印象是您的“肉”可能是一个界面。如果您具有相同行为的不同对象/类,则应创建一个定义该行为的接口,并让这些类实现该接口。反过来,这部分与JSF并不严格相关,而只是“基本”
Java。


别忘了:一次一步。

使用标记文件和组合作为重构工具,以最大程度地减少代码重复。您应该已经有完整的工作代码。

2020-06-08