3
votes

Recently, my question about OmniFaces Ajax.updateColumn() was answered in the following:

How to use OmniFaces Ajax.updateColumn() or Ajax.updateRow() via p:ajax

In that case, the dataTable was the JSF standard h:dataTable, and that worked great.

Now, in this case, I am attempting to update a PrimeFaces p:dataTable when enduser selects a date via p:calendar component that exists outside of the p:dataTable. The p:dataTable xhtml is as follows:

<p:dataTable id="flightDataTable" var="flight" 
             value="#{pf_flightController.flightsForOrder}">

    <p:column headerText="Airport" style="text-align: center !important;">
        <p:inputText value="#{flight.airportTx}" maxlength="25" size="10" label="Airport"/>
    </p:column>

    <p:column headerText="Airline" style="text-align: center !important;">
        <p:inputText value="#{flight.airlineTx}" maxlength="25" size="10" label="Airline"/>
    </p:column>

    <p:column headerText="Flight Number" style="text-align: center !important;">
        <p:inputText value="#{flight.flightNumber}" maxlength="8" size="10" label="Flight Number"/>
    </p:column>

    <p:column headerText="Type" style="text-align: center !important;">
        <h:selectOneMenu value="#{flight.flightType}" label="Flight Type">
            <f:selectItem itemValue="#{bundle.DropdownOptionValueArrive}" itemLabel="#{bundle.DropdownOptionLabelArrive}"/>
            <f:selectItem itemValue="#{bundle.DropdownOptionValueDepart}" itemLabel="#{bundle.DropdownOptionLabelDepart}"/>
        </h:selectOneMenu>
    </p:column>

    <p:column headerText="Date" style="text-align: center !important;">
        <h:panelGroup rendered="#{pf_usersController.loggedInViaAndroid == 'N'}">
            <p:calendar value="#{flight.flightDate}" navigator="true"
                        label="Flight Date"
                        pattern="MM/dd/yyyy" size="10">
                <f:convertDateTime pattern="MM/dd/yyyy" />
            </p:calendar>
        </h:panelGroup>
        <h:panelGroup rendered="#{pf_usersController.loggedInViaAndroid == 'Y'}">
            <p:inputText value="#{flight.flightDate}"
                         label="Flight Date" type="date">
                <f:convertDateTime pattern="yyyy-MM-dd" />
            </p:inputText>
        </h:panelGroup>
    </p:column>

    <p:column headerText="Time" style="text-align: center !important;">
        <h:panelGroup rendered="#{pf_usersController.loggedInViaAndroid == 'N'}">
            <pe:timePicker value="#{flight.flightTime}"
                           label="Flight Time" mode="popup"
                           showPeriod="true" style="width: 80px;">
                <f:convertDateTime pattern="hh:mm a" />
            </pe:timePicker>
        </h:panelGroup>
        <h:panelGroup rendered="#{pf_usersController.loggedInViaAndroid == 'Y'}">
            <p:inputText value="#{flight.flightTime}"
                         label="Flight Time" type="time">
                <f:convertDateTime pattern="HH:mm" />
            </p:inputText>
        </h:panelGroup>
    </p:column>

</p:dataTable>

Originally and currently in production, the p:calendar p:ajax update="..." updates the 'entire' flights p:dataTable, so the dates column in p:dataTable is updated, successfully. I know, i know, if it ain't broke, then don't fix it, but today I was modifying this code and xhtml, so I wanted to use OmniFaces Ajax utility's updateColumn() to update only 'one' column for all rows of this PrimeFaces p:dataTable.

Below is the code fragment that is executed when enduser selects date via p:calendar; this is the code that calls OmniFaces Ajax utility's updateColumn().

private void updateFlightDateOnAdd() {
    flightController.updateFlightDateOnAdd(current.getTripDateTime());

    UIData flightsDataTable = null;
    try {
        flightsDataTable = (UIData) Components.findComponent(":orderAddForm:flightDataTable");
    } catch (Exception e) {
        System.out.println("pf_OrdersController.updateFlightDateOnAdd(): caught exception on Components.findComponent(...)");
    }
    if (flightsDataTable != null) {
        System.out.println("pf_OrdersController.updateFlightDateOnAdd(): Components.findComponent(...) found " + flightsDataTable.getClientId());
        Ajax.updateColumn(flightsDataTable, 4);
    }
    else {
        System.out.println("pf_OrdersController.updateFlightDateOnAdd(): Components.findComponent(...) returned null");
    }
}

Below is the POJO definition:

public class FlightForOrder {
    private Integer flightId;
    private String airportTx, airlineTx, flightNumber;
    private Character flightType;
    private Date flightDate, flightTime;

    public FlightForOrder() {

    }

    public Integer getFlightId() {
        return flightId;
    }

    public void setFlightId(Integer flightId) {
        this.flightId = flightId;
    }

    public String getAirportTx() {
        return airportTx;
    }

    public void setAirportTx(String airportTx) {
        this.airportTx = airportTx;
    }

    public String getAirlineTx() {
        return airlineTx;
    }

    public void setAirlineTx(String airlineTx) {
        this.airlineTx = airlineTx;
    }

    public Date getFlightDate() {
        return flightDate;
    }

    public void setFlightDate(Date flightDate) {
        this.flightDate = flightDate;
    }

    public String getFlightNumber() {
        return flightNumber;
    }

    public void setFlightNumber(String flightNumber) {
        this.flightNumber = flightNumber;
    }

    public Date getFlightTime() {
        return flightTime;
    }

    public void setFlightTime(Date flightTime) {
        this.flightTime = flightTime;
    }

    public Character getFlightType() {
        return flightType;
    }

    public void setFlightType(Character flightType) {
        this.flightType = flightType;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 97 * hash + Objects.hashCode(this.flightId);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final FlightForOrder other = (FlightForOrder) obj;
        if (!Objects.equals(this.flightId, other.flightId)) {
            return false;
        }
        return true;
    }

}

While testing, the following showed up in the server log:

INFO: pf_OrdersController.updateFlightDateOnAdd(): Components.findComponent(...) found orderAddForm:flightDataTable

So, that means that I obtained a good reference of the PrimeFaces p:dataTable, so I passed the component of type UIData to Ajax.updateColumn().

The end result, the PrimeFaces dataTable was not updated by OmniFaces Ajax utility's updateColumn().

I just commented out Ajax.updateColumn(...) and re-added the update="..." to p:calendar p:ajax, and the PrimeFaces dataTable is updated, successfully.

Please advise on what I may need to do to ensure that the PrimeFaces dataTable is updated via Ajax.updateColumn(...). Thanks.

1

1 Answers

1
votes

Replacing

<p:column headerText="Date" style="text-align: center !important;">
    <h:panelGroup rendered="#{pf_usersController.loggedInViaAndroid == 'N'}">
        <p:calendar value="#{flight.flightDate}" navigator="true"
                    label="Flight Date"
                    pattern="MM/dd/yyyy" size="10">
            <f:convertDateTime pattern="MM/dd/yyyy" />
        </p:calendar>
    </h:panelGroup>
    <h:panelGroup rendered="#{pf_usersController.loggedInViaAndroid == 'Y'}">
        <p:inputText value="#{flight.flightDate}"
                     label="Flight Date" type="date">
            <f:convertDateTime pattern="yyyy-MM-dd" />
        </p:inputText>
    </h:panelGroup>
</p:column>

by

<p:column headerText="Date" style="text-align: center !important;">
    <p:calendar value="#{flight.flightDate}" navigator="true"
                label="Flight Date"
                pattern="MM/dd/yyyy" size="10"
                rendered="#{pf_usersController.loggedInViaAndroid == 'N'}">
        <f:convertDateTime pattern="MM/dd/yyyy" />
    </p:calendar>
    <p:inputText value="#{flight.flightDate}"
                 label="Flight Date" type="date"
                 rendered="#{pf_usersController.loggedInViaAndroid == 'Y'}">
        <f:convertDateTime pattern="yyyy-MM-dd" />
    </p:inputText>
</p:column>

should fix it.

Explanation: Ajax#updateColumn() adds only the client ID of the direct children to the PartialViewContext#getRenderIds(). However, a <h:panelGroup> which doesn't have any attributes which should end up in the resulting HTML output, such as id, doesn't render anything into the HTML output. Hence JS/Ajax cannot locate the HTML representation of the <h:panelGroup> in order to update it.

The fix makes the concrete content of the table cell the direct children instead and this way JS/Ajax will be able to locate and replace them. An alternative would be to give those panel groups a concrete id, but that's unnecessary in this construct.