2

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>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Rod
  • 43
  • 2
  • 4
  • Take the 'Map' solution [from here](https://stackoverflow.com/questions/13409444/binding-multiple-components-to-one-instance-in-backing-bean-primefaces-tree) and make sure that in each tab, e.g. the tabID is used as a key to the map. (duplicate?, not quite although...) – Kukeltje Mar 05 '15 at 14:58
  • 1
    @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 Mar 05 '15 at 17:37
  • 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 Mar 05 '15 at 22:13
  • @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 Mar 06 '15 at 13:06
  • 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 Mar 06 '15 at 13:52
  • @Kukeltje- you said you just did it. Could you show me what you did? I looked at that reference but it makes no sense to me. – Rod Mar 06 '15 at 14:59
  • Sorry, I don't have access to that code for the coming 24 hours. And typing one on my phone is not a good idea. But if you tell me what does not make sense, Ican maybe explain a little – Kukeltje Mar 06 '15 at 15:01
  • Ok. To my understanding when the jsf page is opened the managed bean is initialized waiting to be called on. So once I click my commandlink to open another instance of the form I have two tabs with the same form on one JSF page. A managed bean is nothing more than a Class, so if a managed bean can only be instantiated once in a view would that mean the class can only be instantiated once as well? or am I missing the concept of Managed Bean and the use of Classes? – Rod Mar 06 '15 at 15:09
  • Created a short example/answer on a public PC. Hope it answers your questsion. – Kukeltje Mar 06 '15 at 15:38
  • Did it answer your question? Then please accept (and upvote if it really helped) – Kukeltje Aug 05 '17 at 11:21

2 Answers2

2

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)

Kukeltje
  • 12,223
  • 4
  • 24
  • 47
1

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;
Vasil Lukach
  • 3,658
  • 3
  • 31
  • 40
  • The EL should differ from tab to tab. This can be done in the way you describe, but it is stil kind of static than. Using a map with instances of the class is better in my opinion. – Kukeltje Mar 05 '15 at 15:02
  • @Vasil Lukah -There are entirely too many classes to create an entry in faces-config.xml, not too mention there is no way to know how many duplicates will be needed at any given time. – Rod Mar 05 '15 at 17:36