CKEditor Inline Editing on Coldfusion using AJAX and JQuery

Implementing CKEditor Inline Editing on Coldfusion using AJAX and JQuery

Everyone says it's easy to get CKEditor running on ColdFusion, but there are no working examples available. Here is a tutorial and example on how you can perform inline editing within CKEditor and use AJAX to submit to ColdFusion processing.

In this example my editable page is Bootswatch styled. Looking at the provided CKEditor documentation under Samples > Inline Editing by Code, I pulled out just what I needed. It really helps to look at the View Source code of the documentation. What is needed first is the JavaScript call for CKEditor. I placed this at the bottom of my page (under "Le Javascript").

view plain print about
1</script src="ckeditor/ckeditor.js"></script>
Next since I wanted 2 editable sections, each had to have its own ID, editable1 and editable2, and be referenced in the JS. We had to turn off "edit everything" mode and select just the DIVs (by Id) we want to make editable.
view plain print about
1// We need to turn off the automatic editor creation first.
2CKEDITOR.disableAutoInline = true;
3var editor1 = CKEDITOR.inline( 'editable1' );
4var editor2 = CKEDITOR.inline( 'editable2' );
My editable divs were identifed like this:
view plain print about
1<div class="well" id="editable1" contenteditable="true">
2<cfinclude template="RotRugby1.cfm">
3</div>
4more code.....
5<div class="well" id="editable2" contenteditable="true">
6<cfinclude template="RotRugby2.cfm">
7</div>
Note the contenteditable="true". Without this nothing happens, CKEditor will not know to edit this div. Also I cfinclude'd the editable portions of the code. This will make more sense when we look at the processing end.

Now we need to add the code that pushes our code through AJAX for processing. First the button:

view plain print about
1<input onclick="publishRot();" type="button" value="AJAX Baby!">
And then the JQuery function, publishRot() called above, added to the <script> tag
view plain print about
1function publishRot() {
2    var RotStuff1 = document.getElementById( 'editable1' ).innerHTML = html = editor1.getData();
3    var RotStuff2 = document.getElementById( 'editable2' ).innerHTML = html = editor2.getData();
4    
5    $.post(    "rotrugbyaction.cfm",
6            {Rot1:RotStuff1,
7             Rot2:RotStuff2},
8            function(data) {
9                alert(data);
10            }
11    );
12}
Walking through the code we set the variable RotStuff1 calling the editable1 ID. This little bit was stripped out of the CKEditor Examples > Create and destroy editor instances for AJAX applications (view source!).

The JQuery AJAX $.post() method, the URL is placed first. Following are the data we want passed, RotStuff1 and RotStuff2, our edited divs as Rot1 and Rot2 respectively. And the function, which is designed to simply display a pop up with data passed back from our AJAX request.

Let's look at where we are passing the AJAX request, to rotrugbyaction.cfm and what we are doing with it.

view plain print about
1<cfif StructKeyExists(form,"Rot1")>
2    <cfoutput>Latest News Updated!</cfoutput>
3    <cffile action="write" file="C:\ColdFusion10\cfusion\wwwroot\JoeTest\rotrugby1.cfm" output="#form.Rot1#"/>
4</cfif>
5<cfif StructKeyExists(form,"Rot2")>
6    <cfoutput>Bottom Part Updated!</cfoutput>
7    <cffile action="write" file="C:\ColdFusion10\cfusion\wwwroot\JoeTest\rotrugby2.cfm" output="#form.Rot2#"/>
8</cfif>

What is important to note here is that the AJAX data is passed as FORM variables. We test the form scope for existence of the expected fields, Rot1 and Rot2 with StructKeyExists().

I chose to use <cffile> to write the the edited data to an actual CF template, rotrugby1.cfm and rotrugby2.cfm. This is why I used the <cfinclude> for the editable fields.

The <cfoutput> is the message or data gets passed back to the requesting template. This message will show up in the pop up.

Just go back and refresh your page to see you newly edited divs!

I did try using a window.location.reload(); to refresh the page automatically, but the page would refresh before the pop up could be displayed or read. The user would then not know if any action had taken place.

In summary:
I used the inline CKEditor, disabled AutoInline Editor, made the DIVs editable with their Id's in the JS and in the DIV with contenteditable="true". I utilized the JQuery AJAX post() method, setting the variables to grab the edited DIV's by Id, and used the button's onclick attribute to call the whole function. From there, it is passed to the processing CF template. The CF processing template uses <cffile> to write the edits to another template and send a success message back via AJAX. The original template uses <cfinclude> to call the edited templates.

jQuery Functions Based on datepicker Selection

The request is to have a form field display and be required if the datepicker selected date is within 2 weeks of the current date. If the date is later than 2 weeks, hide the form field and make it not required.

In this case, the date field is prepopulated with an outputted ColdFusion variable which is the date two weeks days from today. If the user selects the date field, datepicker displays allowing the user to change the date. If the newly selected date is less than two weeks from today's date, then display the other field and make that form field required. The default (because of the prepopulated date) is to not display the extra form field and not make it required.

Hopefully the code is documented well enough to make sense.

view plain print about
1//use datepicker to select date
2jQuery("##storeDate").datepicker();
3    
4//if the date is changed anytime, run the comparison function     
5jQuery("##storeDate").change(function() {
6    doDateCompare();
7});
8    
9//initially run the comparison function upon opening document     
10doDateCompare();
11    
12// the date comparison function tasks that need to be run
13function doDateCompare() {
14          
15    //create the comparison date object and set for today
16    var objdate = new Date();
17    
18    // get the date initial or selected from the datepicker form field
19             var objformdate = jQuery("##storeDate").datepicker("getDate");    
20             
21             //compare the dates but add 2 weeks to todays date first
22    if (objformdate < objdate.setDate(objdate.getDate() + 14)){
23            
24        //selected date is less than 2 weeks away
25        
26        //show the extra form field table row    
27     jQuery("##storeDateRow").show();
28    
29     //make the extra form field is now required
30     jQuery("##metaData").attr("required", "required");
31    }
32    else {
33        
34        //selected date is more than 2 weeks out
35        
36        //hide the extra form field row
37     jQuery("##storeDateRow").hide();
38    
39     //remove required attribute from extra form field so we can submit form
40     jQuery("##metaData").attr("required", "");
41    }
42};

Here is the table row that will hide and show. Also the metadata field that picks up the required attribute, losing the required attribute when the date selected is over 2 weeks out.

view plain print about
1<tr id="storeDateRow">
2    <td>
3    Expense Num:<br>
4    <span class="formRequired">(required if date < 2 weeks)</span>
5    </td>
6    <td>
7     <input type="text" name="metaData" id="metaData" value="">
8    </td>
9</tr>

Remove Last Character of String createobject("java", "java.lang.StringBuffer").init()

Manipulating strings which are Java objects created in ColdFusion is a lot different that using standard CF functions. Once the Java object is created, Java methods must be used to manipulate the object.

How do I remove the last character of a Java object string created using the StringBuffer class?

view plain print about
1<cfset myString = createobject("java", "java.lang.StringBuffer").init() />

Here is a listing of the methods that can be used:

http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuffer.html

The most common method seen used in CF is the append() method.

view plain print about
1<cfset myString.append("more string text" & thisVariable & " to " & thatVariable & "!")/>

demonstrates some string concatenation. Notice that there is an exclamation point (!) added to the end.

So the boss says, "No more exclamation points!!! Just at the end, you can use them in the middle of strings, like when I call you a @&!!*#% you worthless @#$%#!!"

So, before you start looking for another job (who could work for someone who talks to you like that?), we will remove the exclamation point at the end. The obvious way to do this would be just remove the concatenated

view plain print about
1& "!"

But the point of this lesson is to remove it using a Java StringBuffer method.

We could use deleteCharAt(), that's a StringBuffer method, but how do we find what the index of the exclamation point is?. It's at the end of a varying length string, changing every time the function is called and new variables are introduced.

This is where another StringBuffer method, length() comes to play

view plain print about
1<cfset myString.deleteCharAt(myString.length()-1) />

Starting from the inside, we find the myString length, subtract one and delete the character in that position.

This is a simple example but it is just for demonstration. On this post you can see how it came in really handy.

Invalid JSON property [object Error] found when trying to apply resultTemplate or paging.summaryTemp

Invalid JSON property [object Error] found when trying to apply resultTemplate or paging.summaryTemplate. Please check your spelling and try again. Error: Unable to get value of the property 'name': object is null or undefined

Error with jQuery, combobox, flexbox in IE7 and IE8. Runs fine on IE9, Chrome, and Firefox.

Solution:
My combobox jQuery was dynamically generated using ColdFusion code concatenating JavaScript strings while looping data. I found some bad logic that was erroneously adding an extraneous trailing comma to my dropdown array.

Read more here....

Debugging CF and JavaScript, flexbox combobox jQuery

Invalid JSON property [object Error] found when trying to apply resultTemplate or paging.summaryTemplate. Please check your spelling and try again. Error: Unable to get value of the property 'name': object is null or undefined

Getting errors when using flexbox combobox on IE7 and IE8. The code runs on IE9, Chrome, and all Firefox.

Using ColdFusion to generate JavaScript can sure lead to complications when trying to debug. It's complicated trying to write the CFML to generate the code, but we do it to make our lives easier!

This post is more about the process of debugging than writing or even the actual solution. First I will list efforts that proved worthless. I'll bet you tried at least one of these before finding this post.

  1. Searching for "resultTemplate" and/or "summaryTemplate"
  2. Editing jquery.flexbox.js. I actually put in "alert"s to step debug – waste of time.
  3. Edited the generating CF code to add spaces in the resultant JS.
  4. Did a LOT of Googling. This only helped if you found this article, which wasn't here when I went through the process!
So what worked? or how do we find the cause?

The first objective is to narrow down the area which is causing the problem. Because it was clicking in the combobox throwing the error, that sure helped. Next is to look at the JavaScript generated for the combobox. Here's what I had.

view plain print about
1<scr!pt type="text/javascript">var values_6789 = {};values_6789.results = [{id: "View The Collection", name: "View The Collection"},{id: "View The Trunk Show Collection", name: "View The Trunk Show Collection"},{id: "Personal Appearance", name: "Personal Appearance"},];values_6789.total = values_6789.results.length;$(document).ready(function () {$("#misc2").flexbox(values_6789, {autoCompleteFirstMatch: false, selectBehavior: true, paging: false, width: 180, noResultsText: "" });});</script>

Looking through this code, something very small is out of place, a comma.

view plain print about
1......name: "Personal Appearance"},];values_6789.total =......

The comma just before the closing bracket, "]".

Here's the code that builds that part of the JavaScript

view plain print about
1<cfloop query="arguments.qProductAddorValues">
2 <cfif arguments.qProductAddorValues.productAddorID eq arguments.productAddorID>
3 <!--- write element for the JSON object --->
4 <cfset fldString.append('{id: "#arguments.qProductAddorValues.value#", name: "#arguments.qProductAddorValues.name#"} ' )>
5 <cfif arguments.qProductAddorValues.currentrow neq arguments.qProductAddorValues.recordcount>
6 <cfset fldString.append(',')>
7 </cfif>
8 <cfif (len(arguments.fldval) and not cbBoxFldValAdded)>
9 <!--- this string is used for initialization of the control --->
10 <cfset defaultSelection = ',initialValue: "#arguments.fldval#"'>
11 <cfif arguments.qProductAddorValues.currentrow neq arguments.qProductAddorValues.recordcount>
12 <cfset fldString.append(',')>
13 </cfif>
14 <cfset cbBoxFldValAdded = 1>
15 <cfelse>
16 <cfif (arguments.qProductAddorValues.default eq 1 and not cbBoxFldValAdded)>
17 <cfset defaultSelection = ',initialValue: "#arguments.qProductAddorValues.value#"'>
18 </cfif>
19 </cfif>
20 </cfif>
21</cfloop>

Why is that extra comma there? There's a checking for the end...

view plain print about
1<cfif arguments.qProductAddorValues.currentrow neq arguments.qProductAddorValues.recordcount>
2 <cfset fldString.append(',')>
3</cfif>

The logic is probably bad. To prove it we have to output the 2 variables in the test. We have to output them and NOT break jQuery so the page will render AND still display the variable output.

This is done by commenting out the variable output with JavaScript syntax, /* stuff */. First, I create a string with the 2 test variables and concatenate them together with opening and closing JS comments.

view plain print about
1<cfset JSStringTest = "/* " & arguments.qProductAddorValues.currentrow & " = " & arguments.qProductAddorValues.recordcount & " */" />

Then I stick output of the string into the generated JS string where I think it will be innocuous. Outside of the array but inside the single quote.

view plain print about
1<cfset fldString.append('{id: "#arguments.qProductAddorValues.value#", name: "#arguments.qProductAddorValues.name#"} #JSStringTest#' )>

Here is the resultant JavaScript output:

view plain print about
1<scr!pt type="text/javascript">var values_6789 = {};values_6789.results = [{id: "View The Collection", name: "View The Collection"} /* 2 = 63 */,{id: "View The Trunk Show Collection", name: "View The Trunk Show Collection"} /* 3 = 63 */,{id: "Personal Appearance", name: "Personal Appearance"} /* 4 = 63 */,];values_6789.total = values_6789.results.length;$(document).ready(function () {$("#misc2").flexbox(values_6789, {autoCompleteFirstMatch: false, selectBehavior: true, paging: false, width: 180, noResultsText: "" });});</script>

You can see that the commented /* 4 = 63 */ for the final entry is obviously false. Thus proving the test is bad.

From here I have 2 options

  1. Find an accurate test.
  2. Just remove the last comma.

In this instance there really wasn't a valid test that could be easily and accurately generated. Information was being pulled from too many different places. I decided to just add the trailing comma after every iteration and remove it from end of the string after the loop is completed, but before we appended more JavaScript.

This turned out to not be easy either. If you recall, the string, fldString was appending JavaScript using:

view plain print about
1fldString.append()

This was because fldString is not really a string but a Java object created with:

view plain print about
1fldString = createobject("java", "java.lang.StringBuffer").init()

using CF's create Java object and Java's StringBuffer class. So the "fldString.append()" is a StringBuffer method.

What other StringBuffer method can I use to trim off the pesky trailing comma? We could use deleteCharAt() but how to find what the index is of the comma. It's at the end of a variable length string, changing every time the function is called. Well we just find the length of the string using another StringBuffer method, length(). Can't get much simpler than that! So here is how that code looks:

view plain print about
1<!--- remove trailing comma which is always added above ~ line 156, using Java StringBuffer class --->
2<cfset fldString.deleteCharAt(fldString.length()-1) />

Starting from the inside, we find the fldString length, subtract one and delete the character in that position.

So here's the final bit of completed code:

view plain print about
1<cfloop query="arguments.qProductAddorValues">
2 <cfif arguments.qProductAddorValues.productAddorID eq arguments.productAddorID>
3 <!--- write element for the JSON object --->
4 <cfset fldString.append('{id: "#arguments.qProductAddorValues.value#", name: "#arguments.qProductAddorValues.name#"},' )>
5 <cfif (len(arguments.fldval) and not cbBoxFldValAdded)>
6 <!--- this string is used for initialization of the control --->
7 <cfset defaultSelection = ',initialValue: "#arguments.fldval#"'>
8 <cfset cbBoxFldValAdded = 1>
9 <cfelse>
10 <cfif (arguments.qProductAddorValues.default eq 1 and not cbBoxFldValAdded)>
11 <cfset defaultSelection = ',initialValue: "#arguments.qProductAddorValues.value#"'>
12 </cfif>
13 </cfif>
14 </cfif>
15</cfloop>
16<!--- remove trailing comma which is always added above ~ line 156, using Java StringBuffer class --->
17<cfset fldString.deleteCharAt(fldString.length()-1) />

Bam! The JSON error is gone in IE7 and IE8 and the code still runs perfectly in all other browsers. This is a long story with a few important pieces. I'll break out the useful tidbits and post those in separate blog entries so that others may find specific answers without having to read this saga.

Debugging ColdFusion Generated JavaScript Trick

With JavaScript that is generated with ColdFusion, debugging can be very difficult. One can do step debugging by adding JS alerts each step and output the variables passed as far as the alert. But this becomes more difficult the more one relies on dynamically generated (ColdFusion) JavaScript and jQuery.

One easy trick is to output the testing variables in commented JavaScript. We have to output them and NOT break jQuery so the page will render AND still display the variable output.

This is done by commenting out the variable output with JavaScript syntax, "/* JavaScript comment */".

First, I create a string with the variables for which I am testing and concatenate them together with opening and closing JS comments.

view plain print about
1<cfset JSStringTest = "/* " & variables.thisTestValue & " = " & variables.thatTestValue & " */" />

Then I stick output of the string into the generated JS string where I think it will be innocuous.

This is really handy in loops, where you may be outputing variables based on tests, for example, using

See more here....

JQuery Prefix Textbox with Specific Number

jQuery Masked Input prefixing a form field text box with a specific character set.

A form requiring a project code, which was numeric, needed to have the project code prefixed with 1000 and be 10 digits long. An example of a properly formatted project code is "1000123456". How to do this with jQuery?

Use Josh Bush's Masked Input Plugin for jQuery to start. Make sure you have jquery.js (latest version) in your .

(Please note that the "invalidTag" referenced below is actually "script". CFFormProtect is changing this for my own good and your confusion.)

view plain print about
1<invalidTag src="jquery.js" type="text/javascript"></script>
2<invalidTag src="jquery.maskedinput.js" type="text/javascript"></script>

Using Mask Definitions
My form field text box had the name and id of "projectCode" My first attempt using regex resulted in failure. It looked like this:

view plain print about
1<invalidTag language="javascript">
2 $(document).ready(function () {
3     $.mask.definitions['~']=' \b[1][0]{3}\b ';
4     $("##projectCode").mask('~999999');
5 });
6</script>
FAIL!

I'm using ColdFusion, hence the escaped double pound signs (##).

Next try

view plain print about
1<invalidTag language="javascript">
2 $(document).ready(function () {
3     $.mask.definitions['~']='\b[1][0][0][0]\b';
4     $("##projectCode").mask('~999999');
5 });
6</script>
FAIL!

which lead to:

view plain print about
1<invalidTag language="javascript">
2 $(document).ready(function () {
3     $.mask.definitions['~']='\b[1][0][0][0]\b';
4     $("##projectCode").mask('~~~~999999');
5 });
6</script>
This allowed me to input "1000" into the correct fields but I could also input "1111" and "0000" or any binary combination of 1s and 0s.

Multiple Mask Definitions
So I used multiple mask definitions to successfully accomplish what I needed.

view plain print about
1<invalidTag language="javascript">
2 $(document).ready(function () {
3     $.mask.definitions['~']='[1]';
4     $.mask.definitions['^']='[0]';     
5     $("##projectCode").mask('~^^^999999');
6 });
7</script>
SUCCESS!

So a little explanation may help. The mask definition for requires that the tilde (~) field will only accept the digit 1. The carat (^) field will only accept 0. So the mask '~^^^' will only accept '1000'.

The great thing is that we are not just restricted to numbers, as in this case. You could restrict by specific letters or characters too. Simply adjust the mask definition.

From the user's point of view, the only issue could be that the user could hack away at the keyboard not understanding why they could not put in the numbers they wanted. A little instruction in the label solved that issue. "Please prefix the Project Code with '1000'".

jQuery flexbox IE8 invalid JSON property

Invalid JSON property TypeError: Unable to get value of the property 'name': object is null or undefined found when trying to apply resultTemplate or paging.sumaryTemplate.

We are using a jQuery flexbox for a dropdown combobox in a form. Clicking on the dropdown combobox field generates the above JavaScript alert ONLY in IE8. In all other browsers the combobox works correctly.

So, in short, the error occurs because of the IE8 parsing engine. Quit looking elsewhere!

Here is the fix, place this in your head:

view plain print about
1< meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

This worked. Newer versions of jQuery are backward compatible so having IE run as IE7 doesn't cause any problems for us in our situation.

Reference: MSDN

BlogCFC was created by Raymond Camden. This blog is running version 5.9.7. Contact Blog Owner