Now a days it seems like every one talks about CFCs, or UDFs, and almost no one talks about custom tags. While they are a bit slower than CFCs or UDFs, custom tags are still pretty darn handy. Here are a few tips for people writing custom tags.
- Watch your whitespace.
This is more a general ColdFusion tip, but since custom tags tend to be used inside other documents (otherwise they wouldn't be custom tags), you tend to notice their white space even more, especially if the tag is used inside a loop. I recommend just using cfsetting on top and at the bottom of your file, although be sure to also use cfsetting if you leave your custom tag early.
- If your custom tag is not meant to be used in "wrapped" mode, then always include this line as your last line of code:
<cfexit method="exitTag">
If you don't, and someone calls your tag like so, <cf_foo/>, then your tag will execute twice.
-
While I may use "cf_" format in my examples, I almost never use cf_ when invoking a custom tag. I use cfmodule. This tends to be a bit more wordy, but it means i don't have to worry about "name" confusion. This is where ColdFusion has to look for your custom tag, and could potentially find the wrong one. Not only that, ColdFusion caches the location of the tag, so if you move it, you need to restart ColdFusion. All of this goes away if you use cfmodule.
-
If you return a value, do not hard code it like so:
<cfset caller.foo = 1>
Rather, make it an attribute so the caller can specify what result to return. For example:
<cfparam name="attributes.result" default="result" type="variableName">
<cfset caller[attributes.result] = now()>
This lets me call the tag like so:
<cf_foo>
<cf_foo result="result2">
<cf_foo result="result3">
At the end of those lines, I'd have 3 variables created: result, result2, and result3.
Archived Comments
Good tips here. It would be nice to have a quick guide for custom tags. Does one exist somewhere?
I could hit the little Print button and add ot my guides. Actually, I can just add the link. Count to ten and it will be there.
Thanks, Ray. This is really valuable to those of us that are considering reevaluating the way they do encapsulation.
I do have one question though. It's about CFmodule Here (CFMX Coding Standards if the link doesn't come through) http://livedocs.macromedia.... Adobe says Don't use CFmodule.
Now this was written By Sean Corfield I believe. If the two of you disgree, I don't know who to believe. :)
In a more answerable question, wouldn't using cfimport achieve your goals without using cfmodule?
In general I rank Sean's skills over mine, but I'll disagree here. I believe he was worried about the performance. Custom tags are DEFINITELY slower than UDFs/CFCs. However, I don't think they are a cause for concern for 95% of you (you being my readers), and like anything, if performance is a concern, you want to be careful about _all_ the code you write.
So if you're already taking the performance hit for Custom tags in the first place, you might as well go whole hog, and use less ambigous code.
That makes sense to me. Thanks.
Less amigous? Sorry - can you restate that.
I meant of course "ambiguous."
Being ambiguous on the other hand, was unintented.
I believe Sean argues for cf_ or cfimport syntax over cfmodule.
For my part, it depends on the application.
When using global custom tags for applications that have a relatively permanent home, I prefer to call custom tags from the custom tags directory using cf_ format.
If I am developing an application with an eye on distribution, I prefer cfimport so that I can specify the location of the tags more precisely.
The main advantage that I see of cfimport over cfmodule is readability and clarity when nesting tags.
I have some issues with the whole return variable for custom tags... my feeling is that if it has a return variables, but not just make it UDF, that way you can explicitly use or not use the return value. In my head (which has issues of its own), custom tags are more for page output and the idea of a return variable seems a bit odd. I can understand it for backwards compatibility, but going forward in all the MX stuff and the OOP and the frameworks, does it really make sense to have custom tags that have return variables?
(ps. I am not attacking, I am geninuingly interested in this idea)
Steve: My concern is that I may not know if the app will go to a single server or a hosted server. THerefore, I never trust cf_ syntax.
cfimport: I don't know why, but it never really turned me on. Maybe it's the fact that you must use the import on EVERY file that needs it.
Ben: I can definitely say that non-display custom tags and UDFs are pretty blurred (I mean the differences between them). I tend to keep my UDFs for shorter blocks of logic, while a more advanced logic block would go into a custom tag. Again though, I do -not- have hard rules for it.
Ben,
I don't know about Ray, but I have had custom tags that are primarily for output (a function well suited for a custom tag) but also need to return variable.
Ray,
I actually felt the same about cfimport until a recent project (that I finished up around the end of the month...) where I wanted to use some custom tags but cf_ syntax didn't have the flexibility.
I tried cfmodule, but it was just ugly as heck. I tried cfimport and found that I didn't like including it on every page, but it was still well worth the trouble (a surprising change of a long-held opinion for me).
Are you sure that when someone calls CFC's in the taggy way of not actually creating an object and accessing it that custom tags are slower? I was thinking they were only faster, but not sure, when you actually create an instance and use the methods rather than <cfinvoke...> the CFC like it was a custom tag. (Please, guys who like CF Invoke... don't get heated... just trying to get a techincal answer, not start a flame war.)
I happen to love CFImport. I love that you can use good directory paths and it makes grouping tags sooo freakin' nice. For instance, I have a bunch of FORM tags that all have the prefix FORM:
<form:tag disableonsubit="true">
<form:doubleselect name="" value="" .... />
<form:dateselectcalendar name="" ... />
</form:tag>
I just think it makes reading and understanding the code soo much clearer.
Steve, what do you mean by flexibility? Whether you use cfimport, cf_, or cfmodule, you have the exact same features.
Sorry I was clear. I meant that cfmodule and cfimport are both are more flexible than cf_ inasmuch as I can specify the exact location of the file I want to use.
With cf_, ColdFusion has a set pattern it follows to find the file. I can't direct it to look where I want.
John, I'm not sure I read you right. CFINVOKE isn't slower if you use an instance. Ie
<cfinvoke component="#fooo#">
versus
<cfinvoke component="foo">
The second method will make a new CFC. The first one will use an existing CFC and won't have the startup cost.
I still like custom tags also... in fact we are working on some custom tags with spry. To me there is "more" power for nested attributes of nested tags in custom tags than there is with CFCs. It's not a case either one being the overall best... but rather which one is best for the task at hand. I like and use them both!
Guess we are posting back to back Ray! LOL
That is kinda cool, might have used that tag more but I am one of those guys who likes to do CFScript... so I missed that implementation technique. THANKS!
Steve: Gotcha.
John: Cool. You love Spry too, right?
I agree - nested tagd are very powerful if used correctly.
OK... how do we make the gravitars work Ray?
You have to register at gravatar.com. GOod luck. It took me 2 months.
It wasn't quite two months ago that I registered at Gravatar. I got the email saying mine was rated G, obviously, if you can actually see it here. Cool idea. (Looking forward to the day we can all pick which comments to skip by glancing at the icons next to them...j/k)
Alan
Ahh, yes.. good posting Ray, I had forgotten about those pesky custom tags (after doing lots of spectra, I am glad to be away from them!) but yes, it reminded me that I need to add some functionality to <cfmodule to give you the attributes of the custom tag that you have defined.
I shall look into this as a future feature
MD
Ray,
What would you say about referencing request or application variables within the custom tag?
Should all variables used in the custom tag be passed in as attributes?
Nick
Nick,
I can't speak for Ray, but I would say that using any shared-scope variables from within a custom tag is generally bad form as it break encapsulation.
If you pass them in as attributes, of course, that is OK.
You will find some exceptions. For example, I sometimes use request variables to set defaults for attributes. I do make sure that my custom tags work if none of the request variables exist. I also make sure to name the request variables such that it is unlikely to cause a name conflict (I name put the variables in a structure named for the custom tag).
Hi Steve,
Thanks for the comments... I typically do the same thing. I like to make sure all variables referenced within the tag are declared and in the attributes scope -- but sometimes will use request variables as defaults.
I'm curious as to what some others think about this and whether it's considered good practice or not.
Nick
Ray... if you use cfimport and reference the root directory of your site as your base it works great! (You can of course also put an application.cfm in the directory with your custom tags to keep malicious security hazards away.) What you get is a directory like "shared" off the root that houses your custom tags in a nested directory.
/shared/tags/security
/shared/tags/forms
<cfimport ...>
Then your code is no problem. (The benefit of easy to read code compared to CFModule is worth it. You also don't run into conflicts where the extra attributes of CFModule get in the way of your custom tag attributes. Lastly, it works perfect with shared hosting!)
My hope is that they allow us to map some of these things in the next version of CF. Then we can map the tags in our application page, and you and I won't have to do it page by page. We will see.
I still don't like cfimport. Period. :)
I concur with Steve. You shouldn't break encapsulation when it comes to custom tags... but I've done it myself every now and then. For example with DSNs.
Finally you are posting about custom tags! Today is my lucky day then because I've been meaning to ask you this question for awhile now Ray but first you should be punished for not using cfimport. ;) I find CJ's CF Best Practices on easycfm as a great set to follow. One that I'm very vocal about is to always scope your variables. Is it necessary to scope your variables in custom tags?? By variables I mean regular variables that should only exist in that custom tag, meaning should I just do:
<cfset myName = "Javier Julio">
Or
<cfset Variables.myName = "Javier Julio">
Is there any benefit with either approach?? Or is it more of unneccessary in a custom tag as maybe a custom tag only defaults to the Variables scope whenever you set a simple var?? A great post with some real useful tips. Keep it up!
I would say no, you do not need to variable scope your variables inside a custom tag.
As for your notice of me doing a post on CTs - I was a huge fan of CTs for many years. I truly do love em. One of the changes to CFLib will be to support CTs, since the Macromedia Exchange has lots of commercial stuff on it.
Thats a brilliant idea Ray as I don't dig a lot of the commercial stuff on the MMExchange so it would be great to see cflib support custom tags as well. If you need contributions I will have several for you. One of them being by own version of your data table and column tags. With the neat getMetaData() function thinking about using it to determine column datatypes so you don't have to specify it.
What's up with using CFPARAM within a custom tag?
Never saw this anywhere else, but I just ran across this tip in the CFMX7 Documentation:
"Tip: To improve performance, avoid using the cfparam tag in ColdFusion functions, including in CFC methods. Instead, place the cfparam tags in the body of the CFML pages."
No problem, I'd never use CFPARAM in a function. But what about custom tags? When this tip says "place the cfparam tags in the body of the CFML pages" is it just referring to simple CFML pages that display output?
I have a custom tag that performs some data validation. I send in the form scope as an attribute collection. Within the body of the custom tag, some of the validation is implemented with CFPARAM.
This tip concerning functions now has me second-guessing that usage. What's the truth concerning perfomance and using CFPARAM within a custom tag?
Thanks!
I haven't done speed tests on cfparam versus a cfif block. To be honest I don't think most (not all!) speed tests are really worth your time.
That being said - I personally don't think there is anything wrong with it - however - I tend to NOT like it when the default value is complex, like a struct:
cfparam name="foo" default="#structnew()#"
The reason is that CF has to run the structNew even if foo already exists. Again though, that should be a very minor speed hit.
Wow! Expert advice in real time!
I always confuse myself when I read too much.
Thank you :^)
Remember that my opinion may not be shared by others - especially the "don't worry about speed" comment (which, to be clear, I'm saying to not worry at a very low level). Anyway - just warning you that you may see a bunch of folks chiming in to say I'm wrong. ;)
Hi,
Could any body confirm whether using <cfmodule> will physically include the whole file in to the parent file.
Thanks
"Include" is the wrong word. The entire file is used - but it isn't "included" as the code has it's own variable space. Therefore it is NOT the same as a cfinclude call.
In general CFC's are actually not faster than custom tags, they are slower.
The only way a CFC is faster is if you have cached it in a persistent scope, such as application scope so that it remains in memory. Or if you are calling multiple methods on that CFC after it has instantiated within the same request, then it may turn out to be quicker than calling the same custom tag the same number of times.
The slowness of CFC's is caused by the initial instantiation of it, so this is where you need to calculate if any improvements are gained if you are not goign to cache the CFC.
So to be clear.
<cfinvoke component="foo"...>
is slower than
<cf_foo...>
but
<cfinvoke component="#application.foo#"...>
would be faster.
I'm not so sure that is true in CF8, where CFC creation was improved dramatically.
Frankly - you (developer) need to make the choice between CFC versus custom tag based on what you are doing - not which one is 0.001% faster.
Yes I have heard that about CF8 too, but I have not yet tested it. I would hope it is true because it would certainly make things a lot easier on our hosting servers which are full of badly written code :-)
at first i didn't really like using cfimport, but it was the only clean option available
Hi Ray,
I am not sure whether I am missing something but have you posted the link to the Custom tags guide? I would grateful if you can guide me to where it is.
Thanks
Eh? What guide?
Typo: line reads "<cfexit method="exitTag"%gt;" where it should read "<cfexit method="exitTag">". In case this comment processor does something unexpected, the line ends with the entity & GT ; rather than the right angle character.
Fixed. Thanks.
Here we are in August 2010, a full 4 years after this article came into being and I wonder what everyone's opinions are now for using CFCs, UDFs, and Custom Tags.
I have seen much that talks about encapsulation and separation of logic/data and display/html. I have seen people advocate using CFCs and UDFs for data only and others to display content as well.
What i want to know is does anyone have a reason that the following is a less than optimal choice in using these options:
1. CFC instantiated in application scope which holds small core functions that are used on 99% of all pages of the site (none of which include any display and are purely data get/set) which also holds core set of variables specific to this application for use on any/all pages (like dsn).
2. Additional CFCs for objects in the application which hold functions specific to the object and/or data specific to the object.
3. Custom Tags for display code that is used frequently like table display, widgets, etc.
4. Included UDF library (is this even needed with the use of the others? if so, when or why? )
Thanks in advance -- i an eager to read everyone's take on this in the here and now.
To start off let me say that I have created a framework that does much of what you describe, a couple of frameworks in fact. When we code on the web our "MVC" is actually what is called Model-2. We have an application framework that works as a 'front controller' for all connections to the site. It delegates control of the request to a request controller. In nearly every framework out there this means CFCs acting as controllers and providing an API for interacting with the application framework.
Now about your mixing tags with CFCs question. Take a look at our COOP framework. This is primarily a coding methdology and we are in beta getting closer to release. We are working on our documentation and will be releasing another beta soon when we complete basic tests for these tags. Here is the docs. http://code.google.com/p/cf...
I will also be doing a session at BFlex/BFusion on Custom AJAX tags.
In general, I only use CFCs for data operations. For layout stuff I'll use custom tags. UDFs are rarely used. I do normally have a set of methods that don't fit in my model and are just generic utilities. For example, I may have a UDF for displayDateTime (to wrap up dateFormat and timeFormat calls into one). In cases like that, I'll create a generic "util" CFC and stuff it in there.
What Ray said. Seriously - that's the same thing I do.
I'll always use custom tags for layout and HTML output.
I'm a fan of "util" CFCs too. A couple advantages: (1) It makes it clean and easy to use those same methods in a legitimate CFC -- just inject the util CFC in! (2) Sometimes I have functions that may need some configuration. I can have the config data passed into the util CFC once, and it's a clean approach to that issue. (3) It's a common way the Java community handles it, so you're familiarizing yourself with an approach that is common in other circles.
If I'm writing something quick and dirty, throwing a couple UDFs in there, old-school like, is not beyond me. :-) But typically that's short scripts that I'm just throwing together in an hour or two.
What Todd said :)
Thank you very much to John, Ray, Todd, Josh, and Steini who chimed in! I really appreciate your having taken the time to answer my post. You have confirmed my thoughts on the matter, and we are proceeding in that direction.
<cfparam name="attributes.result" default="result" type="variableName">
<cfset caller[attribute.result] = now()>
BEST code snippet ever...
(ps I believe it should be you are missing a trailing "s" with attribute.result in the last line.)
Thanks Ati - corrected.
Ray. What scope can I use for local variables inside a custom tag. Rather than the attributes scope, which is generally used as an input scope, could I use <cfset local="StructNew()">
In a custom tag, the variables scope is local to it, so you can just do x=1 or variables.x=1 to keep it scoped to the custom tag.
Thanks Ray. Thats exactly what I wanted to know!