Aliaspooryorik
ColdFusion ORM Book

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)

8 comments

  1. Hi John. As you may know I've been lobbying for this feature for a while and I'm over the moon it's been added. For me it was the only thing Fusebox (the more recent versions) had over FW/1 (contentVariables).

    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
  2. Hi Julian, yes I saw you were participating in the thread on the mailing list. The only other thing I can think that Fusebox had was the ability to assign a permission to a circuit/fuse, but that was only if you were using the XML syntax.

    Comment by John Whish – August 04, 2011
  3. I saw on the mailing list that Sean was sticking very strongly to the idea that even with the new functionality there should only ever be one view rendered per request (he also didn't seem to like the idea of using it for emails!).

    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
  4. @Seb, yes I've been on the sharp end of Sean's arguments in favour of keeping the isolation between the controller and view rendering phases :-) But I don't think this breaks that principle if you consider "rendering" as putting stuff in the final http output stream. In John's example what ends up being rendered to the browser? Nothing except an http 302 header (redirect).

    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
  5. @Seb, I don't tend to have a specific separate view for plain text versions instead I automatically rip all the HTML and tabs etc out of the HTML version and use that to create the plain text version. It seems a lot of extra work creating plain and rich HTML versions of the same email when most clients support HTML.

    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
  6. @John, actually I'd rarely pass the entire request context into a service in real code either. I was just trying to keep the example simple.

    Comment by Julian Halliwell – August 08, 2011
  7. Just used this in a real site for the first time.

    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
  8. (Of course, that should have been variables.fw.view("scorecard/build") if it's happening inside the controller...)

    Comment by Seb Duggan – August 08, 2011

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.

Please subscribe me to any further comments
 

Search

Wish List

Found something helpful & want to say ’thanks‘? Then visit my Amazon Wish List :)

Categories

Recent Posts