Yeah, I've tried doing that for some jsf projects as well. Ultimately
I've found though that putting this stuff on top of sheer business logic
cuts down on flexibility. Still, I'm not sure as you would need to
replicate the entire bean. You can bind a managed bean which retrieves
the instance of your domain model for the properties you care to
validate and then after validation it simply delegates to the proper
getter and setter.
It may be slightly inefficient but it should work and your data would
still be located directly on your model.
Scott
Toppac 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.