Gavi
Gavi

Reputation: 1478

OmniFaces validateEquals and composite component

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

Upvotes: 2

Views: 278

Answers (1)

BalusC
BalusC

Reputation: 1108632

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.

Upvotes: 4

Related Questions