O knockoutjs é um framework modular a ponto de dar condições de implementar um plugin para estender suas funcionalidades, como é o caso do knockoutjs mapping. Esse plugin “mapea” um dado formato em propriedades “observáveis”, normalmente é utilizado o formato JSON, e geralmente os dados são providos do servidor, com uma serialização json de um linguagem server side. Muito útil para trabalhar com os dados que devem ser tratados no servidor e visualizados na interface. Vamos a um exemplo bem simples, para entendermos os parâmetros disponíveis. Você pode baixar o plugin nesse link: https://github.com/SteveSanderson/knockout.mapping
ko.mapping.fromJSON('{"Nome":"João","Idade":20,"Ativo":true}', {}, this);
Parâmetro 1: Dados para ser mapeado (transformados em propriedades observable), no formato JSON.
Parâmetro 2: Personalização do mapeamento, podemos definir uma classe, converter tipos, modificar os dados, entre outros (vou exemplificar mais abaixo).
Parâmetro 3: Instancia de destino do mapeamento, no caso estou estamos “this”, ou seja, a instancia da minha classe Javascript irá incluir as propriedades mapeadas em this, com seus respectivos dados.
Um exemplo de como utilizar o mapping.
function ViewModel(dados){ ko.mapping.fromJSON(dados, {}, this); } window.viewModel = new ViewModel('{"Nome":"João","Idade":20,"Ativo":true}'); ko.applyBindings(window.viewModel);
http://jsfiddle.net/andrebtoe/f9fnqbna/3/
Primeiro definimos a classe ViewModel com um parâmetro no construtor (dados). Em seguida configuramos o mapeamento para instancia da classe. Criamos uma instancia da classe ViewModel passando os dados no construtor que devem ser mapeados; Também definimos que a instancia deve ficar disponível em window.viewModel (assim iremos conseguir visualizar facilmente pelo console do navegador os dados mapeados). E por último utilizamos o método applyBindings do knockoutjs, por padrão no elemento document (já que não defini nenhum, e ele é o padrão), se entrarmos no console e digitar: viewModel.Nome() teremos o seguinte resultado:
Na imagem fica claro que os dados que passamos no construtor foram mapeados, temos disponíveis na instancia window.viewModel: Nome, Idade e Ativo.
Ignorando algumas propriedades
É possível ignorar algumas propriedades no mapping utilizando a propriedade “ignore”, com um array de propriedades:
function ViewModel(dados){ ko.mapping.fromJSON(dados, { ignore: ["Idade"] }, this); } window.viewModel = new ViewModel('{"Nome":"João","Idade":20,"Ativo":true}'); ko.applyBindings(window.viewModel);
http://jsfiddle.net/andrebtoe/dv450cs0/1/
Se tentarmos acessar no console: viewModel.Idade() teremos uma exception, já que a mesma não esta no viewModel.
Incluindo propriedades
Também é possível incluir propriedades que não estão no JSON de origem do mapeamento. O mapping disponibiliza o método: ko.mapping.toJSON, esse método cria um JSON do objeto que for passado como parâmetro, mas somente com os dados que foram mapeados, assim evita enviar dados desnecessários para serialização do JSON, e consequentemente para o servidor (caso você envie esse JSON para servidor), o knockoutjs disponibiliza um método para transformar um objeto em JSON tambem: ko.toJSON, vamos comparar o resultado de ambos:
Primeiro estamos utilizando o método ko.toJSON, repera que o JSON foi criado com dados desnecessários (ao nosso ver), agora vamos ver o resultado do método ko.mapping.toJSON
Fica claro na imagem acima que somente os dados que foram mapeados é que estão presente no JSON novamente.
Voltando ao tópico principal que é sobre a propriedade include, para que ela server?
Simples, com ela é possível adicionar um array de propriedades que não foram mapeadas, assim as propriedades serão convertidas para JSON, como no exemplo abaixo
function ViewModel(dados){ ko.mapping.fromJSON(dados, { include: ["DataNascimento"] }, this); this.DataNascimento = ko.observable(""); } window.viewModel = new ViewModel('{"Nome":"João","Idade":20,"Ativo":true}'); ko.applyBindings(window.viewModel);
http://jsfiddle.net/andrebtoe/sgo6gaaq/1/
Se executarmos o comando ko.mapping.toJSON(viewModel) no console teremos:
Como é possivel ver, temos a propriedade “DataNascimento” incluída no JSON.
Utilizando uma classe para personalização
Também podemos utilizar uma classe especifica, nesse exemplo iremos modificar um pouco o JSON, acrescentando um array de pedidos, que tem: Id e Valor, como segue
function PedidoViewModel(dados){ ko.mapping.fromJS(dados, {}, this); } function ViewModel(dados){ ko.mapping.fromJSON(dados, { Pedidos:{ create: function(options){ return new PedidoViewModel(options.data); } } }, this); } window.viewModel = new ViewModel('{"Nome":"João","Idade":20,"Ativo":true,"Pedidos":[{"Id":1,"Valor":100.90}]}'); ko.applyBindings(window.viewModel);
http://jsfiddle.net/andrebtoe/ct4j20zu/1/
Executando no console do navegador: viewModel.Pedidos() teremos:
Como é possível ver, existe um array observable na propriedade “Pedidos”, cujo o tipo de cada item do array é “PedidoViewModel”; No mapping dentro da classe ViewModel, definimos que a propriedade “Pedidos” irá ter uma personalização de mapeamento, passamos um objeto com o atributo create, que por sua vez recebe uma function com os dados de mapeamento (no nosso caso options.data), essa função deve retornar um objeto, para cada item do array de pedido esse mapeamento será utilizado; Dentro da classe “PedidoViewModel” temos um parâmetro no construtor com os dados de pedido; Executamos o método ko.mapping.fromJS, para mapear os dados para classe, utilizamos o “fromJS” dessa vez por que o que o mapping anterior passa como parâmetro é um objeto e não mas um JSON (ele foi desserializado no primeiro mapping). Por que utilizaríamos esse recurso?
Para adicionar um ou mais comportamentos a cada item de pedido, como um cálculo do total por exemplo, algum método que faça sentido para sua regra de negócio, etc.
Espero ter ajudado!