Neste post, vamos aprender sobre o binding handler “template”, como é preenchido, através do elemento DOM, de acordo com sua instância de “viewModel”. Um template é uma maneira simples para construir uma estrutura de interface, com repetição ou aninhada em blocos. Com ele é possível reaproveitar uma estrutura, por exemplo.

Parâmetros

* name: Nome do elemento que contem o template que deve ser utilizado.
* data: Objeto que fornece os dados para o template renderizar.
* if: O template só será processado, se a expressão especificada seja válida. Isso é útil para prever um “observable” nulo, e gerar um possível erro na renderização.
* foreach: Array para renderizar no template.
* as: Quando utilizado com o binding handler “foreach”, é possível especificar um “alias”, para acessar uma propriedade de forma mais amigável. Geralmente é utilizado para acessar estruturas aninhados, para evitar algo como: “$parent”, “$parents[1]”, “$parents[2]”, etc.
Isso facilita a manutenção, e identificação de eventuais erros, ao alterar a estrutura da sua classe de “viewModel”.

Também podemos utilizar funções de callback; Todas as funções são invocadas na “renderização” do template:
* afterRender: Executa depois da renderização.
* afterAdd e beforeRemove: Estes métodos são invocados, ao adicionar/remover algum elemento dinamicamente no template, respectivamente.

Vamos fazer um exemplo simples. Primeiro vamos definir um elemento, para utilizar como template:

<script type="text/html" id="pessoa-template">
    <h3 data-bind="text: $data"></h3>
</script>

Acima temos uma tag “script”, do tipo “text/html” (isso é importante, sem essa especificação, não funcionará como esperado). Também temos um id, sem ele não é possível referenciar o template.
No corpo do script, temos apenas uma tag “h3”, que por sua vez, coloca o valor de “text” da tag. No caso, qualquer valor primitivo, que informarmos. Para isso, utilizamos a variável de contexto “$data”.

Agora que temos o template definido, podemos utilizar ele. Para tal:

<div data-bind="template: { name: 'pessoa-template', data: nome }"></div>

Acimas temos uma tag “div”, que utiliza o binding handler “template”. Utilizamos apenas 2 parâmetros: “name” e “data”. O name é para referenciar o template que definimos no passo anterior. Já o parâmetro “data”, fizemos referencia a propriedade “nome” da classe “ViewModel” (vamos cria-la abaixo). Com isso, já é possível utilizar um template de uma forma bem simples.

E por último, vamos definir nossa classe “ViewModel”:

function ViewModel() {
    this.nome = "André Btoe";
}
ko.applyBindings(new ViewModel());

Acima temos uma classe, com o nome “ViewModel”. Com a propriedade (não “observable”) chamada “nome”.
Veja o primeiro exemplo: http://jsfiddle.net/andrebtoe/dp14pg1q/.

A ordem de declaração de template / chamada do template, não tem importância. Nesse exemplo podemos inverter a ordem de declaração, que ainda sim, continuaria funcionando.

Utilizando foreach

Vamos utilizar o binding handler “foreach”. A classe “ViewModel” deve ficar como abaixo:

function ViewModel() {
    this.nomes = ["André Btoe", "José", "Maria"];
}
ko.applyBindings(new ViewModel());

Acima trocamos a propriedade “nome” por “nomes”, e em vez de ser uma string, é um array de string. Com alguns valores definidos.
Já no html, devemos trocar o parâmetro “data” pelo binding handler “foreach”.

<div data-bind="template: { name: 'pessoa-template', foreach: nomes }"></div>

Veja o exemplo: http://jsfiddle.net/andrebtoe/dp14pg1q/1

Usando alias com foreach

Podemos utilizar um “apelido” quando o “foreach” é empregado. Vamos modificar o template:

<script type="text/html" id="pessoa-template">
    <h3 data-bind="text: pessoaNome"></h3>
</script>

Acima, definimos que vamos utilizar o alias “pessoaNome”, porem, o template não sabe se é um alias ou não; Apenas que deverá existir neste contexto.
E na chamada do template, vamos deixar desta forma:

<div data-bind="template: { name: 'pessoa-template', foreach: nomes, as: 'pessoaNome' }"></div>

Acima, definimos o parâmetro “as”, para informar um “apelido”, com o respectivo valor “pessoaNome”. Esse é um exemplo muito simples e de pouca utilidade. Mais adiante mostro utilidade neste recurso.

Veja o exemplo http://jsfiddle.net/andrebtoe/dp14pg1q/2

Utilizando métodos de callback

Às vezes você pode querer executar algo no pós-processamento sobre os elementos DOM gerados. Creio que na maioria das vezes o ideal é criar um binding handler, porem, se você quer apenas acessar os elementos DOM, pode usar alguns métodos de callback. Como vamos ver. Vamos modificar a chamada do template.

<div data-bind="template: { name: 'pessoa-template', foreach: nomes, as: 'pessoaNome', afterRender: posRender }"></div>

Acima, adicionamos o parâmetro “afterRender”. Ao finalizar a renderização de cada item do array de pessoas, o KO irá invocar o método definido como parâmetro de “afterRender”; No caso, o método se chama “posRender”.
E na classe de “ViewModel”, vamos definir o método “posRender”.

function ViewModel() {
    this.nomes = ["André Btoe", "José", "Maria"];
    this.posRender = function(elements, valor){
    	console.log(elements[1].innerText);
	console.log(elements[1].innerText);
    };
}
ko.applyBindings(new ViewModel());

Acima adicionamos o método “posRender”, que por sua vez, tem 2 argumentos. O primeiro tem os elementos do DOM. E o segundo tem o item do array corrente. Na implementação do método, temos apenas 2 “consoles”, para “printar” o mesmo valor. O primeiro “printa” o valor de “innerText” (como não estamos utilizando nenhum framework de manipulação do DOM, como jQuery, Zeptojs, etc. Vamos utilizar a propriedade nativa do JavaScript), da tag “h3”; E o segundo parâmetro, “printa” o valor do item corrente, provido pela renderização do template.

Veja o exemplo http://jsfiddle.net/andrebtoe/dp14pg1q/4

Vamos criar um exemplo mais elaborado. O exemplo é de um modelo hierárquico, onde iremos ver uma forma de reaproveitar uma estrutura de template.
No modelos teremos dados sobre pessoas, e seus filhos, de forma aninhada. Sem limites de níveis.
Primeiro, vamos mudar a classe “ViewModel”, como segue.

var ModeloHierarquicoViewModel = function(nome, espacamento, filhos){
  	var self = this;
  	self.nome = nome;
	self.espacamento = (espacamento || 0) + "px";
    self.filhos = filhos;
};

function ViewModel() {
    var self = this;
    self.pessoas = [
        new ModeloHierarquicoViewModel("José", 0, [
            new ModeloHierarquicoViewModel("Maria", 25),
            new ModeloHierarquicoViewModel("João", 25)
        ]),
        new ModeloHierarquicoViewModel("Maria", 0, [
            new ModeloHierarquicoViewModel("Felipe", 25, [
            	new ModeloHierarquicoViewModel("Felipe Jr.", 50)
            ])
        ]),
        new ModeloHierarquicoViewModel("Henrique", 0, [
        	new ModeloHierarquicoViewModel("Marcos", 25)
        ]),
        new ModeloHierarquicoViewModel("Mario")
    ];
}
ko.applyBindings(new ViewModel());

Primeiro, definimos uma classe com o nome “ModeloHierarquicoViewModel”, ela tem 3 argumentos no construtor:
* nome: Nome da pessoa ou filho, net, bisneto, etc.
* espacamento: Número que definirá o valor de espaçamento, no nível de hierarquia a qual a instância pertence.
* filhos: Array do tipo “ModeloHierarquicoViewModel”, que deve conter (ou não) os filhos, netos, etc.

E temos a classe “ViewModel”, que tem um array simples, com o nome “pessoas”, e algumas instancias da classe “ModeloHierarquicoViewModel”.
Agora vamos modificar o html.

<script type="text/html" id="hierarquia-template">
    <h3 data-bind="text: nome, style: {marginLeft: espacamento}"></h3>
    <!-- ko template: { name: 'hierarquia-template', if: filhos, foreach: filhos } -->
    <!-- /ko -->
</script>

<div data-bind="template: { name: 'hierarquia-template', foreach: pessoas }"></div>

Acima, criamos uma tag de “script”, com o id “hierarquia-template”; No corpo da tag “script”, temos um tag “h3”, que mostra o valor da propriedade nome (pessoa, filho, neto, etc), e colocar uma “margin-left” do css, para a tag “h3”. Logo abaixo estamos definindo um elemento virtual, que utiliza o binding handler “template”; Ele invoca o template de nome “hierarquia-template”, novamente, só que desta vez, usa como argumento para o “foreach”, a propriedades “filhos”. Assim, ele só irá parar quando o array de filhos for null ou vazio. E para garantir que não ocorra erros, no caso da propriedade “filhos” estar como null; Definimos com o parâmetro “if”, passando como argumento “filhos”.

Veja o exemplo http://jsfiddle.net/andrebtoe/dp14pg1q/10/.

Espero ter ajudado!
Até a próxima!