Archive for the ‘ ajaxCFC ’ Category

I have to say, it’s been a refreshing break to work on one of our older apps that was written pre-Flex. Working on my old jQuery/ExtJS Javascript code has been fun and I’ve been able to add some new tricks I’ve learned in the last year and a half.

One little tidbit that I thought I’d pass along is a simple trick that most Javascript developers know how to do, but may trip up someone just getting into advanced Javascript development with some of the major libraries out there.

Knowing the proper scope of your Javascript variables is a task that everyone who’s worked with the language knows is important. Using libraries like jQuery and ExtJS make it a bit tricker. An example is when you want to pass an entire argument collection from one function to another and then use that argument collection inside a closure for, say, an ExtJS MessageBox.

At first, you may have the desire to simply pass the argument array from the first function to the second.

function confirm(one, two, three, four) {
    Ext.MessageBox.show({
        title: 'Confirmation',
        msg: 'Please confirm that you want to delete this.',
        buttons: Ext.MessageBox.YESNO,
        fn: function (btn) {
           if(btn == 'yes') doAction(arguments);
        }
    });
}

function doAction(one, two, three, four) {
    $.AjaxCFC({
        url: "com/acme/Widget.cfc",
        method: "delete",
        data: { 'one':one,
                'two':two,
                'three':three,
                'four':four
        }
    });
}

But the execution block that actually calls the doAction() method in within a closure inside the main confirm() method. What this means is that the argument array actually contains the arguments for that anonymous function to handle the button click on the confirmation message.

What you need to do is preserve the arguments collection in a variable scoped to the confirm() method – which is available to the closure (one of the strengths of Javascript) – which can then be passed to the doAction() method.

You then use the apply() method on the function (the apply() method allows you to pass the arguments array) and pass that holding variable.

function confirm(one, two, three, four) {
    var args = arguments;

    Ext.MessageBox.show({
        title: 'Confirmation',
        msg: 'Please confirm that you want to delete this.',
        buttons: Ext.MessageBox.YESNO,
        fn: function (btn) {
           if(btn == 'yes') doAction.apply(this, args);
        }
    });
}

function doAction() {
    var args = arguments;

    $.AjaxCFC({
        url: "com/acme/Widget.cfc",
        method: "delete",
        data: { 'one':args[0],
                'two':args[1],
                'three':args[2],
                'four':args[3]
        }
    });
}

Javascript is such a beautiful and powerful language. I have to say I’ve missed working with it.

ajaxCFC – Setting Query Format

In the ajaxCFC library, the CFJSON project is used to serialize data sent back from ColdFusion Components. One of the settings is the format in which the serialization should be returned: query or array. Unfortunately, Rob’s Javascript interface to the component didn’t allow you to specify this. So with the help of a colleague, the queryFormat property was added to ajaxCFC.

Here’s an example call:

$.AjaxCFC({
	url:'com/company/common/ajax/ajax.cfc',
	bean: 'Facility',
	factory:'application.beanFactory',
	method: 'getFacilities',
	queryFormat: 'array',
	data: { 'orderby':orderByField },
	useDefaultErrorHandler: false,
	success: function(results) {
		$("#facilitySelectContainer").html(results);
	},
	error: function(results) {
		Ext.MessageBox.alert('Error Notification', 'There was an error while loading the facilities. Please try again.')
	}
});

Download ajaxCFC update with queryFormat

Invoking ColdSpring beans with ajaxCFC

As a heavy user of Rob Gonda’s ajaxCFC library, I’ve incorporated it into almost every app I’ve written in the past two years. The only restriction that required me to write workarounds was the fact that you could only invoke ColdFusion Components directly. The thing is, I also love ColdSpring to manage dependencies and to implement my Aspect Oriented Programming components.

What I want is to be able to call my ColdSpring beans with ajaxCFC so that I can make my asynchronous calls, get serialized data back, and all the while utilizing the dependencies set up in my beans. Well, I finally got around to modifying Rob’s code to allow for this. I use the jQuery branch, so my solution currently only works for that implementation. I may try to work on the DWR version, but that’s unlikely because I don’t use it.

There are three changes to how you invoke ajaxCFC in order to work with ColdSpring beans.

The URL Attribute

Instead of the URL value being the path to your logic component, its value should be the path to the ajax.cfc component

url:'com/company/common/ajax/ajax.cfc'

The Bean Attribute

This property’s value will be the name of the ColdSpring bean you want to use.

bean: 'Facility'

The Factory Attribute

This property’s value will be the name of your ColdSpring bean factory.

factory:'application.beanFactory'

A Complete Invocation

Here’s a sample call stripped directly from one of my applications

$.AjaxCFC({
	url:'com/company/common/ajax/ajax.cfc',
	bean: 'Facility',
	factory:'application.beanFactory',
	method: 'getFacilities',
	data: { 'orderby':orderByField },
	useDefaultErrorHandler: false,
	success: function(results) {
		$("#facilitySelectContainer").html(results);
	},
	error: function(results) {
		Ext.MessageBox.alert('Error Notification', 'There was an error while loading the facilities. Please try again.')
	}
});

The Code

There are two files you need to replace.

  1. ajax.cfc
  2. jquery.AjaxCFC.js

Download ColdSpring-enabled ajaxCFC

These three Javascript libraries are at the core of how I build fun, well-organized, and easy to maintain web applications. Each one serves a different purpose:

  • jQuery – The best at DOM selection, manipulation and searching
  • Ext – Makes all my little widgets look great and fun for the users
  • ajaxCFC – Handles calls to my business logic and serialization of data

I’ll start off with a nice example of how these three libraries play well together and can make an otherwise complicated action very simple and easy to write.

This code has three pieces of functionality. I’m using the jQuery Datepicker on my page, and when the user selects a date from the calendar, I want the database to immediately be updated with the date chosen. If the method fails, throw up a nice message box with a failure message and ask the user to try again.

// Use jQuery to bind the dataSelected event of the jQuery DatePicker
$('#requestDueDate').bind('dateSelected',function(e, selectedDate, $td){
        // Use ajaxCFC to call the updateRequestDates() function of the Request component
	$.AjaxCFC({
		url: "model/ajax/request/Request.cfc",
		useDefaultErrorHandler: false,
		method: "updateRequestDates",
		data: { 'request_hdr_seq_no': request_hdr_seq_no,
			'request_due_date': selectedDate.asString(),
			'test_completion_date':$("#testCompletionDate").val() },
		timeout:30000,
		success: submitRequestCheck,
		error: function(results) {
                        // If the call fails, throw up an Ext message box to alert the user
			Ext.MessageBox.alert('Update Failed', "Failed to update the request due date. Please try again.");
			$('input#testCompletionDate').val("");
		}
	});
});

All I was required to do was write 14 lines of code. Now, I can’t even imagine having to write this code without the use of the Big 3.

Now for one of my favorite features of this site (it’s actually a boring feature, but how I implemented it is cool).
Notes Accordion Dialog II

This is just a simple note feature where users can record any pertinent information about the work they are performing. I’m using the Ext Accordion Widget to display all past notes. You can see how the user can expand and contract each note item – not really needed, but never hurts to wow the users a little.

This particular page was written to operate as a true application. Any changes to the UI are immediately written to the database via AJAX calls. The UI is then updated when the operation completes, and the user can continue working while several tasks are being executed. No page reloads at all.

So when they add a note, it must be inserted into the database, and the Accordian object must have a new panel added to it, with the new text inserted inside.

I’ll admit, it stretched all my capabilities and imagination for Javascript programming. All in all, it took over 3 days of trying different things, and research, to get it to work.

First, I had to include the required features of the libraries.

<script type="text/javascript" src="js/jquery.AjaxCFC.js"></script>
<script type="text/javascript" src="js/json.js"></script>

<script type="text/javascript" src="js/ext-buttons.js"></script>
<script type="text/javascript" src="js/ext-layout.js"></script>
<script type="text/javascript" src="js/Ext.ux.InfoPanel.js"></script>
<script type="text/javascript" src="js/Ext.ux.Accordion.js"></script>

*This adds up to 300k of jQuery/Ext Javascript libraries.

Then, I build the <div> element that will be used to display the dialog and the accordion panels

<div id="NoteModalDialog">
	<div class="x-dlg-hd">Request Notes</div>
	<div class="x-dlg-bd">
       	<div class="x-dlg-tab" title="Notes">
			<div id="RequestNoteForm"></div>
			<div style="font-size:1.1em; font-weight:800;">Previous Notes</div>
			<div id="acc-ct" style="width:100%; height:200px">
				<cfloop query="requestNotesQuery">
					<cfoutput>
					<div id="panel-#currentRow#">
						<div>
							#first_name# #last_name# - #setup_date#:
						</div>
						<div>
							<div class="text-content">#note_txt#</div>
							<div style="border-bottom:5px solid white;"></div>
						</div>
					</div>
					</cfoutput>
				</cfloop>
			</div>
		</div>
	</div>
</div>

Then I instantiate the accordion object and build all the panels with previous notes

$(document).ready(function(){
	// Create accordion to hold previous notes in note popup
	var accordion = new Ext.ux.Accordion('acc-ct', { height:400,independent:true});

	// Create a panel for each previous note
	<cfloop query="requestNotesQuery"><cfoutput>
		var panel#currentRow# = accordion.add(new Ext.ux.InfoPanel('panel-#currentRow#', {collapsed:false}));
	</cfoutput></cfloop>
}

Then, of course, I need my wonderful Javascript object that handles building the dialog, saving the note, and loading of previous notes back into the UI/DOM.

It first builds a dynamic Ext form (which includes validation).

var noteForm = new Ext.form.Form({ labelWidth:25 });
var noteText = new Ext.form.TextArea({
	fieldLabel: 'Note',
	name: 'noteText',
	width:375,
	allowBlank:false,
	grow:true,
	growMax:200,
	emptyText:"Enter your request notes here..."
});
noteForm.add(noteText);

Then using ajaxCFC, I insert the data into database

$.AjaxCFC({
	url: "model/ajax/note/Note.cfc",
	useDefaultErrorHandler: false,
	method: "add",
	async:false,
	data: { 'seq_no': request_hdr_seq_no, 'note_type':'REQUESTHDR', 'note_txt': noteText.getValue() },
	timeout:30000,
	success: function(results) {
		// Reset the note field to blank
		noteText.setRawValue("");

		{{ This nested ajaxCFC call code shown below }}
	},
	error: function(results) {
		Ext.MessageBox.alert('Save Failed', results.responseText);
	}
});

Upon success of the insertion of the data, update the UI by adding a panel to the Accordion object with the new note inside.

// Return all notes from the database for this request
$.AjaxCFC({
	url: "model/ajax/request/Request.cfc",
	useDefaultErrorHandler: false,
	method: "getAllRequestNotes",
	async:false,
	data: { 'request_hdr_seq_no': request_hdr_seq_no },
	timeout:30000,
	success: function(results) {
		// Empty out the current accordion object and re-populate it with a panel for each note
		var panel = new Object();
		var accordion = new Ext.ux.Accordion('acc-ct', { height:'400', independent:true });
		$("#acc-ct").empty();

		for(i=0; i<results.NOTECOUNT;i++) {
			$("#acc-ct").append('<div id="panel-' + i + '"><div>' + results.PANELS[i].FIRST_NAME + ' ' + results.PANELS[i].LAST_NAME + ' - ' +    results.PANELS[i].SETUP_DATE + '</div><div><div class="text-content">' + results.PANELS[i].NOTE_TXT + '</div></div></div>');
			panel[i] = accordion.add(new Ext.ux.InfoPanel('panel-'+i, {collapsed:false}));
		}
	},
	error: function(results) {
		Ext.MessageBox.alert('UI Update Failed', "Failed to update Notes popup with latest note.");
	}
});

I put it all together, and I get a self-documenting, elegant Javascript object that handles user notes.

// Create the object that will be used for the Request Notes popup
var requestNotesEdit = function() {
	var dialog;
	var noteForm = new Ext.form.Form({ labelWidth:25 });
	var noteText = new Ext.form.TextArea({
		fieldLabel: 'Note',
		name: 'noteText',
		width:375,
		allowBlank:false,
		grow:true,
		growMax:200,
		emptyText:"Enter your request notes here..."
	});
	noteForm.add(noteText);

	return {
		init : function() { this.buildDialog(); },
		saveData : function() {
			if (noteForm.isValid()) {

				$.AjaxCFC({
					url: "model/ajax/note/Note.cfc",
					useDefaultErrorHandler: false,
					method: "add",
					async:false,
					data: { 'seq_no': request_hdr_seq_no, 'note_type':'REQUESTHDR', 'note_txt': noteText.getValue() },
					timeout:30000,
					success: function(results) {
						// Reset the note field to blank
						noteText.setRawValue("");

						// Return all notes from the database for this request
						$.AjaxCFC({
							url: "model/ajax/request/Request.cfc",
							useDefaultErrorHandler: false,
							method: "getAllRequestNotes",
							async:false,
							data: { 'request_hdr_seq_no': request_hdr_seq_no },
							timeout:30000,
							success: function(results) {

								// Empty out the current accordion object and re-populate it with a panel for each note
								var panel = new Object();
								var accordion = new Ext.ux.Accordion('acc-ct', { height:'400', independent:true });
								$("#acc-ct").empty();

								for(i=0; i<results.NOTECOUNT;i++) {
									$("#acc-ct").append('<div id="panel-' + i + '"><div>' + results.PANELS[i].FIRST_NAME + ' ' + results.PANELS[i].LAST_NAME + ' - ' +    results.PANELS[i].SETUP_DATE + '</div><div><div class="text-content">' + results.PANELS[i].NOTE_TXT + '</div></div></div>');
									panel[i] = accordion.add(new Ext.ux.InfoPanel('panel-'+i, {collapsed:false}));
								}
							},
							error: function(results) {
								Ext.MessageBox.alert('UI Update Failed', "Failed to update Notes popup with latest note.");
							}
						});

					},
					error: function(results) {
						Ext.MessageBox.alert('Save Failed', results.responseText);
					}
				});

				dialog.hide();
			}else{
				Ext.Msg.alert('Field Required', 'Please enter in some note text before submitting.');
			}
		},
		buildDialog : function() {
			noteForm.render('RequestNoteForm');
			dialog = new Ext.BasicDialog("NoteModalDialog", {
				modal:true,
			    width:450,
			    height:350,
			    shadow:true,
			    minWidth:400,
			    minHeight:300,
			    animateTarget:'requestNotes'
			});
			dialog.addKeyListener(27, this.hideWindow, this);
	        dialog.addButton('Save', this.saveData, this);
	        dialog.addButton('Cancel', this.hideWindow, this);
		},
		showWindow : function() { dialog.show(); },
		hideWindow : function() { dialog.hide(); }
	};
};