Nerd Business

21
Dec
2017

EJS Element (v1.0 npm release)

EJS is the easiest templating language to use in JavaScript... cause it is JavaScript.

Mustache. Handlebars. Jade, et all - are interesting, but not JavaScript. I just want to use JavaScript - not learn another syntax for doing the same things I already know how to do with JavaScript.

The native EJS library is all you need to get going. Here's a friendly example:


  var ejs = require('ejs'),
      badThings = ['crack', 'cocaine', 'heroin'],
      template = `<h1>bad things:</h1>
        <ul>
          <? badThings.forEach(function(thing) { ?>
            <li class="red"><?= thing ?></li>
          <? }) ?>
        </ul>`, 
      myNewHTML = ejs.render(template, {badThings: badThings});
    

This renders to a string you can then use to populate an (empty) HTML element in your DOM like so:


  
  document.getElementByID('badthings').innerHTML = myNewHTML
  
    

bad things:

  • crack
  • cocaine
  • heroin

'Not bad' but personally I don't want my JS files cluttered up with HTML like that.

Read More

So I began using the following pattern to snuggle an EJS template directly into HTML files; putting the template in a script tag inserted just outside of a given target element so that by reading said HTML it is fairly obviously implied that that template will be output there.


  <html>
  <body>
  <p>This is my HTML page</p>
  <div id="badThingsContainer"></div>
  <script id='badThingsTemplate' type='text/ejs'>
    <h1>bad things:</h1>
    <ul>
      <? badThings.forEach(function(thing) { ?>
        <li class="red"><?= thing ?></li>
      <? }) ?>
    </ul>
  </script>
  </body>
  </html>
    

Then your render call looks like this:


  
  var template = document.getElementByID('badThingsTemplate').innerHTML, 
  document.getElementByID('badThingsContainer').innerHTML = ejs.render(template, {badThings: badThings})
  
    

A little cleaner and better separated, however - this is still a bit clunky if you have to deal with several different templates on a given page (having to keep track of IDs and render them individually etc).

So then I came up with this little wrapper function to help automate things:


  var renderEJS = (baseName, data) => {
    var template = document.getElementById('#' + baseName + 'Template')
    document.getElementById('#' + baseName + 'Container').innerHTML =  ejs.render( template, data )
  }

  //to render then, simply call:  
  renderEJS('badThings', {badThings: badThings})
    

(your HTML / EJS template can remain the same as in the previous example; note the required '...Container' and '...Template' ids)

Enter EJS Element

We can condense this further & make it a lot more intuitive to use by simply allowing the use of EJS directly. Here is the way to do it using my new module EJS Element now available on npm / Github.

In your HTML page:


  <bad-things>
    <h1>bad things:</h1>
    <ul>
      <? badThings.forEach(function(thing) { ?>
        <li class="red"><?= thing ?></li>
      <? }) ?>
    </ul>
  </bad-things>
  

then in your JS:


  var ejsElem = require('ejs-element')
  ejsElem.init('bad-things', {badThings: badThings}, true)

Done! Your element will work in any static HTML page; just load this library, call the init function above and you are good to go.

If you need to render that element again later you can simply call:


  ejsElem.render('bad-things', moreBadThings)

Where  moreBadThings   is your updated state. Or omit the state parameter and it will fallback to the previously supplied state.

Check the docs for complete details - and if you encounter any issues, feel free to post them on the tracker.

p.s. - the parsing logic is rather inefficient - so plenty of room for improvement! Suggestions or pull requests are welcomed.

For example, performance will be slow if doing a high volume of elements. And there are edge cases for what kind of JS you can use inside the element that can break parsing (resulting in an unrendered/broken element on your page; typically with a corresponding EJS error in the console) so keep that in mind if you intend to do complex operations in your template; if you do spot one of such cases please post the issue or try your luck at updating the parsing code to accommodate for your needs.