General patterns of working with Fusebox5
I have been using fusebox for a few years now and really like it. I have a way of doing things which has become familiar and seems to do the job, but I wonder what other patterns there are. Here is mine.
I have a layout circuit which runs as a globalfuseaction, postprocess. This handles all the basic page stuff - HTML meta tags, linked css files, javascript files and including any constant elements of the page - title, footer, navigation etc. It will also pick up and render the main content generated by other circuits that have run for the current request. The circuit.xml file for this layout circuit will handle any logic to determine alternative layouts if required (such as popup windows or pages for print).
Business logic and database access is in cfcs.
All control of flow should be visible in the circuit files which means I will have some 'if' statements in the XML. Where possible I invoke cfc methods direct from the circuit.xml. I always use XFA's for all exit points from a fuseaction. One of my aims is that you should be able to open up the circuit.xml files only and understand the application at a reasonable level without the need to delve into actual code.
I would love to hear how other people commonly use fusebox.
I struggle to decide sometimes where to put cfc creation especially if it's conditional. Do I put an <if... > in the circuit.xml or call a act include with a createObject() inside a <cfif... >?
On form validation I prefer self submitting forms and I have a technique which I'll share but I'd like to know your thoughts first ;-)
Fusebox is excellent though isn't it, and thanks to Sean Corfield has just got better and better.
Michael@ I put mine into a single page because it would upset my tidy mind to have the same screen displayed after a failed validation but with a different URL (Fuseaction).
Here's my pseudo code, I hope it formats okay...
fuseaction name="default"
-- include template="actInitialiseFormDefault"
-- xfa name="strRelocate" value="step2"
-- if condition="StructKeyExists(form,'fieldnames')"
---- include template="actValidateFormDefault"
---- if condition="strErrorMessage EQ ''"
------ relocate xfa="strRelocate"
---- /if
-- /if
-- include template="dspDefault" contentvariable="htmDefault"
-- do action="layout.default"
/fuseaction
And to explain...
* I initialise the form and it's vars for initial display.
* Set an xfa for the next step if the form passes validation.
* Test to see if this request is due to the form being submitted. The best techique I've found over the years is to test for the existence of the form.fieldnames var which is *always* created on form submission.
* I then validate the form fields. I start by setting the var strErrorMessage to blank. Then on each field validation I append a meesage to the var on failure. On success I persist the field value into a session scope var or object for use in either the dsp fuse, on validation failure, or some later process which might be needed on a multi-step form. I display the error message on the dsp fuse if it not blank.
* If strErrorMessage is still blank after validation I know all's fine so I can relocate to the next step knowing that I have for the form in some session var/object. Obviously there could also be someother action fuse/object invoke in there as well but we need to move onto a new screen.
* Finally, for the initial request, we capture the page design and wrap it in a layout.
Any questions/comments?
It's still running though when many requests simply won't need it.
"...every request must produce output"
Must it? As I say what about all your CRUD operations: you're just invoking actions on the server which don't require any HTML output to be returned to the browser. All you need to send back is a <relocate> (cflocation) instruction to move on to a new request/fuseaction.
It's unlikely to cause too much of a performance problem and it does save a bit of typing, but as I say - and agreeing absolutely with your other point in the post, I like to see what each fuseaction is doing in the xml, including whether or not it's generating output.
You're quite right about global postfuseactions in that relocating will halt the request before it gets to them so they won't in fact be generating anything, but I'd still question why you need the code there at all if it isn't going to run. And of course with Ajax requests it would, unless you did some special handling. Personally I much prefer being able to look at each fuseaction individually and see explicitly whether it outputs anything *and* if so what kind of output it is: ajax, pop-up window, normal page...
Anyway, as you say in your post there are different "patterns" you can adopt within the framework and I'm constantly finding better ways of using it.
Cheers
<fuseaction name="ajaxCall">
<invoke object="my.cfc" methodcall="getAjaxData()" returnVariable="request.ajaxOutput"/>
<include circuit="view" template="dspAjaxOutput"/>
</fuseaction>
Of course the whole point of Ajax is to avoid a page refresh so as you say you don't want any <relocate>s. So if your ajax fuseaction deletes an HTML table row, you wouldn't want any <relocate> after the delete method, or any output. You just want it to do the delete silently on the server and let the JS make changes to the page in the browser.
<fuseaction name="ajaxDelete">
<invoke object="my.cfc" methodcall="deleteRow(attributes.ID)"/>
</fuseaction>
If you have any global/circuit-wide fusactions generating layout, then at best they're adding unnecessary processing, and at worst doing stuff that might interfere with the Ajax. And they *would* fire because there'd be no <relocate> to halt the FB request. You could add a flag and logic to your layout fuseaction to suppress it for Ajax, but I prefer to avoid that complexity, even if it means more typing. And again my point really is that I find the presence or otherwise of the layout <do>s helpful to see what's happening rather than it being hidden away in a global layout handler.
But the way you're doing things is perfectly fine too, and I started out that way as well.