Skip to content
Chris edited this page Sep 15, 2017 · 2 revisions

ICanHaz.js

Simple and powerful client-side templating

OKAY, LET'S DO THINGS

The Quick Demo

Step 1: Define your template.

<script id="user" type="text/html">
  <li>
    <p class="name">Hello I'm {{ name }}</p>
    <p><a href="http://twitter.com/{{ twitter }}">@{{ twitter }}</a></p>
  </li>
</script>

Step 2: Retrieve your pop# ulated template.

// I Can Haz User?
var user = ich.user(user_data_object);

Step 3: There is no step 3!

What else do I need to know?

For simplicity and ease of use, ICanHaz includes janl's Mustache.js v0.4.0 (inside a closure) that way you can just include ICanHaz and then YOU CAN HAZ stuffs. Luckily Mustache and Mustache.js are generously MIT licensed. Mr. GitHub founder himself Chris Wanstrath (@defunkt) created mustache. Read the mustache documentation for more info. Then it was ported to JS by Jan Lehnardt.

Why would I need this?

Because building html elements using JavaScript or jQuery is ugly. There are several ways to do it, but the point is — it's ugly.

// vanilla JS
hello_div = document.createElement('div');
hello_div.setAttribute('class', 'hello');
my_list = document.createElement('ul');
hello_div.appendChild(my_list);
list_item = document.createElement('li');
list_item.innerHTML = 'My list item';
my_list.appendChild(list_item);

// jQuery
hello_div = $('<div class="hello"><ul></ul></div>');
hello_div.children('ul').append('<li>My list<li>');

It gets really problematic if you're building something a lot longer or more complex than this example. Not to mention, it's not a clean separation of concerns to write HTML in JavaScript.

Mustache.js gives us an awesome templating solution, here's a snippet from their docs:

var view = {
  title: "Joe",
  calc: function() {
    return 2 + 4;
  }
};

var template = "{{title}} spends {{calc}}";

var html = Mustache.to_html(template, view);

But the beauty fades when we're dealing with multi-line html in the browser because strings in JS can't include new-lines so everything has to be escaped. Then there's the problem of double vs. single quotes and before you know it...we're back in ugly land:

var template = '<div class="hello">\
  <span class="title">{{ title }}</span>\
  <ul></ul>\
</div>';

I Can Haz Better Solution?

YES!

With ICanHaz.js you define your Mustache.js template snippets in script blocks of type="text/html" and give them an id as a title for your snippet (which validates, btw). This approach was suggested by jQuery developer @jeresig on his blog. Then, on document ready ICanHaz.js builds a cache of all the templates and creates a function for each snippet. All you have to do is say to yourself for example "I can haz user?":

var data = {
  firstName: "Henrik",
  lastName: "Joreteg"
};

// I can has user??
html = ich.user(data);

At this point html is a jQuery or Zepto (if they're included) object containing your complete html with your data injected.

For each template you define (except partials), ICanHaz builds a retrieval function with the same name. If you don't want a jQuery object but just want the populated string you can just pass in true as the second argument to get the raw string. This is useful if your template isn't producing html.

If you don't have jQuery or Zepto, you can still use ICanHaz. You'll just get strings back instead of jQuery objects.

I'm in ur templates, making macroz

ICanHaz.js also supports mustache partials. To quote the original mustache.js announcement:

Partials are good for including often-used snippets, like navigation or headers and footer.

In mustache, partials are dead simple. You have a special tag {{>partial}} that you put where you want to insert the partial, create the partial that you want to be displayed and that's it. It is just a basic replace or macro include mechanism. Nothing fancy.

Just add your partial like any other template:

<!-- Main template, includes the "winnings" partial. -->
<script id="welcome" type="text/html">
  <p>Welcome, {{name}}! {{>winnings}}</p>
</script>

<!-- Partial included via {{>winnings}} -->
<script id="winnings" class="partial" type="text/html">
  You just won ${{value}} (which is ${{taxed_value}} after tax)
</script>

Then call the main template normally.

Adding templates/partials later

Optionally, you can call ich.addTemplate(name, templateString) to add templates and partials if you'd prefer to pull them from a server with AJAX or whatnot. You can even do ich.grabTemplates if you've loaded in some other page.

Available Methods

Beyond the retrieval functions that ICanHaz creates based on template names, these additional methods exist.

  • ich.addTemplate(name, mustacheTemplateString): Add new template. Could be useful if you prefer not to use <script type="text/html"> approach or want to lazy load 'em from a server or whatnot.
  • ich.clearAll(): Clears templates and partials cache.
  • ich.grabTemplates(): Looks for any <script type="text/html"> tags to make templates out of. Then removes those elements from the dom (this is the method that runs on document ready when ich first inits).
  • ich.refresh(): Just clears all then grabs new templates. This could be useful for pages loaded with ajax that contain other templates.

Examples

Full Working Example

<!DOCTYPE html>
<html>
  <head>
    <title>ICanHaz.js Demo</title>
    <script src="test/jquery-1.4.4.min.js"></script>
    <script src="ICanHaz.min.js" ></script>

    <script id="user" type="text/html">
      <li>
        <p>Hi I&apos;m <a href="http://twitter.com/{{ twitter }}">@{{ twitter }}</a></p>
        <p>I work for {{ employer }} as a {{ job_title }}.</p>
      </li>
    </script>

    <script type="text/javascript">
      // when the DOM's ready
      $(document).ready(function () {
        // Add a simple click handler for the "add user" button.
        $('#add_user').click(function () {
          var user_data, user;

          // Build a simple user object, in a real app this
          // would probably come from a server somewhere.
          // Otherwise hardcoding here is just silly.
          user_data = {
            name: "Henrik Joreteg",
            twitter: "HenrikJoreteg",
            employer: "&yet",
            job_title: "JS nerd"
          };

          // Here's all the magic.
          user = ich.user(user_data);

          // Append it to the list, TADA!
          // Now go do something more useful with this.
          $('#user_list').append(user);
        });
      });
    </script>

    <style>
      body {
        font-family: Helvetica;
      }
    </style>
  </head>
  <body>
    <h1>ICanHaz.js Demo</h1>
    <h3>User List</h3>
    <button id="add_user">Add User</button>
    <ul id="user_list"></ul>
  </body>
</html>

Pulling Templates from a Remote Server

$.getJSON('/myserver/templates.json', function (templates) {
    $.each(templates, function (index, template) {
        ich.addTemplate(template.name, template.template);
    });
});

There is also the icanhaz.load plugin, which makes this task a little handier

ich.load('template', function() {
  // Called when template has been loaded
  // and is ready for use
  ich.template({
      foo: 'bar'
  });
});

Credits

ICanHaz.js was created by @HenrikJoreteg.

Other contributors

Browser Support

This has been tested in a pile of browsers thanks to BrowserStack. The only thing that may fail is the automatic grabbing of templates if you're in a browser that doesn't support DOMContentLoaded and you didn't include jQuery or Zepto. Other than that all browsers should work. If not, it's a bug.

Changelog

  • 0.10.2
    • Now supports RequireJS.
  • 0.10.1
    • Minor bug fix.
  • 0.10
    • Now includes versions without Mustache bundled.
    • Mustache.js is now a submodule to simplify building different bundles with different versions.
    • If you're using a CommonJS system, you can now do require("ICanHaz").
    • There is no longer any distinction between partials and regular templates. They're all in the same cache. This means that you can use a template as a partial in one place and by itself in another place without having to duplicate the template.
    • Builds the minified versions with Google's Closure web API so you don't need any local minifier installed to run "make".
    • jQuery and Zepto are now optional.
  • 0.9
    • Made templates and partials caches public for easy inspection in console.
    • Trimmed down overall size and removing showAll method.
    • Now compiled with Google's Closure compiler
  • 0.8
    • Now works with either jQuery or Zepto.js
  • 0.7
    • Now includes mustache.js so the only dependency is jQuery or Zepto.
    • Attaches ich to window directly.
    • Added showAll, clearAll, grabTemplates, refresh to public api.
  • 0.6.1
    • Bug fix in trimming templates retrieved from <script> tags.
  • 0.6
    • Added support for partials