Monday, November 2, 2015

Handlebars Essentials

Handlebars is a good little helper library for client-side Javascript development. It allows creating templates MVC-style in an HTML page and binding to data sources.
Here are my Handlebars code bits as a reference.

1. A page with a target element.
<!DOCTYPE html>
<html>
<head>
    <script src="lib/jquery/jquery-2.1.4.min.js"></script>
    <script src="lib/handlebars/handlebars-v4.0.4.js"></script>
    <script src="lib/material-design-lite/material.min.js"></script>
    <script src=“js/MV.js"></script>
    <script src="js/app.js"></script>
</head>

<body>
    <div id="main" class=“mail-class">
    </div>
</body>
</html>


2. A page template. This is the main container of all visible elements.
<script type="text/x-handlebars-template" id="index-template">
  <div class="header">
    <div class="title">
        <h1>{{siteTitle}}</h1>
    </div>
    <div>
      <nav class="navigation">
        <a href="?option=page1">{{page1Name}}</a>
        <a href="?option=page2">{{page2Name}}</a>
        <a href="?option=page3">{{page3Name}}</a>
      </nav>
    </div>
  </div>
  <div class="content">
    <div id="theItems">
    </div>
    <footer class="footer">
    </footer>
  </div>
</script>

3. Partial templates (partials). These simplify code structure by allowing to refactor templates as components into separate source files.
<script type="text/x-handlebars-template" id="item-template">
  <div class="item-card” data-item-id="{{id}}">
    <div class="card-title" style="background: url('images/{{image}}') center 15% no-repeat #46B6AC;">
        <h2>{{name}}</h2>
    </div>
    <div class="card-border">
      <button class="button-one">
        {{language.one}}
      </button>
      <button class="button-two">
        {{language.two}}
      </button>
    </div>
    {{#if moreInfo}}
      {{#if isCorrect}}
        <span class="result-good">{{language.correct}}</span>
      {{else}}
        <span class="result-bad">{{language.incorrect}}</span>
      {{/if}}
    {{/if}}
  </div>
</script>

4. A template-iterator. This implements iteration through items and also references a partial template for rendering an individual item.
<script type="text/x-handlebars-template" id="items-template">
  {{#each items}}
    {{> item language=@root.language}}
  {{else}}
    {{language.noItemsFoundMessage}}
  {{/each}}
</script>

5. A script (app.js) to register partials and to render a page. Also binds page elements to event handlers.
(function() {

  $(function () {
    registerPartials();
    renderPage();
    renderItems();
  });

  function registerPartials() {
    Handlebars.registerPartial("item", $("#item-template").html());
  }

  function renderPage() {
    var template = $("#index-template").html();
    var compiled = Handlebars.compile(template);
    var rendered = compiled(window.language);
    $("#main").html(rendered);
    $("#languageSwitch").click(function() {
      MV.switchLanguage();
    });
  }

  function renderItems() {
    var template = $("#items-template").html();
    var compiled = Handlebars.compile(template);
    var rendered = compiled({ items: MV.items, language: window.language });
    $("#theItems").html(rendered);
    attachItemsButtons();
  }

  function attachItemsButtons() {
    $(".button-one").click(function() {
      var id = $(this).closest(".item-card").data("item-id");
      MV.itemActionOne(id);
      renderItems();
    });

    $(".button-two").click(function() {
      var id = $(this).closest(".item-card").data("item-id");
      MV.itemActionTwo(id);
      renderItems();
    });
  }

})();

6. Helpers. These are global functions that can be invoked out of any context. “this” will refer to a current context.
For example, in helpers.js:
Handlebars.registerHelper("getLanguageFilter", function(langId) {
  var queryParam = "";
  if (langId) {
    queryParam = "&language=" + Handlebars.escapeExpression(langId);
  }
  return new Handlebars.SafeString(queryParam);
});

Handlebars.registerHelper("generatePages", function(items) {
  var pages = [];
  var pageCount = Math.ceil(items.length / 10);
  for (var i = 1; i <= pageCount; i++) {
    var link = “?page=" + i;
    pages.push({
      number: i,
      link: link
    });
  }
  return pages;
});

Then, in a page or in a template:
<!-- Navigation -->
<script type="text/x-handlebars-template" id=“navigation-template">
  <div>
      <nav class="mdl-navigation">
          <a href="?filter=ones{{getLanguageFilter langId}}">{{itemsFilterOne}}</a>
          <a href="?filter=twos{{getLanguageFilter langId}}">{{itemsFilterTwo}}</a>
      </nav>
  </div>
</script>
...
<script type="text/x-handlebars-template" id="page-template">
  <ul>
    {{#each (generatePages items)}}
      <li><a href="{{link}}">{{number}}</a></li>
    {{/each}}
  </ul>
</script>

No comments:

Post a Comment