Using views for emails in FW/1
Sean Corfield has been busy updating has FW/1 MVC Framework. One of the features he's added is the ability to call a view from a controller. Why would I want to do that?
Well, imagine you're building a contact form and want to be able to send out an HTML email with the information to visitor entered. What you can now do is something along the lines of:
controllers/main.cfc
component
{
variables.fw = '';
function init( fw )
{
variables.fw = arguments.fw;
return this;
}
function dosendenquiry( rc )
{
// fake form parameters
rc.name = "John Whish";
rc.email = "foo@bar.moo";
rc.enquiry = "I like this!";
/// this is the new functionality....
var emailbody = variables.fw.view( 'templates/enquiry' );
// create the mail object
var Email = new mail();
Email.setSubject( "Enquiry" );
Email.setTo( "bar@moo.foo" );
Email.setFrom( "moo@bar.foo" );
Email.setBody( emailbody );
Email.setType( "html" );
Email.send();
// redirect after sending the email
variables.fw.redirect( 'main.thanks' );
}
}
The new view() method functionality, means that I can call a template in my views folder and have FW/1 return the rendered content and assign the content to a variable. That content is then passed into the setBody method of the mail object instance.
My view looks like:
views/templates/enquiry.cfm
<cfoutput>
<h2>Website Enquiry</h2>
<dl>
<dt>Name</dt>
<dd>#rc.name#</dd>
<dt>Email</dt>
<dd>#rc.email#</dd>
<dt>Enquiry</dt>
<dd>#rc.enquiry#</dd>
</dl>
</cfoutput>
This technique is not just for emails, you could use it for generating PDFs as well. I've been using the Renderer plugin in ColdBox to do this in my ColdBox apps, so I'm really pleased to see that FW/1 now has a way to do it as well.
If you want to try it out, then you'll need to download it from the develop branch on GitHub. If you try to do this in older versions of FW/1 you'll get an error similar to:
Invalid to call the view method at this point.
The view method should not be called prior to the completion of the controller execution phase. (FW1.viewExecutionFromController)
- Posted in:
- ColdFusion
- FW/1


Other uses include caching and file writing, where you don't want to render the view, just save its output and pass it as "data" to a specific service.
Comment by Julian Halliwell – August 01, 2011
Comment by John Whish – August 04, 2011
Is this the case? And if so, how would you go about building an email with an HTML and a plaintext part?
Comment by Seb Duggan – August 04, 2011
I prefer to think of calling views in this way as generating *data*, just as you'd ask for data from a service, which is not rendered directly but passed to another service for processing (eg. email), or put in the rc to be rendered as part of the actual view for that request (as in the case of "fragments").
The benefit is that you avoid having to output view stuff in services or put logic/service stuff in views. Each stays in its proper place and the controller mediates between the two (which is also its proper role).
As to handling multi-part emails, I'd have 2 views, one for plain, one for HTML and call each separately with view() in the controller, then pass the results to the email service which can then use them within the cfmail tag/call.
rc.htmlContent = fw.view( "/mail/html" );
rc.plainContent = fw.view( "/mail/plain" );
service.email.send( rc );
Comment by Julian Halliwell – August 04, 2011
If I were to use separate views, then I agree with Julian's comment above, although I personally dislike passing the request collection into a service method, but it's all about preference.
Comment by John Whish – August 08, 2011
Comment by Julian Halliwell – August 08, 2011
I've got a complex bunch of data in an object (it's a cricket scorecard - and they can get quite convoluted!), and when it's saved in the admin I want it to generate the HTML-formatted scorecard, which is then saved to the database.
Previously, I had a file included in my service CFC, with all its output wrapped in cfsavecontent. Not very elegant...
Now I just call:
card = view("scorecard/build");
from within the controller, and then pass it off to the service to save it. Much nicer!
Comment by Seb Duggan – August 08, 2011
Comment by Seb Duggan – August 08, 2011