Friday, 7 November 2014

Validation Rich Text Field

For the validation of a reusable Bootstrap Rich Text Field it is possible to use the same validation method as for a reusable Bootstrap Text Field.
See for more information Validation Reusable Text Fields and Reusable Bootstrap Rich Text Fields.
For the validation of a reusable Bootstrap Rich Text Field there are two additions advisable compared to a reusable Bootstrap Text Field.

1. Style property / attribute
A style property can be added to the reusable Bootstrap Richt Text Field to create a red box around the field using .isValid()
<xp:this.style><![CDATA[#{javascript:if(!getComponent('inputRichText1').isValid()){
    return "border-color:rgb(255,0,0);border-style:solid;border-width:thin";
} else{
    return ""};}]]></xp:this.style>

2. Validation 'if user only clicks in the Rich Text Field'
This extra validation is advisable because when a user only clicks in the reusable Bootstrap Rich Text Field the content '<p dir="ltr">&nbsp;</p>' is added in the Rich Text Field and the Rich Text Field is no longer 'empty'. A 'required validation' will fail.


One of the possible solutions is the following (quick and dirty) validation expression.
var string = getComponent("inputRichText1").getValueAsString();
var click = '<p dir="ltr">&nbsp;</p>';
if (@Contains(string,click)){
return false;
}
else {
return true
}

Sample Code Custom Control

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xpt="http://www.openntf.org/xsp/xpt"
xmlns:xe="http://www.ibm.com/xsp/coreex"
xmlns:xc="http://www.ibm.com/xsp/custom">
<xc:ccBSCKE></xc:ccBSCKE>
<xp:div>
<xp:this.styleClass><![CDATA[#{javascript:"form-group" + ( getComponent("inputRichText1").isValid() ? "" : " has-error" )}]]></xp:this.styleClass>
<xp:label styleClass="col-sm-2 control-label"
for="inputRichText1" value="${compositeData.fieldLabel}" />
<div class="col-sm-10">
<xp:inputRichText id="inputRichText1"
required="${compositeData.required}" value="#{document1.Body}">
<xp:this.validators>
<xp:validateRequired
message="#{javascript:compositeData.fieldLabel + ' is required'}">
</xp:validateRequired>
<xp:validateExpression
message="#{javascript:compositeData.fieldLabel + ' is required'}">
<xp:this.expression><![CDATA[#{javascript:var string = getComponent("inputRichText1").getSubmittedValue();
var click = '<p dir="ltr">&nbsp;</p>';
if (@Contains(string,click)){
return false;
}
else {
return true
}}]]></xp:this.expression>
</xp:validateExpression>
</xp:this.validators>
<xp:this.dojoAttributes>
<xp:dojoAttribute name="skin">
<xp:this.value><![CDATA[#{javascript:return @ClientType().equals("Web") ? "bootstrapck,/"+database.getFilePath()+"/bootstrapck/" : ""}]]></xp:this.value>
</xp:dojoAttribute>
<xp:dojoAttribute value="Full" name="toolbar">
</xp:dojoAttribute>
</xp:this.dojoAttributes>
<xp:this.dojoType><![CDATA[#{javascript:return @ClientType().equals("Web") ? "org.wavin.joldenburger.CKEDITOR" : ""}]]></xp:this.dojoType>
<xp:this.style><![CDATA[#{javascript:if(!getComponent('inputRichText1').isValid()){
    return "border-color:rgb(255,0,0);border-style:solid;border-width:thin";
} else{
    return ""};}]]></xp:this.style>
</xp:inputRichText>
<xp:text escape="true" id="computedField1"
styleClass="help-block" value="${compositeData.helpText}">
<xp:this.rendered><![CDATA[#{javascript:getComponent("inputRichText1").isValid() && compositeData.helpText != null}]]></xp:this.rendered>
</xp:text>
<xp:message id="message1" for="inputRichText1"
styleClass="help-block">
</xp:message>
</div>
</xp:div>
</xp:view>

Last Friday, in response to this blog post, another solution was provided by Knut Herrmann. This solution has also been tested and works great.

Sample Code Custom Control

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xpt="http://www.openntf.org/xsp/xpt"
xmlns:xe="http://www.ibm.com/xsp/coreex"
xmlns:xc="http://www.ibm.com/xsp/custom">
<xc:ccBSCKE></xc:ccBSCKE>
<xp:div>
<xp:this.styleClass><![CDATA[#{javascript:"form-group" + ( getComponent("inputRichText1").isValid() ? "" : " has-error" )}]]></xp:this.styleClass>
<xp:label styleClass="col-sm-2 control-label"
for="inputRichText1" value="${compositeData.fieldLabel}" />
<div class="col-sm-10">
<xp:inputRichText id="inputRichText1"
required="${compositeData.required}" value="#{document1.Body}">
<xp:this.validators>
<xp:validateRequired
message="#{javascript:compositeData.fieldLabel + ' is required'}">
</xp:validateRequired>
<xp:validateExpression
message="#{javascript:compositeData.fieldLabel + ' is required'}">
<xp:this.expression><![CDATA[#{javascript:var rt = (value + " ").replace(/(<((?!img|a\s)[^>]+)>)|&nbsp;/ig, "").trim();
return rt !== "";
}]]></xp:this.expression>
</xp:validateExpression>
</xp:this.validators>
<xp:this.dojoAttributes>
<xp:dojoAttribute name="skin">
<xp:this.value><![CDATA[#{javascript:return @ClientType().equals("Web") ? "bootstrapck,/"+database.getFilePath()+"/bootstrapck/" : ""}]]></xp:this.value>
</xp:dojoAttribute>
<xp:dojoAttribute value="Full" name="toolbar">
</xp:dojoAttribute>
</xp:this.dojoAttributes>
<xp:this.dojoType><![CDATA[#{javascript:return @ClientType().equals("Web") ? "org.wavin.joldenburger.CKEDITOR" : ""}]]></xp:this.dojoType>
<xp:this.style><![CDATA[#{javascript:if(!getComponent('inputRichText1').isValid()){
    return "border-color:rgb(255,0,0);border-style:solid;border-width:thin";
} else{
    return ""};}]]></xp:this.style>
</xp:inputRichText>
<xp:text escape="true" id="computedField1"
styleClass="help-block" value="${compositeData.helpText}">
<xp:this.rendered><![CDATA[#{javascript:getComponent("inputRichText1").isValid() && compositeData.helpText != null}]]></xp:this.rendered>
</xp:text>
<xp:message id="message1" for="inputRichText1"
styleClass="help-block">
</xp:message>
</div>
</xp:div>
</xp:view>

Sample Code 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">
<xp:this.data>
<xp:dominoDocument var="document1" formName="Contact"></xp:dominoDocument>
</xp:this.data>
<xc:ccLayoutBootstrap>
<xp:this.facets>
<xp:panel xp:key="facetMiddle">
<div class="page-header">
<h1>
Bootstrap Rich Text Field
<xp:span style="color:rgb(255,255,255)">
</xp:span>
<small>Including Validation</small>
</h1>
</div>
<xp:br></xp:br>
<xc:validationBSRichTextField required="true"
fieldLabel="Remarks" fieldName="Body"
helpText="Remarks is a mandatory field">
</xc:validationBSRichTextField>
<xp:br></xp:br>
<xp:br></xp:br>
<xp:button value="Save" id="button1"
styleClass="btn btn-primary">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:saveDocument></xp:saveDocument>
</xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:panel>
</xp:this.facets>
</xc:ccLayoutBootstrap>
</xp:view>

Final result




Based on the above an excellent validation for a required reuseable Bootstrap Rich Text Field has been created.
Thanks to Knut Herrmann for his reaction and provided solution.
If you have a different or better solution please let me know by leaving a comment.

3 comments:

  1. var rt = (value + " ").replace(/(<((?!img|a\s)[^>]+)>)|&nbsp ;/ig, "").trim();

    doesn't remove image and link tags anymore.
    http://stackoverflow.com/a/26773960/2065611

    ReplyDelete
    Replies
    1. Omit the space in "&nbsp ;". I had to put it in here because it was converted to a space saving my comment.

      Delete
    2. Thanks very much for the comment. I will look into this solution next week. Will post my result.

      Delete