Reputation: 32905
Have you had any success with uses of custom tag for view that increase maintainability?
Does use of custom tag have any benefit of using basic <cfinclude>
?
Any custom tag you have written before that increase HTML maintainability?
Thanks
Upvotes: 3
Views: 1229
Reputation: 9616
Yes and Yes.
Custom tags are awesome for separating out reusable HTML elements. The advantages are that they are scope-safe, and you can pass arguments into them to change their behavior, which you can't do with cfinclude
. You can even create nested tags that change their behavior based on their parents. Most of my HTML layouts in ColdFusion look something like this:
<cf_layout>
<cf_head title="My Page>
<!--- Some local head stuff ---->
</cf_head>
<cf_content>
Lots of stuff goes here.
</cf_content>
</cf_layout>
This is obviously greatly simplified, but I can change the entire layout of a site by changing the custom tag, or even changing the custom tag folder (think dynamic this.customTagPaths
for swapping out layouts.
Custom Tags don't get enough love. Embrace them, and enjoy :).
Here's another example of something I do:
<cfset arOrders = OrderService.getOrders() />
<table>
<cfloop array="#arOrders#" index="currentOrder">
<cf_orderrow order="#currentOrder#" />
</cfloop>
</table>
My OrderRow
custom tag handles all of the display for displaying a row for an order in a table. This includes add/edit/delete buttons, calculations that have to happen, conditions that must be me to change the display. I can display an order row anywhere I want in an admin area. On the customer details page, on a page showing all orders in a certain time period. It makes no difference. My users know that any place they see a row describing an order, it will look just as they expect it to.
Here are a few more concrete examples of small custom tags I use across multiple applications.
cf_jquery
This includes jQuery and jQueryUI in a page, but only if no other template in the application has already added jQuery on this request. This allows me to build multiple jQuery parts to a page, and no matter what order I call them in, I'll always know that jQuery has been added to the head of the document only once.
<cfif thisTag.ExecutionMode EQ "end" AND (NOT StructKeyExists(Request, "JQueryInited") OR Request.JQueryInited EQ False)>
<cfparam name="Attributes.IncludeUI" default="False" />
<cfparam name="Request.jQueryUIInited" default="False" />
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<cfif Attributes.IncludeUI AND Request.jQueryUIInited IS False>
<link href="/shared/css/smoothness/jquery-ui-1.8.11.custom.css" rel="stylesheet" />
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js"></script>
<cfset Request.jQueryUIInited = True />
</cfif>
<cfset Request.JQueryInited = True />
</cfif>
I even build tags for small information that I know will be used throughout the application in multiple places, like this cf_contact
tag:
<cfif thisTag.ExecutionMode EQ "end">
<cfparam name="Attributes.Phone" default="xxx.xxx.xxxx" />
<cfparam name="Attributes.Email" default="[email protected]" />
<cfparam name="Attributes.EmailName" default="Email Support" />
<h2 style="font: bold 12px Verdana, Geneva, Arial, Helvetica, sans-serif; margin-bottom: 0;">Contact Us</h2>
<p style="text-align: left; margin-top: 0;"><cfoutput>#Attributes.Phone#</cfoutput><br />
<cfoutput><a href="mailto:#Attributes.Email#">#Attributes.EmailName#</a></cfoutput></p>
</cfif>
Why make a tag for something so small? Why not? If I style all my contact information the same throughout an application (and I should, so users can easily identify things), then I can make a tag for it just as easily as I can make an include. If I decide I want some other programmatic behavior to happen in there, my app is already set up to use it.
cf_timeblock
This custom tag shows a set of select lists for choosing a time block (hh:mm tt). I use this all over an application dealing with scheduling, so I have a tag that will allow me to display a block for a time, and I can send in which values should be pre-selected (I'm missing validation for the attributes, but just pretend it's there for now):
<cfif thisTag.ExecutionMode EQ "end">
<cfsilent>
<!--- Get Time Blocks --->
<cfset TimeBlocks = Application.DAO.getTimeQueries() />
</cfsilent>
<select name="hour" style="width: auto; float: none;">
<cfoutput query="TimeBlocks.Hours">
<option value="#TimeBlocks.Hours.Value#"<cfif TimeBlocks.Hours.Value EQ Attributes.Hour> selected="selected"</cfif>>
#TimeBlocks.Hours.Value#
</option>
</cfoutput>
</select>:<select name="min" style="width: auto; float: none;">
<cfoutput query="TimeBlocks.Mins">
<option value="#TimeBlocks.Mins.Value#"<cfif TimeBlocks.Mins.Value EQ Attributes.Min> selected="selected"</cfif>>
#TimeBlocks.Mins.Value#
</option>
</cfoutput>
</select>
<select name="mer" style="width: auto; float: none;">
<cfoutput query="TimeBlocks.Mer">
<option value="#TimeBlocks.Mer.Value#"<cfif TimeBlocks.Mer.Value EQ FORM.mer> selected="selected"</cfif>>
#TimeBlocks.Mer.Value#
</option>
</cfoutput>
</select>
</cfif>
I use tons more than this, but they're all very application/object specific. Just about any time I start to pull something out into an include, or if I find myself copying a piece of display logic from one page to another, I start thinking about how I can push that code into a custom tag.
Here are a few more examples of custom tag libraries that I find useful.
Upvotes: 11
Reputation: 29870
One example missing from Dan's otherwise excellent efforts is the very powerful but under-utilised capabilities custom tags have to iterate over enclosed content:
<!--- loop.cfm --->
<cfif (THISTAG.ExecutionMode EQ "Start")>
<cfparam name="attributes.index" type="variablename">
<cfparam name="attributes.iterations" type="integer">
<cfset variables.index = 1>
<cfset caller[attributes.index] = variables.index>
<cfelse>
<cfset variables.index = variables.index + 1>
<cfif variables.index lte attributes.iterations>
<cfset caller[attributes.index] = variables.index>
<cfexit method="loop">
<cfelse>
<cfexit method="exittag">
</cfif>
</cfif>
<!--- testLoop.cfm --->
<cf_loop iterations="10" index="i">
<cfoutput>[#i#]</cfoutput>Hello World<br />
</cf_loop>
The one caveat I would offer for using custom tags is that I have heard they are prone to leaking memory (I cannot substaniate this), so if using them on a heavy-traffic site, I'd load test them thoroughly before going live with them.
Upvotes: 2