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 17:15:39 GMT
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.
>
>

Mime
View raw message