Friday, October 16, 2009

Reset the content of a web page

Resetting the page contents or undoing the changes made by a user is a very common use case scenario in Rich Internet Applications. The 'reset action' should skip all validations defined on the current page and page contents needs to restored from the underlying data layer. Some of the web frameworks support this feature out of the box. Let me try explaining possible ways to reset a page when developing applications using ADFFaces.

1. <af:resetButton>

As the name suggests, af:resetButton resets the contents of a form. Please note that developer doesn't have much control here as he/she might not be able to bind any action method with this built in reset button.
<af:resetButton  text="ResetButton" id="rb1"/>
2. <af:resetActionListener>

This tag is usually bound to an action source like command button to reset all submitted values whenever an action is triggered. Obviously this gives more control to the developer as the action sources like command button can be optionally bound to an action method through the Expression Language(EL). Please note that, you may need to keep immediate="true" for the command button ( for the action source ) to skip the validations while implementing the reset/cancel behavior.
  
<af:commandButton text="Cancel with resetAction" id="cb2"  immediate="true"   
     partialSubmit="true"
     actionListener="#{SomeBean.cancelBusinessAction}">
     <af:resetActionListener/>
</af:commandButton>

Why do we need to reset submitted values?

Each component has local cache value. When you submit a form, submitted value gets assigned to this localValue at the end of 'PROCESS VALIDATIONS' pahse, 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.

3. Custom Reset Implementation

So far so good, but in some scenarios developers may need to dirty their hands a bit to get the job done - where the declarative 'reset' support is missing. I noticed a rare use case scenario recently, that calls for custom reset implementation. There master-detail data is displayed in tabular form and user is allowed to edit one master record (and it's children as well) at a time, then save that record and proceed to next. In case user decided to navigate to next record without saving current record, then current changes needs to be cancelled. Though use case is bit odd one, adding this feature is pretty simple. All we need to is override the default selectionListener and from this custom method, parse the component tree and call reset on each element.
Code Snippet:
   
 public void customSelectionHandler(SelectionEvent selectionEvent) {

     UIComponent source = (UIComponent)selectionEvent.getSource();
     UIComponent uiComp = _getRootToReset(source);
     _resetChildren(uiComp);
     EL.invokeMethod(
     "#{bindings.DepartmentsView11.collectionModel.makeCurrent}",
     SelectionEvent.class, selectionEvent);
}
   

On a related note, I would suggest you to look at the source of org.apache.myfaces.trinidadinternal.taglib.listener.ResetActionListener, that may help you to understand the 'reset' concept much better. (Even the below given sample application just tries to reuse the same reset logic from ResetActionListener class).

You can download the sample workspace from here.

12 comments:

Anonymous said...

I have inputText field:





When I enter values in another field then <af:inputText id="CropTypeKiExplain" are changing. How to Rollback to the orginal value, when button Cancel pressed?

Best regards, Kristaps!

Anonymous said...

I have inputText field :
<af:inputText id="CropTypeKiExplain"

When I enter values in another field then <af:inputText id="CropTypeKiExplain" are changing. How to Rollback to the orginal value, when button Cancel pressed?

Best regards, Debuger!

Jobinesh said...

Kristaps,
If you use ADF BC for implementing your business service, then you can create a save point and roll back to that later. You can opt for ADFController/ADFModel Save points. For ADFC, the following blog by Edwin may help you http://biemond.blogspot.com/2008/04/restoring-transactions-with-adf.html.
For the second option, the you can try the below
Save State:
String savePointId = yourAMImpl.passivateStateForUndo(null,null,0);
--------------
Restore State on Cancel:
yourAMImpl.activateStateForUndo(savePointId,0);

Anonymous said...

Hi, Jobinesh! Can You please give me example of savePoint operations?

Maybe You can implement this Save State:
String savePointId = yourAMImpl.passivateStateForUndo(null,null,0);
--------------
Restore State on Cancel:
yourAMImpl.activateStateForUndo(savePointId,0);

on Your test case, that we can download?

If You need mail, please ask. I realy want to see this on Your test case, it is possible?

Best regards, Kristaps

Anonymous said...

Hi, Jobinesh! PLZ can You explain this savePoint actions on Your test case and send it to me? I don't understand what to do, where to put your writen code and where?

Best Ragards, Kristaps

Jobinesh said...

sure...will upload a sample shortly

Anonymous said...

HI, Jobinesh! I downloaded today new version of Your app. Is it newer version with saveState?

And can You please explain which files You modified and how can I test Your app to see result?

I realy want to implement functionality to my app. so that my app. would work correctly.

Waiting for Your response, best regards, Kristaps

Jobinesh said...

See this post as well http://jobinesh.blogspot.com/2010/09/undoing-changes-using-middle-tier.html

Anonymous said...

Hi, Jobinesh! I add comments to Your newer post, but they disappear. :( So I write there. I want to thanks for Your newer post and Your answers. But I still in problems. So I made may test case on Your test case example, You can download it there:

http://www.failiem.lv/down.php?i=xecqbw&n=UndoChangesSampleWithAjax.zip

You don't need to change DB conn. in app, You should use my db conn. So You can run sample-task-flow-definition. Then You would be on record with DepartmentId = 10 and DepartmentName = 1. Near DepartmentName You can see ReadOnly input text field, with explaining text - value = low. This field is calculated using java class. Please change DepartmentName to value 2. Explaining text would be with value 'high'. When I press Cancel, then the field DepartmentName would have orginal value, but explaining text near it would not be changed to orginal value ('low' that was before changes). Hope You understand, if no, please let me know it. Waiting for Your response and how to resolve my problem.

Best regards, Kristaps

Jobinesh said...

Kristaps,
Uploaded the modified sample.http://adf-samples.googlecode.com/files/UndoChangesSampleWithAjaxModified.zip
You may need to clear the viewScope variable manually. The approach explained in the blog restores your model(ADF BC) layer, not the View specific data

subu said...

SomeBean.cancelBusinessAction

may i know what goes in this method.

subu said...

SomeBean.cancelBusinessAction

may i know what goes in this method.