当组件在 JSTL forEach 循环中时,以编程方式使用值表达式设置属性
Programatically Set Attribute with a Value Expression when component is within JSTL forEach loop
第一次提问,请多多包涵。
我有一个扩展 UIInput(JSF 2.2,Mojarra)的纯 Java 自定义组件,我是这样使用它的:
<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 及其组件实用程序来减少样板代码。
当需要对这三个中的任何一个采取行动时(例如,在提交时验证),结果是:
javax.faces.FacesException: javax.el.PropertyNotFoundException: Target Unreachable, identifier 'item' resolved to null
我很确定我知道为什么,我只是不知道该怎么办。
我相信当我试图以编程方式设置的三个表达式被解析时,它试图通过名称在某个范围内找到一个 bean,'item' 但是它不存在,因为 'item' 是 JSTL forEach 循环中的一个时间点变量。
我认为 Weld 对项目本身使用了一种特殊的延迟表达式(当我调试 setValueExpression 方法时我可以看到)它知道或以其他方式引用了那个时间点变量,但我'当我设置这三个表达式时,我没有做同样的事情,因此稍后需要解决它们时无法处理。
我确定有一种方法可以将其连接在一起,只是我没有看到而已。
此外,我知道我可以像这样将三个属性放在 .xhtml 中的标签上:
<my:component item="#{item}" value="#{item.myValue}" valueChangeListener="#{item.myValueChanged}" validator="#{item.validateMyValue}" />
然后他们会得到他们的特殊延迟表达式,就像项目本身所做的那样(实际上一切都按预期的方式工作)但我宁愿不这样做 - 这是我必须重复很多次的东西,看起来就像应该有一种方法可以做我上面正在尝试的事情。
我相信我找到了答案。
我一直在调试器中看到的内容一直困扰着我,我只是不确定如何手动连接相同的场景,然后我在最底部找到了一个带有以下代码片段的 Stack Overflow post :
VariableMapper varMapper = new DefaultVariableMapper();
varMapper.setVariable(mappingName, component.getValueExpression(mappedAttributeName));
return new ValueExpressionImpl(expression, null, null, varMapper, expectedType);
这足以为我指出正确的方向,即在 VariableMapper 实例中重新使用 item 本身的传入值表达式,该实例又用于创建三个 value/method 表达式,因此它们现在都具有句柄 & 可以稍后解决 '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);
}
}
这似乎成功了(现在看起来很简单...)。
第一次提问,请多多包涵。 我有一个扩展 UIInput(JSF 2.2,Mojarra)的纯 Java 自定义组件,我是这样使用它的:
<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 及其组件实用程序来减少样板代码。
当需要对这三个中的任何一个采取行动时(例如,在提交时验证),结果是:
javax.faces.FacesException: javax.el.PropertyNotFoundException: Target Unreachable, identifier 'item' resolved to null
我很确定我知道为什么,我只是不知道该怎么办。
我相信当我试图以编程方式设置的三个表达式被解析时,它试图通过名称在某个范围内找到一个 bean,'item' 但是它不存在,因为 'item' 是 JSTL forEach 循环中的一个时间点变量。
我认为 Weld 对项目本身使用了一种特殊的延迟表达式(当我调试 setValueExpression 方法时我可以看到)它知道或以其他方式引用了那个时间点变量,但我'当我设置这三个表达式时,我没有做同样的事情,因此稍后需要解决它们时无法处理。
我确定有一种方法可以将其连接在一起,只是我没有看到而已。
此外,我知道我可以像这样将三个属性放在 .xhtml 中的标签上:
<my:component item="#{item}" value="#{item.myValue}" valueChangeListener="#{item.myValueChanged}" validator="#{item.validateMyValue}" />
然后他们会得到他们的特殊延迟表达式,就像项目本身所做的那样(实际上一切都按预期的方式工作)但我宁愿不这样做 - 这是我必须重复很多次的东西,看起来就像应该有一种方法可以做我上面正在尝试的事情。
我相信我找到了答案。
我一直在调试器中看到的内容一直困扰着我,我只是不确定如何手动连接相同的场景,然后我在最底部找到了一个带有以下代码片段的 Stack Overflow post :
VariableMapper varMapper = new DefaultVariableMapper();
varMapper.setVariable(mappingName, component.getValueExpression(mappedAttributeName));
return new ValueExpressionImpl(expression, null, null, varMapper, expectedType);
这足以为我指出正确的方向,即在 VariableMapper 实例中重新使用 item 本身的传入值表达式,该实例又用于创建三个 value/method 表达式,因此它们现在都具有句柄 & 可以稍后解决 '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);
}
}
这似乎成功了(现在看起来很简单...)。