Interesting idea. I will have to give it a try.
Andrew Robinson-5 wrote:
>
> Yes, it should jive, unless someone wrote a custom component extending
> UIInput that changes the behavior so that it doesn't agree with the
> specification.
>
> Process of validate:
> 1) check for submitted value, if none exit
> 2) get converter, and convert if found
> 3) check if valid, exit if not
> 4) validate the value
> 5) check if valid, exit if not
> 6) clear submitted value
> 7) set local value
> 8) check & fire value change event
>
> Process of update:
> 1) check if valid, else return
> 2) check if local value is set, else return
> 3) set the value on the value binding
> 4) clear the local value, and set the "localValueSet" property to false
>
> So, if you can modify the component in between the validation and the
> update because that is when the valueChangeEvent fires.
>
> So in update #2, you will see if the local value is not set, nothing
> happens. Thus in my example, I remove the local value to prevent the
> update.
>
> Renderers for UIInput will use the submitted value if it is present,
> and in my example, it is
>
>
> On 7/10/07, Toppac <[EMAIL PROTECTED]> wrote:
>>
>> Interesting idea. But how will this jive with the code already
>> implemented in
>> UIInput that is clearing the submitted value, setting the local value and
>> so
>> on? I think the converter getAsObject is called before any of those
>> values
>> are changed in the UIInput updateModel phase.
>>
>>
>>
>> Andrew Robinson-5 wrote:
>> >
>> > Perhaps you could leverage custom converters with the ValueChangeEvent
>> >
>> > The process is, convert, validate, clear submitted value, queue event.
>> >
>> > The event will be fired at the end of the phase, before the update
>> phase.
>> >
>> > At that time, you can clear the local value, and re-set the submitted
>> > value. That component will not update the model, since there would be
>> > no local value, and then if the submitted value is reset, then it
>> > would re-render the invalid value as desired.
>> >
>> > public class SubmittedValueBean implements Converter {
>> > private final static String ORIG_SUBMITTED_KEY =
>> > "original-submitted-value";
>> >
>> > public Object getAsObject(FacesContext context, UIComponent component,
>> > String value) {
>> > UIInput input = (UIInput)component;
>> > input.getAttributes().remove(ORIG_SUBMITTED_KEY);
>> > boolean valid = true;
>> > Object val = value.toString(); // attempt to convert the value here.
>> > You could use
>> > // <f:attribute> to store information on how to convert the data
>> > or something like that
>> > if (!valid) {
>> > input.getAttributes().put(ORIG_SUBMITTED_KEY, value);
>> > return null;
>> > }
>> > else {
>> > return val;
>> > }
>> > }
>> >
>> > public void onValueChange(ValueChangeEvent evt) {
>> > UIInput input = (UIInput)evt.getComponent();
>> > String origSubmitted = (String)input.getAttributes().get(
>> > ORIG_SUBMITTED_KEY);
>> > if (origSubmitted != null) {
>> > input.setSubmittedValue(origSubmitted);
>> > input.setValue(null);
>> > input.setLocalValueSet(false);
>> > }
>> > }
>> >
>> >
>> >
>> > On 7/9/07, Toppac <[EMAIL PROTECTED]> wrote:
>> >>
>> >> The only problem with this approach is I already have a domain model
>> that
>> >> i
>> >> have written a custom resolver for, so I can bind directly to it.
>> Going
>> >> this
>> >> approach I would have to copy the value from the backing bean to my
>> >> domain
>> >> model. I don't want to have to maintain a copy like that.
>> >>
>> >>
>> >>
>> >> Scott O'Bryan wrote:
>> >> >
>> >> > :) That was going to be my suggestion.
>> >> >
>> >> > Andrew Robinson wrote:
>> >> >> Another solution you could consider, is doing the
>> >> >> conversion/validation in the backing bean instead of in the
>> component,
>> >> >> it is messy, but possibly less messy than what I told you. It is
>> more
>> >> >> work though.
>> >> >>
>> >> >> public class Bean {
>> >> >> private Integer intValue;
>> >> >> private String submittedIntValue;
>> >> >> // standard get/set properties here for intValue
>> >> >>
>> >> >> public String getSubmittedValue() {
>> >> >> if (submittedIntValue == null) { return intValue.toString(); }
>> >> >> return submittedIntValue();
>> >> >> }
>> >> >> public void setSubmittedValue(String value) {
>> >> >> try {
>> >> >> submittedIntValue = value;
>> >> >> if (value == null || value.length() == 0) { intValue = null; }
>> >> >> intValue = Integer.parseInt(value);
>> >> >> submittedIntValue = null;
>> >> >> } catch (NumberFormatException ex) { }
>> >> >> }
>> >> >> ...
>> >> >>
>> >> >> <h:inputText value="#{bean.submittedIntValue}" />
>> >> >>
>> >> >> Since the backing bean takes any string, you will never get
>> conversion
>> >> >> or validation errors and update model will always take place. Then
>> >> >> since the inputText is valid, there is no submitted value and no
>> local
>> >> >> value, and thus it would get the value from the value binding. Thus
>> it
>> >> >> is the bean that is doing the work of the conversion and
>> validation.
>> >> >>
>> >> >> -Andrew
>> >> >>
>> >> >> On 7/6/07, Toppac <[EMAIL PROTECTED]> wrote:
>> >> >>>
>> >> >>> Unfortunately I was afraid this may be the answer. I knew from
>> >> >>> looking at the
>> >> >>> code that it may require heavy modfications and a departure from
>> the
>> >> JSF
>> >> >>> spec. Hopefully I can get the requirements changed. Thanks.
>> >> >>>
>> >> >>>
>> >> >>> Andrew Robinson-5 wrote:
>> >> >>> >
>> >> >>> > The problem is really that you are going against the UIInput
>> part
>> >> of
>> >> >>> > the JSF specification. By definition, during the
>> processValidators
>> >> >>> > method, if a UIInput component is found to be invalid, then
>> >> >>> > renderResponse is called on the current facescontext.
>> >> >>> >
>> >> >>> > Since you want control of the component's submitted value,
>> perhaps
>> >> >>> > your best bet is component binding or a phase listener, or you
>> >> could
>> >> >>> > even queue custom events.
>> >> >>> >
>> >> >>> > I can't see an easy way for you do this without a significant
>> hack
>> >> >>> > though. Especially, trying to reset submitted values will get
>> >> >>> > especially ugly for components in iterating parents (data table,
>> >> data
>> >> >>> > list, tree2, etc.).
>> >> >>> >
>> >> >>> > The easiest hack I would say is to create a phase listener that
>> >> >>> > listens for the before and after validation phase. In that phase
>> >> >>> > listener, replace the faces context. Crude example:
>> >> >>> >
>> >> >>> > public void beforePhase(PhaseEvent evt) {
>> >> >>> > new CustomFacesContext(evt.getFacesContext());
>> >> >>> > }
>> >> >>> > public void afterPhase(PhaseEvent evt) {
>> >> >>> > ((CustomFacesContext)evt.getFacesContext()).unwrap();
>> >> >>> > }
>> >> >>> > private class CustomFacesContext extends FacesContextWrapper
>> >> >>> > {
>> >> >>> > private FacesContext wrapped;
>> >> >>> > public CustomFacesContext(FacesContext orig) {
>> >> >>> > super(orig);
>> >> >>> > wrapped = orig;
>> >> >>> > FacesContext.setCurrentInstance(this);
>> >> >>> > }
>> >> >>> >
>> >> >>> > public void renderResponse() { /* swallow */ }
>> >> >>> >
>> >> >>> > public void unwrap() { wrapped.setFacesContext(wrapped); }
>> >> >>> > }
>> >> >>> >
>> >> >>> >
>> >> >>> > Now despite invalid UIInput components, the phases will
>> continue. I
>> >> >>> > haven't thought through all the ramifications though. You may
>> get
>> >> some
>> >> >>> > ugly side-effects from doing this.
>> >> >>> >
>> >> >>> > -Andrew
>> >> >>> >
>> >> >>> > On 7/6/07, Toppac <[EMAIL PROTECTED]> wrote:
>> >> >>> >>
>> >> >>> >> I am using a custom converter to return null so I can bypass
>> these
>> >> >>> >> conversion
>> >> >>> >> errors during submission. What I want to happen is that even
>> when
>> >> I
>> >> >>> >> bypass
>> >> >>> >> these and have my converter return null, I want to have a way
>> to
>> >> >>> inject
>> >> >>> >> the
>> >> >>> >> submitted value back in during renderResponse. This does not
>> >> >>> appear to
>> >> >>> >> work
>> >> >>> >> with MyFaces out of the box. There is not hook to the converter
>> >> >>> for some
>> >> >>> >> reason and the local value of the component is null. That is
>> the
>> >> >>> problem
>> >> >>> >> I
>> >> >>> >> am trying to solve.
>> >> >>> >>
>> >> >>> >>
>> >> >>> >>
>> >> >>> >> Andrew Robinson-5 wrote:
>> >> >>> >> >
>> >> >>> >> > Just about all components check their member values before
>> they
>> >> >>> check
>> >> >>> >> > the value bindings. UIOutput is no different. So if either
>> the
>> >> >>> >> > submittedValue or local value is set, your ValueBinding
>> >> expression
>> >> >>> >> > will not be called. The local value should be converted
>> before
>> >> >>> being
>> >> >>> >> > rendered however.
>> >> >>> >> >
>> >> >>> >> > If you don't want to throw a conversion error when the user
>> >> enters
>> >> >>> >> > "abc", then write a custom converter that doesn't throw an
>> >> >>> error. You
>> >> >>> >> > could turn "abc" in to null or 0, based on your requirements
>> for
>> >> >>> >> > example.
>> >> >>> >> >
>> >> >>> >> > If you use custom validators and custom converters, you can
>> make
>> >> >>> it so
>> >> >>> >> > that exceptions are never thrown from converting and
>> validation.
>> >> >>> >> >
>> >> >>> >> > Out of curiosity, why do you want to update the model when
>> there
>> >> is
>> >> >>> >> > invalid data?
>> >> >>> >> >
>> >> >>> >> > If you are only wanting to submit one value and don't care if
>> >> other
>> >> >>> >> > values are invalid, then I would suggest using the subForm
>> >> >>> component
>> >> >>> >> > from the sandbox or the a4j:region if you are using ajax4jsf.
>> >> >>> >> >
>> >> >>> >> > -Andrew
>> >> >>> >> >
>> >> >>> >> > On 7/6/07, Toppac <[EMAIL PROTECTED]> wrote:
>> >> >>> >> >>
>> >> >>> >> >> Ok I follow. There a couple of problems here though. let's
>> say
>> >> my
>> >> >>> >> >> converter
>> >> >>> >> >> is for java.lang.Integer and the submitted value from the
>> POST
>> >> is
>> >> >>> >> "abc".
>> >> >>> >> >> I
>> >> >>> >> >> can't use the default converter since it will throw an
>> >> >>> exception and
>> >> >>> >> try
>> >> >>> >> >> to
>> >> >>> >> >> skip directly to renderResponse, not what i want to happen.
>> >> >>> Right now
>> >> >>> >> my
>> >> >>> >> >> converter is just returning null at this point and caches
>> the
>> >> >>> >> submitted
>> >> >>> >> >> value. The rest of the lifecycle goes through and during
>> update
>> >> >>> model
>> >> >>> >> the
>> >> >>> >> >> submitted value is set to null since updateModel succeeds.
>> >> >>> >> >>
>> >> >>> >> >> During renderResponse it appears that it tries to get the
>> value
>> >> >>> from
>> >> >>> >> the
>> >> >>> >> >> component first, and if the component does not have a value
>> it
>> >> >>> tries
>> >> >>> >> to
>> >> >>> >> >> get
>> >> >>> >> >> it from evaluating the value binding (see
>> UiOutput.getValue).
>> >> >>> In this
>> >> >>> >> >> case
>> >> >>> >> >> they are both null, which is expected. But it never tries to
>> >> >>> call my
>> >> >>> >> >> converter which would restore the cached submitted valued.
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >> Andrew Robinson-5 wrote:
>> >> >>> >> >> >
>> >> >>> >> >> > Typical UIInput behavior:
>> >> >>> >> >> >
>> >> >>> >> >> > Decode phase ->
>> >> >>> >> >> > Is there a value in the POST values with the current
>> >> component's
>> >> >>> >> client
>> >> >>> >> >> > ID?
>> >> >>> >> >> > If so, set the submitted value to that
>> >> >>> >> >> >
>> >> >>> >> >> > Validate phase ->
>> >> >>> >> >> > If there is a submitted value, get the converter
>> >> >>> >> >> > If there is a converter, convert the submitted value using
>> >> >>> >> getAsObject
>> >> >>> >> >> > Validate the submitted value
>> >> >>> >> >> > If valid, set the local value
>> >> >>> >> >> >
>> >> >>> >> >> > Update phase ->
>> >> >>> >> >> > If there is a local value, update the value binding
>> property
>> >> >>> >> >> >
>> >> >>> >> >> > Render phase ->
>> >> >>> >> >> > If there is a submitted value, render that
>> >> >>> >> >> > Otherwise, get the value from the component
>> >> >>> >> >> > If there is a converter, convert the value using
>> getAsString
>> >> >>> >> >> > Render the value
>> >> >>> >> >> >
>> >> >>> >> >> > So as you can see, as long as a UIInput control has a
>> >> submitted
>> >> >>> >> value,
>> >> >>> >> >> > it will never render the value from the value attribute of
>> >> the
>> >> >>> >> >> > component. Typically submitted values are only cleared in
>> the
>> >> >>> >> validate
>> >> >>> >> >> > method of UIInput (if the converted value is valid)
>> >> >>> >> >> >
>> >> >>> >> >> > On 7/6/07, Toppac <[EMAIL PROTECTED]> wrote:
>> >> >>> >> >> >>
>> >> >>> >> >> >> I don't think I follow what you are saying exactly. Can
>> you
>> >> >>> >> elaborate?
>> >> >>> >> >> >>
>> >> >>> >> >> >>
>> >> >>> >> >> >>
>> >> >>> >> >> >> Andrew Robinson-5 wrote:
>> >> >>> >> >> >> >
>> >> >>> >> >> >> > Converter will only be called if there is no submitted
>> >> value.
>> >> >>> >> >> >> > Submitted values are already technically converted as
>> they
>> >> >>> came
>> >> >>> >> from
>> >> >>> >> >> >> > the client, so there is no need to use the converter.
>> >> >>> >> >> >> >
>> >> >>> >> >> >> > On 7/6/07, Toppac <[EMAIL PROTECTED]> wrote:
>> >> >>> >> >> >> >>
>> >> >>> >> >> >> >> Just for a small background. I am trying to find a way
>> to
>> >> >>> fail
>> >> >>> >> >> >> validation
>> >> >>> >> >> >> >> and/or conversion without dumping out of the JSF
>> >> >>> lifecycle to
>> >> >>> >> >> render
>> >> >>> >> >> >> >> response. I'd like to inject a custom converter that
>> when
>> >> it
>> >> >>> >> fails
>> >> >>> >> >> to
>> >> >>> >> >> >> >> convert it saves the submitted value to a session
>> scoped
>> >> >>> Map and
>> >> >>> >> >> then
>> >> >>> >> >> >> >> during
>> >> >>> >> >> >> >> render response, when it tries to render the component
>> >> that
>> >> >>> >> failed
>> >> >>> >> >> >> >> conversion, the getAsString method would see that the
>> >> >>> incoming
>> >> >>> >> >> value
>> >> >>> >> >> >> is
>> >> >>> >> >> >> >> null
>> >> >>> >> >> >> >> and would go to the session map to grab the last
>> >> submitted
>> >> >>> >> value.
>> >> >>> >> >> >> >>
>> >> >>> >> >> >> >> Sounds easy enough and should work. But when I tried
>> it,
>> >> it
>> >> >>> >> appears
>> >> >>> >> >> >> that
>> >> >>> >> >> >> >> during render response, if the value on the domain
>> model
>> >> >>> is null
>> >> >>> >> >> (or
>> >> >>> >> >> >> if
>> >> >>> >> >> >> >> the
>> >> >>> >> >> >> >> component value is null, not sure) it does not call
>> the
>> >> >>> >> >> >> >> converter.getAsString method. I am not sure why. If
>> >> >>> someone can
>> >> >>> >> >> tell
>> >> >>> >> >> >> me
>> >> >>> >> >> >> >> why
>> >> >>> >> >> >> >> and where it makes this decision that would be great.
>> If
>> >> >>> this is
>> >> >>> >> a
>> >> >>> >> >> bug
>> >> >>> >> >> >> >> then
>> >> >>> >> >> >> >> great also. But if it is not a bug then can anyone
>> >> >>> suggest a way
>> >> >>> >> to
>> >> >>> >> >> do
>> >> >>> >> >> >> >> what
>> >> >>> >> >> >> >> I am trying to do?
>> >> >>> >> >> >> >> --
>> >> >>> >> >> >> >> View this message in context:
>> >> >>> >> >> >> >>
>> >> >>> >> >> >>
>> >> >>> >> >>
>> >> >>> >>
>> >> >>>
>> >>
>> http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11472287
>> >> >>>
>> >> >>> >> >> >> >> Sent from the MyFaces - Users mailing list archive at
>> >> >>> >> Nabble.com.
>> >> >>> >> >> >> >>
>> >> >>> >> >> >> >>
>> >> >>> >> >> >> >
>> >> >>> >> >> >> >
>> >> >>> >> >> >>
>> >> >>> >> >> >> --
>> >> >>> >> >> >> View this message in context:
>> >> >>> >> >> >>
>> >> >>> >> >>
>> >> >>> >>
>> >> >>>
>> >>
>> http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11472464
>> >> >>>
>> >> >>> >> >> >> Sent from the MyFaces - Users mailing list archive at
>> >> >>> Nabble.com.
>> >> >>> >> >> >>
>> >> >>> >> >> >>
>> >> >>> >> >> >
>> >> >>> >> >> >
>> >> >>> >> >>
>> >> >>> >> >> --
>> >> >>> >> >> View this message in context:
>> >> >>> >> >>
>> >> >>> >>
>> >> >>>
>> >>
>> http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11472718
>> >> >>>
>> >> >>> >> >> Sent from the MyFaces - Users mailing list archive at
>> >> Nabble.com.
>> >> >>> >> >>
>> >> >>> >> >>
>> >> >>> >> >
>> >> >>> >> >
>> >> >>> >>
>> >> >>> >> --
>> >> >>> >> View this message in context:
>> >> >>> >>
>> >> >>>
>> >>
>> http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11473052
>> >> >>>
>> >> >>> >> Sent from the MyFaces - Users mailing list archive at
>> Nabble.com.
>> >> >>> >>
>> >> >>> >>
>> >> >>> >
>> >> >>> >
>> >> >>>
>> >> >>> --
>> >> >>> View this message in context:
>> >> >>>
>> >>
>> http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11473455
>> >> >>>
>> >> >>> Sent from the MyFaces - Users mailing list archive at Nabble.com.
>> >> >>>
>> >> >>>
>> >> >>
>> >> >
>> >> >
>> >> >
>> >>
>> >> --
>> >> View this message in context:
>> >>
>> http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11509233
>> >> Sent from the MyFaces - Users mailing list archive at Nabble.com.
>> >>
>> >>
>> >
>> >
>>
>> --
>> View this message in context:
>> http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11524847
>> Sent from the MyFaces - Users mailing list archive at Nabble.com.
>>
>>
>
>
--
View this message in context:
http://www.nabble.com/Converter.getAsString-not-called--tf4038047.html#a11525710
Sent from the MyFaces - Users mailing list archive at Nabble.com.