问题 Primefaces outputLabel用于复合组件


我有使用问题 p:outputLabel 与复合材料组件一起使用时我有复合组件 p:inputText 字段(我从组件中删除了不相关的部分):

<cc:interface>
  <cc:editableValueHolder name="myInput" targets="myInput"/>
  <cc:attribute name="required" required="true" type="java.lang.Boolean" default="false"/>
</cc:interface>

<cc:implementation>
  <p:inputText id="myInput" required="#{cc.attrs.required}"/>
</cc:implementation>

现在,我不会使用这个组件 p:outputLabel

<p:outputLabel for="myComponent:myInput" value="#{resources['myLabel']}:"/>
<my:myComponent id="myComponent" required="#{myBean.required}"/>

一切正常,需要验证,消息也显示,但没有 * 在标签上签名,因为我直接连接标签 p:inputText 零件。另一方面,如果我硬编码 required="true" 上 p:inputText 一切正常。

我通过调试 org.primefaces.component.outputlabel.OutputLabelRenderer 并发现该组件被识别为 UIInput但是 input.isRequired() 返回false。进一步调试发现了这一点 required 属性尚未在组件上定义,因此返回 false 作为默认值i UIInput

(Boolean) getStateHelper().eval(PropertyKeys.required, false);

另外,如果我移动 p:outputLabel 内部复合组件一切正常。像EL一样,后来在复合元件内进行评估?

我正在使用Primefaces 3.5和Mojarra 2.1.14


9898
2018-02-21 12:36


起源



答案:


不幸的是,这是“按设计”。评价 #{} 表达式被推迟到访问时间的确切时刻。它们与“标准”EL不同 ${} 在JSP中  在标记处理程序解析它们并在同一请求/视图中“缓存”以供将来访问的确切时刻进行评估。此刻 <p:outputLabel> 被渲染,因此 #{cc.attrs.required} 如引用 UIInput#isRequired() 需要评估,没有任何手段 #{cc} 在EL语境中。所以它的任何属性都不会评估任何东西。只有当你坐在里面 <cc:implementation>#{cc} 在EL环境中可用,因此它的所有属性都将成功评估。

从技术上讲,这是一个不幸的角落案例监督设计 <p:outputLabel>。标准JSF和EL表现为指定的行为。基本上,标签的星号表示取决于输入 required 属性应该反过来评估:此刻 <p:inputText> 复合内部将被渲染,或者甚至已经建成。因此,标签组件不应询问输入组件是否需要,但输入组件应以某种方式通知标签组件它是否需要。这反过来又变得艰难而且笨拙(因而效率低下)。

如果将标签移动到复合材料内部不是一个选项,那么最好的办法是在输入组件周围创建一个标签文件而不是复合组件。它只需要一些额外的XML样板。

/WEB-INF/tags/input.xhtml

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:p="http://primefaces.org/ui"
>
    <c:set var="id" value="#{not empty id ? id : 'myInput'}" />
    <c:set var="required" value="#{not empty required and required}" />

    <p:inputText id="#{id}" required="#{required}"/>
</ui:composition>

/WEB-INF/my.taglib.xml

<?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://example.com/my</namespace>

    <tag>
        <tag-name>input</tag-name>
        <source>tags/input.xhtml</source>
    </tag>
</facelet-taglib>

/WEB-INF/web.xml

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>

用法:

<html ... xmlns:my="http://example.com/my">
...
<p:outputLabel for="myInput" value="#{resources['myLabel']}:" />
<my:input id="myInput" required="#{myBean.required}" />

我刚做了一个快速测试,它对我来说很好。

也可以看看:


13
2018-02-25 14:43



这对我有用。我之所以无法在复合组件中放置标签,只是因为这个输入会在某些组件中使用 panelGrid,一列用于标签,一列用于输入。这样看起来很难接受。 - partlov
然后使用标记文件而不是复合组件有一个额外的优势:标记文件不像复合组件不被解释为单个组件!事实上,您可以安全地将标签放在标签文件中的输入旁边,最后仍然有2个单元格。也可以看看 stackoverflow.com/questions/5713718/... 这应该进一步减少样板。 - BalusC
我找到了一种方法 o:tagAttribute 但是当我们使用1.14时,它可以从2.1开始,你是否计划在未来创建一个可能包含此版本的新版本? - Rapster
@Rapster Nope,1.1x是生命的终点。几个月前推出了JSF 2.3,它具有所需的CDI依赖性。因此,所有重点都集中在OmniFaces 2.x和3.x. - BalusC
谢谢你快速回答@BalusC - Rapster


答案:


不幸的是,这是“按设计”。评价 #{} 表达式被推迟到访问时间的确切时刻。它们与“标准”EL不同 ${} 在JSP中  在标记处理程序解析它们并在同一请求/视图中“缓存”以供将来访问的确切时刻进行评估。此刻 <p:outputLabel> 被渲染,因此 #{cc.attrs.required} 如引用 UIInput#isRequired() 需要评估,没有任何手段 #{cc} 在EL语境中。所以它的任何属性都不会评估任何东西。只有当你坐在里面 <cc:implementation>#{cc} 在EL环境中可用,因此它的所有属性都将成功评估。

从技术上讲,这是一个不幸的角落案例监督设计 <p:outputLabel>。标准JSF和EL表现为指定的行为。基本上,标签的星号表示取决于输入 required 属性应该反过来评估:此刻 <p:inputText> 复合内部将被渲染,或者甚至已经建成。因此,标签组件不应询问输入组件是否需要,但输入组件应以某种方式通知标签组件它是否需要。这反过来又变得艰难而且笨拙(因而效率低下)。

如果将标签移动到复合材料内部不是一个选项,那么最好的办法是在输入组件周围创建一个标签文件而不是复合组件。它只需要一些额外的XML样板。

/WEB-INF/tags/input.xhtml

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:p="http://primefaces.org/ui"
>
    <c:set var="id" value="#{not empty id ? id : 'myInput'}" />
    <c:set var="required" value="#{not empty required and required}" />

    <p:inputText id="#{id}" required="#{required}"/>
</ui:composition>

/WEB-INF/my.taglib.xml

<?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://example.com/my</namespace>

    <tag>
        <tag-name>input</tag-name>
        <source>tags/input.xhtml</source>
    </tag>
</facelet-taglib>

/WEB-INF/web.xml

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>

用法:

<html ... xmlns:my="http://example.com/my">
...
<p:outputLabel for="myInput" value="#{resources['myLabel']}:" />
<my:input id="myInput" required="#{myBean.required}" />

我刚做了一个快速测试,它对我来说很好。

也可以看看:


13
2018-02-25 14:43



这对我有用。我之所以无法在复合组件中放置标签,只是因为这个输入会在某些组件中使用 panelGrid,一列用于标签,一列用于输入。这样看起来很难接受。 - partlov
然后使用标记文件而不是复合组件有一个额外的优势:标记文件不像复合组件不被解释为单个组件!事实上,您可以安全地将标签放在标签文件中的输入旁边,最后仍然有2个单元格。也可以看看 stackoverflow.com/questions/5713718/... 这应该进一步减少样板。 - BalusC
我找到了一种方法 o:tagAttribute 但是当我们使用1.14时,它可以从2.1开始,你是否计划在未来创建一个可能包含此版本的新版本? - Rapster
@Rapster Nope,1.1x是生命的终点。几个月前推出了JSF 2.3,它具有所需的CDI依赖性。因此,所有重点都集中在OmniFaces 2.x和3.x. - BalusC
谢谢你快速回答@BalusC - Rapster