当组件在JSTL forEach循环中时,以程序化的方式用一个值表达式设置属性。

问题描述 投票:1回答:1

我有一个纯Java的自定义组件,它扩展了UIInput(JSF 2.2,Mojarra),我是这样使用的。

<c:forEach items="#{bean.items}" var="item">
    <my:component item="#{item}" />
</c:forEach>

我试图避免在.xhtml文件中的标签上不必要地指定 "value"、"valueChangeListener "和 "validator "属性。

在我的自定义组件中,我已经覆盖了setValueExpression方法,就像这样。

@Override
public void setValueExpression(String name, ValueExpression expression) {
    super.setValueExpression(name, expression);
    if ("item".equals(name)) {
        this.setValue(Components.createValueExpression("#{item.myValue}", MyValue.class));
        this.addValueChangeListener(new MethodExpressionValueChangeListener(Components.createVoidMethodExpression("#{item.myValueChanged}", ValueChangeEvent.class)));
        this.addValidator(new MethodExpressionValidator(Components.createVoidMethodExpression("#{item.validateMyValue}", FacesContext.class, UIComponent.class, Object.class)));
    }
}

我使用OmniFaces的Components工具来减少模板代码。

当需要对这三个方法中的任何一个采取行动时(例如,在提交时进行验证),它的结果是。

javax.faces.FacesException: javax.el.PropertyNotFoundException: Target Unreachable, identifier 'item' resolved to null

我很确定我知道原因,只是不知道该怎么做。

我相信,当我试图以程序化方式设置的三个表达式被解析时,它正试图在某个作用域中找到一个名称为'item'的bean,然而这个bean并不存在,因为'item'是JSTL forEach循环中的一个时间点变量。

我认为Weld对item本身使用了一种特殊的递延表达式(当我调试那个setValueExpression方法的时候,我还挺能看到的),它是知道或以其他方式对那个时间点变量有引用的,但是我在设置这三个表达式的时候,并没有做同样的事情,因此在以后解决它们的时候,没有处理办法。

我相信有一种方法可以把这个连接起来,只是我没有看到。

此外,我知道我可以像这样把三个属性放在.xhtml中的标签上。

<my:component item="#{item}" value="#{item.myValue}" valueChangeListener="#{item.myValueChanged}" validator="#{item.validateMyValue}" />

这样它们就会像item本身一样得到特殊的递延表达式(事实上,这样一切都能像预期的那样工作),但我宁可不这样做--这是我不得不重复的事情,而且似乎应该有一种方法来实现我上面的尝试。

jsf jstl el
1个回答
1
投票

我相信我找到了答案。

我在调试器中看到的东西一直让我耿耿于怀,我只是不知道如何手动接上同样的方案,后来我发现了一个 Stack Overflow帖子 并在最下面加上以下代码。

VariableMapper varMapper = new DefaultVariableMapper();
varMapper.setVariable(mappingName, component.getValueExpression(mappedAttributeName));
return new ValueExpressionImpl(expression, null, null, varMapper, expectedType);

这足以让我找到正确的方向,在VariableMapper实例中重新使用传入的item本身的值表达式,而VariableMapper实例又被用来创建三个valuemethod表达式,所以它们现在都有一个句柄&amp;可以在稍后的时候解决 "item"。

@Override
public void setValueExpression(String name, ValueExpression expression) {
    super.setValueExpression(name, expression);
    if ("item".equals(name)) {

        VariableMapper varMapper = new DefaultVariableMapper();
        varMapper.setVariable("item", expression);

        ValueExpressionImpl valExprImpl = new ValueExpressionImpl("#{item.myValue}", null, null, varMapper, MyValue.class);
        super.setValueExpression("value", valExprImpl);

        MethodExpressionImpl meExprImpl = new MethodExpressionImpl("#{item.myValueChanged}", null, null, varMapper, Void.class, new Class<?>[] {ValueChangeEvent.class});
        MethodExpressionValueChangeListener mevcl = new MethodExpressionValueChangeListener(meExprImpl);
        this.addValueChangeListener(mevcl);

        meExprImpl = new MethodExpressionImpl("#{item.validateMyValue}", null, null, varMapper, Void.class, new Class<?>[] {FacesContext.class, UIComponent.class, Object.class});
        MethodExpressionValidator mev = new MethodExpressionValidator(meExprImpl);
        this.addValidator(mev);

    }
}

这似乎是个好办法(现在看起来很简单... )。

© www.soinside.com 2019 - 2024. All rights reserved.