AJAX Pagination with Coldfusion and Jquery

In my continued fascination with Pagination (does that make me a Geek?) i am detailing here how to achieve it with AJAX (Jquery/JSON really) and Coldfusion. You can see a running example of the code here, and download all the code here. If you are interested in learning Jquery i recommend reading the book Learning Jquery.

Just to diverge slighly if you were like i was and really wanted to get to grips with AJAX. But got confused as to where to start due to the amount of different frameworks around, not to mention Coldfusion's own AJAX tools, like CFAjaxproxy. Then let me enlighten you. CFAjaxproxy is like a mini JS framework. So if you are wanting to use Jquery then i think you don't really need to worry about what CFajaxproxy does. I think Cfajaxproxy suits best if you need to get something done in a hurry and can't spare the time to learn Jquery.

Features

  • Displays record set query data one page at a time.
  • Page numbered links are also built and placed displayed records
  • Allows for sorting of returned results by clicking on table headers.
  • Includes highlighting of current page link and showing only up to 20 page numbers/links on page at any one time.
  • CF returns only the records needed for each page (ie 10 at a time) rather than the whole record set.
  • Has previous and next links at either end of page links.

The CFC
Each time the CFC is called from Jquery it returns a struct containing a result set of 10 records along with info about the archive being queried. Which can be used to help show user how many records they are paging through. Accepts 2 parameters: PageNumber - needed to help the CFC determine what records to return. orderBy - passes in what database column to order the returned query by.

<cfcomponent displayname="paginationRealData" hint="calculates and returns pagination info and record set" output="false">


   <cffunction name="pageInfo" displayname="pageInfo" access="remote" output="false" returnFormat="json">
      <cfargument name="pageNumber" type="Numeric" required="true">
      <cfargument name="orderBy" type="string" required="true">
      
<cfset var result = structNew()>

<CFIF not isDefined("arguments.pageNumber") >
<cfset arguments.pageNumber = 1>
</CFIF>

<cfquery datasource="which_new" name="myQuery">
select name, region, area, population from bbc
order by #arguments.orderBy#
<cfif #arguments.orderBy# is "population" or #arguments.orderBy# is "area">DESC</cfif>
</cfquery>


<cfset result.orderBy = #arguments.orderby#>
<!---determines how records are shown on one page --->
<cfset result.numberOfRecordsOnPage = 10>
<!---calculate the number of pages the archive will have --->
<cfset numberOfPagesNoCeil = (#myQuery.recordCount# / #result.numberOfRecordsOnPage#)>
<cfset result.numberOfPages = #ceiling(numberOfPagesNoCeil)#>
      
      <!---here using url.pagenumber to work out what records to display on current page --->
      <cfset result.from = ((#arguments.pageNumber# * #result.numberOfRecordsOnPage#) - #result.numberOfRecordsOnPage#) + 1>
    <cfset result.to = (#arguments.pageNumber# * #result.numberOfRecordsOnPage#)>
      
      <!---if on last page display the actual number of records in record set as the last to' figure'. Otherwise it gives
      a false reading and gives the pagenumber * numberOfRecordsOnPage which is always a multiple of 10--->

      <cfif #result.to# eq (#result.numberOfPages# * 10)>
       <cfset result.to = myQuery.recordCount>
      </cfif>
      
<!---Query to send back the 10 records relevant to pageNumber --->
<!--- Create a new 4-column query, specifying the column data types --->
<cfset recordsToGoBack = QueryNew("field1, field2, field3, field4", "VarChar, VarChar, VarChar, VarChar")>
<!--- --->
<cfloop query="myQuery" startrow="#result.from#" endrow="#result.TO#">
<!--- Set the values of the cells in the query --->
<cfset temp = QueryAddRow(recordsToGoBack)>
<cfset temp = QuerySetCell(recordsToGoBack, "field1", #name#)>
<cfset temp = QuerySetCell(recordsToGoBack, "field2", #region#)>
<cfset temp = QuerySetCell(recordsToGoBack, "field3", #area#)>
<cfset temp = QuerySetCell(recordsToGoBack, "field4", #population#)>
</cfloop>

<cfset result.recordsToGoBack = #recordsToGoBack#>
      
      
      <cfreturn result/>
   </cffunction>
</cfcomponent>

The JS (Jquery code) I am unable to display the Jquery code on my blog, as it come up all skew-whiff when i cut and paste via blog CFC admin. You can follow and view the JS code here. The code is mostly Jquery.

Displaying the records / Data

To communicate with the CFC i used Jquery's global function getJSON(). Then it was a matter of traversing the returned JSON object and using the values in there to help build a string containing a HTML table that displays the returned records. The HTML string need not be a table. It can be as simple as a
tag appended to the returned database values. Once i retrived and built my strings i then appended the strings to the appropriate divs on my .cfm page.

I came across a BIG gotcha when traversing the JSON. "THE JSON VARIABLES RETURNED FROM A CFC ALL SEEM TO BE IN UPPER CASE. AS JAVA SCRIPT IS CASE SENSITIVE REMEMBER TO REF THOSE VARIABLES IN UPPERCASE IN YOUR JS CODE OR THE CODE WILL FAIL"

Sorting

To achieve sorting i added a click handler to each table header. Which calls buildTable() with the appropriate orderBy column passed in, as well as the number of the current page. In my html string each table header cell's id is set with the relevant database column name. By doing this i can use this.id as a parameter in the function and the id of the dom element that the event originates from will be automatically passed into the function. In this case the id of each dom elemen (table header cell) will be a database column name, thereby allowing the recieving CFC to use this value in the order by clause of its sql code

Page Links

Building the page links meant looping for the numberOfPages in the archive, on each loop building a span tag for each page link, giving each tag a click handler that calls buildTable(pageNumber,sortByOrder) each time it is clicked, with the currentPage number and current sorting order as parameters. So that each time one of these links is clicked JS builds a new table with the appropriate records showing for the current page number, sorted in the order last selected by the user (or the default if none chosen). Changing the sort order while on a page will cause different records too be shown as you are reordering the record set.

Highlighting Current Page Link

By finding the span id of the current page (which is the value of the global pageNumber) i could simply use addClass() to highlight the selected link

Next and Previous Links

The next and previous links were created and added to the pager div. Each of them has an event handler that prevents the default event of hyperlink occuring, and calls buildTable() on each click. Finally i get a reference to the table that's built at the start, and insert the page links before and after the table (HTML String).

Finally i insert the page links both before and after HTML string (table).

The HTML on .cfm page

This is simply 2 divs that are used by the JS to update the page, and a css class to highlight the selected link.

<html>
<head>
<title>Jquery Pagination</title>
<script src="jquery.js" type="text/javascript"></script>
<script src="paginationRealData.js" type="text/javascript"></script>
<style type="text/css">

.selectedLink { background-color: #CCFFFF; font-family:Arial, Helvetica, sans-serif; font-size:12px; border: medium solid; border-color:#000066; border-width: thin }
.notSelectedLinks { font-family:Arial, Helvetica, sans-serif; font-size:12px; }
</style>
</head>
<body>
<div id="displayDiv" align="center"></div>
<div id="queryData" align="center"></div>
</body>
</html>

An introduction to Coldfusion Components



Learn how to model the structure and behaviour of an object in Coldfusion using a CFC, and also how to use the functionality of that CFC in an application.

What is a Coldfusion Component?
A Coldfusion Component (CFC) is a self contained (encapsulated, modular) piece of code that has the capacity to create instances of itself in applications. These instances closely follow the structure and behaviour of objects in the real world. In Object Orientation (OO) terms a CFC is analogous to a Class. In the real world objects have state (shape,colour,capacity). In a CFC we represent that state using instance variables. To describe the state and behaviour of an object derived from a Class there are what is known as instance methods. In a CFC these instance methods are represented as functions, using the cffunction tag with its access attribute set to public.

These functions provide an interface to your CFC, know as an Application Programming Interface (API), that allow other developers to utilise your CFC in their own applications. However, a CFC is just a blueprint of an object, and it is useless unless it can produce objects that match that blueprint. To do that a CFC has what is known as a init() function method. In pure OO languages this init() function would known as a Constructor

Why bother?
When your code is properly encapsulated in a CFC it creates by default an API that gives access to the CFC's functionality; but at the same time hides the complexity of your code. This allows for potentially very complex functionality to be plugged into in any application in a very simple fashion. Your code becomes reusable not only by you - but by all developers. All they need to know to implement your functionality is the API of your CFC.

Encapsulation also makes maintaining your code also much easier. As your CFC is running as a self-contained unit in any application where its deployed. So as long as you don't change the API you can update the code in your CFC without worrying about how it will affect the rest of your application, or other people's applications.

Resuse and maintainability of code are the main reasons for writing OO Code. I can't tell you how good a feeling it is to have someone from the other side of the world contact you to tell you that they are using your code in their application!

The other 2 main principles of OOP are inheritance and polymorphism. These are very good concepts to employ in your code but are not something you need to have in your CFCs when writing OO. Encapsulation is enough to get you going.


Glossary CFC
I have created a CFC that can provide applications with the functionality to produce glossary pages for a Website. Click here to view is it's API. (A CFC's API can be self documented if you fill in the hint attributes of an cffunction and cfcomponent).

To view an application using the CFC click here. To view the source code of that application implementing the CFC using the CFC's API click here. To see the full source code for the CFC click here.


About instance variables

First of all we declare our instance variables. These variables hold the data that each object instance created by the glossary component is made up off. By declaring the variables here, outside any function, they are accessible to every function in the component. By putting them in the variables scope we also make them unavailable to any code outside the component which stops them being accessed and breaking encapsulation.  These instance variables have what is known in OO as class scope. Where as if we declared them inside a function they would only be of local scope to that particular function and not accessible outside of that function. Which is what we don't want as each method including the constructor must use the same data. That is the data passed into the constructor when the component is called/invoked:

<!---instance varibles --->
<cfset variables.numberOfWordsInGlossary = 0>
<cfset variables.numberOfWordsLookedFor = 0>
<cfset variables.textToRead = "">
<cfset variables.glossaryOfWords = "">

<cfset variables.templateName = 0>


About Constructors

The init() function (constructor) builds objects of type Glossary. It takes the values passed into it from the calling code and instantiates the instance variables declared at the top of the component making that data available throughout the CFC.

<!---create object of type glossary called linuxGloss2 and pass in the values that will make up the glossary object--->
<cfset linuxGloss2 = createObject("component","glossary").init(documentText,glossaryList2,"glossary.cfm")>


The return type for an init() function must be the same as the component name. This ensures an actual object of type Glossary is returned to the calling code.
In here i have also put the code that builds an object of type Glossary. There is some jiggery-pokery going on here with regular expressions to actually bring about the purpose of this component, which is to create a glossary. But I won't go that here (maybe on another tutorial on regEx). For the sake of modularity it is an option to put this code into a private method and then call that method from the init() function but i have included it in the init() just to show that the object is being constructed by the init() function (constructor).

<cffunction name="init" access="public" returntype="Glossary" output="false"
            hint="builds a glossary object by initialize members and calling private glossorize method. Object creation will fail if no words in the glossary list appear in the supplied text">
<cfargument name="textToRead" type="String" required="true" hint="This is document / string to search for glossary words in.">
<cfargument name="glossaryOfWords" type="String" required="true" hint="The list of glossary words that are to be used to search textToRead">
<cfargument name="templateName" type="String" required="true" hint="This is the name of the template which contains the HTML glossay page that each found glossary word will link to in order to get its meaning.">

<cfset var holder = "">

<cfset setTextToRead(arguments.textToRead)>
<cfset setGlossaryOfWords(arguments.glossaryOfWords)>
<cfset setTemplateName(arguments.templateName)>

<!---Will store the full text with all glossary words surrounded with HTML  --->
<cfloop list="#variables.glossaryOfWords#" index="word">
<!---Find and replace function uses 2 regEx expressions.
 By having the first expression, which is just simply a literal word from the glossary list, as a subexpression
i.e enclosed in (). We are able to use backrefernces in the 2nd expression; because backrefernces can span patterns.
The backrefernce to the first expression allows the word found in the doc by the
 first expression to be used in the 2nd expression to build a string that
replaces the word found by 1st expression in the document.
Also appended to this string is the HTML code that will point each found word to
 its meaning on the 'templatename' page

The 1st RegEx is enclosed in parenthesis to make it a sub expression, and with \b to mark the boundary of
the literal word in the expression.
 --->   
<cfset holder = #REReplace(variables.textToRead,"(" & '\b' & trim(word) & '\b' & ")",
        "<a href='" &  variables.templateName & "##" & '\1' & "' title='Click to view definition on our glossary page'>" & '\1' & "</a>")#>
<!---Update the full document text with a new html link for each glossary word processed by loop--->
<cfset variables.textToRead = holder>
<!---Record number words looked for instance variable = number of loop iterations --->
<cfset variables.numberOfWordsLookedFor = variables.numberOfWordsLookedFor + 1>
</cfloop>

<!---call private method which will set the numberOfWordsInGlossary instance variable --->
<cfoutput>
#countWordsFound()#
</cfoutput>   

<!---in no glossary words are found in textToRead then dont abort / create object --->
<cfif variables.numberOfWordsInGlossary lt 1>
<cfabort showerror="Failed to create object of type Glossary!
         Because no word in the glossary value list exists in the supplied documentText string">
</cfif>
<cfreturn this>
</cffunction>


About Public Methods/Functions


The public functions in the CFC can be accessed from outside the component i.e. from the calling code. This is done by invoking a method call on an instance(object) of type Glossary that has been created in the calling code.

E.G.
<!---create object of type glossary called linuxGloss2 --->
<cfset linuxGloss2 = createObject("component","glossary").init(documentText,glossaryList2,"glossary.cfm")>
<!---call/invoke a PUBLIC method on linuxGloss2  --->
<cfoutput>
 #linuxGloss2.numberOfWordsFound()#

</cfoutput>

Public functions perform an operation on each instance of the component, or give info on the current state of the object, e.g...

<cffunction returntype="Numeric" access="public" name="numberOfWordsFound" output="false"
            hint="returns the number of glossary words found in text">
<cfreturn variables.numberOfWordsInGlossary>
</cffunction>

The above function simply returns the value of the instance variable that was set and calculated in the init() function. Note the access attribute - this has to be set to public for the function to be accessable outside the CFC.


About Private Methods/Functions

The private methods are routines used only by component itself to perform an internal operation and cannot be accessed from outside the component. E.G. ...


<cffunction access="private" name="countWordsFound" output="false" hint="the text string that will be checked for words that appear in glossary list">
<cfset var holder2 = 0>
<cfloop list="#variables.glossaryOfWords#" index="word">
<!---find the number of words found --->
<cfset holder2 = #find(trim(word),variables.textToRead)#>
<cfif holder2 gt 0>
<cfset variables.numberOfWordsInGlossary = variables.numberOfWordsInGlossary + 1>
</cfif>
</cfloop>
</cffunction>


Summary

I have illustrated how to model and build an object as a CFC. Instance variables contain the state of an object. Init() functions are called/invoked on an instance of a CFC in an application, allowing the functionality of the CFC to used in the application. Publicly accessible instance functions allow info about the state of object to be obtained. Private functions are used to perform functions internal to the CFC and are not accessible to cod outside the CFC. By providing a documented API to a CFC it allows for the reuse of code and ease of maintainability of the code. We showed a real application implementing and using the functionality of a CFC by using it's API.

coldfusion (CF) 8 ajax features not working

When ever i have added a new site via IIS none of the ajax features in Coldfusion 8 functioned - no response whatsoever. It seems that when i ran the web server connector tool it was not adding a virtual CFIDE directory to my new site in IIS, just a JRUN scripts directory.

To get around this you need to manually add a virtual CFIDE directory with the local path pointing to location of the CFIDE in inetpub (on my server this is C:\inetpub\wwwroot\CFIDE and set the directory execute permissions to Scripts and Executables.

Adventures in Model Glue - Redirecting to an anchor link

I needed to redirect users if they failed a captcha test when submitting a form back to that form. Straightforward enough eh? Not so as the form itself appeared at the bottom of a pages content. Which meant when the user was redirected by model glue he came back to the page but saw no form or any message telling him why he had ended up back here.

Outside of Model Glue it would have been straightforward but in the event handler for the captchaTest, as far as i could see, there was no way to add append an anchor to the end of the url of the view so that model glue would load up the view with the form displaying to the user at the bottom of the page. In other words redirect to a view with its url looking like this index.cfm?event=whatever&var=value#anchorName

What i ended doing to get round this is a complete hack, and will probably have Model Glue users covering their eyes in horror.

Depending on whether the captcha passed or not i had to add either a pass or fail result to event handler. This would allow mg to redirect back to the page ok but not at the form. To get this to happen i had to add an argument to view when the captcha failed, and in the view (gasp!) i had to test for that value, and if it was telling that the captacha failed i had to include a piece of javascript forcing the view to reload at the form.

<!---If redirected here because of captcha fail. Then the JSscript allows the page to be reloaded at
the form once again, ready for captcha to be tried again --->

<cfset variables.cp = viewstate.getValue("captchaError")>
<cfif #variables.cp# is "yes">
<script type="text/javascript" language="javascript">
location.hash = "#captchaFail";
location.reload();
</script>
</cfif>
Ugly i know but it works and is all i could come up with. If anybody has any better ways to get round this then please chip in.

Glossary CFC

The inspiration for this cfc came to me whilst reading an article by Michael Dinowitz in the excellent Fusion Authority Quarterly Update called Making Google Pay (Part 1). Michael explained why having a glossary on a Website can be a good thing for your users and search engines. I thought it would be a good idea to write something in Coldfusion that allowed glossary functionality to be employed in web applications. E.G. a CMS.

What is glossary cfc?
Glossary CFC is a Coldfusion component that searches through a string of text looking for words that you have designated as being part of your Website's glossary. As it finds a glossary word it wraps it in a hyperlink pointing to the words definition on the glossary page, before finally returning the full text with all the glossary words found in it hyper-linking to their definition. You can see an example of Glossary CFC running here

Why have a glossary page on your site?

  1. Help new users by making your site more user friendly. E.G. All industry specific words on your site can link to their own definitions. This can help new user's understand concepts on your Website that they might not be familiar with.
  2. Google likes internal links on a Website. Having a glossary can be a powerful way to improve Google pagerank.

How does it work?
It takes 3 required attributes.

  1. glossaryOfWords - The list of words you want in your glossary.
  2. textToRead - The string of text you want to check for glossary words in.
  3. templateName - The name of the .cfm template displaying the actual defintions of the glossary words. That each glossary word found will link to.
It outputs your text string with each glossary word hyperlinked to its definition.

Suggested Usage
Although its possible to use a static list of words for the value of the glossaryOfWords attribute this value is best stored in a database. In each record there should be aa seperate field. That way the Websites glossary page and value for glossaryOfWords can updated by CMS users as they wish.

Ideally the component should be integrated completely into your CMS so that as each article is input it can be glossarized with the resulting output text stored as the defintive article text.

Running Example
Click here to see Glossary cfc running as used in the code below.

<!---START init documentText with text you want to glossarize --->
<cfset documentText = "The KDE desktop offers audiokonverter, while GNOME provides audio-convert and audio-convert-mod.
       The latter two are separate projects despite their similar names.
       audiokonverter provides packages for Debian, Ubuntu, openSUSE,
       and generic RPM distributions on its home page, even though the distribution repositories
                 for Gutsy, Fedora, and the openSUSE Build Service do not offer packages for audiokonverter.
                    Packages for audio-convert are offered for Gutsy as nautilus-script-audio-convert.
                       The Fedora repositories contain audio-convert-mod, but it is not packaged for other distributions.
                          For this article I used the
      generic RPM for audiokonverter, installed audio-convert from source, and used audio-convert-mod from the Fedora repository."
>

<!---END init documentText with text you want to glossarize --->
<!---Set the words that appear in your glossary --->
<cfset glossaryList = "KDE, GNOME, Fedora, Debian, Ubuntu, openSUSE, RPM, Gutsy, repository, jkj, jkju">
<cfset glossaryList2 = "KDE, GNOME, Fedora, Debian, Ubuntu, openSUSE, RPM, Gutsy, repository">

<!---create 2 glossary instances and pass each a different glossary list--->
<cfset linuxGloss = createObject("component","glossary").init(documentText,glossaryList,"glossary.cfm")>
<cfset linuxGloss2 = createObject("component","glossary").init(documentText,glossaryList2,"glossary.cfm")>


<!---output glossarized text from cfglossary --->
<h1>Text after Glossary cfc. </h1>
<!---confirm linuxGloss glossary object was created --->
<cfif #linuxGloss.glossaryCreated()#>
<cfoutput>
<!---show the number of words in the glossary list that were searched for in the documentText value --->   
Number of words looked for: #linuxGloss.numberOfWordsLookedFor()#
<br>
<!---show the number of words from the glossaryList that were actually found in the document text --->   
Number of words found: #linuxGloss.numberOfWordsFound()#<br>
<!---Finally ouput document text with all words that were in the glossary list
hyperlinked to their definiton on glossary.cfm--->

Glossarized Text: #linuxGloss.glossarizedText()#
</cfoutput>
<cfelse><!---ELSE END --->
No Glossary words were found
</cfif>
</p>

<br>

<h1>Text after Glossary 2 cfc. </h1>

<!---confirm linuxGloss2 glossary object was created ELSE START--->
<cfif #linuxGloss2.glossaryCreated()#>
<cfoutput>
   <!---show the number of words in the glossary list that were searched for in the documentText value --->
Number of words looked for: #linuxGloss2.numberOfWordsLookedFor()#
<br>
<!---show the number of words from the glossaryList that were actually found in the document text --->   
Number of words found: #linuxGloss2.numberOfWordsFound()#<br>
<!---Finally ouput document text with all words that were in the glossary list
hyperlinked to their definiton on glossary.cfm--->
Glossarized Text: #linuxGloss2.glossarizedText()#
</cfoutput>
<cfelse><!---ELSE END --->
No Glossary words were found
</cfif>
</p>

<br>

Download You can download a zip of cfGlossary here along with docs and example code.

Subversion The latest version can be retrievied from its SVN repository here https://svn2.hosted-projects.com/stewy/myApps/glossary/

Scotch on the Rocks 2008 - Coldfusion Conference Edinburgh

Europes best and most established Coldfusion conference takes place in Edinburgh from June 04 - 06. I have been at the last 2 and they have been great. The content is of course mainly Coldfusion related but there is also an awful lot on Flex, and AIR.

With 3 seperate tracks running each day i highly recommend you get along.

See more info here

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