problem with f:validator when validatorId is an EL expression

Here is the scenario:

I am generating a datatable with h:inputText in it. The value for the inputText is coming from the database. The value is ediatble. The validator to be attached to each inputText is also coming from the database. But when i use an EL expression as the value of thevalidatorId attribute off:validator, it gets null value hence the error that validatorId cannot be null.

Here is the snippet of my code:

<t:dataTable value="#{agentHandler.agentTypePropertiesModel}"

var="agentTypeProperty" id="propertiesInfo" preserveDataModel="false">

<h:column>

<p align="right" class="pagecontents">

<h:outputText value="#{agentTypeProperty.property.displayName}"/>

</h:column>

<h:column>

<p align="left" class="pagecontents">

<h:inputText id="abc" value="#{agentTypeProperty.value}" valueChangeListener="#{agentHandler.setPropertyValue}" immediate="true">

<f:validator validatorId="#{agentTypeProperty.property.validator.jsfValidatorId}" />

</h:inputText>

<h:message for="abc" />

</h:column>

</t:dataTable>

If i use the same EL expression in h:outputText, it displays the value perfectly fine. I am stuck very badly with this. Can somebody pls. help me with this?

Another unsuccessful attempt:

- Then i thought of writing a ValidatorController which will internally call the appropriate validator based on an attribute validatorId But value attribute of f:attribute tag also reporting null for the same EL expression. The following is the code snippet of this failed attempt -

<h:inputText id="abc" value="#{agentTypeProperty.value}" valueChangeListener="#{agentHandler.setPropertyValue}" immediate="true">

<f:validator validatorId="soa.validatorController" />

<f:attribute name="validatorId" value="#{agentTypeProperty.property.validator.jsfValidatorId}"/>

</h:inputText>

<h:message for="abc" />

I would prefer to make the former working i.e the one with dynamic validatorId. If that is not possible then i will live with the second not-so-good approach. If both are not possible then pls. suggest me some other way to make this working.

Pls... Pls.. help me.. I am badly stuck.. I have already wasted almost 2 days on this. Its very urgent.....

Thanks in advance,

Ritesh

Message was edited by:

RJohar

[3103 byte] By [RJohara] at [2007-11-26 15:18:20]
# 1
This should just work. Is the agentTypeProperty managed bean request or session scoped? This sounds like if it is a request scoped bean and that the validatorId for the validator is retrieved at the time when the value is still null, or became garbaged.
BalusCa at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 2

Thanks for your reply buddy.

> This should just work.

I think so too. But it doesn't.

> Is the agentTypeProperty managed bean request or session scoped? This sounds like if it is a request scoped bean and that the validatorId for the validator is retrieved at the time when the value is still null, or became garbaged.

agentTypeProperty is not a managed bean at all. agentHandler is a managed bean. I have tried with both request and session scope but no luck. agentTypeProperty is defined as var for iterating thru the agentTypePropertiesModel:

<t:dataTable value="#{agentHandler.agentTypePropertiesModel}"

var="agentTypeProperty" id="propertiesInfo" preserveDataModel="false">

RJohara at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 3

Oh well, I overlooked the datatable =)

OK, it doesn't work in the session scope either? Well, this comes to mind .. Try something like:

JSF<h:dataTable binding="#{agentHandler.table}" ... >

...

<h:inputText ... >

<f:validator validatorId="#{agentHandler.validatorId}" />

...

AgentHandlerprivate HtmlDataTable table; // + getter + setter

public String getValidatorId() {

AgentTypeProperty agentTypeProperty = (AgentTypeProperty) table.getRowData();

return agentTypeProperty.getProperty().getValidator().getJsfValidatorId();

}

Edit:

I now see that you're using Tomahawk datatable with preserveDataModel="false". Try to change this to "true" otherwise. Or try to test it with h:dataTable of SUN JSF RI at least.

BalusCa at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 4
Just to mention again....If i use the same EL expression in h:outputText, it displays the value perfectly fine.
RJohara at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 5

I tried this but it doesn't work. I guess the reason is that validator is being set while rendering the datatable and rowdata is not yet available as part of the bound HtmlDataTable. I am getting the exception pasted at the end.

I cannot change preserveDataModel to true as this is an editable datatable. I have tried the above with t:datatable as well as h:datatable but NO LUCK...

com.sun.facelets.tag.TagAttributeException: /doc_convert_settings.jsp @90,76 validatorId="#{agentHandler.validatorId}" /doc_convert_settings.jsp @90,76 validatorId="#{agentHandler.validatorId}": com.scholarone.service.config.AgentHandler

at com.sun.facelets.tag.TagAttribute.getObject(TagAttribute.java:235)

at com.sun.facelets.tag.TagAttribute.getValue(TagAttribute.java:200)

at com.sun.facelets.tag.jsf.core.ValidateDelegateHandler.createValidator(ValidateDelegateHandler.java:51)

RJohara at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 6

> Just to mention again....

> If i use the same EL expression in h:outputText,

> it displays the value perfectly fine.

Yes, but the actual validation is invoked in another phase of the lifecycle. And my guess was that in this phase the rowdata isn't available yet somehow. And I have reproduced it with a code snippet.

This JSF piece didn't work, I got the error message that the validatorId is null:<h:form>

<h:dataTable value="#{myBean.list}" var="item" binding="#{myBean.table}">

<h:column>

<h:inputText value="#{item.value}">

<f:validator validatorId="#{item.validatorId}" />

</h:inputText>

</h:column>

</h:dataTable>

<h:commandButton value="submit" action="#{myBean.action}" />

</h:form>

But this one works:<h:form>

<h:dataTable value="#{myBean.list}" var="item" binding="#{myBean.table}">

<h:column>

<h:inputText value="#{item.value}">

<f:validator validatorId="#{myBean.validatorId}" />

</h:inputText>

</h:column>

</h:dataTable>

<h:commandButton value="submit" action="#{myBean.action}" />

</h:form>

MyBean (request scoped):public class MyBean {

private List list;

private HtmlDataTable table;

public void action() {

System.out.println(list);

}

public String getValidatorId() {

return ((MyData) table.getRowData()).getValidatorId();

}

public HtmlDataTable getTable() {

return table;

}

public List getList() {

if (list == null) {

loadList();

}

return list;

}

public void setTable(HtmlDataTable table) {

this.table = table;

}

public void setList(List list) {

this.list = list;

}

private void loadList() {

list = new ArrayList();

list.add(new MyData("value1", "myValidator"));

list.add(new MyData("value2", "myValidator"));

list.add(new MyData("value3", "myValidator"));

}

}

MyData:private String value;

private String validatorId;

// + getters + setters

Give it a try at your local development environment. If it works, then build further on it step by step.

BalusCa at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 7

Thanks a lot for your help..

i am able to get the validatorId from agentHandler but this has a issue... the control goes in my agentHandler.getValidatorId only once so all the inputTexts are assigned the same validator whatever is associated with the first one. Here is my code:

Snippet from AgentHandler:

private String validatorId;

private HtmlDataTable propsTable;

public HtmlDataTable getPropsTable() {return propsTable; }

public void setPropsTable(HtmlDataTable propsTable) {

this.propsTable = propsTable;

}

public String getValidatorId() {

System.out.println("inside AgentHandler.getValidatorId.. ");

AgentTypeProperty agentTypeProperty = (AgentTypeProperty) propsTable.getRowData();

validatorId = agentTypeProperty.getProperty().getValidator().getJsfValidatorId();

return validatorId;

}

jsp:

<h:dataTable value="#{agentHandler.agentTypePropertiesModel}" var="agentTypeProperty" id="propertiesInfo" binding="#{agentHandler.propsTable}">

<h:column>

<h:outputText value="#{agentTypeProperty.property.displayName}" />

</h:column>

<h:column>

<h:inputText id="abc" value="#{agentTypeProperty.value}" valueChangeListener="#{agentHandler.setPropertyValue}" immediate="true">

<f:validator validatorId="#{agentHandler.validatorId}" />

</h:inputText>

<h:message for="abc" />

</h:column>

</h:dataTable>

inside AgentHandler.getValidatorId.. gets printed only once whereas there are 5 rows. One of my field needs numbervalidator & other needs emailvalidator. Since the one with numberValidator comes before the one with emailValidator so the email field is also assigned the numberValidator only.

RJohara at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 8

Balus,

Since, i am like running out of time, so for now.. i have made my second approach using a ValidatorController and then based on the rowdata, pass on the control to the respective validator working. I have got the idea from a forum thread on your site: http://balusc.xs4all.nl/frm/list_messages/630

Only 1 problem i am facing now is that when the value is not valid, it shows the validation-error msg & the resets the value back to the original value. e.g. if i had 3 as value in a number field, i change it to junk & click save, the number validator is used & i see my custom not a number message but my value gets reset to 3 automatically.

Is this a known behavior? If not then what i might be doing wrong?

RJohara at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 9

> i am able to get the validatorId from agentHandler

> but this has a issue... the control goes in my

> agentHandler.getValidatorId only once so all the

> inputTexts are assigned the same validator whatever

> is associated with the first one.

Hmm, I see .. Nasty one :(

I've debugged for hours, I know I had this one earlier, but I can't find a way to keep the datamodel available during the validation.

But I've found another way to invoke the validation, also a nasty one, but it works =)

JSF<h:inputText value="#{agentTypeProperty.value}" validator="#{agentHandler.invokeValidator}" />

AgentHandlerpublic void invokeValidator(FacesContext context, UIComponent component, Object value)

throws ValidatorException

{

String validatorId = ((AgentTypeProperty) table.getRowData()).getProperty().getValidator().getJsfValidatorId();

Validator validator = context.getApplication().createValidator(validatorId);

validator.validate(context, component, value);

}

edit

Nice, you've found the solution already :D

> Only 1 problem i am facing now is that when the value is

> not valid, it shows the validation-error msg & the resets

> the value back to the original value. e.g. if i had 3 as value

> in a number field, i change it to junk & click save, the

> number validator is used & i see my custom not a number

> message but my value gets reset to 3 automatically.

> Is this a known behavior?

It looks like that the datamodel of the datatable is being reloaded.

Message was edited by:

BalusC

BalusCa at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 10

Balus,

Thanks a lot buddy.. you have been a great help.

I have changed my implementation to your invokeValidator way. It cleaner & more manageable than what i did.

For the values getting reset, you are right that the datamodel for the datatable is getting reloaded. But i don't know how to avoid the reload as that is controlled by jsf lifecycle. If you have any idea for that.. that will be really helpful.

Anyways.. i will live with the value reset for now...

You have been a great help & i don't have words to thank you for this. Only way i can think of right now is to award the duke dollars to you so i am doing that.

Thanks again,

Ritesh

RJohara at 2007-7-8 11:00:57 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...
# 11

Yw.

> For the values getting reset, you are right that the

> datamodel for the datatable is getting reloaded. But

> i don't know how to avoid the reload as that is

> controlled by jsf lifecycle. If you have any idea for

> that.. that will be really helpful.

I would say, put the data in session (session scoped bean or sessionmap) and only reload it manually after succesful action invocations.

BalusCa at 2007-7-8 11:00:58 > top of Java-index,Enterprise & Remote Computing,Web Tier APIs...