myfaces-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Christian Beikov <christian.bei...@gmail.com>
Subject Re: Clear Input Components after validation or conversion error with JSF 2.1
Date Wed, 28 Mar 2012 18:23:49 GMT
Specify the phaselistener as global one in the facesconfig.xml and adapt
the findComponent method to ignore naming containers when looking for the
render-ids then every use case should be handeled by the listener and
everything should work as expected.

Regards,
Christian
Am 28.03.2012 18:13 schrieb "Michael Heinen" <mhn4dev@googlemail.com>:

> Afaik these global ActionListeners are not called for behaviors (f:ajax or
> a4j:ajax attached to a component) so it would not work for all kind of ajax
> requests. Is this correct?
>
> In my case it does also not help to run only over the parent form of the
> command which submitted the request because I have multiple forms on my
> pages. A command could render areas inside other forms and those
> EditableValueHolders would not be reset with this approach in subsequent
> requests.
>
> I can run over the submitted form only during the request that results in
> a valdiation/conversion error but in this case the INVOKE_APPLICATION phase
> is not invoked. That's the dilemma.
>
> So I'll change my "ResetInputPhaseListener" from after RESTORE_VIEW to
> before INVOKE_APPLICATION for better state handling.
>
> Thanks,
> Michael
>
>
>
> Am 28.03.2012 16:42, schrieb Martin Koci:
>
>> Michael Heinen píše v St 28. 03. 2012 v 16:22 +0200:
>>
>>> Hi,
>>>
>>> thanks to Martin and Christan for your answers.
>>>
>>> Trinidad and primefaces are not used in my project so far.
>>> I had a quick look and it seems that the tr:resetActionListener as well
>>> as the primefaces extension must be attached to all commands and I have
>>> nearly 600. So I am looking for a more generic way.
>>>
>> Interesting requirement.
>>  Try custom ActionListenerImpl. The default implementation is [1]. This
>> code implements the "action" feature at h:commandButton/link. You can
>> provide own implementation, find parent UIForm and clear all
>> EditableValueHolder in this UIForm. This is generally  faster as
>> interating over whole UIViewRoot. Also submittedValues are part of
>> application state and manipulation of those value is responsibility of
>> invoke-application phase.
>> Register your implementation in faces-config.xml as:
>>
>> <application>
>>   <action-listener>my.reset.**action.Listener</action-**listener>
>> </application>
>>
>>
>> [1]
>> https://svn.apache.org/repos/**asf/myfaces/core/trunk/impl/**
>> src/main/java/org/apache/**myfaces/application/**ActionListenerImpl.java<https://svn.apache.org/repos/asf/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/application/ActionListenerImpl.java>
>>
>>  Christias solution did not work in detail in combination with richfaces
>>> 4.2.Final.
>>> The renderIds are not full qualified client ids and therefore component
>>> lookups fail.
>>> Moreover I would not reset self-rendered outputpanels with this approach.
>>>
>>> But I have a workaorund now.
>>> I use also a PhaseListener that runs afterRestoreView phase over the
>>> complete view tree(!) and resets all EditableValueOlders.
>>> At first I thought this would cost too much performance to run over the
>>> complete view and to do it on every request.
>>> But to my surprise it takes only one millisecond on my dev machine.
>>>
>>> However I would prefer to reset only submitted components and only in
>>> case of a validation/conversion error after rendering the invalid values
>>> and before state saving.  From my pov this should be done always
>>> internally in JSF.
>>> Or does it make any sense to keep the local/submitted values after a
>>> conversion/validation error occured and after the response is rendered?
>>>
>>> Thanks again,
>>> Michael
>>>
>>>
>>>
>>> Am 26.03.2012 17:35, schrieb Christian Beikov:
>>>
>>>> Hey,
>>>>
>>>> the following PhaseListener implementation will do the trick, at least
>>>> this is what I would expect that should be done by jsf in general.
>>>>
>>>> public class ResetInputPhaseListener implements PhaseListener {
>>>>
>>>>     private static final Logger log =
>>>> LoggerFactory.getLogger(**ResetInputPhaseListener.class)**;
>>>>
>>>>     @Override
>>>>     public void afterPhase(PhaseEvent pe) {
>>>>         if
>>>> (pe.getFacesContext().**getPartialViewContext().**isAjaxRequest()) {
>>>>             Collection<String>  renderIds =
>>>> pe.getFacesContext().**getPartialViewContext().**getRenderIds();
>>>>             UIViewRoot view = pe.getFacesContext().**getViewRoot();
>>>>
>>>>             for (String renderId : renderIds) {
>>>>                 UIComponent comp = findComponent(view, renderId);
>>>>
>>>>                 if (comp != null) {
>>>>                     recursiveReset(comp);
>>>>                 } else {
>>>>                     log.warn("Could not find component with id '" +
>>>> renderId + "' in view '" + view.getViewId() + "'");
>>>>                 }
>>>>             }
>>>>         }
>>>>     }
>>>>
>>>>     /**
>>>>      * Originally taken from Mojarra implementation of UIComponentBase
>>>> with the
>>>>      * addition of special checking when the NamingContainer is of the
>>>> type UIForm.
>>>>      */
>>>>     private UIComponent findComponent(UIComponent comp, String expr) {
>>>>         if (expr == null) {
>>>>             throw new NullPointerException();
>>>>         }
>>>>
>>>>         FacesContext ctx = FacesContext.**getCurrentInstance();
>>>>         final char sepChar = UINamingContainer.**getSeparatorChar(ctx);
>>>>         final String SEPARATOR_STRING = String.valueOf(sepChar);
>>>>
>>>>         if (expr.length() == 0) {
>>>>             // if an empty value is provided, fail fast.
>>>>             throw new IllegalArgumentException("\"\"**");
>>>>         }
>>>>
>>>>         // Identify the base component from which we will perform our
>>>> search
>>>>         UIComponent base = comp;
>>>>         if (expr.charAt(0) == sepChar) {
>>>>             // Absolute searches start at the root of the tree
>>>>             while (base.getParent() != null) {
>>>>                 base = base.getParent();
>>>>             }
>>>>             // Treat remainder of the expression as relative
>>>>             expr = expr.substring(1);
>>>>         } else if (!(base instanceof NamingContainer)) {
>>>>             // Relative expressions start at the closest
>>>> NamingContainer or root
>>>>             while (base.getParent() != null) {
>>>>                 if (base instanceof NamingContainer) {
>>>>                     break;
>>>>                 }
>>>>                 base = base.getParent();
>>>>             }
>>>>         }
>>>>
>>>>         // Evaluate the search expression (now guaranteed to be
>>>> relative)
>>>>         UIComponent result = null;
>>>>         String[] segments = expr.split(SEPARATOR_STRING);
>>>>         for (int i = 0, length = (segments.length - 1);
>>>>                 i<  segments.length;
>>>>                 i++, length--) {
>>>>             result = findComponent(base, segments[i], (i == 0));
>>>>             // the first element of the expression may match base.id
>>>>             // (vs. a child if of base)
>>>>             if (i == 0&&  result == null
>>>> &&  segments[i].equals(base.getId(**))) {
>>>>                 result = base;
>>>>             }
>>>>             if (result != null&&  (!(result instanceof
>>>> NamingContainer))&&  length>  0) {
>>>>                 throw new IllegalArgumentException(**segments[i]);
>>>>             }
>>>>             if (result == null) {
>>>>                 break;
>>>>             }
>>>>             base = result;
>>>>         }
>>>>
>>>>         // Return the final result of our search
>>>>         return (result);
>>>>     }
>>>>
>>>>     private UIComponent findComponent(UIComponent base, String id,
>>>> boolean checkId) {
>>>>         if (checkId&&  id.equals(base.getId())) {
>>>>             return base;
>>>>         }
>>>>         // Search through our facets and children
>>>>         UIComponent result = null;
>>>>         for (Iterator i = base.getFacetsAndChildren(); i.hasNext();) {
>>>>             UIComponent kid = (UIComponent) i.next();
>>>>             // Special handling for UIForm because of the attribute
>>>> prependId
>>>>             if (!(kid instanceof NamingContainer) || (kid instanceof
>>>> UIForm&&  !((UIForm) kid).isPrependId()&&  !id.equals(kid.getId())))
{
>>>>                 if (checkId&&  id.equals(kid.getId())) {
>>>>                     result = kid;
>>>>                     break;
>>>>                 }
>>>>                 result = findComponent(kid, id, true);
>>>>                 if (result != null) {
>>>>                     break;
>>>>                 }
>>>>             } else if (id.equals(kid.getId())) {
>>>>                 result = kid;
>>>>                 break;
>>>>             }
>>>>         }
>>>>         return (result);
>>>>
>>>>     }
>>>>
>>>>     private void recursiveReset(UIComponent comp) {
>>>>         if (comp instanceof EditableValueHolder) {
>>>>             ((EditableValueHolder) comp).resetValue();
>>>>         }
>>>>
>>>>         for (Iterator<UIComponent>  i = comp.getFacetsAndChildren();
>>>> i.hasNext();) {
>>>>             recursiveReset(i.next());
>>>>         }
>>>>     }
>>>>
>>>>     @Override
>>>>     public void beforePhase(PhaseEvent pe) {
>>>>     }
>>>>
>>>>     @Override
>>>>     public PhaseId getPhaseId() {
>>>>         return PhaseId.RESTORE_VIEW;
>>>>     }
>>>> }
>>>>
>>>>
>>>> Am 26.03.2012 17:22, schrieb Martin Koci:
>>>>
>>>>> Hi,
>>>>>
>>>>> normal JSF  behaviour (as you already know) is preserving and
>>>>> resdisplaying the invalid input to allow re-type it later, for example:
>>>>> '"12-14-200001" can be understood as date': a user can fix it with
>>>>> minimal effort only by removing two additional zeros.
>>>>>
>>>>> The classic solution for your problem is [1] tr:resetActionListener or
>>>>> [2] pe:resetEditableValues. Do those two solve your task or is it other
>>>>> problem?
>>>>>
>>>>>  a) How do I get the JSF 1.2 behavior with 2.1? Is resetValue() the
>>>>>>
>>>>> wrong method?
>>>>>
>>>>> This is the right method.
>>>>>
>>>>>
>>>>>  b) Ideally I would reset only the executed components instead of all
>>>>>> input fields of the form. How can I determine them?
>>>>>>     With richfaces 3.3.3 I checked for request keys representing
the
>>>>>> submitted region or the ajaxSingle key. is there anything similar
for
>>>>>> ajax commands and JSF 2.1?
>>>>>>
>>>>> for example pe:resetEditableValues has attribute 'for' or the standard
>>>>> key javax.faces.partial.execute says it.
>>>>>
>>>>>
>>>>>  c) Shouldn't there be out of the box support for this szenario?
>>>>>>
>>>>> Yes, in JSF 2.2 will be such thing as pe:resetEditableValues in core
>>>>> (in
>>>>> prefix f:)
>>>>>
>>>>> [1]
>>>>> http://myfaces.apache.org/**trinidad/trinidad-api/tagdoc/**
>>>>> tr_resetActionListener.html<http://myfaces.apache.org/trinidad/trinidad-api/tagdoc/tr_resetActionListener.html>
>>>>>
>>>>> [2]
>>>>> http://fractalsoft.net/**primeext-showcase-mojarra/**
>>>>> views/resetEditableValues.jsf<http://fractalsoft.net/primeext-showcase-mojarra/views/resetEditableValues.jsf>
>>>>>
>>>>>
>>>>> Michael Heinen píše v Po 26. 03. 2012 v 15:22 +0200:
>>>>>
>>>>>> So I would like to clear the component values AFTER rendering and
>>>>>> BEFORE
>>>>>> state saving.
>>>>>> Is this possible at all?
>>>>>> Or do I have any better alternatives with JSF 2.1?
>>>>>>
>>>>>> Thanks,
>>>>>> Michael
>>>>>>
>>>>>> Am 26.03.2012 13:49, schrieb Michael Heinen:
>>>>>>
>>>>>>> Hi all,
>>>>>>>
>>>>>>> I refer to the problem described at:
>>>>>>> https://cwiki.apache.org/**confluence/display/MYFACES/**
>>>>>>> Clear+Input+Components#<https://cwiki.apache.org/confluence/display/MYFACES/Clear+Input+Components#>
>>>>>>>
>>>>>>>
>>>>>>> Workflow:
>>>>>>> 1) Form is submitted
>>>>>>> 2) Validation or conversion fails
>>>>>>> 3) Another command is clicked that loads new or "fresh" data
into the
>>>>>>> same area
>>>>>>> Problem:
>>>>>>> The data of request (1) is shown instead of (3) because the submitted
>>>>>>> or local values of the components are not cleared after
>>>>>>> validation/conversion error.
>>>>>>>
>>>>>>> Solution for JSF 1.2
>>>>>>> I patched com.sun.facelets.**FaceletViewHandler and cleared the
>>>>>>> UIInput
>>>>>>> components via resetValue() before the new state was saved.
>>>>>>> As a result new (fresh) data was shown correctly and the submitted
>>>>>>> value was also shown directly after the validation/conversion
error.
>>>>>>>
>>>>>>> With JSF 2.1 this does not work the same way.
>>>>>>> - I patched
>>>>>>> org.apache.myfaces.view.**facelets.**FaceletViewDeclarationLanguage*
>>>>>>> *.renderView
>>>>>>>
>>>>>>> - Before accessing the state manager I iterate over the
>>>>>>> clientIdsWithMessages, determine the correspoding form and call
>>>>>>> resetValue for all inputComponents.
>>>>>>> - As a result new (fresh) data is shown correctly after step
(3)
>>>>>>> - But the submitted value is NOT shown directly after the
>>>>>>> validation/conversion error (step 2). Instead the original value
is
>>>>>>> shown.
>>>>>>>    (e.g. User clears required value and submits form, message
'Field
>>>>>>> must not be empty' is shown and field contains the original value
>>>>>>> instead of being empty)
>>>>>>>
>>>>>>> Questions:
>>>>>>> a) How do I get the JSF 1.2 behavior with 2.1? Is resetValue()
the
>>>>>>> wrong method?
>>>>>>> b) Ideally I would reset only the executed components instead
of all
>>>>>>> input fields of the form. How can I determine them?
>>>>>>>     With richfaces 3.3.3 I checked for request keys representing
the
>>>>>>> submitted region or the ajaxSingle key. is there anything similar
for
>>>>>>> ajax commands and JSF 2.1?
>>>>>>> c) Shouldn't there be out of the box support for this szenario?
>>>>>>>
>>>>>>> Used versions:
>>>>>>> MyFaces 2.1.6 with partial state saving activated
>>>>>>> Tomahawk2 1.1.11
>>>>>>> Richfaces 4.2.0 Final
>>>>>>>
>>>>>>> Regards,
>>>>>>> Michael
>>>>>>>
>>>>>>
>>
>>
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message