如何将旧JSP的代码段重构为等效的JSF? [英] How to refactor snippet of old JSP to some JSF equivalent?

查看:115
本文介绍了如何将旧JSP的代码段重构为等效的JSF?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

原始JSP (WorkItem.jsp)

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>

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

NEW 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

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

What I am after is a more elegant way to inject the model (what I am calling meat) into my view for each action. Under a work item (single page view), there are multiple actions. Actions that are checklists can be of various types (yes/no/na, quantity major/minor, yes/no/na/resolved, etc).

复合组件done_button很简单,因为我只访问基本的action模型,而没有访问meat.例如done_button.xhtml复合组件的片段

The composite component done_button was straight forward because I am only accessing the base action model and no meat. For example a snippet of the done_button.xhtml composite component

<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类似于

but the include of the dynamic_checklist facelet code has me perplexed because my approach of injecting various Objects into this generic attribute meat :) seems wrong. In my original JSP I used <c:set var="checklist" value="${actionItem.meat}" scope="request" /> and then the original JSP for inc_dynamic_checklist_v.jsp looked something like

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

those includes also needed access to the actionItem.meat which was set using c:set in WorkItem.jsp

我正在寻找有关是的指导,即使我嵌套了包含,我也应该将所有这些包含转换为复合组件.还是我应该使用基本的ui:includes?我知道我可以使用include或cc发送param,但是我仍在模型中使用通用字段private Object meat,还是有更好的方法来检索这些单独的动作模型.

I'm looking for guidance as to yes I should convert all these includes into composite components, even though I have nested includes. Or I should use basic ui:includes? I know I can send param with either include or cc but do I still use the generic field private Object meat in my model or is there a better way to retrieve these individual action models.

也许这是行不通的

<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

and then in the 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)以及其他未实现/未显示的操作.我现在所拥有的有效,但这是正确的方法吗?

Work item backing bean. A work Item contains an array of actions. Actions can be done buttons (action type id=1) checklists (action type id=2), and other things not implemented/shown. What I have now works but is it the right way?

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).这将显示肉"

inc_dynamic_checklist.xhtml (see WorkItem.xhtm above for how this is included) This is displaying the "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

推荐答案

一次仅一步.

在前进到下一步之前,一切都应按预期进行,这一点很重要.

It's important that everything keeps working as intented before advancing to next step.

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

Just keep using JSTL and only replace JSP includes by <ui:include> until you get it all to work. Don't change too much yet. First get it all working and then refactor into tagfiles or composites.

在您最初的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具有相同的生命周期.它也是标记处理程序,而不是组件.另请参见 JSF2 Facelets中的JSTL ...有意义吗?

In your initial JSP approach you're basically dynamically building the view with help of JSTL. You can just continue doing the same in JSF 2.x, provided that you're using a more recent JSF impl version to prevent broken view scoped beans (Mojarra 2.1.18+). You can keep using <c:forEach>, <c:if> and <c:set> this way in JSF. You only need to replace @include and <jsp:include> by <ui:include>. Do note that <ui:include> has the same lifecycle as JSTL. It's also a taghandler and not a component. See also JSTL in JSF2 Facelets... makes sense?

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

The <ui:fragment>, however, is an UI component. It does not conditionally build the view. Regardless of the outcome of its rendered attribute, it and all of its children will still end up in JSF component tree. They will only conditionally render their HTML output during render response phase. The payoff as compared to <c:if> is that the JSF component tree size will grow for every condition. It would grow at least 4 times as big, given that you've 4 conditional includes in that inc_dynamic_checklist_v file. Just keep using JSTL to dynamically build the view. It's a perfectly fine tool for that. See also a.o. How to make a grid of JSF composite component? The alternative would be to manually create components in backing bean via binding, findComponent(), createComponent(), new SomeComponent(), getChildren().add() and what not and this would only end up in verbose and brittle code which is hard to maintain. Absolutely don't do that.

失败尝试中显示的<f|o:viewParam>具有不同的用途.正如您所期望的那样,它们无法对<ui:include>中的<ui:param>值进行操作.它们仅对HTTP请求参数起作用.另请参见>< f:metadata>,< f :viewParam>和< f:viewAction> 您可以为<ui:include>继续使用<ui:param>而不是<c:set>,但是您应该像使用<c:set>一样直接访问它们.唯一的区别是这些变量仅在include本身内部可用,而不是在整个请求中可用(即,因此也可以在include外部使用). <ui:param>的JSP等效方式是<jsp:param>,您实际上应该首先使用它.

The <f|o:viewParam> as shown in your failed attempt serves a different purpose. They can't act on <ui:param> values from the <ui:include>, as you seemed to expect. They act only on HTTP request parameters. See also What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for? You can for your <ui:include> keep using <ui:param> instead of <c:set>, but you should just be accessing them directly, like as you did with <c:set>. The only difference is that those variables are only available inside the include itself instead of in the entire request (i.e. thus also outside the include). The JSP equivalent of <ui:param> is by the way <jsp:param>, which you actually should have used in first place.

关于后备逻辑,只需将预处理Java代码放入后备bean的@PostConstruct中,然后将后处理Java代码放入与<h:commandXxx>组件绑定的后备bean的操作方法中. <f:viewAction>preRenderView是不合适的,因为它们在视图构建时间之后运行很长时间,因此JSTL无法获得其期望的模型.仅使用这些参数来处理用户提交的HTTP请求参数.

As to backing bean logic, just put the pre-processing Java code in @PostConstruct of the backing bean and the post-processing Java code in action methods of backing bean, tied to <h:commandXxx> components. The <f:viewAction> and preRenderView are insuitable because they run far after view build time and thus JSTL wouldn't get the model it expects. Use those only to process user-submitted HTTP request parameters.

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

If you're bitten by the chicken-egg view state bug in an older Mojarra version and you absolutely can't upgrade, nor can disable the partial state saving by setting javax.faces.PARTIAL_STATE_SAVING to false, then you can't attach JSTL tag attributes to view scoped bean properties. If you've indeed a view scoped bean here, and it's not an option to use a request scoped bean here, you'd need to drop JSTL and exclusively use <ui:repeat> and <ui:fragment> instead of <c:forEach> and <c:if>. You can however keep using <c:set> (where applicable). You should also keep the guidelines for backing bean logic as described above.

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

Once you get it all to work, then you can start looking at repeated include-with-params (i.e. <ui:include><ui:param> chunks which are used more than once) and refactor them to tagfiles by simply registering them in your.taglib.xml file. This actually doesn't change anything as to the logic and flow, but makes the code more clean and concise. See also How to create a custom Facelets tag? for complete *.taglib.xml example and registration in web.xml.

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

This fictive example include of a "yes/no/na checklist"

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

...可以按如下方式使用

... could be used as below

<my:tristateChecklist value="#{actionItem}" />

...将物理文件移至/WEB-INF/tags/tristateChecklist.xhtml并按如下所示在/WEB-INF/your.taglib.xml中注册后,所有包含参数都作为标记属性.

... after moving the physical file into /WEB-INF/tags/tristateChecklist.xhtml and registering it in /WEB-INF/your.taglib.xml as below with all include params as tag attributes.

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

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

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

Once you get it all to work again, then you can start looking at repeated model pre/post-processing and refactor them into composites with a "backing component", along with the associated XHTML inside <cc:implementation>.

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

Basically, when you have quite some Java code in @PostConstruct to convert the "external" model as returned by the service/DB to the "internal" model as exactly expected by the view, and/or when you have quite some Java code in action method to convert the "internal" model back to the "external" model as the service/DB expects, then you could consider refactoring it into a reusable composite component. This way you don't need to copypaste/repeat this pre/post-processing task into a different backing bean when you want to reuse the same functionality in a different view. And, you end up with a view which refers exactly the "external" model type instead of "internal" one, possibly consisting of multiple properies.

如果没有对所有模型前后处理的完整概述,则很难用这个例子来回答您的具体案例.以下答案包含一些示例,这些示例应该可以对复合组件的意义和无意义提供足够的了解:

This part is hard to answer with an example for your specific case without having a full overview of all your model pre/post-processing. The below answers contain examples which should provide sufficient insight on the sense and nonsense of composite components:

  • 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

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

At least, I have the impression that your "meat" could be an interface. If you have different objects/classes with same common behavior, then you should create an interface defining that common behavior and have those classes implement that interface. This part is in turn not strictly JSF related, but just "basic" Java.

别忘了:一次一步.

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

Use tagfiles and composites as a refactoring tool to minimize code duplication. You should already have fully working code.

这篇关于如何将旧JSP的代码段重构为等效的JSF?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆