我正在使用以下方法在 PreRenderViewEvent 上动态添加组件http://blog.kennardconsulting.com/2010/10/safely-manipulating-component-tree-with.html。
它适用于组件添加部分,但是当我尝试动态实例化 ValueExpression-s 时出现问题。
更具体地说,当我尝试使用动态传递的参数伪造动态 ValueExpression 时遇到问题。
让我们试着解释一个例子......
在顶层,我使用标签组件(标签文件中描述的组件,不是复合组件,也不是自定义组件。
<my:topComponent param=#{toto}"/>
在 my:topComponent 中,我将参数传递给嵌套组件。
<my:nestedComponent param2=#{param}/>
这个nestedComponent 正在使用一个自定义组件(在我的例子中,一个我从primefaces Datatable 派生的组件),将它作为另一个参数传递给param2...
<my:customComponent finalParam=#{param2}/>
在customComponent 中,我在PreRenderViewEvent 上动态添加了一些子组件,并为customComponent 设置了一些ValueExpression-s。
其中一些表达式使用 finalParam。所以,我解开 finalParam 值,然后构建一个新的 ValueExpression :
String varName = getValueExpression("finalParam").getExpressionString().replace("#{", "").replace("}", "");
然后我使用这个辅助函数实例化我的动态值表达式:
public static ValueExpression createValueExpression(String expression, Class clazz) {
FacesContext fc = FacesContext.getCurrentInstance();
ELContext elContext = fc.getELContext();
ExpressionFactory expFactory = fc.getApplication().getExpressionFactory();
ValueExpression ret = expFactory.createValueExpression(elContext, expression, clazz);
return ret;
}
例子 :
ValueExpression dynExpression = JSFUtils.createValueExpression("#{" + varName + ".code" + "}"), Object.class);
在此示例中,值表达式为“#{param2.code}”
然后我可以将此 valueExpression 设置为我的组件:
this.setValueExpression("rowKey", dynExpression);
所有这些代码都在自定义组件类中。我使用基类的渲染器。
但是,以编程方式实例化的 ValueExpression 在呈现期间未正确评估。例如,当 primefaces 数据表渲染器尝试计算 rowKey 时,#{param2.code} 被评估为“null”,因为 param2 似乎是未知的。
我应该怎么做才能纠正这个?在调试时,我注意到 getValueExpression("finalParam") 有一个 VariableMapper 集,而 dynExpression 没有(空值)
如果我做对了,这个 VariableMapper 用于将 param2 转换为 param。
如何实例化我的动态表达式以便保留 VariableMapper(s) 链?FunctionMapper 的问题也是一样的。
提前致谢。
更新 我同意 Richard Kennard 的回复:这似乎是同一个错误。
由于我等不及多年的修复,我使用以下内容递归解析变量。它适用于我的 MyFaces 2.1.9 / CODI 1.0.5 / OWB 1.1.6 / Tomcat 7 堆栈的简单案例。
public static String getValueExpressionExpression(ValueExpression valueExpression) {
return valueExpression.getExpressionString().replace("#{", "").replace("}", "");
}
public static String getMappedValueExpression(ValueExpression valueExpression) {
ContextAwareTagValueExpression ctxAware = (ContextAwareTagValueExpression)valueExpression;
if(ctxAware != null) {
return getMappedValueExpression((WrappedValueExpression)ctxAware.getWrapped());
}
return getValueExpressionExpression(valueExpression);
}
public static String getMappedValueExpression(WrappedValueExpression wrappedExpression) {
String exprString = wrappedExpression.getExpressionString().replace("#{", "").replace("}", "");
String ret = exprString;
try {
Field valueExpression = WrappedValueExpression.class.getDeclaredField("valueExpression");
valueExpression.setAccessible(true);
ValueExpressionImpl vei = (ValueExpressionImpl) valueExpression.get(wrappedExpression);
Field varMapper = ValueExpressionImpl.class.getDeclaredField("varMapper");
varMapper.setAccessible(true);
VariableMapperImpl vmi = (VariableMapperImpl) varMapper.get(vei);
if(vmi != null) {
String[] components = exprString.split("\\.");
components[0] = getMappedValueExpression(vmi.resolveVariable(components[0]));
ret = "";
for(int i = 0 ; i < components.length ; i++) {
if(i != 0) {
ret += ".";
}
ret += components[i];
}
}
} catch (Exception ex) {
logger.error("Exception lors du mapping de l'expression EL " + exprString, ex);
} finally {
return ret;
}
}
在 MyFaces 或 Mojarra 中拥有此解决方法的更简洁版本会很棒...