Primefaces outputLabel for composite component
Asked Answered
B

1

11

I have an issue with using p:outputLabel when used with composite component. I have composite component with p:inputText field (I removed irrelevant parts from component):

<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>

Now, I wont to use this component with p:outputLabel:

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

Everything works fine, required validation, message is displayed as well, but there is no * sign on label, as there is when I connect label directly to p:inputText component. If I, on the other hand, hardcode required="true" on p:inputText everything works fine.

I debugged through org.primefaces.component.outputlabel.OutputLabelRenderer and discovered that component is recognized as UIInput, but input.isRequired() returns false. Farther debugging discovered that required attribute isn't yet defined on component, so it returns false as default value i UIInput:

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

Also, if I just move p:outputLabel inside composite component everything works fine. Like EL is evaluated later inside composite component?

I'm using Primefaces 3.5 with Mojarra 2.1.14

Bucci answered 21/2, 2013 at 12:36 Comment(0)
G
13

This is, unfortunately, "by design". The evaluation of the #{} expressions is deferred to the exact moment of the access-time. They're unlike "standard" EL ${} in JSP not evaluated at the exact moment they're been parsed by the tag handler and "cached" for future access during the same request/view. At the moment the <p:outputLabel> is rendered, and thus the #{cc.attrs.required} as referenced by UIInput#isRequired() needs to be evaluated, there's no means of any #{cc} in the EL context. So any of its attributes would not evaluate to anything. Only when you're sitting inside the <cc:implementation>, the #{cc} is available in the EL context and all of its attribues would thus successfully evaluate.

Technically, this is an unfortunate corner case oversight in the design of <p:outputLabel>. Standard JSF and EL are namely behaving as specified. Basically, the presentation of the label's asterisk depending on the input's required attribute should be evaluated the other way round: at the moment the <p:inputText> inside the composite is to be rendered or perhaps even already when it's to be built. Thus, the label component should not ask the input component if it's required, but the input component should somehow notify the label component that it's required. This is in turn hard and clumsy (and thus inefficient) to implement.

If moving the label to inside the composite is not an option, then your best bet is to create a tag file instead of a composite component around the input component. It only requires some additional XML boilerplate.

/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>

Usage:

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

I just did a quick test and it works fine for me.

See also:

Goldfarb answered 25/2, 2013 at 14:43 Comment(6)
This works for me. Only reason why I can't put label inside composite component is because this input will be used in some panelGrid, one column for label and one for input. With this it will be hard make this looks acceptable.Bucci
Then using a tag file instead of a composite component has an extra advantage for you: a tag file is unlike a composite component not interpreted as a single component! You can in fact safely put the label next the input in the tag file and still end up with 2 cells. See also stackoverflow.com/questions/5713718/… This should further reduce the boilerplate.Goldfarb
I've found a way using o:tagAttribute but it's available starting 2.1 while we're using 1.14, do you have plan to create a new release in the future that might include this?Cooke
@Cooke Nope, 1.1x is end of life. JSF 2.3 was introduced a few months ago and it has a required CDI dependency. So all focus will be on OmniFaces 2.x and 3.x.Goldfarb
Thanks for your quick answer @GoldfarbCooke
To add a relevant comment to this post, p:outputLabel supports composite now, see migration guide github.com/primefaces/primefaces/wiki/Migration-Guide#60-to-61 but more i think about it, more I think the use case is wrong (using composite) while facelet should be used instead. More explanations explained here: issues.apache.org/jira/browse/MYFACES-4059Cooke

© 2022 - 2024 — McMap. All rights reserved.