Monday, September 26, 2011

Serialize Your CFC's Getters Into JavaScript Objects With CF PB&J

This weekend I worked on a little project called CF PB&J that will serialize a CFC's getters and their output into a well-encapsulated JavaScript object.  It's meant to be lightweight and work seamless with your JavaScript  while not cluttering up your global by having to create a  bunch of global variables to store your dynamic data.  You can find the project up on github at https://github.com/bittersweetryan/CF-PB-J.

Lets take a look at a quick example of how it works.  First lets create an Account object (note that there would typically be setter methods for these functions as well but they are left out for brevity's sake):

<cfcomponent hint="Test very simple cfc for searalization to javascript">
	<cfscript>
		variables.accountNumber = 0;
		variables.accountName = "";
		variables.productArray = [];
		variables.insuredProducts = {};
		
		
		variables.PBJ = "";
	</cfscript>
	
	<cffunction name="init" returntype="Account" access="public" output="false" hint="" >
		<cfscript>
//create the PBJ object and pass in a reference to this
			variables.PBJ = createObject("PBJ").init(this);
			return this;
		</cfscript>
	</cffunction>
	
	<cffunction name="getAccountNumber" returntype="numeric" access="public" output="false" hint="" >
		<cfscript>
			return variables.accountNumber; 
		</cfscript>
	</cffunction>
	
	<cffunction name="getAccountName" returntype="String" access="public" output="false" hint="" >
		<cfscript>
			return variables.accountName; 
		</cfscript>
	</cffunction>
	
	<cffunction name="toJS" returntype="String" access="public" output="false" hint="Proxy to the PBJ toJS method" >
		<cfscript>
			return variables.PBJ.toJS();
		</cfscript>
	</cffunction>
	
	<cffunction name="getProductArray" returntype="Array" access="public" output="false" hint="" >
		<cfscript>
			return variables.productArray;
		</cfscript>
	</cffunction>


	<cffunction name="getInsuredProducts" returntype="Struct" access="public" output="false" hint="" >
		<cfscript>
			return variables.insuredProducts;
		</cfscript>
	</cffunction>
	
</cfcomponent>

If you look closely at the Init method you'll see that a PBJ object is created and initalized and the "this" reference is passed to it.  This tells PB&J to inspect the current object for its "getter" methods defined by the naming convention getXXX.  PB&J will look for all getter methods that return strings, numbers, arrays, and structures.  This object also defines a proxy function called "toJS"that calls the PBJ.toJS() method.  When this method is invoked it will run all of your object's getter methods, save the return data, and output JavaScript that is a mirror of your object with its getter methods and its data.  Lets take a look at an example of this in action.  First lets create an instance of the account object and set some of its data:

account = createObject("Account").init();
	
account.setAccountNumber(12345);
account.setAccountName("Ryan ""Pretty Boy"" Anklam");

Now in our .cfm page we output the value of  #account.toJS()#  in the header to produce our JavaScript representation of our object:

< script type = "text/javascript" charset = "utf-8" >
var Account = (function() {
    var accountName = "Ryan \"Pretty Boy\" Anklam";
    var productArray = [];
    var insuredProducts = {};
    var accountNumber = 12345;
    return {
        getAccountName: function() {
            return accountName;
        },
        getProductArray: function() {
            return productArray;
        },
        getInsuredProducts: function() {
            return insuredProducts;
        },
        getAccountNumber: function() {
            return accountNumber;
        }
    };
})(); 
< /script>

Looking at the JavaScript you'll see that CF PB&J created a few private variables and returned public methods to return that data.   It's meant to be a "read-only" representation of the internal state of your object.  PB&J will also work on calculated fields as it just invokes your getter methods and saves its output.

Now in any of your scripts you can access the object's data by calling the JavaScript object's getter methods:

< script type = "text/javascript" charset = "utf-8" >
     Account.getAccountName();
     Account.getAccountNumber();
< /script>
Fork me on GitHub