0

I tried c:set and ui:param, but in the end I think I did something wrong.

Here is the crucial XHTML part:

    <h:form id="formTable">
    <p:dataTable editable="true" value="#{types.dataList}" var="datalist" style="width:90%; margin-bottom:20px"
        rows="10" paginator="true" rowsPerPageTemplate="10,25" resizableColumns="true" liveResize="true" tableStyle="width:auto">
        <!-- save the old value for the edit action-->
        <c:set var="oldValue" value="#{datalist.getType()}"/>
        <!-- column for testing purpose-->
        <p:column>
            <h:outputText value="#{oldValue}" />
        </p:column>
        <p:column headerText="Type" sortBy="#{datalist.type}">
            <p:cellEditor>
                <f:facet name="output">
                    <h:outputText value="#{datalist.type}" />
                </f:facet>
                <f:facet name="input">
                    <h:inputText value="#{datalist.type}" binding="#{inputUpdateType}" required="true" />
                </f:facet>
            </p:cellEditor>
        </p:column>
        <p:column headerText="Date" sortBy="#{datalist.lastModifyDate}">
            <h:outputText value="#{datalist.lastModifyDate}"/>
        </p:column>
        <p:column headerText="Edit">
            <p:rowEditor rendered="#{!datalist.dependenciesFound}"/>
            <h:graphicImage value="../images/attention.png" rendered="#{datalist.dependenciesFound}" />
        </p:column>
        <p:column headerText="Delete">
            <p:commandLink action="#{types.delete(datalist.type)}" rendered="#{!datalist.dependenciesFound}" update=":formTable">
                <p:graphicImage url="../images/delete.png" />
                <p:confirm header="Confirmation" message="Are you sure?" icon="ui-icon-alert" />
            </p:commandLink>
            <h:outputText value="Not editable because of dependency" rendered="#{datalist.dependenciesFound}"/>
        </p:column>
        <!-- We edit/update the value here. This is where the old value is needed-->
        <p:ajax event="rowEdit" listener="#{types.update(oldValue, inputUpdateType.value)}"/>
    </p:dataTable>
    <p:confirmDialog global="true" showEffect="fade" hideEffect="fade">
        <p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" icon="ui-icon-check" />
        <p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no" icon="ui-icon-close" />
    </p:confirmDialog>
</h:form>

I hope this is somewhat understandable. For my project I use Primefaces 6.1. To edit a row in this table I need to save the old value and use that value in the ajax call at the bottom. I need this, because the application or rather the DB table behind this demands it (id is this value). The edited value is stored in the variable inputUpdateType.

My problem:

When I enter a new value and hit edit, the old value is the same as the edited value, when the application enters the update method. I made a log entry in my update method to verify what values are passed. Same with c:set and ui:param.

What am I doing wrong? How can I make this work?

EDIT: Here is how I try to get the old value stored in my bean:

<p:ajax event="rowEdit" listener="#{types.update(types.dataListOld.toArray()[rowIndex].getType(), inputUpdateType.value)}"/>
kinglite
  • 339
  • 3
  • 20
  • Start by reading https://stackoverflow.com/questions/3342984/jstl-in-jsf2-facelets-makes-sense. You cannot assign a var via jstl `c:set` in an iteratimg h/ui/p component. Thw jstl is processed before them – Kukeltje Nov 30 '17 at 16:02
  • Ok, so from what I can read there, it is not possible to do it like I tried it. Is there another way? I now edited my entity bean so that it can store the old value and it works, but I don't like this solution. – kinglite Dec 05 '17 at 11:07
  • Keep an instance of the old entity in your controller and compare with that? And why do you need to pass this value from the ui layer to the control/service layer? It should already be (made) available server side. – Kukeltje Dec 05 '17 at 11:12
  • I tried to keep the old value in an extra instance, but this was not working too well: The entities are stored in a collection. To get to the old value I used the rowIndex of the table to get the needed entity and its value. Unfortunately this only works, if the table was not sorted (Primefaces). If the sort function was used, the original collection is not the same as it was when I created the old values instance. So when comparing the values based on the index it does not match anymore. Is there a good way to do this? – kinglite Dec 05 '17 at 15:21
  • And I don't get what you mean by "It should already be (made) available server side." Could you please elaborate? Maybe this could be the solution to my problem. – kinglite Dec 05 '17 at 15:31
  • I thought about your sentence and I think the ajax call is responsible that the old value is not available server side. If I recall right, the values are updated before the execution. – kinglite Dec 06 '17 at 07:33

1 Answers1

0

So, it seems with ajax the values will be updated before executing the method. Therefore the old value is not available on server side anymore. Unfortunately storing the variable in JSF is also not possible because c:set will be processed before the JSF tags and will thus not hold any values (without a scope it will act as an alias and in the end also been updated before the method gets executed).

To make this whole thing work I used cell edit mode instead of the default row edit mode. With this I'm able to get the old and new value:

    public void onCellEdit(CellEditEvent event){
    //We will call this method on edits made to a cell
    //Values are stored in a Arraylist. Number of entries depend on how many components there are in the edited cell/facet
    //Here we have two because in "input" there are two components defined (although both are rendered at the same time).
    @SuppressWarnings("unchecked")
    ArrayList<String> oldValue = ((ArrayList<String>)(event.getOldValue()));
    @SuppressWarnings("unchecked")
    ArrayList<String> newValue = ((ArrayList<String>)(event.getNewValue()));

    //In the end both entries have the same value, so we just take the first.
    if(newValue != null && !newValue.equals(oldValue)) {
        update(oldValue.get(0), newValue.get(0));
        FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Cell Changed", "Old: " + oldValue.get(0) + ", New:" + newValue.get(0));
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }       
}

Here is why I have two entries in my values list (two components in "input"):

    <f:facet name="input">
        <h:inputText value="#{datalist.type}" binding="#{inputUpdateType}" required="true" rendered="#{!datalist.dependenciesFound}"/>
        <h:outputText value="#{datalist.type}" rendered="#{datalist.dependenciesFound}"/>
    </f:facet>
</p:cellEditor>

kinglite
  • 339
  • 3
  • 20