JSF 2 验证器不能使用“var”来自数据表

发布于 2024-10-10 02:53:50 字数 4516 浏览 2 评论 0原文

我们最近将系统从 JSF 1.2 升级到 JSF 2.0,并且正在使一切正常运行。但是,当在数据表或类似组件中使用验证器时,我们遇到了问题。基本上,问题是验证器无法使用数据表设置的变量。

下面是一个示例:

VALIDATOR:

package test;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

@FacesValidator("test.TestValidator")
public class TestValidator implements Validator {

    private Integer length;

    public TestValidator() {
        super();
    }

    @Override
    public void validate(FacesContext context,
            UIComponent component, Object value) throws ValidatorException {

        String text = (String) value;
        if (text == null || text.trim().length() == 0) {
            return;
        }

        if (length != null && text != null && length.intValue() < text.length()) {
            String message = "The text is too long. It was " + text.length() +
                    ", but only " + length + " characters are allowed.";
            FacesMessage fm = new FacesMessage(
                    FacesMessage.SEVERITY_ERROR, message, null);
            throw new ValidatorException(fm);
        }
    }

    public Integer getLength() {
        return length;
    }

    public void setLength(Integer length) {
        this.length = length;
    }
}

TAGLIB:

<?xml version="1.0" encoding="UTF-8"?>
    <facelet-taglib
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
         version="2.0">
        <namespace>http://industry-supply.dk/test</namespace>
        <tag>
            <tag-name>testValidator</tag-name>
            <validator>
                <validator-id>test.TestValidator</validator-id>
            </validator>
            <attribute>
                <name>length</name>
                <required>true</required>
                <type>java.lang.Integer</type>
            </attribute>
        </tag>
    </facelet-taglib>

MANAGED BEAN:

package test;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean(name = "testBean")
@RequestScoped
public class TestBean {

    public TestBean() {
    }

    public String[] getKey() {
        return new String[]{
                    "0",
                    "1",
                    "2",
                    "3"
                };
    }

    public String[] getValue() {
        return new String[]{
                    "This is a text and it's too long.",
                    "This is a text and it's too long.",
                    "This is a text and it's too long.",
                    "This is a text and it's too long."
                };
    }
}

JSF:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:test="http://industry-supply.dk/test">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:dataTable value="#{testBean.key}" var="k">
                <h:column>
                    <h:message for="val"/>
                    <h:inputText id="val" value="#{testBean.value[k]}">
                        <test:testValidator length="#{testBean.key[k]}"/>
                    </h:inputText>
                </h:column>
            </h:dataTable>
            <h:commandButton value="Submit"/>
        </h:form>
    </h:body>
</html>

运行项目时,会显示 4 个输入字段和一个命令按钮。每个输入字段包含一个 33 个字符长的文本。当您点击“提交”时,出现错误消息“文本太长。它是 33,但只允许 0 个字符。”显示每行/字段。这是错误的,因为“test:testValidator length="#{testBean.key[k]}"" 将第一行的长度指定为 0,第二行的长度为 1,第三行的长度为 2,第四行的长度为 3。因此,对于最后一行,错误消息应该是:“文本太长。它是 33,但只允许 3 个字符。”。

问题是验证器似乎无法从 JSF 文件中的 dataTable 组件访问 k 变量。这在 JSF 1.2 中有效,但我们无法让它在 JSF 2.0 中工作。我们已经花了几天时间来解决这个问题,确实需要一些帮助。有人有想法吗?

We've recently upgraded our systems from JSF 1.2 to JSF 2.0 and are in the process of making everything work. However, we're experiencing problems with validators, when used inside data tables or similar components. Basically, the problem is that the validator can't use the variable set by the data table.

Here's an example:

VALIDATOR:

package test;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

@FacesValidator("test.TestValidator")
public class TestValidator implements Validator {

    private Integer length;

    public TestValidator() {
        super();
    }

    @Override
    public void validate(FacesContext context,
            UIComponent component, Object value) throws ValidatorException {

        String text = (String) value;
        if (text == null || text.trim().length() == 0) {
            return;
        }

        if (length != null && text != null && length.intValue() < text.length()) {
            String message = "The text is too long. It was " + text.length() +
                    ", but only " + length + " characters are allowed.";
            FacesMessage fm = new FacesMessage(
                    FacesMessage.SEVERITY_ERROR, message, null);
            throw new ValidatorException(fm);
        }
    }

    public Integer getLength() {
        return length;
    }

    public void setLength(Integer length) {
        this.length = length;
    }
}

TAGLIB:

<?xml version="1.0" encoding="UTF-8"?>
    <facelet-taglib
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
         version="2.0">
        <namespace>http://industry-supply.dk/test</namespace>
        <tag>
            <tag-name>testValidator</tag-name>
            <validator>
                <validator-id>test.TestValidator</validator-id>
            </validator>
            <attribute>
                <name>length</name>
                <required>true</required>
                <type>java.lang.Integer</type>
            </attribute>
        </tag>
    </facelet-taglib>

MANAGED BEAN:

package test;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean(name = "testBean")
@RequestScoped
public class TestBean {

    public TestBean() {
    }

    public String[] getKey() {
        return new String[]{
                    "0",
                    "1",
                    "2",
                    "3"
                };
    }

    public String[] getValue() {
        return new String[]{
                    "This is a text and it's too long.",
                    "This is a text and it's too long.",
                    "This is a text and it's too long.",
                    "This is a text and it's too long."
                };
    }
}

JSF:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:test="http://industry-supply.dk/test">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form>
            <h:dataTable value="#{testBean.key}" var="k">
                <h:column>
                    <h:message for="val"/>
                    <h:inputText id="val" value="#{testBean.value[k]}">
                        <test:testValidator length="#{testBean.key[k]}"/>
                    </h:inputText>
                </h:column>
            </h:dataTable>
            <h:commandButton value="Submit"/>
        </h:form>
    </h:body>
</html>

When running the project, 4 input fields and a command button are shown. Each input field contains a 33 character long text. When you click "submit", the error message "The text is too long. It was 33, but only 0 characters are allowed." is shown for each row/field. This is wrong, because "test:testValidator length="#{testBean.key[k]}"" specifies length to 0 for the first row, 1 for the second row, 2 for the third row and 3 for the fourth row. So for the last row, the error message should have said: "The text is too long. It was 33, but only 3 characters are allowed.".

The problem is that the validator doesn't seem to have access to the k-variable from the dataTable component in the JSF file. This worked in JSF 1.2, but we can't get it to work in JSF 2.0. We've spend days on the problem now and really need some help. Ideas anyone?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

鹿童谣 2024-10-17 02:53:50

好吧,看来现在一切正常了。我按照 McDowell 的建议创建了一个自定义 ValidatorHandler。这是我的更改:

TAGLIB:

<handler-class>test.CustomValidatorHandler</handler-class>

VALIDATOR:

package test;

import javax.el.ValueExpression;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

@FacesValidator("test.TestValidator")
public class TestValidator implements Validator {

    public TestValidator() {
        super();
    }

    @Override
    public void validate(FacesContext context,
            UIComponent component, Object value) throws ValidatorException {

        String text = (String) value;
        if (text == null || text.trim().length() == 0) {
            return;
        }

        Integer length = getLengthValue();

        if (length != null && text != null && length.intValue() < text.length()) {
            String message = "The text is too long. It was " + text.length() +
                    ", but only " + length + " characters are allowed.";
            FacesMessage fm = new FacesMessage(
                    FacesMessage.SEVERITY_ERROR, message, null);
            throw new ValidatorException(fm);
        }
    }

    private ValueExpression lengthExpression;

    public ValueExpression getLength() {
        return lengthExpression;
    }

    public void setLength(ValueExpression length) {
        this.lengthExpression = length;
    }

    public Integer getLengthValue() {
        Integer length = null;
        if (lengthExpression != null) {
            length = (Integer) lengthExpression.getValue(
                    FacesContext.getCurrentInstance().getELContext());
        }
        return length;
    }
}

VALIDATOR HANDLER:

package test;

import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.ValidatorConfig;
import javax.faces.view.facelets.ValidatorHandler;

public class CustomValidatorHandler extends ValidatorHandler {

    private ValueExpression lengthExpression;

    public CustomValidatorHandler(ValidatorConfig config) {
        super(config);
        FacesContext facesContext = FacesContext.getCurrentInstance();
        FaceletContext faceletContext = (FaceletContext) facesContext.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
        lengthExpression = config.getTag().getAttributes().get("length").getValueExpression(faceletContext, Integer.class);
    }

    @Override
    public void setAttributes(FaceletContext ctx, Object instance) {
//        super.setAttributes(ctx, instance);
        if (instance instanceof TestValidator) {
            TestValidator validator = (TestValidator) instance;
            validator.setLength(lengthExpression);
        }
    }
}

Alright, it seems everything is working now. I've created a custom ValidatorHandler, as suggested by McDowell. Here are my changes:

TAGLIB:

<handler-class>test.CustomValidatorHandler</handler-class>

VALIDATOR:

package test;

import javax.el.ValueExpression;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

@FacesValidator("test.TestValidator")
public class TestValidator implements Validator {

    public TestValidator() {
        super();
    }

    @Override
    public void validate(FacesContext context,
            UIComponent component, Object value) throws ValidatorException {

        String text = (String) value;
        if (text == null || text.trim().length() == 0) {
            return;
        }

        Integer length = getLengthValue();

        if (length != null && text != null && length.intValue() < text.length()) {
            String message = "The text is too long. It was " + text.length() +
                    ", but only " + length + " characters are allowed.";
            FacesMessage fm = new FacesMessage(
                    FacesMessage.SEVERITY_ERROR, message, null);
            throw new ValidatorException(fm);
        }
    }

    private ValueExpression lengthExpression;

    public ValueExpression getLength() {
        return lengthExpression;
    }

    public void setLength(ValueExpression length) {
        this.lengthExpression = length;
    }

    public Integer getLengthValue() {
        Integer length = null;
        if (lengthExpression != null) {
            length = (Integer) lengthExpression.getValue(
                    FacesContext.getCurrentInstance().getELContext());
        }
        return length;
    }
}

VALIDATOR HANDLER:

package test;

import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.ValidatorConfig;
import javax.faces.view.facelets.ValidatorHandler;

public class CustomValidatorHandler extends ValidatorHandler {

    private ValueExpression lengthExpression;

    public CustomValidatorHandler(ValidatorConfig config) {
        super(config);
        FacesContext facesContext = FacesContext.getCurrentInstance();
        FaceletContext faceletContext = (FaceletContext) facesContext.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
        lengthExpression = config.getTag().getAttributes().get("length").getValueExpression(faceletContext, Integer.class);
    }

    @Override
    public void setAttributes(FaceletContext ctx, Object instance) {
//        super.setAttributes(ctx, instance);
        if (instance instanceof TestValidator) {
            TestValidator validator = (TestValidator) instance;
            validator.setLength(lengthExpression);
        }
    }
}
暮年 2024-10-17 02:53:50

我相信你的验证器有一个错误,而你在以前的实现中只是运气好。

来自文档

...如果 Validator 类希望使用视图保存和恢复配置属性值,则实现还必须实现 StateHolder

当视图在 POST 操作期间恢复时,长度状态可能不再存在。

我不记得验证器上的 ValueExpressions 发生了什么 - 与组件不同,验证器往往没有 绑定映射来保存它们。您可能必须创建自己的ValidatorHandler 或其他东西 - 我还没看过这个API 部分详细信息。

I believe your validator has a bug in it and you were just getting lucky under previous implementations.

From the documentation:

...if the Validator class wishes to have configuration property values saved and restored with the view, the implementation must also implement StateHolder.

When the view is restored during the POST operation the length state may no longer be present.

I don't remember what happens with ValueExpressions on validators - unlike components, validators tend not to have binding maps to hold them in. You may have to create your own ValidatorHandler or something - I haven't looked at this part of the API in detail.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文