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>

Pagination in Coldfusion Model Glue Applications

Intro
I am describing here how i achieve pagination in my Model Glue apps. I hope the code is modular enough to be reused in other people's apps. You can see a running example here. The code mainly consists of a custom tag (cf_paginationMG) located in the view, and a coldfusion component (Pagination cfc) placed in the model. These both have straightforward APIs that can be easily wired via your controller. In your view, the event that generates the pagination can be labelled anything you like. As long as it calls a function you have defined in your controller that in turn invokes the pagination component. A seperate component is needed in the model that returns to the controller the actual record set that needs to be paged. You can see how i have done this in source code of my example.

Source Code of my example
Config Files
modelGlue.xml
coldspring.xml

The View
dspIndex.cfm

The controller

The Model
Pagination Component
Archive Component

Download a zip of the source code here

The View cf_paginationMG
This is where the meat and bones of the work is carried out. The code is wrapped in a custom tag (cf_paginationMG) called from a view template. (see my example view code)

The tag outputs the page numbered links (1,2,3,4,5,6... etc) as well as the 'next' 'previous' links which allow a user to page through an archive/record set. Having all the code for this in a custom tag makes it much easier to position the final pagination output above and below the output of your record set.

How to display the actual records / content is left up to you. The values returned from the Pagination component's getFromRecord() and getToRecord() methods dictate what particular records can be displayed for the current request. (see my example view code)

The Pagination component's getNumberOfPages() method returns a value that should be used as the value for the numberOfPages parameter of the custom tag.

cf_paginationMG parameters * are required

  • * numberOfpages: The number of pages your archive will contain. On each request the value for this parameter is returned by the model pagination method getNumberOfPages()
  • * numberOfRecordsOnPage: Determines the number of records that will be show from the record set/archive on a page at any one time. This value can be set (globally) in a coldspring config bean.
  • * event: the model glue event (in the case of my example getArchive) that fires each time a user clicks a numbered page link, or 'next' 'previous' link. This event generates the pagination on each request.
  • numberlinkClass: Take a CCS class that will determine how you want the page link numbers to look. Defaults to browser default.
  • nextPreviouslinkClass: Take a CCS class that will determine how you want the next previous links to look. Defaults to browser default.
  • currentPageNumberClass: Take a CCS class that will determine how you want the page number of the currently being viewed page to be displayed (highlighted). Defaults to browser default.

The Model
Pagination.cfc

The value returned from getNumberOfPages() should be used as the value for the numberOfpages parameter in the paginationMG custom tag. On each request from a user, the values returned by getFromRecord() and getToRecord() should be used in the view to help determine what particular records are to be displayed from the record set for that specific request. (see example controller view code)

Pagination.cfc parameters

  • currentPageNumber: the number of the page in the archive that is currently being displayed on the view. This defaults to 1.
  • recordsInArchive: the total number of records that you want to page through. i.e the record count of the record set (in my example this record set is returned from archive.cfc) to be paged through. Your record set's actual content is output independently of the custom tag using a query loop of the record set returned from archive.cfc, and the fromRecord, toRecord values from returned from the pagination methods getFromRecord() and getToRecord().
  • numberOfRecordsOnPage: the number of archive page links that will be displayed at the bottom/top (both) of your archives view

archive.cfc
This can be built by you. It should simply return record the records you want to page through. As you can see in the controller code of my example the record set's record count value is used to set the value of the recordsInArchive parameter of the Pagination component.

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.

cf_MagicPager: Updated.

I have updated cf_MagicPager See its previous entry. It now has 3 added parameters that allow CSS classes to be added to style the look of the output. Download sample code, and docs in a zip here.

View running example of the below source code here

<!---change the appearance (colours/fonts/etc) by modifying these styles. Or add your own. --->
<style type="text/css">
.numberlinkClass {color:#0080ff;font-family:courier;font-weight:normal;font-size:large;}
.currentPageNumberClass {color:#0000ff;font-family:courier;font-weight:normal;font-size:large;}
.nextPreviouslinkClass {color:#000000;font-family:impact;font-weight:normal;font-size:x-large;}
</style>

<cfoutput>
<!---creates a query with some test data. This process would be best done in a cfc but is shown
this way for easy demo purposes.

Your cfc should return a record set and the two variables needed by the the tag to function, in a
structure. i.e NumberOfLinksOnPage and numberOfPages --->
   

<!--- Create a new three-column query, specifying the column data types --->
<cfset myQuery = QueryNew("recordNumber", "Integer")>
<!--- Make two rows in the query --->
<cfset newRow = QueryAddRow(MyQuery, 307)>
<cfloop from="1" to="307" index="i">
<!--- Set the values of the cells in the query --->
<cfset temp = QuerySetCell(myQuery, "recordNumber", #i#, #i#)>
</cfloop>

<!---These are the only 2 values that the tag needs to run. --->
<cfset variables.numberOfRecordsOnPage = 10>
<cfset variables.numberOfPages = #myQuery.recordCount# / #variables.numberOfRecordsOnPage#>

      <!---This is ensures page number default to number 1 when first called --->
<cfparam name="url.pageNumber" default="1" type="numeric">
      <!---here using url.pagenumber to work out what records to display on current page --->
      <cfset variables.from = ((#url.pageNumber# * #variables.numberOfRecordsOnPage#) - #variables.numberOfRecordsOnPage#) + 1>
    <cfset variables.to = (#url.pageNumber# * #variables.numberOfRecordsOnPage#)>
</cfoutput>

<P><b>Running cfMagicPager. Browsing through 307 records.</b></P>
<cf_magicPager
NumberOfPages="#variables.numberOfPages#"
numberOfRecordsOnPage="#variables.numberOfRecordsOnPage#"
numberlinkClass="numberlinkClass"
nextPreviouslinkClass="nextPreviouslinkClass"
currentPageNumberClass="currentPageNumberClass">

</cf_magicPager>

<P>
<!---dislay your records here --->
<cfloop query = "myQuery" startrow="#variables.from#" endrow="#variables.to#">
<CFOUTPUT><div align="center">Record Number: <b>#recordNumber#</b></div></CFOUTPUT><br>
</cfloop>
</P>


<cf_magicPager NumberOfPages="#variables.numberOfPages#"
numberOfRecordsOnPage="#variables.numberOfRecordsOnPage#"
numberlinkClass="numberlinkClass"
nextPreviouslinkClass="nextPreviouslinkClass"
currentPageNumberClass="currentPageNumberClass">

</cf_magicPager>

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

cf_MagicPager: Coldfusion custom tag

I have updated this tag. See this entry to get latest version.

cf_Magicpager is a Coldfusion custom tag that enables pagination. Pagination allows a website's users to navigate through a large record set in a user friendly way by generating page numbered links to each page in the archive. See example here

  • Previous and next links are also displayed to allow user to go back and forward one page at a time.
  • The pageNumber currently being viewed is highlighted a different color from the other pageNumbers.

The tag does not retrive and display records from a database .This is left up the developer to do in a seperate process. Giving flexibility as to what and how you display your records. In this respect the tag is very much an UI tag.An example of how records could be retrived and displayed is included in the code example in the docs.

The tag is designed to be linked to the displayed records using a variable called url.pageNumber. Each time a generated page number/link is clicked a variable called url.pageNumber is set with the page number.(Its defaults to one) Use this variable to help determine what records from your recordset (archive) should queryed and displayed on each page. E.G. If the number of the page currently being processed is 7 then you should seperately retrive and display records 70 to 79 from your recordset/archive.

Possible Tag Usage:
Place tag above or/and below where the records are output on template.

<!---CFC which generates your record set/ archive --->
<cfinvoke component="archive" method="getArchive" returnvariable="result">

<!---Calling magicPager to appear above record set --->

<cf_magicPager NumberOfPages="#result.numberOfPages#" NumberOfLinksOnPage="#result.NumberOfLinksOnPage#">
</cf_magicPager>


<!---Output your records here --->
<cfoutput query="result.records">
<p>
#title#<br>
#intro#<br>
<a href="fullArticle.cfm?articleId=#articleId#">Read more...</a><br>
</p>
</cfoutput>

<!---Calling magicPager to appear below record set --->
<cf_magicPager NumberOfPages="#result.numberOfPages#" NumberOfLinksOnPage="#result.NumberOfLinksOnPage#">
</cf_magicPager>

Attributes: (all are required) These values are best calculated alongside whatever code you use to retrive your database records. A cfc would be best, returning a structure that contains your database records and these values.

  • NumberOfPages: The number of pages you want your archive to have.This can be determined by dividing the record count value of a retrived record set by the number of records/articles you want to appear on any one page. e.g. 300 records retrived / 10 records on a page: this will give a figure of 30 pages. This is the amount of pages that will be needed to display the whole archive/record set.With each individual page being generated when a user clicks on its page number.
  • NumberOfPageLinks: The amount of page links to display on screen at any one time. This figure is doubled by the tag. e.g. If you enter 10 it will generate 20 links. 10 links before current page number, (if current page number is greater than 10) and 10 links after current page number (until there are less than 10 pages left to display in archive)

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