1

我有一个具有复杂验证的应用程序,我只能基于模型层次结构执行,所以我不能使用 JSF 的验证阶段。

我的问题是我需要将验证错误链接回 gui(直观地显示需要注意的文本字段)。所以我需要在某处将 UI 组件与域模型项链接起来。

我希望使用 PreRenderComponentEventListener 来实现这一点:

  • 我的验证器组件存储约束验证,我可以在其中访问需要注意的对象和属性。
  • 我使用 UIComponent 的 ValueExpression 来检索 Component 绑定到的正确对象。

起初这似乎有效:我在 xhtml 页面中有以下内容:

<ui:composition template="/templates/mainTemplate.xhtml">
<ui:define name="content">  
   <ui:repeat value="#{contactController.contactManager.contactList}" var="contact">
          <h:inputText value="#{contact.name}"/>
          <h:inputText value="#{contact.firstName}"/>
          <br/>
      </ui:repeat>
</ui:define>
</ui:composition>

现在假设我有一个名字失败的联系人列表。

ValueExpression 的表达式字符串给了我“#{contact.firstName}”:我从这个构建一个新的 ValueExpression 来检索父级“#{contact}”,并且可以检查失败的名字是否是列表中正确的联系人之一。

所以我在 Listener 中做了这样的事情:

private Object extractParent(UIInput input) {
   FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
   ValueExpression orig = input.getValueExpression("value");
   String parent = getParent(orig.getExpressionString());
   ValueExpression valueExpression = FacesContext.getCurrentInstance().getApplication().getExpressionFactory().createValueExpression(faceletElContext,"#{"+parent+"}", Object.class);
   return valueExpression.getValue(faceletElContext);
}

现在,这仅在重复位于顶部 facelet 中时才有效。
一旦我的复杂应用程序使用包含文件,或者由于 2 个原因而开始失败:

  • facelet 上下文是错误的上下文,而不是嵌套的 xhtml 上下文,因此它的 variableMapper 中没有变量
  • 该变量是在 UIComponent 的 valueexpression 本身中定义的,但该变量不可访问,仅具有私有访问权限,因此我也无法使用该变量。

所以我完全被卡住了!有人可以给我一个替代方案吗?

在评估@BalusC 的响应后更新:我尝试了ELContext但没有成功。我进行了进一步调查,核心问题是既ELContext不允许也FaceletContext不允许我访问包含文件中定义的变量:

  • 没有ELContext他们
  • FaceletContext似乎一直是最后一个包含文件之一,因此不是正确的。

我有一个小测试用例证明了这一点:当呈现“contactinner”输入文本时,ELContext 没有这个变量,并且 faceletcontext 有错误的 xhtml 层次结构。

非常感谢所有帮助。

测试.xhtml:

<ui:composition>
<h:form id="myform">
    <c:set var="itemContact" value="#{contactController.contact}"/>
    <c:set var="itemPerson" value="#{contactController.person}"/>
    <ui:include src="contact.xhtml"/>
    <ui:include src="person.xhtml"/>
    <h:commandButton action="#{contactController.process}"/>
</h:form>
</ui:composition>

联系人.xhtml:

<ui:composition>
     <c:set var="contactinner" value="#{itemContact}"/>
     <h:inputText value="#{contactinner}"/>
</ui:composition>

人.xhtml:

<ui:composition>
    <c:set var="personinner" value="#{itemPerson}"/>
    <h:inputText value="#{personinner}"/>
</ui:composition>

faces-config.xml(摘录):

<system-event-listener>
<system-event-listener-class>TestSystemEventListener</system-event-listener-class>
<system-event-class>javax.faces.event.PreRenderComponentEvent</system-event-class>
</system-event-listener>

TestSystemEventListener(摘录):

public class TestSystemEventListener implements SystemEventListener {
    @Override
    public void processEvent(SystemEvent event) throws AbortProcessingException {
       UIInput input = (UIInput) event.getSource();
       FaceletContext faceletElContext = (FaceletContext) FacesContext.getCurrentInstance().getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
       FacesContext context = FacesContext.getCurrentInstance();
       ValueExpression orig = input.getValueExpression("value");
}

运行这个,当在第一个 xhtml 中为 inputext 启动 evenlistener 时,#{contactInner}我在 listener 中得到以下数据:

orig = {com.sun.faces.facelets.el.TagValueExpression@24201}"/xhtml/contact.xhtml @6,43 value=\"#{contactinner}\""
    orig = {com.sun.el.ValueExpressionImpl@24220}"ValueExpression[#{contactinner}]"
    expr = {java.lang.String@24270}"#{contactinner}"
    varMapper = {com.sun.el.lang.VariableMapperImpl@24271}
    vars = {java.util.HashMap@24275} size = 1
       [0] = {java.util.HashMap$Entry@24278}"contactinner" -> "/xhtml/contact.xhtml @5,54 value=\"#{itemContact}\""


context = {com.sun.faces.context.FacesContextImpl@24200}
    elContext = {com.sun.faces.el.ELContextImpl@24205}
    functionMapper = {com.sun.faces.facelets.compiler.NamespaceHandler@24284}
    variableMapper = {com.sun.faces.el.ELContextImpl$VariableMapperImpl@24221}
        variables = {java.util.HashMap@24289} size = 2
        [0] = {java.util.HashMap$Entry@24292}"itemContact" -> "/xhtml/test.xhtml @9,68 value=\"#{contactController.contact}\""
        [1] = {java.util.HashMap$Entry@24295}"itemPerson" -> "/xhtml/test.xhtml @10,66 value=\"#{contactController.person}\""           

faceletElContext = {com.sun.faces.facelets.impl.DefaultFaceletContext@24199}
    faces = {com.sun.faces.context.FacesContextImpl@24200}
    ctx = {com.sun.faces.el.ELContextImpl@24205}
    facelet = {com.sun.faces.facelets.impl.DefaultFacelet@24151}"/xhtml/person.xhtml"
    faceletHierarchy = {java.util.ArrayList@24206} size = 2
    [0] = {com.sun.faces.facelets.impl.DefaultFacelet@23792}"/xhtml/test.xhtml"
    [1] = {com.sun.faces.facelets.impl.DefaultFacelet@24151}"/xhtml/person.xhtml"
    varMapper = {com.sun.faces.facelets.el.VariableMapperWrapper@24207}
    target = {com.sun.faces.el.ELContextImpl$VariableMapperImpl@24221}
    vars = {java.util.HashMap@24222} size = 1
        [0] = {java.util.HashMap$Entry@24225}"personinner" -> "/xhtml/person.xhtml @5,52 value=\"#{itemPerson}\""               
4

1 回答 1

0

You should use the ELContext instance as obtained by FacesContext#getELContext(), not the FaceletContext.

private Object extractParent(UIInput input) {
    ELContext elContext = FacesContext.getCurrentInstance().getELContext();
    ValueExpression orig = input.getValueExpression("value");
    String parent = getParent(orig.getExpressionString());
    ValueExpression valueExpression = FacesContext.getCurrentInstance().getApplication().getExpressionFactory().createValueExpression(elContext, "#{" + parent + "}", Object.class);
    return valueExpression.getValue(elContext);
}

A shorthand is by the way Application#evaluateExpressionGet().

private Object extractParent(UIInput input) {
    FacesContext context = FacesContext.getCurrentInstance();
    ValueExpression orig = input.getValueExpression("value");
    String parent = getParent(orig.getExpressionString());
    return context.getApplication().evaluateExpressionGet(context, "#{" + parent + "}", Object.class);
}
于 2013-01-04T11:55:37.500 回答