Skip to end of metadata
Go to start of metadata

Problem Description

Sometimes you want to provide a command component (eg a link or button) that performs some server action, and renders the same page but with completely "fresh" values for all components.

When using command components with the normal immediate setting (false), achieving this is just a matter of clearing the beans that the JSF component value attributes access. Any values entered by the user will have been pushed into these beans as part of the Update Model phase, so the components themselves will not be holding any information about submitted data. The action method associated with the command is then run which resets the model, and when the components render themselves they will draw fresh data from the (reset) beans.

Note that because data is being pushed into the model, the validation phase must run, and therefore any invalid data in the page will cause the action to be skipped, and the page is redisplayed with the validation errors displayed. This is not generally the desired behaviour for a "clear" type operation! The solution is to set attribute immediate=true on the command so that its associated action is invoked before validation is applied to the input components in the same view (see How The Immediate Attribute Works).

However when using command components with immediate=true, things become more complex. All components will retrieve the raw submitted values submitted by the user, but the immediate command will then run before they can be pushed into the backing beans; the components therefore remember this data. When the (immediate) action causes navigation to another view then this is no problem; these components will be discarded anyway. However if the action method causes JSF to go directly to the render phase 'of the same view' by calling facesContext.renderResponse(), then the components will behave as they do for a validation failure - by displaying the value cached in the component rather than fetching data from the backing bean.

Below are a number of possible solutions.

Force a new View

Call this method from the action method of the immediate command component:

java public void refresh() { FacesContext context = FacesContext.getCurrentInstance(); Application application = context.getApplication(); ViewHandler viewHandler = application.getViewHandler(); UIViewRoot viewRoot = viewHandler.createView(context, context .getViewRoot().getViewId()); context.setViewRoot(viewRoot); context.renderResponse(); //Optional }

This causes the current View tree to be discarded and a fresh one created. The new components of course then have no submitted values,
and so fetch their displayed values via their value-bindings.

Note: If you call this method (in its current form) from the valueChangeListener of an immediate component, be sure to call the value's setter manually first. Otherwise, you will lose the new value entirely, as the lifecycle is skipped and the view is recreated, leaving the new value stored in neither place.

Note: If you want to navigate to another page then omit the optional context.renderResponse(); and save the view with application.getStateManager().saveSerializedView(context);. This will avoid session to reference to the old view when you go back to the page (tested only in JSF RI 1.1.01).

In addition to immediate components, this method can be useful when working with multiple forms or subForms. <<BR>> Omit the last line "context.renderResponse()", when you don't want to skip the other life-cycle phases.

Delete Components Holding Unwanted State

Find the parent component of the problem inputs, and call

java parentComponent.getChildren().clear();

During the render phase, new instances of these child components will then be created, while other components will not be affected.

This is effectively the same as the above solution, but discards a selected subset of components rather than the UI!ViewRoot.

Obtaining the parent component to discard can be done via binding. Alternatively, the "action listener" form of callback can be used for the command; this is passed an !ActionEvent from which the command component that was clicked can be found. A call to "findComponent" can be made on this to locate the desired parent component by id, or other similar solutions.

Explicitly clear submitted value

For each component that you want to reset, specify a binding attribute so that the backing bean can access them. From the immediate component's action method, call:

java component.setSubmittedValue(null); // The following is only needed for immediate input components // but it won't do any harm in other situations.. component.setValue(null); component.setLocalValueSet(false);

This will cause that component to refetch its value via its value-binding when rendered. In JSF 1.2, a resetValue() convenience function was added for just this set of calls.

This only affects specific components, though. If you want to clear a whole form, you can bind some parent component, then explicitly walk the tree of child components testing each one for type !EditableValueHolder, and if so performing the above action. You could even do this by fetching the UI!ViewRoot component, and not need to use any component bindings at all.

There may be some complications with walking the component tree when the page contains a UIData component with input fields. The UIData uses a single set of UIComponent objects to represent all the rows in the table, and stores the state of each row separately from the components themselves. It is probably therefore necessary to do something like this to reset editable components within table rows:

java for(int i=0; i<uiDataComponent.rows(); ++i) { uiDataComponent.setRowIndex(i); // walk the child components, resetting any submitted value } uiDataComponent.setRowIndex(-1);

MyFaces Trinidad includes a <tr:resetActionListener> tag that automates this strategy of walking the tree, including tables.

Return a non-null navigation value

Commonly, null is returned from the action method of the immediate command component. This causes the current view to be re-rendered.
If this method returns a string that maps to the same view, then this will cause a ''new component tree'' for that page to be created
and rendered, rather than re-rendering the current component tree.

It's not currently clear whether this behaviour is required by the spec, or is implementation-specific (ie, only works in MyFaces). If you find something in the spec about this, please also update this page with that information.

  • No labels