.NET - Apresentando o Knockout (KO)


Você conhece o Knockout (KO) ?

Na definição oficial - http://knockoutjs.com/ o KnockoutJS é uma biblioteca JavaScript (Open Source) que simplifica a construção de interfaces gráficas dinâmicas, usando o padrão MVVM. (Model-View-ViewModel)

Estruturalmente, uma aplicação que usa o padrão MVVM consiste basicamente em três componentes principais: o Modelo, a Visão(View) e a ViewModel.
   O Model ou modelo é a entidade que representa o conceito do negócio, que pode ser desde uma simples entidade de cliente para uma entidade complexa de ações comerciais;

   A View ou Visão é o controle gráfico ou um conjunto de controles responsáveis pela apresentação do modelo de dados ao usuário. Uma visão pode ser uma janela WPF, uma 
página do Silverlight, ou apenas um modelo de controle de dados XAML.

   O ViewModel - É realmente quem faz a diferença; O ViewModel contém a lógica de interface do usuário, os comandos, os eventos, e uma referência para o modelo. Na MVVM, 
o ViewModel não é responsável por atualizar os dados exibidos na interface do usuário graças ao poderoso motor de vinculação de dados fornecidos pelo WPF e Silverlight, a 
ViewModel não precisa fazer isso. Como o View é um observador do ViewModel, tão logo o ViewModel sofra mudanças, a Interface do Usuário se atualiza de forma automática. 
Para que isso ocorre a ViewModel tem que implementar a interface INotifyPropertyChanged e acionar o evento PropertyChanged.

Dessa forma a Knockout (KO) é uma biblioteca JavaScript , da mesma forma que a jQuery, que o ajuda a criar interfaces ricas com um correspondente modelo de dados.

A seguir temos alguns dos recursos presentes na Knockout :

Você pode baixar o KnockoutJS neste link: http://knockoutjs.com/downloads/index.html

Existe um tutorial on-line rápido que pode ser acessado neste link: http://learn.knockoutjs.com/

Para você se familiarizar com os recursos do KO neste artigo eu vou explicar como criar uma lista de Contatos com opção para incluir, atualizar e deletar itens da lista usando o KO.

Como é o nosso primeiro contato com o KO não vamos usar um banco de dados para persistência. No próximo artigo eu irei mostrar como usar o KO com banco de dados em uma aplicação ASP .NET MVC.

Usando Knockout

Vamos criar um ambiente para trabalhar com a biblioteca KO em uma aplicação HTML que permite gerenciar uma lista de contatos.

Crie uma pasta em sua máquina local para onde você deve copiar os arquivos javascript que serão usado no exemplo.

Eu vou criar a pasta Macoratti_KO e copiar os seguintes arquivos javascript nesta pasta:

Abra um editor de textos qualquer e crie um arquivo de estilos com extensão .css chamado style.css como seguinte conteúdo:

.list{font:10pt Tahoma; color:#000; background:#9bbb59;}
.head{background:#9bbb59; }
.row{background:#e6eed5; }
.boldtext{font-weight:bold}
.editing { display: none; }
.hiden{display: none;}
.editing_input {display: block;}

Abra um editor de textos qualquer, eu estou usando o NotePad++, e crie um arquivo HTML com o nome MacorattiKO.htm e no início do arquivo declare a utilização das bibliotecas JavaScript e do arquivo de estilo conforme abaixo:

<script type="text/javascript" src="jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="knockout-2.2.1.js"></script>
<link href="style.css" rel="stylesheet" type="text/css" />

Vamos continuar a definir o código do arquivo HTML. Na tag body eu vou adicionar uma tabela com a lista de com três colunas (Nome Completo, Telefone e coluna de opções).

A tabela irá conter itens dos contatos, cada item de contato tem duas linhas, uma linha de dados de contato de exibição e outra linha oculta para editar dados de contato.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="jquery-1.8.2.min.js" type="text/javascript"></script>
    <script src="knockout-2.2.1.js" type="text/javascript"></script>
    <link href="style.css" rel="stylesheet" type="text/css" />

</head>
<body>
    <table class="list" cellpadding="5" cellspacing="1">
        <thead>
            <tr class="head">
                <th>
                    Nome
                </th>
                <th>
                    Telefone
                </th>
                <th>
                </th>
            </tr>
        </thead>
        <tbody data-bind="foreach: Contatos">
            <tr  class="row" data-bind="css: { editing: isEditing }">
                <td class="boldtext">
                    <span data-bind="text:Nome"></span>
                </td>
                <td>
                    <span data-bind="text: Contato"></span>
                </td>
                <td>
                    <a href="#" data-bind="click: $parent.removerContato">Deletar</a>
                    <a href="#" data-bind="click: editarContato">Editar</a>
                </td>
            </tr>
            <tr  class="row" data-bind="visible: isEditing">
                <td>
                    Nome:<input data-bind="value: Nome" />
                    SobreNome:<input data-bind="value: Sobrenome" />
                </td>
                <td>
                    <input data-bind="value: Contato" />
                </td>
                <td>
                    <a href="#" data-bind="click: atualizarContato">Concluído</a>
                </td>
            </tr>
        </tbody>
        <tfoot>
            <form data-bind="submit: adicionarContato">
            <tr class="row">
                <td>
                    Nome:<input data-bind="value: Nome" />
                    SobreNome:<input data-bind="value: Sobrenome" />
                </td>
                <td>
                    <input data-bind="value: Contato" />
                </td>
                <td>
                    <input type="submit" value="Adicionar" />
                </td>
            </tr>
            </form>
        </tfoot>
    </table>

A aparência desta página HTML pode ser vista abaixo (aberta no navegador Chrome):

A aplicação ainda não esta funcional. Vamos dar vida a esta pequena aplicação incluindo o código JavaScript abaixo no arquivo HTML:

  <script type="text/javascript">
        function _contato(Nome, Sobrenome, Contato) {
            this.Nome = ko.observable(Nome);
            this.Sobrenome = ko.observable(Sobrenome);
            this.Nome = ko.computed(function () {
                return this.Nome() + ' ' + this.Sobrenome();
            }, this);
            this.Contato = ko.observable(Contato);

            this.isEditing = ko.observable(false);

            this.editarContato = function (event) {
                this.isEditing(true);
            }

            this.atualizarContato = function (task) {
                this.isEditing(false);
            }
        }

        var viewModel = function () {
            self = this;
            var listaContatos = [new _contato("Jose Carlos", "Macoratti", "99850-9652"),
                                new _contato("Janice", "Lima", "8850-0011"),
                                new _contato("Jefferson", "Bueno", "7850-9080")];
            self.Nome = ko.observable();
            self.Sobrenome = ko.observable();
            self.Contato = ko.observable();
            self.Contatos = ko.observableArray(listaContatos);

            // remove contato
            self.removerContato = function (_contato) { self.Contatos.remove(_contato) }

            //inclui contato
            self.adicionarContato = function () {
                var con = new _contato(self.Nome(), self.Sobrenome(), self.Contato())
                self.Contatos.push(con);
                self.Nome("");
                self.Sobrenome("");
                self.Contato("");
            };
        }

        ko.applyBindings(new viewModel());
    
    </script>

Dessa forma o código completo do arquivo HTML MacorattiKO.html deverá estar assim:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="jquery-1.8.2.min.js" type="text/javascript"></script>
    <script src="knockout-2.2.1.js" type="text/javascript"></script>
    <link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <table class="list" cellpadding="5" cellspacing="1">
        <thead>
            <tr class="head">
                <th>
                    Nome
                </th>
                <th>
                    Telefone
                </th>
                <th>
                </th>
            </tr>
        </thead>
        <tbody data-bind="foreach: Contatos">
            <tr  class="row" data-bind="css: { editing: isEditing }">
                <td class="boldtext">
                    <span data-bind="text:Nome"></span>
                </td>
                <td>
                    <span data-bind="text: Contato"></span>
                </td>
                <td>
                    <a href="#" data-bind="click: $parent.removerContato">Deletar</a>
                    <a href="#" data-bind="click: editarContato">Editar</a>
                </td>
            </tr>
            <tr  class="row" data-bind="visible: isEditing">
                <td>
                    Nome:<input data-bind="value: Nome" />
                    SobreNome:<input data-bind="value: Sobrenome" />
                </td>
                <td>
                    <input data-bind="value: Contato" />
                </td>
                <td>
                    <a href="#" data-bind="click: atualizarContato">Concluído</a>
                </td>
            </tr>
        </tbody>
        <tfoot>
            <form data-bind="submit: adicionarContato">
            <tr class="row">
                <td>
                    Nome:<input data-bind="value: Nome" />
                    SobreNome:<input data-bind="value: Sobrenome" />
                </td>
                <td>
                    <input data-bind="value: Contato" />
                </td>
                <td>
                    <input type="submit" value="Adicionar" />
                </td>
            </tr>
            </form>
        </tfoot>
    </table>

    <script type="text/javascript">
        function _contato(Nome, Sobrenome, Contato) {
                                    this.Nome = ko.observable(Nome);
                                    this.Sobrenome = ko.observable(Sobrenome);
                                    this.Nome = ko.computed(function () {
                                    return this.Nome() + ' ' + this.Sobrenome();
                                    }, this);
            this.Contato = ko.observable(Contato);
            this.isEditing = ko.observable(false);
            this.editarContato = function (event) {
                this.isEditing(true);
            }

            this.atualizarContato = function (task) {
                this.isEditing(false);
            }
        }

        var viewModel = function () {
            self = this;
     
             var listaContatos = [new _contato("Jose Carlos", "Macoratti", "99850-9652"),
                                           new _contato("Janice", "Lima", "8850-0011"),
                                           new _contato("Jefferson", "Bueno", "7850-9080")];

            self.Nome = ko.observable();
            self.Sobrenome = ko.observable();
            self.Contato = ko.observable();
            self.Contatos = ko.observableArray(listaContatos);

            //remover contato
            self.removerContato = function (_contato) { self.Contatos.remove(_contato) }

            //incluir contato
            self.adicionarContato = function () {
                var con = new _contato(self.Nome(), self.Sobrenome(), self.Contato())
                self.Contatos.push(con);
                self.Nome("");
                self.Sobrenome("");
                self.Contato("");
            };
        }

        ko.applyBindings(new viewModel());
    
    </script>
</body>
</html>

Executando arquivo no navegador Chrome iremos obter:

Clicando no botão Editar podemos atualizar os dados.

A o clicar no botão Adicionar os dados do TextBox para Nome e Sobrenome e Telefone serão adicionados e exibidos na tabela conforme mostra a sequência das figuras abaixo:

]

O código acima usa os seguintes recursos do KO:

O Knockout adiciona rastreamento de dependências por meio de observables, que são objetos que podem notificar os ouvintes quando valores subjacentes foram alterados (semelhante ao conceito da interface INotifyPropertyChanged na tecnologia XAML). O KO implementa as propriedades observable dispondo propriedades de objetos com uma função personalizada chamada observable.

O exemplo também utiliza os seguintes tipos de vinculação:

Assim temos as seguintes associações mais comuns no KO:

Exemplo Cenário
text: modelo Associa a propriedade (modelo) ao valor do texto do elemento de destino. Usada frequentemente para elementos somente leitura como spans.
visible: Estoque Associa a propriedade de valor (Estoque) à visibilidade do elemento de destino. O valor da propriedade será avaliado como verdadeiro ou falso.
value: preco Associa o valor da propriedade (preco) ao elemento de destino. Usada frequentemente com elementos input, select e textarea.
css: className Associa o valor da propriedade (className) ao elemento de destino. Usado frequentemente para definir ou alternar nomes de classes css para elementos DOM.
checked: isNaCesta Associa o valor da propriedade (isNaCesta) ao elemento de destino. Usado para elementos checkbox.
click: salvarDados Adiciona um manipulador de eventos para a função JavaScript associada (salvarDados) quando o usuário clica no elemento DOM. Funciona em qualquer elemento DOM, mas é usado frequentemente para elementos button, input e a.
attr: {src: photoUrl, alt: name} Associa qualquer atributo especificado para o elemento DOM ao objeto de origem. Usado frequentemente quando as outras associações internas não cobrem o cenário, como com o atributo src de uma marcação img.

A biblioteca KO se concentra na vinculação de dados usando a tag data-bind que vincula os dados aos elementos HTML da interface.

Dessa forma o código usa : data-bind=”foreach: Contatos” que permite associar de forma declarativa as propriedades do ViewModel com os elementos DOM.

O código <tr class=”row” data-bind=”css: { editing: isEditing }”> é usado para exibir dados usando o estilo 'row', onde a variável observable 'isEditing' do tipo booleana atribui o estilo à linha caso contrário ela será escondida.

Para exibir os valores de texto atribuídos usamos a vinculação de texto com: <span data-bind=”text:Nome”>

Como não implementamos a persistência usando um banco de dados os dados incluídos serão perdidos no refresh da página.

Embora bem simples o exemplo mostra os recursos básicos do KO.

No próximo artigo irei mostrar como usar os recursos do KO em um aplicação ASP .NET MVC4 com Entity Framework.

Pegue os arquivos aqui: Macoratt_KO.zip

Mar 1:23 Ora, estava na sinagoga um homem possesso dum espírito imundo, o qual gritou:

Mar 1:24 Que temos nós contigo, Jesus, nazareno? Vieste destruir-nos? Bem sei quem és: o Santo de Deus.

Mar 1:25 Mas Jesus o repreendeu, dizendo: Cala-te, e sai dele.

Mar 1:26 Então o espírito imundo, convulsionando-o e clamando com grande voz, saiu dele.

Mar 1:27 E todos se maravilharam a ponto de perguntarem entre si, dizendo: Que é isto? Uma nova doutrina com autoridade! Pois ele ordena aos espíritos imundos, e eles lhe obedecem!

Mar 1:28 E logo correu a sua fama por toda a região da Galiléia.

Referências:


José Carlos Macoratti