Scala / Lift - Trying to understand Lift's simultaneous claim to use valid html and propensity for lift: tags and tag rewriting in render - templates

All of the Seven Things (http://seventhings.liftweb.net/) are certainly nice, but I was particularly enthusiastic about the claim in Templates (http://seventhings.liftweb.net/templates) that "Lift supports designer friendly templates."
As one of my steps in learning Lift's way of doing things I'm attempting to create a simple object creation form: take a few parameters, use them as constructor arguments, then stow the object away. After some research and experimentation, tho, I have two concerns:
There seems to be a considerable propensity for significantly rewriting/embellishing the template markup in snippets.
Forms don't seem to use valid or recognizable html elements.
What I'm basing this on:
The form examples/documentation seems all about special lift: tags. Exploring Lift suggests that a form should look like this: (http://exploring.liftweb.net/master/index-6.html)
<lift:Ledger.add form="POST">
<entry:description />
<entry:amount /><br />
<entry:submit />
</lift:Ledger.add>
I'm not sure that's even valid html5 and while it might be valid xhtml, it doesn't feel like that meets the spirit of having your templates look like real html for our designer friends. I read somewhere else (can't find it again) that we did have the option of using actual input tags, but then we wouldn't get some parts of Lift's fancy form wire-up or somesuch, the passage wasn't very clear on what exactly I'd be missing out and the examples don't seem interested in my writing a plain html form making a plain html post.
The code for a demo.liftweb.net example (1) suggests that your template should look like this (2)
<lift:surround with="default" at="content">
<div class="lift:PersonScreen"></div>
</lift:surround>
The code for PersonScreen snippet isn't exactly illuminating, either (3). There are several other examples of a template that has, e.g. only a ul tag in a particular location only to generate a whole series of complex li's with nested elements in the snippet. Sure, you can use xml in Scala and it reads tolerably, but it's still scattering your markup everywhere. This seems to violate the spirit of "designer friendly templates".
What I want to understand.
For a long time I've strictly followed two rules in my webapp development:
No markup in 'code' (controllers, business models).
No business logic in the templates whatsoever.
Idiomatic Lift seems to completely forego the first rule and completely miss the value of the second rule. These rules have served me well and I'm not ready to just follow along with the examples that seem to be violating them without understanding why its not going to create a mess. I want to understand why it's okay in Lift to have so much display code generated in the Snippets. I also want to understand why its okay that the markup in the templates so rarely reflects the output.
What I (think I) want:
I want all of my markup with very few, if any, exceptions to be in my templates. I want my snippets to do minimal template mangling, generally only replacing element text on "leaf" tags and possibly tweaking attribute values. I think I've done this for a reasonably complex display example and I suspect I could use the same technique to generate a vanilla html form and then handle the params myself. Is that what I need to do if I want my template to look like the end-result form?
Responses and any other thoughts, especially on understand the Lift mindset regarding this stuff, would be tremendously appreciated.
Thanks!
http://demo.liftweb.net/simple_screen?F674431078927QJVVYD=_
https://github.com/lift/examples/blob/master/combo/example/src/main/webapp/simple_screen.html
https://github.com/lift/examples/blob/master/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala#L94
EDIT
In response to #OXMO456. (Thanks for the response.)
I have, and they seem to just confirm my concerns: E.g. we start with:
Lift templates contain no executable code. They are pure, raw, valid HTML.
which is awesome. Then later:
The latter two mechanisms for invoking snippets will not result in valid Html5 templates.
and yet everyone seems to use the first of those two mechanisms. Also, it says:
Third, the designers don’t have to worry about learning to program anything in order to design HTML pages because the program execution is abstracted away from the HTML rather than embedded in the HTML.
But pretty consistently the example snippets like the one I referenced in the OP generate markup entirely programmatically. This seems counter to the goals (a) of having designer friendly templates so the designers don't have to be bothered with Freemarker markup and (b) separating display logic from business logic.
The second link is helpful and instructive, but it makes it pretty clear that this isn't The Lift Way. However, The Lift Way also seems to drag a whole load of markup generation into snippets, which is (I think) a huge compounding of markup and business logic. Is that The Lift Way?

Those are old-style tags, not designer-friendly tags.
<lift:MySnippet>
<b:field />
</lift:MySnippet>
becomes
<div class="lift:MySnippet">
<div class="field"></div>
</div>
Old-style Lift templates are valid XML, not XHTML - so you can't have unclosed tags or anything - this differentiates Lift from most frameworks, which treat templates as raw strings with bits of code intertwined throughout, without regard to tags or structure.
BTW, in old-style tags, those fields are all fabricated - they aren't part of some standard set of Lift tags. I could just as easily do:
<lift:MySnippet>
<frobnicate:blorb />
</lift:MySnippet>
as long as my snippet code is looking for that specific tag.
Lift doesn't allow any logic in your templates. All of the logic happens in your Snippet class. So for the designer-friendly example above, I might have a snippet class like this:
class MySnippet {
def render(in: NodeSeq): NodeSeq = ".field" #> Text("some text here")
}
which would yield this result:
<div>
<div class="field">some text here</div>
</div>
It's impossible to put any logic in Lift templates - all they can do is invoke Lift snippets, which are regular Scala classes where all of the work happens.
Lift discards the rule that you shouldn't have any display logic in your real code. Why? Because it makes for more reusable code, because Scala has powerful XML support baked into the language and because all of your logic is now treated as plain old Scala code.
If I define a Lift snippet called CurrentTime, I can simply drop that in to any template and it will display the current time - with old-school MVC frameworks, each action method needs to set the time as a page variable and then my templates would need to be modified to print it out. For more complicated logic, old-school frameworks would probably require a conditional in the templates. Lift doesn't allow that - all of your logic is regular Scala code, eligible for refactoring, easily testable, and compatible with modern IDE's.

In answer to you're "what I think I want" question, sure you can do that no problem. Lift is really all about choices, and understanding your use case. Often the samples you see with Lift intermingle code and markup, which is of course sub-optiomal. The important thing to note is that snippets conceptually are solely designed to produce markup, and render the view.
In addition, as Lift follows its view first paradigm the only thing that the view actually requires is a notation to outline which sections of markup you want to process in which rendering snippets. There are several ways, as illustrate by both the OP and "Bill", but personally I prefer:
<div lift="YourSnippet.method">
<p>Some other code</p>
</div>
This is preferable because you're then not culturing up the class attribute which (IMO) can be confusing for designers. Lift can be very designer friendly, but I think the main problem here is that you have to be both disciplined when writing your snippets whilst ignoring many of the samples available today which mix Scala and markup.
You may also be interested in this post ( http://blog.getintheloop.eu/2011/04/11/using-type-classes-for-lift-snippet-binding/ ); using this sort of pattern you can define decoupled, reusable parts of rendering logic whilst keeping your business logic safely out of the snippets.
Finally, and without wanting to shamelessly promote my own product, but I specifically went out of my way to not use any mixed Scala xml literals in my example code for Lift in Action (other than to illustrate that its possible), so perhaps it might be of assistance if you're looking at Lift ( http://manning.com/perrett/ )

Related

Can JS-DOM test code that uses Tailwind's peer / peer-invalid mechanism?

Tailwind offers a feature where you can give an input element the peer class and then, in a sibling element use the peer-invalid: pseudoclass to apply conditional stylings. This is commonly used to provide helper text when a form field is not valid.
I don't see how this can be tested in JS-DOM though, as JS-DOM doesn't have access to the actual CSS when it is just rendering components in unit tests.
The information I have seen about getting JS-DOM to use CSS at all is kinda sketchy, so I was wondering if it's even worth trying to get it to evaluate a bunch of tailwind classes in a NextJS/Jest/RTL project. I'm using Tailwind 3 so it's not even like I have a big file full of generated classes I could try and pass to JS-DOM :/
So can it be done? (Note the hard fact please mods!!!)
(And, somewhat more subjectively, should it be done? Or is there a better way to approach this?)

Vue: keep linebreaks and indentation intact

I'm working on a Vue app that (obviously ;)) loads Vue templates, lets the user fill in content and then export the markup of those templates/components. However, all linebreaks and indentation are lost in the process.
Is there any way to make Vue keep that information? So far at least I achieved to keep (conditional) comments via the
comments: true
option. There are several other options like expectHTML or shouldDecodeNewlines, but they don't seem to be documented and none of them seem to help me accomplish what I want.
The problem is that we're in part handling email templates (meh...) and it's a client requirement that "input === output" (well, markup-wise), because indeed a space or linebreak here or there can indeed make a difference in fussy mail clients. So simply running the markup through a beautifier is not an option.
I know, I know... That's not really what Vue is made for, and markup !== DOM... But the tool itself is written in Vue and I love to currently be able to use all the magic of Vue syntax inside the templates being processed as well.
If all else fails, I might have to resort to using mustache (or any other template system - recommendations welcome) - but I would really like to keep the templating as it is right now, for the aforementioned reasons.

Is it possible to add regular expressions (or, at least, wildcards) to list of custom html attributes?

WebStorm has inspection for html attribute names and allows to create list of exceptions for that rule. It's works fine, but it's not enough - some JS frameworks (like VueJS) comes with special syntax (for data binding, event binding and so on). It looks like this:
<input class="search-box__input"
type="text"
#keyup.enter="notifySearchRequested()"
v-el:input />
That syntax is well-known and predictable, so it might be perfect to add couple of regular expressions (or, maybe, wildcards - they more secure from performance point of view) to list of inspection exceptions in order to describe framework-specific attributes.
It sounds good for me, but I am not sure that it is possible with WebStorm :-(
So, can I deal with it with WebStorm (or, maybe, some plugin can help me)?

What's the advantage of Logic-less template (such as mustache)?

Recently, I ran into mustache which is claimed to be Logic-less template.
However, there is no explaining why it is designed in Logic-less way. In another word, what's the advantage of Logic-less template?
In other words, it prevents you from shooting yourself in the foot. In the old JSP days, it was very common to have JSP files sprinkled with Java code, which made refactoring much harder, since you had your code scattered.
If you prevent logic in templates by design (like mustache does), you will be obliged to put the logic elsewhere, so your templates will end up uncluttered.
Another advantage is that you are forced to think in terms of separation of concerns: your controller or logic code will have to do the data massaging before sending data to the UI. If you later switch your template for another (let's say you start using a different templating engine), the transition would be easy because you only had to implement UI details (since there's no logic on the template, remember).
I get the feeling that I am nearly alone in my opinion, but I am firmly in the opposite camp. I don't believe that the possible mixing of business logic in your templates is enough reason not to use the full power of your programming language.
The usual argument for logic-less templates is that if you have full access to your programming language you might mix in logic that has no place being in a template. I find this akin to reasoning that you should use a spoon to slice meat because you could cut yourself if you use a knife. This is very true, and yet you will be far more productive if you use the latter, albeit carefully.
For instance, consider the following template snippet using mustache:
{{name}}:
<ul>
{{#items}}
<li>{{.}}</li>
{{/items}}
</ul>
I can understand this, but I find the following (using underscore) to be much more simple and direct:
<%- name %>:
<ul>
<% _.each(items, function(i){ %>
<li><%- i %></li>
<% }); %>
</ul>
That being said, I do understand that logicless templates have advantages (for instance, they can be used with multiple programming languages without changes). I think these other advantages are very important. I just don't think their logic-less nature is one of them.
Mustache is logic-less?
Isn't this:
{{#x}}
foo
{{/x}}
{{^x}}
bar
{{/x}}
Pretty similar to this?
if x
"foo"
else
"bar"
end
And isn't that pretty similar to (read: almost a definition of) presentation logic?
A logic-less template is a template that contains holes for you to fill, and not how you fill them. The logic is placed elsewhere and mapped directly to the template. This separation of concerns is ideal because then the template can easily be built with different logic, or even with a different programming language.
From the mustache manual:
We call it "logic-less" because there
are no if statements, else clauses, or
for loops. Instead there are only
tags. Some tags are replaced with a
value, some nothing, and others a
series of values. This document
explains the different types of
Mustache tags.
The flip side of the coin is that in a desperate attempt to keep business logic out of the presentation, you end up putting lots of presentation logic in the model. A common example might be that you want to put "odd" and "even" classes on alternating rows in a table, which could be done with a simple modulo operator in the view template. But if your view template doesn't allow you to do that, then in your model data you have to not only store which row is odd or even, but depending on how limited your template engine is, you may even need to pollute your model with the names of actual CSS classes. Views should be separate from Models, full-stop. But Models should also be View agnostic, and that's what many of these "no-logic" template engines make you forget. Logic goes in both places, but you have to be judicious about what the logic actually does to decide correctly where it goes. Is it a presentation concern, or a business/data concern? In an effort to have 100% pristine views, the pollution just lands in another less visible but equally inappropriate place.
There's a growing movement back in the other direction, and hopefully things will center somewhere in the more reasonable middle-ground.
It makes your templates cleaner, and it forces you to keep logic in a place where it can be properly unit-tested.
This conversation feels like when the monks of the middle-ages would debate over how many angels can fit on the end of a pin. In other words its starting to feel religious, futile and incorrectly focused.
Mini-rant ensues (feel free to ignore):
If you don't want to continue reading.. My short response to the above topic is:
I don't agree with logic-less templates. I think of it as a programming form of extremism. :-) :-)
Now my rant continues in full swing: :-)
I think when you take many ideas to the extreme, the outcome becomes ludicrous. And sometimes (ie this topic) the issue is that we take the "wrong" idea to the extreme.
Removing all logic from the view is "ludicroous" and the wrong idea.
Step back for a moment.
The question we need to ask ourselves is why remove the logic? The concept, obviously is separation of concerns. Keep the view processing as separate from the business logic as possible. Why do this? It allows us to swap out views (for different platforms: mobile,browser,desktop etc) and it allows us to more easily swap out the control-flow, page sequence, validation changes, model changes, security access etc. Also when logic is removed from the views (especially web views), it makes the views much more readable and therefore more maintainable. I get that and agree with that.
However the overriding focus should be on separation of concerns. Not 100% logic-less views. The logic within the views should relate to how to render the "model". As far as I'm concerned, logic in the views is perfectly fine. You can have view-logic that is not business-logic.
Yes, back in the day when we wrote JSPs, PHP or ASP pages with little or no separation of code logic and view logic, the maintenance of these web-apps was an absolute nightmare. Believe me I know, I created and then maintained some of these monstrosities. It was during that maintenance phase that I really understood (viscerally) the error of my and my colleagues ways. :-) :-)
So the edict from on high (the industry pundits) became, thou must structure your web-apps using something like a front-view controller (that dispatched to handlers or actions [pick your web-framework]) and thy views must contain no code. The views were to become dumb templates.
So I agree in general with the above sentiment, not for the specifics of the items of the edict, but rather the motivation behind the edict - which is the desire for separations of concerns between view and business logic.
In one project that I was involved in, we tried to follow the logic-less view idea to the ludicrous extreme. We had a home-grown template engine that would allow us to render model objects in html. It was a simple token based system. It was terrible for one very simple reason. Sometimes in a view we had to decide, should I display this little snippet of HTML .. or not.. The decision is usually based on some value in the model. When you have absolutely no logic in the view, how do you do that? Well you can't . I had some major arguments with our architect about this. The front-end HTML people writing our views were completely hamstrung when they were faced with this and were very stressed because they could not achieve their otherwise simple objectives. So I introduced the concept of a simple IF-statement within our templating engine. I cannot describe to you the relief and the calm that ensued. Problem was solved with a simple IF-Statement concept in our templates! Suddenly our templating engine became good.
So how did we get into this silly stressful situation? We focused on the wrong objective. We followed the rule, you must not have any logic in your views. That was wrong. I think the "rule-of-thumb" should be, minimize that amount of logic in your views. Because if you don't you could inadvertently allow business logic to creep into the view - which violates separation of concerns.
I understand that when you declare that "You must have no logic in the views", it becomes easy to know when you are being a "good" programmer. (If that is your measure of goodness). Now try implementing a web-app of even medium complexity with the above rule. Its not so easily done.
For me, the rule of logic in the views is not so clear cut and frankly that's where I want it to be.
When I see lots of logic in the views, I detect a code-smell and try to eliminate most of the logic from the views - I try to ensure business logic lives elsewhere - I try to separate the concerns. But when I start chatting with people who say we must remove all logic from the view, well, to me, that just smacks of fanaticism as I know you can end up in situations like I described above.
I'm done with my rant. :-)
Cheers,
David
The best argument I've come up with for logicless templates is then you can use the exact same templates on both the client and server. However you don't really need logicless, just one that has it's own "language". I agree with the people who complain that mustache is pointlessly limiting. Thanks, but I'm a big boy and I can keep my templates clean without your help.
Another option is to just find a templating syntax that uses a language that is supported on both client and server, namely javascript on the server either by using node.js or you can use a js interpreter and json via something like therubyracer.
Then you could use something like haml.js which is much cleaner than any of the examples provided so far and works great.
In one sentence: Logic-less means the template engine itself is less complex and therefore has a smaller footprint and there are less ways for it to behave unexpectedly.
The main advantages of using logic-less templates are:
Strictly enforces model-view separation, see this paper for details (highly recommended)
Very easy to understand and use, because no programming logic (and knowledge!) is needed and the syntax is minimal
(particular Mustache) highly portable and language independent, can be used without modification in almost all programming environments
Even though the question is old and answered, I'd like to add my 2¢ (which may sound like a rant, but it isn't, it's about limitations and when they become unacceptable).
The goal of a template is to render out something, not to perform business logic. Now there's a thin line between not being able to do what you need to do in a template and having "business logic" in them. Even though I was really positive towards Mustache and tried to use it, I ended up not being able to do what I need in pretty simple cases.
The "massaging" of the data (to use the words in the accepted answer) can become a real problem - not even simple paths are supported (something which Handlebars.js addresses). If I have view data and I need to tweak that every time I want to render something because my template engine is too limiting, then this is not helpful in the end. And it defeats part of the platform-independence that mustache claims for itself; I have to duplicate the massaging logic everywhere.
That said, after some frustration and after trying other template engines we ended up creating our own (...yet another...), which uses a syntax inspired by the .NET Razor templates. It is parsed and compiled on the server and generats a simple, self-contained JS function (actually as RequireJS module) which can be invoked to "execute" the template, returning a string as the result. The sample given by brad would look like this when using our engine (which I personally find far superior in readabily compared to both Mustache and Underscore):
#name:
<ul>
#for (items) {
<li>#.</li>
}
</ul>
Another logic-free limitation hit us when calling into partials with Mustache. While partials are supported by Mustache, there is no possibility to customize the data to be passed in first. So instead of being able to create a modular template and reuse small blocks I'll end up doing templates with repeated code in them.
We solved that by implementing a query language inspired by XPath, which we called JPath. Basically, instead of using / for traversing to children we use dots, and not only string, number and boolean literals are supported but also objects and arrays (just like JSON). The language is side-effect-free (which is a must for templating) but allows "massaging" data as needed by creating new literal objects.
Let's say we want to render a "data grid" table with cusomizable headers and links to actions on the rows, and later dynamically add rows using jQuery. The rows therefore need to be in a partial if I don't want to duplicate the code. And that's where the trouble begins if some additional information such as what columns shall be rendered are part of the viewmodel, and just the same for those actions on each row. Here's some actual working code using our template and query engine:
Table template:
<table>
<thead>
<tr>
#for (columns) {
<th>#title</th>
}
#if (actions) {
<th>Actions</th>
}
</tr>
</thead>
<tbody>
#for (rows) {
#partial Row({ row: ., actions: $.actions, columns: $.columns })
}
</tbody>
</table>
Row template:
<tr id="#(row.id)">
#for (var $col in columns) {
<td>#row.*[name()=$col.property]</td>
}
#if (actions) {
<td>
#for (actions) {
<button class="btn #(id)" value="#(id)">#(name)...</button>
}
</td>
}
</tr>
Invocation from JS code:
var html = table({
columns: [
{ title: "Username", property: "username" },
{ title: "E-Mail", property: "email" }
],
actions: [
{ id: "delete", name: "Delete" }
],
rows: GetAjaxRows()
})
It does not have any business logic in it, yet it is reusable and configurable, and it also is side-effect free.
Here are 3 ways of rendering a list, with character counts. All but the first and shortest one are in logic-less templating languages..
CoffeeScript (with Reactive Coffee builder DSL) - 37 chars
"#{name}"
ul items.map (i) ->
li i
Knockout - 100 chars
<span data-bind="value: name"/>
<ul data-bind="foreach: items">
<li data-bind="value: i"/>
</ul>
Handlebars/Mustache - 66 chars
{{name}}:
<ul>
{{#items}}
<li>{{.}}</li>
{{/items}}
</ul>
Underscore - 87 chars
<%- name %>:
<ul>
<% _.each(items, function(i){ %>
<li><%- i %></li>
<% }); %>
</ul>
The promise of logic-less templates was, I suppose, that people with broader skillsets would be able to manage logic-less templates without shooting themselves in the foot. However, what you see in the above examples is that when you add a minimal-logic language to string-based markup, the result is more complex, not less. Also, you look like you're doing old-school PHP.
Clearly I don't object to keeping "business logic" (extensive computation) out of templates. But I think that by giving them a pseudo-language for display logic instead of a first class language, the price is paid. Not just more to type, but a heinous mix of context-switching somebody needs to read it.
In conclusion, I fail to see the logic of logic-less templates, so I'd say their advantage is nil to me, but I respect that many in the community see it differently :)
I agree with Brad: the underscore style is easier to understand. But I must admit the syntactic sugar may not please to everybody. If _.each is somewhat confusing, you can use a traditional for loop.
<% for(var i = 0; i < items.length; i++) { %>
<%= items[i] %>
<% } %>
It is always nice if you can fallback to standard constructs such as for or if. Just use <% if() %> or <% for() %> while Mustache use somewhat neologism for if-then-else (and confusing if you didn't read the documentation):
{{#x}}
foo
{{/x}}
{{^x}}
bar
{{/x}}
Template engine is great when you can achieve nested templates easily (underscore style):
<script id="items-tmpl" type="text/template">
<ul>
<% for(var i = 0; i < obj.items.length; i++) { %>
<%= innerTmpl(obj.items[i]) %>
<% } %>
</ul>
</script>
<script id="item-tmpl" type="text/template">
<li>
<%= name %>
</li>
</script>
var tmplFn = function(outerTmpl, innerTmpl) {
return function(obj) {
return outerTmpl({obj: obj, innerTmpl: innerTmpl});
};
};
var tmpl = tmplFn($('#items-tmpl').html(), $('#item-tmpl').html());
var context = { items: [{name:'A',{name:'B'}}] };
tmpl(context);
Basically you pass your inner tmpl as a property of your context. And call it accordingly. Sweet :)
By the way, if the only stuff you are interested in is the template engine, use the standalone template implementation. It is only 900 characters when minified (4 long lines):
https://gist.github.com/marlun78/2701678

How do you decide what to use: UDF or Custom Tag?

WACK says:
If you feel you need to have lots of arguments, consider creating a CT instead.
...
CT are significantly more powerful and
flexible than custom functions. Try to
use UDFs for simple matters... Use CT
and Components for more involved
processes, especially those you can
think of as discrete actions rather
than simple "massaging"
Okay, but how are you usually making decisions? Interesting to know real-life practice and examples.
For me it happens when a function has many not-required arguments, so I have to call them myFunc(arg1="foo", arg2="bar"). Sometimes <cfmodule> syntax simply becomes more readable, but not always.
Other reason is that I don't like long (say, more than 2 screens of code) UDFs.
But all these thoughts are very subjective, that's why I'm interested in reading other people opinions. Maybe there's better rules for that?
Thanks in advance.
There a probably plenty of people in the community that would disagree with me, but here is how I generally think about it. If what you need to do involves output to the screen, and if it makes sense to "wrap" this around some other code or text, then a custom tag might be in order. In all other cases a UDF works fine and generally better. That being said, in close to 8 years of CF development, I really haven't ever came across a very good reason for a custom tag. I'm not saying the reasons don't exist, but I would say that they are rare.
If you have a very long UDF, is it safe to assume that this is something where you are outputting to the screen by calling this UDF (not really returning a value I mean)? I would consider breaking that UDF into smaller more manageable parts if that could make sense, but like you are alluding to, what matters in the end is what is the most readable, to you, your team and those who may come after you.
Update: Out of curiosity, which CFWACK book are you referring to (version and volume) and which page?
As I remember things, custom tags can be called at any time, where as UDFs have to be defined before you can use them. This makes custom tags easier generally. If you had huge libraries of UDFs it would be burdensome to make sure they are all included and potentially hard work for the server to parse them all (in olden days at least).
However UDFs can be used in a more compact way
<cfif myUdf(myVariable)>
The advantage of custom tags is that they can sit nicely with your markup.
<h1>Order Page</h1>
<cf_basket_nav>
<ul>
<cfloop ...>
<li>
<cf_basket_item item="#items[i]#">
</li>
</cfloop>
</ul>
</cf_basket_nav>
Generally nowadays I would have a 'utils' CFC with methods for what were your UDFs.
one consideration on the use of custom tags over udfs or cfc methods is when you find that a subroutine needs to be passed an array of child items, you can use nested custom tags to associate a child custom tag and its elements to a parent custom tag. this allows you to do very nice clean coding thats easy to read:
<cfmenubar ... >
<cfloop array="menuitems" ...>
<cfmenubaritem url="#i.url#">
#i.label#
</cfmenubaritem>
</cfloop>
</cfmenubar>
yes,yes i know we have nicer dhtml stuff like menus and tabs, this is simply to point out an example. you can use cfassociate in the custom tag to "pass" the attributes to the parent custom tag and then in the executionmode="end" access all the dynamically generated child items in the array of associated attributes. this is where you would loop and output the menu to the screen in this example.
also, as another commented, allows you to do some clever things... one thing in particular i use is that i set prefix="" and then i can essentially force simple html tags (like the <a> tag) to get kicked through a custom tag handler at runtime - so an html tag becomes intelligent at runtime... i do this so i can analize the href and the target attributes and decide if i want to display a pdf icon (or other mime type icon) next to the link... its pretty slick! this is especially helpful in a content management system or when 7you have html developers using dreamweaver or contribute and you want to have their tags fire smart coldfusion tags without them doing anything outside of standard html - the editor doesnt know any difference and they dont need to go into "code" view to make some fairly powerful functionality.
finally, in a custom tag you can choose to suppress output (or use a cache), so this can be very useful to wrap around chunck of dynamically generated html... access the thistag.generatedcontent variable in the executionmode EQ "end" mode
dont throw out the baby with the bathwater on this one ... i agree their usage is much less frequent since we have cfcs, however there is still some powerful functionality in custom tags... i usually have one or 2 in every application (and at least dozens of cfcs)
hth
jon
cfmodule has no advantage over functions; cfinvoke works just the same.
Of course, with cfimport you can enable a nice tidy namespaced CT syntax - and this is when custom tags are good: when working with clear open/close construct, or if nested logic is important.
For everything else, especially when returning results, functions are easier to handle.
Jeremy pointed to useful option: wrapping HTML by CT. But this feature seems to be so rarely used. Any way, it's a plus for CT.
Also I thought that I prefer function because I love cfscript coding. Few times I had to wrap legacy CT into UDF only to be able to use it at totally cfscript-ed page. It's a plus for UDF.
Simple reusable code can go into a UDF. This might include things like string formatting, data structure manipulation, etc etc.
For anything more complex than that, I would consider using CFCs instead of a custom tag. You'll be able to write much cleaner code in an OO model. You can always wrap the logic in your CFCs with a custom tag if you want to provide ease of use for someone who is more used to working with tags.
Not sure why I fell into this pattern, but in general I use Custom Tags (always with cfmodule) for anything that outputs HTML and UDFs for anything that merely returns simple data/objects. I realize that UDFs can do output as well, but I don't like my functions to have any side effects and this feels like one. For similar reasons, I now use Custom Tags anywhere where previously I would have used a cfinclude, since they provide encapsulation of data.