Tuesday, 21 October 2014

Reusable Rich Text Field

In a previous blog post I described a first setup for a reusable Bootstrap Text Field. In this post I describe a first setup for a reusable Bootstrap Rich Text Field including a Bootstrap CKEditor Skin.

Steps for creating a reusable Bootstrap Rich Text Field.

1. Custom Control
First create a new Custom Control for a reusable Bootstrap Rich Text  Field (inputRichText).


2. Property Definitions
The next step is to create some new properties.
In this example, I use the following properties for a reusable Bootstrap Rich Text Field.
- dataSource (Object)
- fieldName (String)
- fieldLabel (String)
- helpText (String)
- required (Boolean, True of False)


3. Reusable Rich Text Field
After creating the properties the reusable Bootstrap Rich Text Field can be build up.
Similar to using a reusable Bootstrap Text Field we need to make the contents of the control dynamic by referencing the custom control properties and create a dynamic field binding of the input / rich text control using Expression Language.

A. Label (computed - property fieldLabel)

<xp:label styleClass="col-sm-2
control-label" for="inputRichText1" value="${compositeData.fieldLabel}"/>


B. Field inputRichText1
In this example I am using the BootstrapCK Skin for the CKEditor4.
For more information about using a CKEditor Skin see the blog post BootstrapCK4 Skin for CKEditor.
In order for this to work you need to declare an own xspCKEditor-instance on your Custom Control.

<xp:scriptBlock id="scriptBlockCKEditor">
<xp:this.value>
      <![CDATA[
         require( ['dojo/_base/declare', 'ibm/xsp/widget/layout/xspCKEditor'], function( declare, xspCKEditor ){
            return declare( 'org.wavin.joldenburger.CKEDITOR', xspCKEditor, {
               constructor: function ckew_ctor(/*Object*/options){
                  CKEDITOR.timestamp = '';
               }
            });    
         });
      ]]>
</xp:this.value>
</xp:scriptBlock>

Code Custom Control for the reusable Bootstrap Rich Text Field

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex"
xmlns:xc="http://www.ibm.com/xsp/custom">
<xp:scriptBlock id="scriptBlockCKEditor">
<xp:this.value>
      <![CDATA[
         require( ['dojo/_base/declare', 'ibm/xsp/widget/layout/xspCKEditor'], function( declare, xspCKEditor ){
            return declare( 'org.wavin.joldenburger.CKEDITOR', xspCKEditor, {
               constructor: function ckew_ctor(/*Object*/options){
                  CKEDITOR.timestamp = '';
               }
            });  
         });
      ]]>
</xp:this.value>
</xp:scriptBlock>
<xp:div styleClass="form-group">
<xp:label styleClass="col-sm-2 control-label"
for="inputRichText1" value="${compositeData.fieldLabel}" />
<div class="col-sm-10">
<xp:inputRichText id="inputRichText1"
value="#{compositeData.dataSource[compositeData.fieldName]}"
required="${compositeData.required}"
htmlFilter="identity" htmlFilterIn="identity">
<xp:this.dojoAttributes>
<xp:dojoAttribute name="extraPlugins"
value="autogrow">
</xp:dojoAttribute>
<xp:dojoAttribute name="skin">
<xp:this.value><![CDATA[#{javascript:return @ClientType().equals("Web") ? "bootstrapck,/"+database.getFilePath()+"/bootstrapck/" : ""}]]></xp:this.value>
</xp:dojoAttribute>
<xp:dojoAttribute name="width" value="100%">
</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:inputRichText>
<xp:text escape="true" id="computedField2"
styleClass="help-block" value="${compositeData.helpText}"
loaded="${!empty compositeData.helpText}">
</xp:text>
</div>
</xp:div>
</xp:view>

4. Add Custom Control to an XPage
The last step is to add the Custom Control with the reusable Bootstrap Rich Text Field on an XPage.
In this example I added the Custom Control to the XPage with the reusable Bootstrap Text Fields I created in a previous blog post.
The properties of the Custom Control (reusable Bootstrap Rich Text Field) can now be entered.


Also add a dataSource to your XPage.
<xp:this.data>
<xp:dominoDocument var="document1" formName="Contact"></xp:dominoDocument>
</xp:this.data>

Code XPage with the reusable Bootstrap Rich Text Field and reusable Bootstrap Text Fields

<?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" xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:this.data>
<xp:dominoDocument var="document1" formName="Contact"></xp:dominoDocument>
</xp:this.data>
<xc:ccLayoutBootstrap>
<xp:this.facets>
<xp:div xp:key="facetMiddle">
<div class="form-horizontal" id="ex1">
<xp:panel xp:key="facetMiddle">
<div class="page-header">
<h1>
Reusable Bootstrap Fields
<xp:span style="color:rgb(255,255,255)">
</xp:span>
<small>Text / Rich Text Fields</small>
</h1>
</div>
 
    <xc:RBSTextField dataSource="#{document1}"
required="false" fieldLabel="FirstName" fieldName="FirstName"
placeholder="FirstName">
</xc:RBSTextField>
 
    <xc:RBSTextField dataSource="#{document1}"
required="false" fieldLabel="LastName" fieldName="LastName"
placeholder="LastName">
</xc:RBSTextField>
 
  <xc:RBSTextField dataSource="#{document1}"
required="false" fieldLabel="Subject" fieldName="Subject"
placeholder="Enter a Subject">
</xc:RBSTextField>
 
     <xc:RBSRichText dataSource="#{document1}"
required="false" fieldLabel="Remarks" fieldName="Body">
</xc:RBSRichText>

<xp:br></xp:br>
<div class="col-sm-2">
</div>
<div class="col-sm-10">
<xp:button value="Save Document" id="button1"
styleClass="btn btn-primary"> <span class="glyphicon glyphicon-ok"></span>
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action>
<xp:actionGroup>
<xp:saveDocument></xp:saveDocument>
<xp:openPage name="/xView.xsp">
</xp:openPage>
</xp:actionGroup>
</xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button value="Cancel" id="button2"
styleClass="btn btn-warning"><span class="glyphicon glyphicon-remove"></span>
<xp:eventHandler event="onclick" submit="true" refreshMode="complete">
<xp:this.action>
<xp:openPage name="/xView.xsp"></xp:openPage>
</xp:this.action></xp:eventHandler></xp:button>
</div>
</xp:panel>
</div>
</xp:div>
</xp:this.facets>
</xc:ccLayoutBootstrap>
</xp:view>

The final result


Remarks:
This blog post is based on the use of Notes Domino 9.0.1 FP2 including the OpenNTF Bootstrap4XPages plugin. 
In Notes Domino 9.0.1 FP2 the CKEditor is updated to 4.3.2 and Dojo to version 1.9.2.
During testing of the reusable Bootstrap Rich Text Field I encountered the following problem.
When adding an image (from a local drive) in the reusable Bootstrap Rich Text Field when using Dynamic Value Binding (value="#{compositeData.dataSource[compositeData.fieldName])") the following error message appears:



All other functions are working properly including adding an Image URL.


I have tried to find a solution for this problem but until now without success.
At this time I am looking at the documentation about the CKEditor and see if I can find a solution there.
The only (relevant) information I have found so far is this in the  Notes/Domino Fix List:


My findings in this first setup for a reusable Bootstrap Rich Text Field:
Usage of a reusable Bootstrap Rich Text Field works properly when no images (from a local drive) are added to the Rich Text Field.
In the situation that images should be able to be added in the reusable Bootstrap Rich Text Field, Dynamic Value Binding fails.

I tested adding an image without Dynamic Value Binding and this works properly.
The dataSource in this example is 'document1' (compositeData.dataSource)and the Rich Text Field in the documents dataSource is named 'Body' ([compositeData.fieldName]).
So if I change the value value="#{compositeData.dataSource[compositeData.fieldName]}" to value="#{document1.Body}" the problem is 'solved' when adding an image in the Bootstrap Rich Text Field. This is of course not a solution to the problem given the field is no longer 'reusable'.

If anyone has a solution for the above problem then please let me know.
If I find a solution I will update this blog post.

2 comments:

  1. Thanks for the post Johnny.
    I had exactly the same issue and I also had to revert to an explicit item name, ie. {document1.Body}

    The other issue I have with my implementation of this (which is almost exactly the same as yours) is that it does not work when the rich text element is bound to an Extension Library xe:objectData. I use this to bind all the elements to a Java bean.
    When loading the page I get a javascript alert with the message:
    "An error occurred while updating some of the page. Cannot read property 'initForEdit' of undefined"
    It works fine when the data source is a xp:DominoDocument.

    ReplyDelete
    Replies
    1. Thanks for your comment Murray.
      I'm still trying to figure out why this does not work this way if you use Dynamic Value Binding.
      All functions within the CKEditor are working properly except adding a picture.
      Currently sifting through the documentation about the CKEditor4 and Dojo 1.9.2 to see if there is a possible problem.
      Hope I can update this post soon with a relatively simple solution.

      Delete