JSF2.0 Ajax导航模式

时间:2011-10-14 12:22:45

标签: java ajax jsf-2

非常感谢有关使用JSF 2.0,PrimeFaces和Ajax的Web App模式的一些指导。我们当前的系统使用带有标准提交的JSP,我们的应用程序中的每个功能页面都有1个JSP。每个JSP都调用逻辑,导航和EJB调用的动作类。使用相同的设置迁移到JSF将相对简单,即1 xhtml页面和相关的辅助bean以及通过do方法完成的导航/逻辑。但是我们想通过Ajax提交,这会引起我头脑中的谜题。如果我加载abc1.xhtml并使用ajax提交然后我留在abc1.xhtml,虽然我可能需要转到abc2.xhtml。我想在1 xhtml页面上使用关联的表单并使用rendered属性来决定要显示的内容。这有效,但我不习惯在1页中有很多表格。理想情况下,我希望将每个页面分开,但不知道如何使用Ajax实现这一点。任何想法都会非常感激。

编辑 - 这是原始解决方案,但现在已在我的答案中进行了改进 。这有效,但Params似乎存在问题。当我点击AAA或BBB链接时,我需要传递一个参数,以便ViewController bean知道点击了什么以及在哪里设置目标页面。但是,如果我单击AAA.xhtml中的“提交”按钮,则内容不会更改,除非我还将<f:param name="tranID" value="AAA"/>添加到命令按钮。我以为我在ViewController构造函数中处理了一个空Param,但显然我错过了一些重要的东西。我唯一的想法是使用URL。当我点击菜单链接时,它会将参数添加到网址?tranID=AAA。如果我然后不将param添加到后续提交中,这是否会有效地更改url并导致某种不匹配?

viewController.xhtml

<h:body>
    <h:panelGroup layout="block" id="transactionControl">

                    <h4>
                        <h:outputLink id="mfa" value="#{facesContext.externalContext.requestContextPath}/xhtml/viewController.xhtml" styleClass="menuLink">
                            <h:outputText value="AAA"></h:outputText>
                            <f:param name="tranID" value="AAA"/>
                        </h:outputLink>
                    </h4>

                    <h4>
                        <h:outputLink id="inq" value="#{facesContext.externalContext.requestContextPath}/xhtml/viewController.xhtml" styleClass="menuLink">
                            <h:outputText value="BBB"></h:outputText>
                            <f:param name="tranID" value="BBB"/>
                        </h:outputLink>
                    </h4>



    </h:panelGroup>

    <h:panelGroup layout="block" id="content" style="border-style: solid;">

        <ui:include src="#{viewController.destinationPage}.xhtml"></ui:include>

    </h:panelGroup>

</h:body>

AAA.xhtml

<h:body>
    <h:form prependId="false">
        <h:outputText value="Click the button to go to AAB"></h:outputText>

        <p>
            <p:commandButton id="submitButton" value="Go" ajax="true" actionListener="#{viewController.doAAAtoAAB}"
                             process="@form"
                             update="content">
                <f:param name="tranID" value="AAA"/>
            </p:commandButton>
        </p>

    </h:form>

</h:body>

AAB.xhtml

<h:body>
    <h:panelGroup layout="block" id="subContent">
        <h:outputText value="This is the AAB content"></h:outputText>
    </h:panelGroup>
</h:body>

BBB.xhtml和BBC.xhtml如上

ViewController bean

package com.mcpplc.supportclient.webapp.managedBeans;


import javax.faces.context.FacesContext;    
import java.io.Serializable;

@ManagedBean
@ViewScoped
public class ViewController implements Serializable
{
    String destinationPage = "splash";
    FacesContext context;
    String callingTranID;

    public ViewController ()
    {
        context = FacesContext.getCurrentInstance();
        callingTranID = context.getExternalContext().getRequestParameterMap().get("tranID");

        if (callingTranID != null )
        {
            destinationPage = callingTranID;
        }
    }


    public void doAAAtoAAB()
    {
         destinationPage = "AAB";
    }

    public void doBBBtoBBC()
    {
         destinationPage = "BBC";
    }

    public String getDestinationPage()
    {
        return destinationPage;
    }

    public void setDestinationPage( String destinationPage )
    {
        this.destinationPage = destinationPage;
    }
}

2 个答案:

答案 0 :(得分:0)

我的推荐是,除非你需要,否则不要使用ajax。如果您需要在请求后实际调用另一个页面,那么使用ajax有什么意义?

尝试在应用程序工作流程中思考并在必要时应用ajax。您认为ajax会令人困惑的案例是因为您可能不应该使用它

干杯

答案 1 :(得分:0)

决定回答我自己的问题,因为我现在有一个完整的工作ajax原型应用程序。这符合我最初的要求,即将每个视图保存为一个.xhtml页面,每个视图都有自己的支持bean。这里的简化代码不是进一步污染我的问题。 (感谢BalusC以前的答案虽然现在已经消失了但确实有所帮助。)

viewController.xhtml

<h:body>
    <h:panelGroup layout="block" id="transactionControl">

                    <h:form>
                <h4>

                    <p:commandLink id="mfa" ajax="true" process="@form" update="content transactionBannerContent" styleClass="menuLink" oncomplete="showTransaction01Screens();">
                        <h:outputText value="MFA"></h:outputText>
                        <f:setPropertyActionListener target="#{viewController.destinationPage}" value="MFA01"/>
                    </p:commandLink>

                </h4>

                <h4>
                    <p:commandLink id="inq" ajax="true" process="@form" update="content transactionBannerContent" styleClass="menuLink" oncomplete="showTransaction01Screens();">
                        <h:outputText value="INQ"></h:outputText>
                        <f:setPropertyActionListener target="#{viewController.destinationPage}" value="INQ01"/>                            
                    </p:commandLink>


                </h4>

            </h:form>



    </h:panelGroup>

    <h:panelGroup layout="block" id="content" style="border-style: solid;">

        <ui:include src="#{viewController.destinationPage}.xhtml"></ui:include>

    </h:panelGroup>

</h:body>

mfa01.xhtml

<h:panelGroup layout="block" id="mfa01">

            <h:form id="mfa01Form">

                <p>
                    <span>
                        <h:outputLabel value="UCN" for="ucn"/>
                        <h:inputText id="ucn" value="#{mfa01BackingBean.mfa01FormVO.ucn}" size="20"/>
                    </span>
                </p>


                <p class="submitButton">
                    <p:commandButton id="submitButton" value="Go" ajax="true" actionListener="#{mfa01BackingBean.doMFA01}" process="@form"
                                     update="content transactionBannerContent" oncomplete="ajaxFinished('MFA01')">
                    </p:commandButton>
                </p>

            </h:form>

        </h:panelGroup>

mfa02.xhtml

<h:panelGroup layout="block" id="mfa02">


            <h:form id="mfa02Form" prependId="true">

                <p>
                    <span style="width:25%">
                        <h:outputLabel value="Vessel Name"/>
                        <h:outputText id="vesselName"
                                     value="#{mfa02BackingBean.mfa02FormVO.vesselName}"/>
                    </span>


                    <span style="width:75%">
                        <h:outputLabel value="ETA"/>
                        <h:outputText id="eta"
                                     value="#{mfa02BackingBean.mfa02FormVO.eta}"/>
                    </span>

                </p>




                <p>
                    <span>
                        <h:outputLabel value="Unit ID" for="unitID"/>
                        <h:inputText id="unitID"
                                     value="#{mfa02BackingBean.mfa02FormVO.unitID}"
                                     size="20"
                                     required="true" validator="#{mfa02BackingBean.validateData}"/>
                    </span>
                </p>


                <p class="submitButton">
                    <p:commandButton id="submitButton" value="Go" ajax="true" action="#{mfa02BackingBean.doMFA02}"
                                     process="@form" update="content transactionBannerContent" oncomplete="ajaxFinished('MFA02')">
                        <f:param name="randomString" value="AAA"/>
                                     </p:commandButton>
                </p>


            </h:form>
        </h:panelGroup>

ViewController.java

package com.mcpplc.supportclient.webapp.managedBeans;


import javax.faces.bean.*;
import java.io.Serializable;

@ManagedBean (name = "viewController")
@SessionScoped
public class ViewController implements Serializable
{
    String destinationPage = "splash";
    String transactionID;


    public String getDestinationPage()
    {
        return destinationPage;
    }

    public void setDestinationPage( String destinationPage )
    {
        this.destinationPage = destinationPage;
        transactionID = destinationPage.toUpperCase();
    }

    public String getTransactionID()
    {
        return transactionID;
    }

    public void setTransactionID( String transactionID )
    {
        this.transactionID = transactionID;
    }
}

mfa01BackingBean.java

package com.mcpplc.supportclient.webapp.managedBeans;

import com.mcpplc.supportclient.webapp.Utils.JSFUtils;
import com.mcpplc.supportclient.webapp.valueObjects.*;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Mfa01BackingBean
{
    @ManagedProperty(value = "#{viewController}")
    private ViewController viewController;

    private ImportConsignmentVO importConsignmentVO;
    private VoyageVO voyageVO;

    private Mfa01FormVO mfa01FormVO;

    private Mfa02FormVO mfa02FormVO;

    @PostConstruct
    public void init()
    {
        mfa01FormVO = new Mfa01FormVO();
    }

    public void doMFA01()
    {
        //pretend to get VOs
        importConsignmentVO = new ImportConsignmentVO();
        voyageVO = new VoyageVO();

        //set some VO stuff
        if ( mfa01FormVO.getUcn().equalsIgnoreCase( "123" ) )
        {
            importConsignmentVO.setUnitID("AJF1");
            voyageVO.setVesselName("Ever Glade");
        }
        else {
            importConsignmentVO.setUnitID("ZZZ1");
            voyageVO.setVesselName("Ever Champion");
        }
        importConsignmentVO.setType("41G1");
        importConsignmentVO.setWeight("25000");
        voyageVO.setEta("01/01/2011");

        constructMFA02Form();

        viewController.setDestinationPage("mfa02");

    }

    private void constructMFA02Form()
    {
        mfa02FormVO = new Mfa02FormVO();
        mfa02FormVO.setUnitID(importConsignmentVO.getUnitID());
        mfa02FormVO.setType(importConsignmentVO.getType());
        mfa02FormVO.setWeight(importConsignmentVO.getWeight());
        mfa02FormVO.setMfaRef("12345");
        mfa02FormVO.setVesselName(voyageVO.getVesselName());
        mfa02FormVO.setEta(voyageVO.getEta());

        JSFUtils.addObjectToRequest(Mfa02FormVO.class.getName(), mfa02FormVO);
    }

  .....getters&setters

}

mfa02BackingBean.java

package com.mcpplc.supportclient.webapp.managedBeans;

import com.mcpplc.supportclient.webapp.Utils.JSFUtils;
import com.mcpplc.supportclient.webapp.valueObjects.*;

import java.io.Serializable;
import java.util.*;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;

@ManagedBean
@ViewScoped
public class Mfa02BackingBean implements Serializable
{
    @ManagedProperty(value = "#{viewController}")
    private ViewController viewController;


    private Mfa02FormVO mfa02FormVO;    

    @PostConstruct
    public void init()
    {
       mfa02FormVO = (Mfa02FormVO) JSFUtils.getObjectFromRequest(Mfa02FormVO.class.getName());

    }

    public String doMFA02()
    {
        viewController.setDestinationPage("mfa01");
        return "viewController";
    }


     public void validateData ( FacesContext facesContext, UIComponent uiComponentToValidate, Object value)
    {
        String message = "";
        String requestData = (String) value;
        if ( !requestData.contains( "0" )  )
            {
                ((UIInput )uiComponentToValidate).setValid( false );
                message = "Incorrect format for Unit ID";
                facesContext.addMessage( uiComponentToValidate.getClientId(facesContext), new FacesMessage(message) );
                //JSFUtils.addObjectToRequest(Mfa02FormVO.class.getName(), mfa02FormVO);

            }



    }

.....getters&setters
}

Mfa01FormVO&amp; Mfa02FormVO只是具有吸气剂和放大器的价值对象。 setter方法

我遇到的主要问题是在请求之间保存对象数据。当我从mfa01转到mfa02时,我会查找填充一些vos然后将表单vo传递给Flash。然后用vo值构造Mfa02屏幕。我遇到的最大问题是mfa02上存在验证错误。最初我将bean设置为requestScoped但是在验证失败后丢失了vo对象。尽管我从Flash中获取了该对象,但我还是将其添加回了闪存中。关于这一点的奇怪之处在于,当我丢失物体时,我无法保证。如果我再次点击,有时我会立即失去对象,但有时会再次点击一次,然后我会失去它。

然后我将mfa02backingbean更改为ViewScoped。但是因为它完全是ajax mfa02backingbean没有在后续请求中重新初始化。这意味着无论我在第一次请求中设置值,都将永远显示出来。我最终使用ViewScoped解决了这个问题并更改了Command Button以使用Action而不是ActionListener,并在该方法中返回“viewController”字符串。我假设生命周期认为我正在返回一个新视图,因此它会清除视图中留下的任何内容。

我希望这段代码可以帮助其他人寻找类似的解决方案。