Antes de começarmos, uma pergunta: quem nunca implementou um “template” nos moldes do código abaixo?
html = '<li class="clearfix">'; html += ' <div class="foto">'; html += ' <a href="' + item.permalink + '">'; html += ' <img src="' + item.thumb + '" width="180" height="124" alt="' + item.titulo + '">'; html += ' </a>'; html += ' </div>'; html += ' <span>'; html += item.titulo; html += ' </span>'; html += '</li>';
Se você ainda faz isso, chegou a hora de parar.
Neste artigo abordaremos a implementação JavaScript para templates Mustache. A sintaxe já foi portada para diferentes linguagens, incluindo Ruby, Python, PHP e Java. Para uma lista completa, visite o site oficial do projeto.
A principal diferença do Mustache para outras formas de templating no client-side (jQuery Template, por exemplo) é que ele não aceita lógica, como declarações condicionais, loops etc. Pode não parecer, mas isso é muito bom: um template não deveria conter nenhuma lógica, já que é apenas uma camada de apresentação.
Por ser “logic-less”, o Mustache.js é bem enxuto, pesando 8.5kb em sua versão minificada.
Criando um template
Os templates Mustache esperam receber dados no formato JSON. Os dados podem ser textos, variáveis e até mesmo funções. Vamos começar com um exemplo básico onde renderizamos um template de um artigo.
var item = { titulo: "Templates client-side com Mustache.js", permalink: "https://tableless.com.br/templates-client-side-com-mustache-js" thumb: "mustache.jpg", }, output = Mustache.render("<h1>{{title}}</h1><p>{{abstract}}</p>", item); console.log(output);
O método render é o responsável por retornar o template com os dados formatados. O primeiro parâmetro é o template e o segundo o objeto JSON com os dados que devem ser aplicados.
Os dados armazenados no JSON são representados no template utilizando duas chaves (bigode-bigode) com o nome da propriedade.
Uma boa prática é separar o template do seu código JavaScript armazenando a estrutura do template em uma tag script do tipo “text/template”.
<script id="item-template" type="text/template"> <li class="clearfix"> <div class="foto"> <a href="{{ item.permalink }}"> <img src="{{ item.thumb }}" width="180" height="124" alt="{{ item.titulo }}"> </a> </div> <span>{{ item.titulo }}</span> </li> </script>
Os dados do template são utilizados buscando o conteúdo HTML da tag.
var item = { titulo: "Templates client-side com Mustache.js", permalink: "https://tableless.com.br/templates-client-side-com-mustache-js" thumb: "mustache.jpg", }, template = document.getElementById('article-template').innerHTML; output = Mustache.render(template, item); console.log(output);
Nos exemplos a seguir, para facilitar a leitura, vamos continuar utilizando os templates diretamente no método render do Mustache.
Lógica?
É verdade que não existem tags para condicionais e loops, mas sua implementação é possível utilizando caracteres especiais nas tags do template.
No exemplo abaixo temos um objeto tableless que armazena um conjunto de artigos. Para realizarmos um loop nos artigos, basta utilizar o caractere ‘#’ seguido do nome da propriedade como uma tag do nosso template. Dentro do bloco #artigos temos acesso às propriedades de cada artigo.
var tableless = { 'artigos': [ { 'titulo': 'Templates client-side com Mustache.js' }, { 'titulo': 'Zepto.js: JavaScript peso-leve' }, { 'titulo': 'JavaScript: o que fazer e aprender para se tornar um dev melhor?' } ] }, output = Mustache.render('{{#artigos}}<li>{{titulo}}</li>{{/artigos}}', tableless); console.log(output);
A lógica de condicionais é tratada no próprio objeto. Assim como o loop acima, basta adicionarmos o caracatere ‘#’ a uma variável booleana para exibir ou não um conteúdo de acordo com o valor da mesma:
var tableless = { 'artigos': [ { 'titulo': 'Templates client-side com Mustache.js', publicado: true }, { 'titulo': 'Zepto.js: JavaScript peso-leve', publicado: true }, { 'titulo': 'JavaScript: o que fazer e aprender para se tornar um dev melhor?', publicado: false } ] }, output = Mustache.render('{{#artigos}}{{#publicado}}<li>{{titulo}}</li>{{/publicado}}{{/artigos}}', tableless); console.log(output);
Funções
As tags podem conter funções como valores. Por exemplo, no objeto abaixo, a função buzz soma a quantidade de likes, tweets e comentários de um artigo.
var artigo = { 'titulo': 'Templates client-side com Mustache.js', 'likes': 32, 'tweets': 22, 'comentarios': 45, 'buzz': function () { return this.likes + this.tweets + this.comentarios; } }, output = Mustache.render('<h1>{{titulo}} <small>{{buzz}}</small></h1>', artigo); console.log(output);
Quando o valor de uma tag é representado por uma função e o caractere ‘#’ é utilizado, a mesma pode retornar uma outra função que recebe dois parâmetros: o texto do bloco do template e uma versão especial do método render para ser executada no contexto do bloco.
var artigo = { 'titulo': 'Templates client-side com Mustache.js', 'url': 'https://tableless.com.br/templates-client-side-com-mustache-js', 'permalink': function () { return function (text, render) { return '<a href="' + this.url + '" class="permalink">' + render(text) + '</a>'; } } }, output = Mustache.render('<p>{{#permalink}}{{titulo}}{{/permalink}}</p>', artigo); console.log(output);
Esse tipo de função é chamado de helper, podendo conter lógicas exclusivas da view e evitando a repetição de trechos de template comuns.
Parciais
Outra maneira de evitar repetição é utilizando templates parciais. Parciais são referenciados com o caractere ‘>’ dentro da tag.
var tableless = { 'artigos': [ { 'titulo': 'Templates client-side com Mustache.js' }, { 'titulo': 'Zepto.js: JavaScript peso-leve' }, { 'titulo': 'JavaScript: o que fazer e aprender para se tornar um dev melhor?' } ] }, parciais = {'artigo': '
Os templates parciais devem ser informados como terceiro parâmetro do método render e seu valor deve ser um objeto contendo um ou mais templates. A chave informada no objeto é o nome da tag que será utilizada no template principal.
E tem mais
Além das funcionalidades apresentadas neste artigo, a biblioteca Mustache.js ainda trás alguns outros benefícios como templates compilados, delimitadores personalizados e seções invertidas. Para saber mais, visite a página do projeto no GitHub.
E você? Utiliza alguma solução de templating no client-side? Compartilhe nos comentários!