myfaces-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Toppac <top...@gmail.com>
Subject Re: Converter.getAsString not called?
Date Tue, 10 Jul 2007 16:50:41 GMT

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