Custom Tags
- To create custom tags.
- To call custom tags.
In ColdFusion, you can create your own custom tags, which you can reuse throughout an application. One of the huge benefits of this is that more seasoned developers can hide complex code in custom tags, allowing less advanced developers to use those tags without needing to understand or rewrite the code.
Custom tag templates can contain any code that any other ColdFusion page can contain. Custom tags are similar to <cfinclude>s in that the code for a custom tag goes on a separate ColdFusion page, which is then called by another page.
Calling Custom Tags
To call a custom tag, use the following syntax:
<cf_tagname>
The cf_ is simply a prefix that tells the ColdFusion Application Server that this is a custom tag. The string that follows the cf_ is the name of the custom tag file minus the file extension. So, in the example above, the file would be called tagname.cfm. Any ColdFusion file can be called as a custom tag.
To illustrate, let's assume that we have a page called Foo.cfm, which has nothing more than the word "foo" written on it. Foo.cfm will be our custom tag template. You call it with the <cf_foo> tag, which will simply output the word "foo". The code for the two files is shown below.
Code Sample: CustomTags/Demos/Foo.cfm
foo
Code Sample: CustomTags/Demos/FooCaller.cfm
<html> <head> <title><cf_foo></title> </head> <body> <cf_foo> </body> </html>
thisTag Structure
The thisTag structure holds data related to the custom tag instance. The table below shows the variables available in the thisTag structure.
| Variable | Description |
|---|---|
| ExecutionMode | The mode of execution: "start", "end", or "inactive" |
| HasEndTag | true if the tag is called with an end tag; otherwise, false |
| GeneratedContent | The content in the body of the tag (i.e, between the open and close custom tag) |
| AssocAttribs | Contains the attributes of nested custom tags that have been made available through the <cfassociate> tag. |
ExecutionMode
Custom tags can be called with end tags, like so: <cf_foo></cf_foo> or <cf_foo/>. This instructs ColdFusion to call the custom tag twice. Open CustomTags/Demos/FooCallerClosed.cfm in your browser and you will see that the word "foo" is output twice.
However, you may not want the code in the tag to be executed twice or you may want the start tag to behave differently from the end tag. This can be handled by checking the ExecutionMode of the ThisTag structure. Take a look at our modified custom tag template.
Code Sample: CustomTags/Demos/Foo2.cfm
<cfif ThisTag.executionMode EQ "start"> foo <cfelse> bar </cfif>
Code Sample: CustomTags/Demos/Foo2Caller.cfm
<html> <head> <title><cf_foo2></title> </head> <body> <cf_foo2/> </body> </html>
As you can see, the word "foo" is output when the tag is opened and the word "bar" is output when it is closed. Now that's useful! But let's take a look at an even more interesting example. The following page creates a custom tag which displays an image from a specified directory. Each time the custom tag is called, it returns a different image from the directory.
Code Sample: CustomTags/Demos/ImageFlipper.cfm
<cfif thisTag.executionMode EQ "start">
<cfdirectory action="list" name="getImages" directory="#ExpandPath(ATTRIBUTES.directory)#">
<cfparam name="SESSION.picture" default="0">
<cfset SESSION.picture = SESSION.picture + 1>
<cfif SESSION.picture GT getImages.RecordCount>
<cfset SESSION.picture = 1>
</cfif>
<cfoutput>
<img src="#ATTRIBUTES.directory#/#getImages.name[SESSION.picture]#"
<cfif isDefined("ATTRIBUTES.height")>
height="#ATTRIBUTES.height#"
</cfif>
<cfif isDefined("ATTRIBUTES.width")>
width="#ATTRIBUTES.width#"
</cfif>
<cfif isDefined("ATTRIBUTES.alt")>
alt="#ATTRIBUTES.alt#"
<cfelse>
alt="#getImages.name[SESSION.picture]#"
</cfif>>
</cfoutput>
</cfif>
Code Sample: CustomTags/Demos/Runners.cfm
<html> <head> <title>Flipping Image</title> </head> <body> <cf_ImageFlipper directory="Images/Runners" height="205" width="150">Runners</cf_ImageFlipper> </body> </html>
In the code above, you'll notice the ATTRIBUTES scope, which contains any variables passed in as attributes as shown below...
<cf_ImageFlipper directory="Images/Runners" height="205" width="150">We check for the existence of these attributes using the isDefined() function.
GeneratedContent
The GeneratedContent variable, which is only accessible in the "end" mode, holds all content between the open tag and close tag (e.g., between <cf_foo> and </cf_foo>). Its value can be modified in the custom tag. The following files show how a custom tag can be used to comment out the body based on the value of an attribute.
Code Sample: CustomTags/Demos/ImageFlipper2.cfm
<cfif thisTag.executionMode EQ "start">
<cfdirectory action="list" name="getImages" directory="#ExpandPath(ATTRIBUTES.directory)#">
<cfparam name="SESSION.picture" default="0">
<cfset SESSION.picture = SESSION.picture + 1>
<cfif SESSION.picture GT getImages.RecordCount>
<cfset SESSION.picture = 1>
</cfif>
<cfoutput>
<img src="#ATTRIBUTES.directory#/#getImages.name[SESSION.picture]#"
<cfif isDefined("ATTRIBUTES.height")>
height="#ATTRIBUTES.height#"
</cfif>
<cfif isDefined("ATTRIBUTES.width")>
width="#ATTRIBUTES.width#"
</cfif>
<cfif isDefined("ATTRIBUTES.alt")>
alt="#ATTRIBUTES.alt#"
<cfelse>
alt="#getImages.name[SESSION.picture]#"
</cfif>>
</cfoutput>
<cfelseif thisTag.executionMode EQ "end">
<cfif isDefined("ATTRIBUTES.CommentBody") AND ATTRIBUTES.CommentBody EQ "true">
<cfset thisTag.GeneratedContent = "<!--#thisTag.GeneratedContent#-->">
</cfif>
</cfif>
Code Sample: CustomTags/Demos/Runners2.cfm
<html> <head> <title>Flipping Image</title> </head> <body> <cf_ImageFlipper2 directory="Images/Runners" height="205" width="150" CommentBody="true"> <br/>Runners </cf_ImageFlipper2> </body> </html>
Storing Custom Tags
Custom tags can be stored in any of the following locations. ColdFusion will search for them in the order listed.
- In the same directory as the calling page - while this is easy for demonstration purposes, it's not very practical, as it means that the custom tag is only available within that directory.
- In a directory (or subdirectory of a directory) specified in ColdFusion Administrator under Extensions -> Custom Tag Paths.
- In the cfusion/CustomTags directory or one of its subdirectories.
Other Ways of Calling Custom Tags
One problem with calling custom tags using the cf_ syntax we have seen thus far is that there no way to deal with name conflicts. ColdFusion simply uses the first file it finds that matches the tag name specified. For example, if you had a file in the current directory called "FetchRecord.cfm" and a custom tag file in the cfusion/CustomTags directory with the same name. You cannot access the custom tag file in the cfusion/CustomTags directory using the cf_FetchRecord syntax. ColdFusion provides two ways to deal with the naming conflict.
CFMODULE
The <cfmodule> tag allows you to point directly to the custom tag you want to call. It takes the following syntax:
<cfmodule template="path_to_tag_file" attribute_name="custom_tag_attribute_value">
The custom tag's attributes are passed to the custom tag in the same way as they are using the cf_ syntax. The example below illustrates the use of <cfmodule> to call a custom tag.
Code Sample: CustomTags/Demos/Runners-cfmodule.cfm
<html> <head> <title>Flipping Image</title> </head> <body> <cfmodule template="ImageFlipper.cfm" directory="Images/Runners" height="205" width="150"> </body> </html>
The example above shows how the attributes can be passed in to the custom tags in separate name-value pairs. It is also possible to pass in the attributes as a single structure using <cfmodule>'s attributecollection attribute:
Code Sample: CustomTags/Demos/Runners-cfmodule2.cfm
<html> <head> <title>Flipping Image</title> </head> <body> <cfset Runner.directory = "Images/Runners"> <cfset Runner.height = "205"> <cfset Runner.width = "150"> <cfmodule template="ImageFlipper.cfm" attributecollection="#Runner#"> </body> </html>
Custom tags with close tags can also be called with <cfmodule> as shown in the example below.
Code Sample: CustomTags/Demos/ProfitLoss-cfmodule.cfm
<html>
<head>
<title>Profit Loss</title>
</head>
<body>
<h1>Profit Loss</h1>
<cfset FilePath = ExpandPath("ProfitLoss.txt")>
<cffile action="read" file="#FilePath#" variable="myfile">
<cfmodule template="Text2Table.cfm"><cfoutput>#myfile#</cfoutput></cfmodule>
</body>
</html>
Finally, with <cfmodule>, custom tags can be specified using dot notation. In this case, the name attribute replaces the template attribute. This is shown in the sample below:
Code Sample: CustomTags/Demos/ProfitLoss-cfmodule-name.cfm
<html>
<head>
<title>Profit Loss</title>
</head>
<body>
<h1>Profit Loss</h1>
<cfset FilePath = ExpandPath("ProfitLoss.txt")>
<cffile action="read" file="#FilePath#" variable="myfile">
<cfmodule name="Strings.Text2Table"><cfoutput>#myfile#</cfoutput></cfmodule>
<!---This calls the Text2Table.cfm custom tag file in the cfusion/CustomTags/Strings directory.--->
</body>
</html>
CFIMPORT
The <cfimport> tag is used to import a group of custom tags that can be referenced with a given prefix. The syntax is shown below:
<cfimport prefix="prefix" taglib="path_to_tag_directory">
The prefix attribute is used to specifiy a tag qualifier. A custom tag called "bar" in a library qualified with the "foo" prefix would be called as follows:
<foo:bar>
The taglib attribute points to the directory holding the custom tags. The directory path can be relative to:
- the current page location
- the web root (the page should start with a "/")
- a directory specified in the Administrator ColdFusion mappings page (the page should start with a "/")
Custom Tags Conclusion
In this lesson of the tutorial, you have learned how to create and use custom tags, which provide a great mechanism for code reuse. They are also easy for less experienced ColdFusion developers to make use of as they work just like standard ColdFusion tags.
