1

I'm using JSF 2.3 with Omnifaces 3.0. I'm trying to create a custom component that represents an e-mail text field. Nothing special.

I've got the following code that is perfectly working

<h:form id="formAggiungiUtente">

    <p:outputLabel for="inputTextEmail" value="#{msg['email']}"/>    
    <h:inputText id="inputTextEmail" pt:type="email" value="#{userController.userBean.mail}" class="form-control" required="true" requiredMessage="#{msg['email']} #{msg['campoObbligatorio']}" validatorMessage="#{msg['email']} #{msg['nonValido']}">
        <f:validator binding="#{emailJsfValidator}"/>
    </h:inputText>

    <p:outputLabel for="inputTextSecondEmail" value="#{msg['ripetiMail']}"/>
    <h:inputText id="inputTextSecondEmail" pt:type="email" value="#{userController.secondMail}" class="form-control" required="true" requiredMessage="#{msg['ripetiMail']} #{msg['campoObbligatorio']}" validatorMessage="#{msg['ripetiMail']} #{msg['nonValido']}">
        <f:validator binding="#{emailJsfValidator}"/>
    </h:inputText>

    <o:validateEqual id="equal" components="inputTextEmail inputTextSecondEmail" message="#{msg['indirizziMailDiversi']}" />
</h:form>

In other parts of the application, I need the component that represents the e-mail text field, so the first step was to create something like the following

<h:form id="formAggiungiUtente">
    <ui:include src="templates/mailTextField.xhtml">
        <ui:param name="id" value="inputTextEmail" />
        <ui:param name="label" value="#{msg['email']}" />
        <ui:param name="value" value="#{userController.userBean.mail}" />
        <ui:param name="required" value="true" />
        <ui:param name="requiredMessage" value="#{msg['email']} #{msg['campoObbligatorio']}" />
        <ui:param name="validatorMessage" value="#{msg['email']} #{msg['nonValido']}" />
    </ui:include>

    <ui:include src="templates/mailTextField.xhtml">
        <ui:param name="id" value="inputTextSecondEmail" />
        <ui:param name="label" value="#{msg['ripetiMail']}" />
        <ui:param name="value" value="#{userController.secondMail}" />
        <ui:param name="required" value="true" />
        <ui:param name="requiredMessage" value="#{msg['ripetiMail']} #{msg['campoObbligatorio']}" />
        <ui:param name="validatorMessage" value="#{msg['ripetiMail']} #{msg['nonValido']}" />
    </ui:include>

    <o:validateEqual id="equal" components="inputTextEmail inputTextSecondEmail" message="#{msg['indirizziMailDiversi']}" />

</h:form>

and here the mailTextField.xhtml

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:p="http://primefaces.org/ui"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

    <p:outputLabel for="#{id}" value="#{label}"/>    
    <h:inputText 
        id="#{id}" 
        pt:type="email" 
        value="#{value}"
        class="form-control" 
        required="#{required}" 
        requiredMessage="#{requiredMessage}" 
        validatorMessage="#{validatorMessage}">
        <f:validator binding="#{emailJsfValidator}"/>
    </h:inputText>

</ui:composition>

Also, this solution is working but, by reading a lot of answers I've understood that if there're too many parameters may is the case to create a custom component. So I've created the following component by using "cvl" as library name

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:cc="http://xmlns.jcp.org/jsf/composite"
      xmlns:p="http://primefaces.org/ui"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">

    <cc:interface>
        <cc:attribute name="label" type="java.lang.String" default="#{msg['email']}" />
        <cc:attribute name="value" type="java.lang.String" />
        <cc:attribute name="required" type="java.lang.Boolean" default="true" />
        <cc:attribute name="requiredMessage" type="java.lang.String" default="#{msg['email']} #{msg['campoObbligatorio']}" />
        <cc:attribute name="validatorMessage" type="java.lang.String" default="#{msg['email']} #{msg['nonValido']}" />
    </cc:interface>

    <cc:implementation>
        <p:outputLabel for="#{cc.clientId}" value="#{cc.attrs.label}"/>    
        <h:inputText 
            id="#{cc.clientId}" 
            pt:type="email" 
            value="#{cc.attrs.value}"
            class="form-control" 
            required="#{cc.attrs.required}" 
            requiredMessage="#{cc.attrs.requiredMessage}" 
            validatorMessage="#{cc.attrs.validatorMessage}">
            <f:validator binding="#{emailJsfValidator}"/>
        </h:inputText>
    </cc:implementation>

</html>

and now my page is like this

<h:form id="formAggiungiUtente">
    <cvl:mailTextField 
        id="inputTextEmail"
        value="#{userController.userBean.mail}"
    />

    <cvl:mailTextField 
        id="inputTextSecondEmail"
        value="#{userController.userBean.mail}"
        requiredMessage="#{msg['ripetiMail']} #{msg['campoObbligatorio']}"
        validatorMessage="#{msg['ripetiMail']} #{msg['nonValido']}"
    />

    <o:validateEqual id="equal" components="inputTextEmail inputTextSecondEmail" message="#{msg['indirizziMailDiversi']}" />
</h:form>

The problem is that I don't how to fill the parameter "components" of the validateEqual because the error is always something like the following.

ValidateEqual attribute 'components' must refer UIInput client IDs. Client ID is of type 'javax.faces.component.UINamingContainer'

I've tried a lot of combination but without success. What I'm doing wrong? Thanks

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Gavi
  • 1,300
  • 1
  • 19
  • 39

1 Answers1

4

by reading a lot of answers I've understood that if there're too many parameter may is the case to create a custom component

This one? When to use <ui:include>, tag files, composite components and/or custom components? It says to create a tag file, not a custom component, let alone a composite component.

If you had used a tag file, then this construct would have worked just fine. Composite components, on the other hand, are naming containers. This means that they add an extra client ID layer over their children, like as <h:form> and <h:dataTable> also do. You should also include the composite component's own id in the client ID search expression. It goes like:

<cc:someComposite id="idOfComposite1" ... />
<cc:someComposite id="idOfComposite2" ... />

<o:validateEqual components="idOfComposite1:idOfInput idOfComposite2:idOfInput" />

Whereby the composite implementation has:

<h:inputText id="idOfInput" ... />

You thus only need to fix your composite component to not reference #{cc.clientId} anymore in the id attribute of a child JSF component. This is outright wrong. It's supposed to be only used on a plain HTML <span> or <div> wrapping the composite component's implementation, and then with the sole reason of Rerendering composite component by ajax.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • It's working now. The only problem is that the p:outputLabel is not highlighted. All the validators (required and emailJsfValidator) are correctly called, but I don't see the "*" as a mandatory field and the field is not highlighted when there's a validation error. I've found this question. Is it related? https://stackoverflow.com/questions/15002441/primefaces-outputlabel-for-composite-component – Gavi Jan 10 '18 at 20:47
  • I've found the problem. If the id of the composite component and the id of the h:input are the same, the p:outputLabel will not work. – Gavi Jan 10 '18 at 20:53
  • You should not assign the composite component's children a dynamic ID but a fixed ID. – BalusC Jan 10 '18 at 21:11
  • Yes this was done from your previous answer. But if you use in the composite component the fixed id "franco" and in the inputText inside him the same fixed id "franco", this will not work. Obviously many thanks @BalusC – Gavi Jan 10 '18 at 21:58
  • That's most likely a bug in ``. The IDs of the composite component children should by the way best be kept as neutral as possible. E.g. `id="input"`. Nonetheless, you'd still better create a tag file instead of a composite. – BalusC Jan 10 '18 at 22:46
  • Ok, so after plain tags, include and composite component, the tag file will be my next step. Thank you – Gavi Jan 10 '18 at 23:12