Question

I am building a custom control to format my input controls for bootstrap. The way I validate data I have a bean with a validate method (that follows the method signature of a validator). However, I cannot find a way to specify the validator via the custom control properties...

This is the "normal" inputText control:

<xp:inputText xp:key="inputControl" id="inputCity" value="#{User.user.city}"
    validator="#{User.validate}">
    <xp:this.attrs>
        <xp:attr name="placeholder" value="Enter city"></xp:attr>
    </xp:this.attrs>
            <xp:this.validators>
                <xp:validateRequired message="You must enter a city"></xp:validateRequired>
            </xp:this.validators>
</xp:inputText>

And this is the one from the custom control:

<xp:inputText type="text" id="inputText1" value="#{compositeData.dataSource[compositeData.fieldName]}"
    required="${compositeData.required}" disableClientSideValidation="true" validator="${compositeData.dataValidator}">
    <xp:this.attrs>
        <xp:attr name="placeHolder" value="${compositeData.placeHolder}"></xp:attr>
    </xp:this.attrs>
    <xp:this.validators>
        <xp:validateRequired message="${compositeData.helpText}"></xp:validateRequired>
    </xp:this.validators>
</xp:inputText>
<xp:message id="message1" for="inputText1"></xp:message>

... and on the XPage I try to call it like this:

<xc:field fieldName="city" dataSource="#{User.user}" placeHolder="Enter city" fieldLabel="City" required="true"
    helpText="You must enter a city" dataValidator="#{User.validate}">
</xc:field>

The "dataValidator" is specified as a "com.ibm.xsp.validator.ValidatorImpl" - I have tried to use "object" as well... - but same error 500:

> Page Name: /editUser.xsp javax.faces.el.PropertyNotFoundException:
> Error getting property 'validate' from bean of type
> dk.dtu.aqua.catchlog.bean.UserBean    at
> com.sun.faces.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:119)
>   at
> com.ibm.xsp.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:144)

So obviously the custom control is trying to read it as a property (through an implicit "getter").

Any ideas??

Edit:

Ok, after having written a little back and forth with Toby Samples I tried to register the bean as a validator in faces-config.xml and give it an id. That seems to work - to some extent.

In the custom control I removed the "validator" attribute and added a "validator" under attrs:

<xp:validator validatorId="${compositeData.validatorId}"></xp:validator>

and where I call it from the XPage I added the new property instead (as a string):

<xc:field fieldName="city" dataSource="#{User.user}" placeHolder="Enter city" fieldLabel="City" required="true"
    helpText="You must enter a city" validatorId="userValidator">
</xc:field>

However, what I really want is to use a general CC which holds all of the bootstrap formating - and an editable area where I can drop the various input types (e.g. inputText, comboBox, etc.). This way the structure of the bootstrap markup is the same without having to copy the same code over and over again.

So if I create a new CC, say fieldLayout with this content (for simplicity):

<xp:label value="#{compositeData.label}" for="#{compositeData.inputField}"></xp:label>
<xp:div styleClass="col-sm-6">
    <xp:callback facetName="inputControl" id="callback1"></xp:callback>
</xp:div>

and drop that onto my field CC (mentioned in the beginning) where I put all of the into the editable area - with the same attributes etc. so that it now looks like this:

<xc:fieldLayout label="#{compositeData.label}" inputField="inputText1">
    <xp:this.facets>
        <xp:inputText xp:key="inputControl" type="text" id="inputText1" value="#{compositeData.dataSource[compositeData.fieldName]}"
            required="${compositeData.required}" disableClientSideValidation="true">
            <xp:this.attrs>
                <xp:attr name="placeHolder" value="${compositeData.placeHolder}"></xp:attr>
            </xp:this.attrs>
            <xp:this.validators>
                <xp:validator validatorId="${compositeData.validatorId}"></xp:validator>
                <xp:validateRequired message="You must enter something"></xp:validateRequired>
            </xp:this.validators>
        </xp:inputText>
        <xp:message id="message1" for="inputText1"></xp:message>
    </xp:this.facets>
</xc:fieldLayout>

Then it fails.... with the following error:

Caused by: java.lang.NullPointerException: Argument Error: One or more parameters are null. validatorId null
    at com.sun.faces.application.ApplicationImpl.createValidator(ApplicationImpl.java:630)
    at com.ibm.xsp.application.ApplicationExImpl.createValidator(ApplicationExImpl.java:1161)
    at com.ibm.xsp.validator.ValidatorImpl.getValidator(ValidatorImpl.java:75)
    at com.ibm.xsp.validator.ValidatorImpl.validate(ValidatorImpl.java:48)
    at javax.faces.component.UIInput.validateValue(UIInput.java:828)

This can be directly related to the fact that the compositeData.validatorId is now referenced in an inputText within the facet of another CC. Please note that I changed the "validateRequired" message to a string literal. So when I test without having entered anything then it gives me that message. If I enter just one character it calls the validator instead - or tries, because then the exception is thrown... :-(

Any ideas? This severely affects the usage of custom controls - unless I am doing it entirely wrong...?

/John

Was it helpful?

Solution

You could try using a validatorId in the xp:validator validator.

Faces-Config

  <validator>
    <validator-id>val</validator-id>
    <validator-class>com.tobysamples.validators.user</validator-class>
</validator>

Custom Control

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
    <xp:inputText>
        <xp:this.validators>
            <xp:validator validatorId="${compositeData.val}"></xp:validator>
        </xp:this.validators></xp:inputText>
</xp:view>

Xpage

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
    xmlns:xc="http://www.ibm.com/xsp/custom">

    <xc:inputfield val="val">
    </xc:inputfield>
    <xp:br></xp:br>
    <xp:br></xp:br>
    <xp:button value="Label" id="button1">
        <xp:eventHandler event="onclick" submit="true"
            refreshMode="complete">
        </xp:eventHandler></xp:button></xp:view>

Custom Control Definition

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <faces-config-extension>
    <namespace-uri>http://www.ibm.com/xsp/custom</namespace-uri>
    <default-prefix>xc</default-prefix>
  </faces-config-extension>
  <composite-component>
    <component-type>inputfield</component-type>
    <composite-name>inputfield</composite-name>
    <composite-file>/inputfield.xsp</composite-file>
    <composite-extension>
      <designer-extension>
        <in-palette>true</in-palette>
      </designer-extension>
    </composite-extension>
    <property>
      <property-name>val</property-name>
      <property-class>string</property-class>
      <display-name>val</display-name>
      <property-extension>
        <designer-extension>
          <editor>com.ibm.std.String</editor>
        </designer-extension>
      </property-extension>
    </property>
  </composite-component>
</faces-config>

New Second CC

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
<xc:field val2="${compositeData.val}">
    <xp:this.facets>
    <xp:inputText xp:key="inputControl">
        <xp:this.validators>
            <xp:validator validatorId="${compositeData.val2}"></xp:validator>
        </xp:this.validators></xp:inputText>
        </xp:this.facets>
        </xc:field>
</xp:view>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top