myfaces-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Andrew Robinson" <andrew.rw.robin...@gmail.com>
Subject Re: Converter.getAsString not called?
Date Tue, 10 Jul 2007 19:54:30 GMT
Yes that is a problem. Okay, another approach.:

<my:keepSubmitted>
  <h:inputText value="#{bean.value}" />
</my:keepSubmitted>

public class UIKeepSubmitted extends UIComponentBase {
  public void processValidators(FacesContext context) {
    UIInput child = (UIInput)getChildren().get(0);
    Converter converter = child.getConverter();
    boolean localConverter = false;
    String submitted = child.getSubmittedValue();
    if (converter != null) {
      localConverter = child.getValueBinding("converter") == null;
      child.setConverter(new ConverterWrapper(converter));
    }
    super.processValidators(context);
    if (Boolean.FALSE.equals(child.getAttributes().get(ConverterWrapper.VALID_FLAG)))
{
      child.setSubmittedValue(submitted);
      child.setLocalValue(null);
      child.setLocalValueSet(false);
    }
    child.setConverter(localConverter ? converter : null);
  }
}

public class ConverterWrapper implements Converter {
  public final static String VALID_FLAG = "UIInputConversionSucceeded";
  private Converter orignal;
  public ConverterWrapper(Converter orignal) {
    this.original = original;
  }
  public Object getAsObject(FacesContext context, UIComponent component,
    String value) throws ConverterException {
    boolean valid = true;
    try {
      Object obj = original.getAsObject(context, component, value);
      if (!component.isValid()) {
        component.setValid(true);
        valid = false;
      }
      return obj;
    } catch (ConverterException ex) {
      component.setValid(true);
      valid = false;
    } finally {
      component.getAttributes().put(VALID_FLAG, Boolean.valueOf(valid));
    }
  }

  public String getAsString(FacesContext context, UIComponent component,
    Object value) throws ConverterException {
    return original.getAsString(context, component, value);
  }
}

If you want to use validators, you will have to wrap them too.

On 7/10/07, Toppac <toppac@gmail.com> wrote:
>
> Small problem with this approach. If a component begins with value of Null
> and my custom converter returns a value of null for invalid values, the
> value change never fires.
>
>
> 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 <toppac@gmail.com> 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 <toppac@gmail.com> 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 <toppac@gmail.com> 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 <toppac@gmail.com> 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 <toppac@gmail.com>
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 <toppac@gmail.com>
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 <toppac@gmail.com>
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#a11527672
> Sent from the MyFaces - Users mailing list archive at Nabble.com.
>
>

Mime
View raw message