Creating an AOL OpenBlog Reader: Part 2

Enable the Subscriptions block here!

Last Friday we created the shell of the user interface we'll be using for our AOL OpenBlog reader application using the Ext JS JavaScript libraries. Today we'll create a web service in ColdFusion to act as the data store.

If you've been following this blog for the past few weeks, you're more than familiar with the ongoing development of the AOL.Journals CFC, a ColdFusion implementation of the AOL OpenBlog API. We'll be using this as the core data source for the two main user interface components we've already developed using Ext JS, the Select a blog... combo box control and the grid control used to display blog entry information. Since method calls to the AOL.Journals CFC generally return ColdFusion native data structures, we need to write a web service to transform the data to a flexible, friendly data format, JSON.

Ext JS is capable of configuring data stores using many different formats. XML, JSON, and even user-defined native data structures are all valid and have their own pros and cons. JSON has proven to be a very stable, lightweight solution for data interchange and is one of the easiest to work with in Ext JS. As noted earlier, we basically need to provide data to two user interface controls, the blog selection control and the blog entry listing in the form of a grid. This translates to two method calls to our ColdFusion service, getWorkspace and getBlogEntries respectively (see below).

<cfcomponent displayname="OpenBlogRemoteFacade">

	<!--- create a new Journals object --->
    <cfset variables.obj_aolJournals = CreateObject( "component", 
              "AOL.Journals" ).init( form.screenname ) />

	<cffunction name="getWorkspace" access="remote" output="true" returntype="void">
    	<cfargument name="screenname" type="string" required="yes" />
    
    	<cfset var local = StructNew() />
        <cftry>
        <cfset local.blogWorkspace = variables.obj_aolJournals.getWorkspace() />
        <cfcatch type="any">
        	{ "blogs": [] }
            <cfreturn />
        </cfcatch>
        </cftry>
        
        <!--- configure the return data structure --->
        <cfset local.st_blogs = StructNew() />
        <cfset StructInsert( local.st_blogs, "title", local.blogWorkspace.title ) />
        <cfset StructInsert( local.st_blogs, "blogs", ArrayNew(1) ) />
        
        <cfloop collection="#local.blogWorkspace#" item="local.b">
        	<cfif local.b neq "title">
				<cfset local.st_tmp = StructNew() />
                <cfset StructInsert( local.st_tmp, "title", local.b ) />
                <cfset StructInsert( local.st_tmp, "href", local.blogWorkspace[ local.b ].href ) />
                <cfset ArrayAppend( local.st_blogs.blogs, local.st_tmp ) />
            </cfif>
        </cfloop>
        
        <cfoutput>#serializeJSON( local.st_blogs )#</cfoutput>
    
    	<cfreturn />
    </cffunction>
    
    <!--- -------------------------------------------------- --->
    
    <cffunction name="getEntries" access="remote" output="true" returntype="void">
    	<cfargument name="entryHref" type="string" required="yes" default="" />
    	
        <cfset var local = StructNew() />
        <cfset local.arr_entries = variables.obj_aolJournals.getEntries( arguments.entryHref ) />
        
        <cfset local.st_entries = StructNew() />
        <cfset StructInsert( local.st_entries, "entries", local.arr_entries ) />
        
        <cfoutput>#serializeJSON( local.st_entries )#</cfoutput>
    
    	<cfreturn />
    </cffunction>

</cfcomponent>

The OpenBlogRemoteFacade service basically translates native ColdFusion data structures returned by the getWorkspace and getEntries methods of the AOL.Journals CFC to JSON. The getWorkspace method will output JSON data as follows:

{ 
    "title": "someAOLScreenname blogs",
    "blogs": [
               { "title": "A Blog",
                 "href":  "http://api.blogs.aol.com/_atom/collection/someAOLScreenname/a-blog" },
               { "title": "Another Blog",
                 "href":  "http://api.blogs.aol.com/_atom/collection/someAOLScreenname/another-blog" }
             ]
}

The getEntries method call will output JSON data as follows:

{
    "entries": [
                 { "authorName": "bricemason",
                   "title":      "Some blog entry",
                   "content":    "Some blog content",
                   "published":  "2008-01-11 11:52:06"
                 },
                 ... more entries ...
              ]
}

Now that we have the interface mapped out and the remote service developed, we're able to re-visit our Ext JS components to add the interactivity. The most straightforward change involves updating the data stores we've defined to handle the workspace and blog entry information to start using the data returned from the remote service. This is accomplished by removing the data option from the JsonStore configuration and add in the url option like so:

var ds_aolBlogs = new Ext.data.JsonStore(
    {
        url:    remoteFacade,
        root:   "blogs",
        fields: [ "title", "href" ]
    }
);	

var ds_aolBlogEntries = new Ext.data.JsonStore(
    {
        url: blogEntryRemoteFacade,
        root: "entries",
        fields: [ "authorName", "title", "content", "published" ]
    }
);

The next update involves adding a handler to the submit button in the top toolbar:

var btn_submit     = new Ext.Button(
    {
        text: "Go!",
        handler: function(){
            loadBlogSelection( txt_screenname.getValue() );	
        }
    }
);

function loadBlogSelection( sn ){
    
    cbx_blogs.reset();
    
    ds_aolBlogs.load( 
        {
            params: {
                screenname: sn
            }
        }
    );
    
}

The handler defined for the btn_submit button calls a function which basically resets the blog entry combo box and then reloads the data obtained from the ds_aolBlogs data store. This data will update itself in the combo box. The last two updates involve applying additional dynamic behavior to the combo box and grid control with the updated code as follows:

var cbx_blogs = new Ext.form.ComboBox(
    {
        store:         ds_aolBlogs,
        editable:      false,
        displayField:  "title",
        mode:          "remote",
        triggerAction: "all",
        emptyText:     "Select a blog..."
    }
);
cbx_blogs.on( "select", function( c, r, i ) {
    var aolScreenname = txt_screenname.getValue();
    var blogTitle = r.data.title;
    var blogHref = r.data.href;
    
    ds_aolBlogEntries.load(
        {
            params: { screenname: aolScreenname,
                      entryHref:  blogHref }
        }
    );
} ); 

// create the grid to 
var grid_aolBlogs = new Ext.grid.GridPanel(
    {
        store:    ds_aolBlogEntries,
        columns:  [
                        { header: "Posted By", width: 150, sortable: true, dataIndex: "authorName" },
                        { header: "Title",     width: 200, sortable: true, dataIndex: "title"      },
                        { header: "Published", width: 200, sortable: true, dataIndex: "published"  }
                  ],
        loadMask: true,
        width:    555,
        height:   400,
        stripeRows: true,
        tbar:     tb_gridHeader,
        bbar:     tb_gridFooter,
        renderTo: "grid-aolBlogs"
    }
);
grid_aolBlogs.on( "rowclick", function( g, i, e ){
    g.getBottomToolbar().getEl().setStyle(
        {
            "font-size": "0.85em",
            "background": "white",
            "overflow": "auto"
        }
    );
    g.getBottomToolbar().getEl().dom.innerHTML = g.getStore().getAt( i ).get( "content" );
} );

Besides the noted difference of hooking in the store configuration option to both the combo box and grid control above, each control registers some events to deal with the selection of data returned. The combo box registers an onselect event that when fired will load blog entries for the blog selected. The GridPanel registers the onrowclick event to update the bottom toolbar with the full content of the target entry.

In very few lines of code, we were able to create an engaging, dynamic application with little effort. The OpenBlog service together with Ext JS and ColdFusion is just one of many combinations of technologies you can use to create engaging applications for your user base.