Foundation-emails: Escaping handlebars expressions that need to be present in output or disabling handlebars at the page level

Created on 8 Mar 2016  Â·  23Comments  Â·  Source: foundation/foundation-emails

Hi!

We are using the handlebars feature of Mandrill to send templates for our transactional email. The templates we upload to mandrill include {{handlebars}} expressions and we would like to be able to generate this templates with foundation for emails.

The problem is that panini compiles all handlebars expressions in our pages that it sees so we don't have a way to produce an output template with handlebars expressions in it. It would be much needed to have a way to disable the compiling of expressions. The most convienient way would be in a Front Matter block in each page or some config in the guplfile.

I don't mind if handlebars/panini is used for the layout/partial infrastructure. Is just there should be a way to disable handlebars compiling at the page/template level.

in gulpfile would be something like this.

    .pipe(panini({
      root: 'src/pages',
      layouts: 'src/layouts',
      partials: 'src/partials',
      disableHandlebarsInPages: true
    }))

If such a global configuration is too extreme, with a special variable in Front Matter block it would be great to disable Handlebars

title: "My title"
disableHandlebars: true

---
<!-- rest of the template -->
<container>
  <row>
    <columns small="12" large="12">
    <h1>{{ExpressionToBeProcessedByOtherHandlebarsEngine}}</h1>
    </columns>
  </row>
</container>

Do you think this would be useful?

Germán

awaiting reply feature

Most helpful comment

You can create a raw Handlebars helper, which ignores any parsing inside of it.

Create the file src/helpers/raw.js with this function:

module.exports = function(content) {
  return content.fn();
}

Then call it with four curly braces:

{{{{raw}}}}
  <p>Nothing {{ inside }} this block will be parsed as Handlebars.</p>
{{{{/raw}}}}

This has the benefit of letting you mix Handlebars/not-Handlebars on one page.

All 23 comments

This would be my proposed solution with a frontmatter block. Just read first the frontmatter block and compile the template or just output the template body as is.

panini-> render.js

    // Build page data with globals
    pageData = extend({}, this.data);

    // Add any data from stream plugins
    pageData = (file.data) ? extend(pageData, file.data) : pageData;

    // Add this page's front matter
    pageData = extend(pageData, page.attributes);

    // Finish by adding constants
    pageData = extend(pageData, {
      page: path.basename(file.path, '.html'),
      layout: layout,
      root: processRoot(file.path, this.options.root)
    });

    // Now create Handlebars templates out of them
    var pageTemplate = !pageData.disableHandlebars? 
                  this.Handlebars.compile(page.body + '\n')
                  : function() { return page.body + '\n'};

It could be useful in the use case you provided. Are you getting the other benefits of the stack and have your own handlebars solution?

yes, with this hack in place I still can get the benefit of inky() that translates the grid, I still get the layouts, although helpers included in the page template will not be included.

You can create a raw Handlebars helper, which ignores any parsing inside of it.

Create the file src/helpers/raw.js with this function:

module.exports = function(content) {
  return content.fn();
}

Then call it with four curly braces:

{{{{raw}}}}
  <p>Nothing {{ inside }} this block will be parsed as Handlebars.</p>
{{{{/raw}}}}

This has the benefit of letting you mix Handlebars/not-Handlebars on one page.

@germanftorres How did this work out for you?

@germanftorres @markholmes I'll implement this solution myself and see what the problem is.

I created the helper file and tried this method, but it does not seem to work for me for some reason I cannot figure out.
Content inside {{{{raw}}} is not present in the output at all so it's basically stripping all of it.

I'm using the normal npm start way to work on the email template, is there anything else I'll need to do in order to make this work?

@houmark Are you setting the path to the helpers dir in your gulpfile.babel.js ?

function pages() {
  return gulp.src('src/pages/**/*.html')
    .pipe(panini({
      root: 'src/pages',
      layouts: 'src/layouts',
      partials: 'src/partials',
      helpers: 'src/helpers/'
    }))
    .pipe(inky())
    .pipe(gulp.dest('dist'));
}

@alexblunck That's a no ;) That was no-where written so while it had been on my mind, I thought it was maybe something foundation automatically did when it detected files in that folder.

It works now after doing that ;) Thanks for the hint...

Can we get this reopened, and considered as a possible future feature? "handlebars style" template tags are an extremely common tool for per recipient substitutions and other data inclusion tasks as part of sending email. I don't think my use case of building emails and email templates that will be used by other tools such as Django, SparkPost, Mandrill, etc, is uncommon and the current 'default' behaviour interferes with doing this effectively.

As I see it the only way to do this, is by having this helper, so I agree that this helper should be included by default so {{{{raw}}}} works out of the box.

I've hit a side effect of this.

Inside the raw code I have a #if statement that is to be executed in our Handlebars engine. To make more complex Handlebars statements we are creating a bunch of helpers much like here: http://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional

This enables us to do stuff like this {{{{raw}}}}{{#if (eq signupType "external")}}{{{{/raw}}}}

Problem is "external" becomes &quot;external&quot; — problem persists if we use single quotes.

This seems to be Inky (or the Gulp inlining, not sure) not respecting {{{{raw}}}} and thus it still escapes inside {{{{raw}}}}, which I believe it should not. I can create a new issue if that helps keeping better track of this, but it pretty directly relates to this part, which I see will now be part of the core.

another side effect with {{{{raw}}}}<%= variable %>{{{{/raw}}}}
becomes to <%= variable="" %="">

@houmark , I think its how htmlparser2 (which is ultimately what cheerio ends up using) returns the HTML string.

I'm trying to see if there is a way to have htmlparser2 skip over certain tags, but so far I haven't had any luck.

I just came across this and decide to do with a helper

/**
 * use the given name as a variable for marketing cms
 * @example
 * {{data 'firstName'}}
 * @outputs
 * {{{firstName}}}
 */
module.exports = function(content) {
  return "{{{" + content + "}}}";
}

It was simpler than the raw situation.

Inky escapes quotes. Given how little Inky saves (it adds some shorthand for table definition) and how limiting it is (you can't add CSS classes in interesting places on those tables, plus this escaping issue), I just disabled it completely. All you have to do is remove the one Inky command from the "pages" task in your gulpfile.

Just my 2cents, but I tend to agree with @bleonard here, using a helper will require little effort on a the part of the developer, while introducing an overarching solution that fits a range of corner cases will be cumbersome at best.

True, this issue shall be reopened adhering to the issues which {{{raw}}} tag's usage has.

After seeing belnoard's script which I got me to look around further, as well as having trouble with complex, dynamic urls > I borrowed the helper from handlebars and modified for purposes of utm codes:
`Handlebars.registerHelper('link', function(url, utm) {
url = Handlebars.Utils.escapeExpression(url);
utm = Handlebars.Utils.escapeExpression(utm);

var result = '';

return new Handlebars.SafeString(result);
});`

Can escaping urls be an option of true or false in the gulp stream?

If your goal is to use curly braces in Front Matter block in order to render them as they are, you can use the multi-line syntax of YAML, e.g:

---
layout: default
subject: >-
         {{ title }}
---

Make sure to vertical align your content with the > symbol above.

other side effect with {{{{raw}}}}<%= variable %>{{{{/raw}}}}
becomes to <%= variable="" %="">

Any fix for this issue

Was this page helpful?
0 / 5 - 0 ratings