Why does programmatic refresh fail for child table when the parent table has immediate=true?

Story so far...
There is a master table with immediate=true and a custom selectionListener. An editable(clickToEdit) child table needs to be refreshed programmatically from master table's custom selectionListener.

  <af:table id="masterTabl" immediate="true"   
    selectionListener="#{MyBean.customSelectionHandlerForParentTable}" ...  
  <af:table id="childTabl" partialTriggers="::masterTabl"   
    editingMode="clickToEdit" ...  
  public void customSelectionHandlerForParentTable(SelectionEvent selectionEvent) {  
   RichTable childTable = getChildTable();  
   refreshChildTableCollectionModel()  
   RequestContext.getCurrentInstance().addPartialTarget(childTable);  
  }  

Now the fun starts...Though everything looks perfect, child table fails to reflect the refreshed CollectioModel (which is updated from the SelectionEvent of master table). Child table just repaints the previously submitted values from UI as is, not the updated model :(

What goes wrong here?

Well, a variant of this specific case has been discussed in one of my previous post -Reset the content of a web page. Let me explain few basic concepts from the perspective of the above use case.

What does happen when you set immediate="true" on a component?

When you keep immediate="true" for a UI component, the normal JSF life cycle for that component gets short circuited as explained in the following section. btw, please see this topic 'Life Cycle of a JSF Page' if you are not familiar with JSF life cycle concept as such.


• What happens when you set immediate="true" set on an ActionSource(e.g. af:commandButton) component?

The ActionEvent for this component is normally delivered in 'Invoke Application Phase'. When immediate is set true the ActionEvent delivery is moved up to the 'Apply Request Values Phase'. An ActionEvent always results in 'renderResponse' being called implicitly in the default ActionListener whether or not an "action" is attached.

• What happens when you set immediate="true" on an EditableValueHolder(e.g. af:inputText) component?

The validation and valueChangeEvent for this component usually take place in the 'Process Validations Phase'. When immediate is true the component's value will be converted and validated in the 'Apply Request Values Phase', and ValueChangeEvents will be delivered in that phase as well. Please note that framework is not going to skip any lifecycle phase even if you keep immediate=true. You may need to call FacesContext::renderResponse() if you really want to by pass the rest of the phases from the listener.

How does 'component renderer' treat submitted value of the component?

Each UI component has local cache value. When you submit a form, submitted value gets assigned to this localValue at the end of 'Process Validations Phase', and thereafter submitted value is set as null. During the 'Render Response', first consult the local value property of this component. If non-null return it. If null, see if we have a ValueExpression for the value property. UIInput::resetValue() reset the submitted value that would force the rendering logic to take the value from the model/binding layer.

Enough theory, let me answer the question now :)

Solution

In the above use case when you select a parent row, the submitted content contains other editable fields from the form as well(including the child table's editable fields too). As you have immediate="true" set for parent table, SelectionEvent gets fired as part of Apply Request Values Phase. If you 'blindly' refresh the CollectionModel for the child table at this stage, it does not really help you because the 'submitted form' holds set of editable components and they are lined up to go through normal JSF life cycle phases. JSF runtime will use values of these submitted fields for model update later point, which may even override the the model changes done by you in the early phase of life cycle. Apparently, solution for the above use case is to clear the submitted values from the components which needs to be refreshed with updated model. For input UI components (such as af:inputText), you can call UIInput::resetValue() on each component to clear the submitted value. For stamped components like af:table, you are supposed to call resetStampState(). Now your custom selection listener for parent table may look like as shown in the following code snippet.

  public void customSelectionHandlerForParentTable(SelectionEvent selectionEvent) {  
   RichTable childTable = getChildTable();  
   childTable.resetStampState();  
   refreshChildTableCollectionModel()  
   RequestContext.getCurrentInstance().addPartialTarget(childTable);  
  }  



Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Comments

  1. That really saved my day, thanks.

    ReplyDelete
  2. hello sir,

    i am new to adf.
    in my project have header,static menu (left side),content pane is center

    i want to create declarative component for content pane title(header) and i am using that dc whenever i want
    ???
    please save

    ReplyDelete
  3. That was exactly what i was searching for... thank you

    ReplyDelete
  4. Thank you so much! You saved my life)))

    ReplyDelete
  5. Thank you so mush. That saved my life!!

    ReplyDelete

Post a Comment