1

In my project, on click of checkbox and add more button, I am trying to add one row and on click of remove button, remove exact row.

Sometimes I get wrong parameter values in controller methods. So the whole component generation logic isn't working properly.

Index.xhtml

<h:form>
    <h:panelGroup id="shipmentTermsBox">
        <c:forEach items="#{postOffer.shipmentTerms}" var="shipment" varStatus="shipmentCount">
            OuterIndex : #{shipmentCount.index}
            <h:selectBooleanCheckbox value="#{shipment.status}">
                <f:ajax event="change" listener="#{postOffer.addShipmentTermsRow(shipmentCount.index)}" render="shipmentTermsBox" />
            </h:selectBooleanCheckbox>
            <label for="stayin">#{shipment.name} </label>
            <br/>
            <table border="1">
                <c:forEach items="#{shipment.shipmentRowList}" var="shipmentRow" varStatus="shipmentRowCount">
                    <tr>
                        <td>
                            InnerIndex : #{shipmentRowCount.index}
                        </td>
                        <td>
                            #{shipmentRow.priceChoice}
                            <h:selectOneMenu value="#{shipmentRow.priceChoice}">
                                <f:selectItem itemValue="Above Price" itemLabel="Above Price"/>
                                <f:selectItem itemValue="+ (more)" itemLabel="+ (more)"/>
                                <f:selectItem itemValue="- (more)" itemLabel="- (more)"/>
                                <f:ajax event="change" listener="#{postOffer.processPriceDiffChoice(shipmentCount.index,shipmentRowCount.index)}" render="shipmentTermsBox"/>
                            </h:selectOneMenu>
                        </td>
                        <td>
                            #{shipmentRow.priceEnable}
                            <h:panelGroup rendered="#{shipmentRow.priceEnable}">
                                <h:inputText value="#{shipmentRow.price}">
                                    <f:ajax/>
                                </h:inputText>
                            </h:panelGroup>
                        </td>
                        <td>
                            <h:commandButton value="Remove">
                                <f:ajax event="action" listener="#{postOffer.removeShipmentTermsRow(shipmentCount.index,shipmentRowCount.index)}" render="shipmentTermsBox"/>
                            </h:commandButton>
                        </td>
                    </tr>
                </c:forEach>
            </table>
            <h:panelGroup rendered="#{shipment.status}">
                <h:commandButton value="Add More">
                    <f:ajax event="action" listener="#{postOffer.addShipmentTermsRow(shipmentCount.index)}" render="shipmentTermsBox"/>
                </h:commandButton>
                <br/><br/>
            </h:panelGroup>
        </c:forEach>
    </h:panelGroup>
</h:form>

PostOffer.java

@ManagedBean
@ViewScoped
public class PostOffer implements Serializable {
    private List<ShipmentProxy> shipmentTerms = new ArrayList<ShipmentProxy>();

    public PostOffer() {}

    @PostConstruct
    public void init() {
        shipmentTerms.add(new ShipmentProxy(1l, "FAS"));
        shipmentTerms.add(new ShipmentProxy(2l, "CFR"));
    }

    public void processPriceDiffChoice(int shipmentIndex, int rowIndex) {
        ShipmentRow row = shipmentTerms.get(shipmentIndex).getShipmentRowList().get(rowIndex);
        if (row.getPriceChoice().equals("Above Price")) {
            row.setPriceEnable(false);
        } else {
            row.setPriceEnable(true);
        }
    }

    public void addShipmentTermsRow(int shipmentIndex) {
        ShipmentProxy proxy = shipmentTerms.get(shipmentIndex);
        if (proxy.isStatus()) {
            proxy.getShipmentRowList().add(new ShipmentRow());
        } else {
            proxy.getShipmentRowList().clear();
        }
    }

    public void removeShipmentTermsRow(int shipmentIndex, int rowIndex) {
        shipmentTerms.get(shipmentIndex).getShipmentRowList().remove(rowIndex);
    }
    //getters and setters
}

ShipmentProxy.java

public class ShipmentProxy {
    private Long id;
    private boolean status;
    private String name;
    private List<ShipmentRow> shipmentRowList = new ArrayList<ShipmentRow>();

    public ShipmentProxy(Long id, String name) {
        this.id = id;
        this.name = name;
    }
    //getters and setters
}

ShipmentRow.java

public class ShipmentRow {
    private String priceChoice = "Above Price";
    private String price = "0";
    private boolean priceEnable = false;
    //getters and setters
}

Output :

enter image description here

What I am doing wrong? Is there any logical bug in my code?

Darshan Patel
  • 2,839
  • 2
  • 25
  • 38
  • You could safely ignore (or reduce) HTML boilerplate along with a manual JSTL `` loop, if you happened to use a corresponding iterating JSF component like ``. – Tiny Aug 12 '15 at 10:59
  • 1
    Which JSF impl/version? In any case, you've there a dynamic model, not a fixed model, so dynamically building the view isn't the right solution. The outer loop should be `` and inner loop should be ``. And instead of index, just pass `var` itself. – BalusC Aug 12 '15 at 11:04
  • Why does it create additional history even though subsequent edits have been made within five minutes by the same editor? – Tiny Aug 12 '15 at 11:05
  • @Tiny: Grace period will stop when a comment or answer was posted in meanwhile. This is indeed new since March (http://meta.stackexchange.com/questions/255845/wheres-my-5-minute-grace-period-gone) – BalusC Aug 12 '15 at 11:06
  • @BalusC I am using JSF 2.1 and I tried ``, `` and direct passing `var`, but still not working properly.. – Darshan Patel Aug 12 '15 at 11:58
  • "JSF 2.1" is a specification/API version. I was asking for implementation name (Mojarra or MyFaces) and its version. Regardless, tried the latest just to exclude an already long fixed bug from being the cause? – BalusC Aug 12 '15 at 12:00
  • It's printed in webapp's startup log. Search for an INFO line containing "Mojarra". – BalusC Aug 12 '15 at 12:20
  • thanks..Its `Mojarra 2.1.7`. I found this line in log : `Initializing Mojarra 2.1.7-jbossorg-1 (20120227-1401) for context`. Is it related with my dynamic component generation issue? – Darshan Patel Aug 12 '15 at 12:23
  • 1
    In Mojarra versions older than 2.1.18, the ViewScoped will behave like RequestScoped when bean properties are bound to JSTL tags, due to a chicken-egg bug in view state. See also http://stackoverflow.com/q/2842401. Furthermore, has quite some state saving related bugs in older versions. It's strongly recommended to upgrade to latest 2.1.x or perhaps 2.2.x. This way this major probable cause can be excluded. You can find JBoss upgrade instructions here: http://stackoverflow.com/q/17085717 – BalusC Aug 12 '15 at 12:32

1 Answers1

0

There is no logical bug in my code. It was just JSF bug in older version.

As per BalusC's comment, I have followed these steps :

  1. I have upgraded Mojarra(jsf-api-2.2.9.jar,jsf-impl-2.2.10.jar) in JBoss AS 7.1. Refer this
  2. I changed c:forEach loop to ui:repeat and h:dataTable in JSF page.
  3. Passed direct var instead of index in method parameter.

Now, the new changed code is looked something like this :

Index.xhtml

<h:form id="form">
    <h:panelGroup id="shipmentTermsBox">
        <ui:repeat value="#{postOffer.shipmentTerms}" var="shipment">
            <h:selectBooleanCheckbox value="#{shipment.status}">
                <f:ajax event="change" listener="#{postOffer.addShipmentTermsRow(shipment)}" render=":form:shipmentTermsBox" />
            </h:selectBooleanCheckbox>
            <label for="stayin">#{shipment.name} </label> <br/>
            <h:dataTable var="shipmentRow" value="#{shipment.shipmentRowList}">
                <h:column>
                    <h:selectOneMenu value="#{shipmentRow.priceChoice}">
                        <f:selectItem itemValue="Above Price" itemLabel="Above Price"/>
                        <f:selectItem itemValue="+ (more)" itemLabel="+ (more)"/>
                        <f:selectItem itemValue="- (more)" itemLabel="- (more)"/>                                                                   
                        <f:ajax event="change" listener="#{postOffer.processPriceDiffChoice(shipmentRow)}" render=":form:shipmentTermsBox"/>
                    </h:selectOneMenu>
                </h:column>
                <h:column>
                    <h:panelGroup rendered="#{shipmentRow.priceEnable}">
                        <h:inputText value="#{shipmentRow.price}">
                            <f:ajax/>
                        </h:inputText>
                    </h:panelGroup>
                </h:column>
                <h:column>
                    <h:commandButton value="Remove">
                        <f:ajax event="action" listener="#{postOffer.removeShipmentTermsRow(shipment,shipmentRow)}" render=":form:shipmentTermsBox"/>
                    </h:commandButton>
                </h:column>
            </h:dataTable>
            <h:panelGroup rendered="#{shipment.status}">
                <h:commandButton value="Add More">
                    <f:ajax event="action" listener="#{postOffer.addShipmentTermsRow(shipment)}" render=":form:shipmentTermsBox"/>
                </h:commandButton>
                <br/><br/>
            </h:panelGroup>
        </ui:repeat>
    </h:panelGroup>
</h:form>

PostOffer.java

public void processPriceDiffChoice(ShipmentRow row) {
    if (row.getPriceChoice().equals("Above Price")) {
        row.setPriceEnable(false);
    } else {
        row.setPriceEnable(true);
    }
}

public void addShipmentTermsRow(ShipmentProxy proxy) {
    if (proxy.isStatus()) {
        proxy.getShipmentRowList().add(new ShipmentRow());
    } else {
        proxy.getShipmentRowList().clear();
    }
}

public void removeShipmentTermsRow(ShipmentProxy proxy,ShipmentRow row) {
    proxy.getShipmentRowList().remove(row);
}

Note : Rest of the code is same exactly mentioned in question.

Community
  • 1
  • 1
Darshan Patel
  • 2,839
  • 2
  • 25
  • 38