2
votes

I'm using prime-faces Tabs to display multiple input forms. The problem is, there are times when I need to instantiate 2 of the same form. They both of course use the same Managed Bean which causes the input of the first initialized form to override the other with the same data. I need to be able to put different data in each form and submit both of them collectively. Each form goes into a list and forwarded on for calculation. I've been reading scope scope scope scope but the only scope that works somewhat is "Sessioned". Session only allows one instance of the Managed Bean and places the form data into the list, "Request, View scope" Doesn't place the data into the list respectively nor does it hold the data. The Managed Bean is serializable and in sessioned scope. I've read the other post and they don't work. Any ideas?

Backing Bean

@ManagedBean
@SessionScoped

public class RequestCalculation {
    private CalculationRequest calcReq;
    private List<ViewTabs> formTabs;
    private String id;
    private boolean calcButton;

    public RequestCalculation() {
        calcReq = new CalculationRequest();
        formTabs = new ArrayList<ViewTabs>();
        calcButton = false;

    }

    public String loadForm(TcsBase loadForm) {

        id = loadForm.getId();

        ViewTabs tab = new ViewTabs();
        tab.setFormTitle("Form".concat(id));
        tab.setFormPage("Form".concat(id).concat(".xhtml"));
        formTabs.add(0, tab);
        calcReq.getFormCollection().add(0, loadForm);
        loadCalcButton();
        return "Main";
    }

    public void loadCalcButton() {
        if (formTabs.isEmpty())
            isCalcButton();
        else {
            calcButton = true;
        }
    }

    public void onTabClosed(TabCloseEvent e) {
        TabView tabView = (TabView) e.getComponent();
        int closingTabIndex = tabView.getChildren().indexOf(e.getTab());
        removeForm(closingTabIndex);
        formTabs.remove(closingTabIndex);
        loadCalcButton();
    }

    public void removeForm(int index) {
        TcsBase formIndex = calcReq.getFormCollection().get(index);
        String formId = formIndex.getId();

        // Creates a new instance of the selected form
        FacesContext fc = FacesContext.getCurrentInstance();
        fc.getELContext().getELResolver()
                .setValue(fc.getELContext(), null, "form".concat(formId), null);

        calcReq.getFormCollection().remove(index);
        formTabs.remove(index);
    }

    public String calculate() {
    CalculateService service = new CalculateService();
        CalculatorInterface calculateInterface = service.getCalculatePort();

        XStream xstream = new XStream(new StaxDriver());
        xstream.registerConverter(new JodaTimeConverter());

        // Here is where the client request (input) is converted to an Xml
        // string before going
        // to the Web Service

        String xml = xstream.toXML(calcReq);
        String request = calculateInterface.calculate(xml);

        // Here the response back from the Web Service is converted back from
        // Xml to a string
        // to be displayed to the user in Xhtml

        calcReq = (CalculationRequest) xstream.fromXML(request);

        FacesContext fc = FacesContext.getCurrentInstance();

        for (int i = 0; i < calcReq.getFormCollection().size(); i++) {

            TcsBase newFrm = calcReq.getFormCollection().get(i);
            String frmId = newFrm.getId();
            fc.getELContext()
                    .getELResolver()
                    .setValue(fc.getELContext(), null, "form".concat(frmId),
                            newFrm);
        }

        return null;

    }

    public List<ViewTabs> getFormTabs() {
        return formTabs;
    }

    public void setFormTabs(List<ViewTabs> formTabs) {
        this.formTabs = formTabs;
    }

    public boolean isCalcButton() {
        return calcButton;
    }

    public void setCalcButton(boolean calcButton) {
        this.calcButton = calcButton;
    }

}

**Html Menu **

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">

<head>

</head>

<f:view>
    <h:body>

        <h:commandLink action="#{requestCalculation.loadForm(formA)}" value="FormA"  /> <br/>
        <h:commandLink action="#{requestCalculation.loadForm(formB)}" value="FormB" /> <br/><br/><br/>

    </h:body>
</f:view>
</html>

Html Main page

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:c="http://java.sun.com/jsp/jstl/core">
<f:view>    

    <h:head>
        <title> TCS </title>

    </h:head>

    <h:form >


        <p:layout style="min-width:400px;min-height:700px " >
            <p:layoutUnit  position="north" style="text-align:center">
            <p style="text-align:center; font-size:28px">Tax Computation Service</p>
            </p:layoutUnit>

            <p:layoutUnit header="Menu" position="west" style="min-width:190px; min-height:50px; ">
                <p:panelMenu>
                    <p:submenu label="Forms">
                        <p:submenu label="Individual Forms">
                            <p:menuitem>
                                <ui:include src="Menu.xhtml" />
                            </p:menuitem>
                        </p:submenu>
                    </p:submenu>
                </p:panelMenu>
            </p:layoutUnit>

            <p:layoutUnit position="center" >
                <p:tabView  onTabShow="focus" widgetVar="tabView">
                    <p:ajax event="tabClose" listener="#{requestCalculation.onTabClosed}"/>
                    <c:forEach items="#{requestCalculation.formTabs}" var="listItem">
                        <p:tab  title="#{listItem.formTitle}"  closable="true" >
                            <ui:include src="#{listItem.formPage}" />
                         </p:tab>
                    </c:forEach>
                </p:tabView>

                    <p:panelGrid columns="0" >
                        <p:commandButton value="Calculate" action = "#{requestCalculation.calculate}"   ajax="false" rendered="#{requestCalculation.calcButton}" /> 
                    </p:panelGrid>
            </p:layoutUnit>

        </p:layout>

    </h:form>

</f:view>   
</html>

FormA Html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui">

    <f:view>

        <h:head>
            <title> FormA</title>
        </h:head>

        <h:body>

            <p:focus />

            <p:panelGrid id="panelGridA" columns="2">
                <p:outputLabel value="Form ID: "  style="width: 725px" />
                <p:outputLabel value="#{formA.id}"  />        

                <p:outputLabel value="4.  . . "  style="width: 725px" />        
                <p:inputText id="input1" style="text-align:right" value="#{formA.line4}" converter="bdNullableConverter" onfocus="this.select()"/> 
                <p:outputLabel value="5. . . . . . . "  style="width: 725px" />        
                <p:inputText style="text-align:right" value="#{formA.line5}" converter="bdNullableConverter" onfocus="this.select()" /> 
                <p:outputLabel value="6.  . . . . . . . "  style="width: 725px" />        
                <p:outputLabel value="#{formA.line6}" converter="bdNullableConverter" /> 
            </p:panelGrid>
        </h:body>

    </f:view>
</html>
2
Take the 'Map' solution from here and make sure that in each tab, e.g. the tabID is used as a key to the map. (duplicate?, not quite although...) - Kukeltje
@Kukeltje - using the Map would be difficult as well because the tab id would be the same if I'm selecting the same form for the same reason as above. - Rod
Same form in a different tab has a different tabindex (not tabview id). So how can that be the same?. I just tried and it works. So if you are sure it does not work, I do not understand the 'same reason as above' - Kukeltje
@Kukeltje - You're correct I was thinking on another path a new Tab would generate a new index. So tell me this because I'm not following, How is using a HashMap solving the problem of instantiating the Managed Bean multiple times? - Rod
There is no multiple instances of a managed bean. There never can be within one view. In the hasmap there are multiple instances of the same class. - Kukeltje

2 Answers

2
votes

Instead of trying to use multiple instances of a managed bean, use ONE managed bean that gives access to multiple instances of a class via e.g. a hashmap or arraylist or whatever you want to use. Just like you would in plain old java programming. You cannot have two variables with the same name:

@ViewScoped
@Named
public class RequestCalculations {

    Map<String, RequestCalculation> hm;

    @PostConstruct
    public init() {
        hm = new HashMap<>();
        // prepopulate if known upfront
        hm.put("1", new RequestCalculation());
        hm.put("2", new RequestCalculation());
    }

    public HashMap<String, RequestCalculation> getCalculations() {
        return hm;
    }
}

Then use the tabIndex of the tab as the key to the hashmap (or an array list). And in your xhtml do something like

#{requestCalculations.calculations[myTabIndex]}

You might need to pass this on to the include via a include param if you need this IN the include (as I think you do)

1
votes

If you want to use the multiple instances of manager bean, then you can declare it in faces-config.xml file with different names and use it independently. See example

<managed-bean>
    <managed-bean-name>productSearchForm</managed-bean-name>
    <managed-bean-class>com.company.package.ProductSearchForm</managed-bean-class>
    <managed-bean-scope>view</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>productChildFilter</managed-bean-name>
    <managed-bean-class>com.company.package.ProductSearchForm</managed-bean-class>
    <managed-bean-scope>view</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>productAttachFilter</managed-bean-name>
    <managed-bean-class>com.company.package.ProductSearchForm</managed-bean-class>
    <managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Edit for JSF 2.3:

If you are using CDI, then you can inject files with different names

@Inject
@ManagedProperty(value = "#{productSearchForm}")
private ProductSearchForm productSearchForm;

@Inject
@ManagedProperty(value = "#{productCandidateForm}")
private ProductSearchForm productCandidateForm;