Setembro de 2015

Volume 30 - Número 9

Pontos de dados: Revisitando a vinculação de dados em JavaScript — agora com Aurelia

Julie Lerman

Julie LermanNunca fui muito uma desenvolvedora de front-end, mas de vez em quando encontro um motivo para brincar com uma interface do usuário. Em minha coluna de junho de 2012, após ver uma apresentação de um grupo de usuários sobre o Knockout.js, pesquisei e escrevi um artigo sobre vinculação de dados OData em um site da Web usando o Knockout (msdn.microsoft.com/magazine/jj133816). Alguns meses depois, escrevi sobre como adicionar o Breeze.js à mistura para simplificar ainda mais a vinculação de dados com Knockout.js (msdn.microsoft.com/magazine/jj863129). Quando escrevi sobre como modernizar um antigo aplicativo de Web Forms do ASP.NET 2.0 e novamente sobre o uso do Knockout, em 2014, alguns amigos pegaram no meu pé, dizendo que o Knockout era “muito 2012”. Novas estruturas, como o Angular, fazem vinculação de dados e muito mais. Eu, no entanto, não estava interessada no “muito mais”, por isso o Knockout me bastava.

Agora estamos em 2015 e, apesar do Knockout ainda ser válido, relevante e fantástico para vinculação de dados em JavaScript, eu queria passar algum tempo com uma das novas estruturas e escolhi o Aurelia (Aurelia.io), porque sei que muitos desenvolvedores da Web estão entusiasmados com ele. O Aurelia foi iniciado por Rob Eisenberg, que estava por trás do Durandal, outra estrutura do cliente JavaScript, embora a ele tenha abandonado a produção para trabalhar na equipe do Angular no Google. Eisenberg acabou saindo do Angular e, em vez de reviver o Durandal, criou o Aurelia desde a base. Há muitas coisas interessantes relacionadas ao Aurelia. Não resta dúvida de que ainda há muito mais a aprender, mas quero compartilhar com vocês alguns truques de vinculação de dados que aprendi, bem como alguns truques a ser usados com o EcmaScript 6 (ES6), a última versão do JavaScript, que se tornou padrão em junho de 2015.

Um API Web ASP.NET para fornecer dados ao meu site da Web

Estou usando o API Web ASP.NET que criei para expor os dados que eu vou manter com o Entity Framework 6. A API da Web tem um punhado de métodos simples que podem ser chamados via HTTP.

O método Get, mostrado na Figura 1, demanda algumas consultas e parâmetros de paginação, e os passa para um método de repositório que recupera uma lista de objetos Ninja e objetos Clan relacionados usando o DbContext do Entity Framework. Obtidos os resultados, o método Get transforma os resultados em um conjunto de objetos de transferência de dados (DTOs) ViewListNinja, definidos em outro lugar. Isso é uma etapa importante devido ao modo como o JSON é serializado, que toma precauções demais com referências circulares do Clan para outros Ninjas. Com os DTOs, evitei que um enorme volume de dados inúteis chegasse à transmissão e moldei os resultados para acompanhar mais de perto o que estava no cliente.

Figura 1 Método Get do API da Web

public IEnumerable<ViewListNinja> Get(string query = "",
  int page = 0, int pageSize = 20)
  {
    var ninjas = _repo.GetQueryableNinjasWithClan(query, page, pageSize);
    return ninjas.Select(n => new ViewListNinja
                              {
                                ClanName = n.Clan.ClanName,
                                DateOfBirth = n.DateOfBirth,
                                Id = n.Id,
                                Name = n.Name,
                                ServedInOniwaban = n.ServedInOniwaban
                              });
    }

A Figura 2 mostra uma exibição dos resultados JSON provenientes do método com base em uma consulta que recuperou dois objetos Ninja.

Resultados JSON de solicitação de API da Web para lista de Ninjas
Figura 2 Resultados JSON de solicitação de API da Web para lista de Ninjas

Consultando a API da Web usando a estrutura Aurelia

O paradigma do Aurelia emparelha um modelo de exibição (de classe JavaScript) e uma exibição (arquivo HTML) e executa a vinculação de dados entre os dois. Assim, tenho um arquivo ninjas.js e um ninjas.html. O modelo de exibição de Ninjas é definido como tendo uma matriz ninja, bem como um objeto Ninja:

export class Ninja {
  searchEntry = '';
  ninjas = [];
  ninjaId = '';
  ninja = '';
  currentPage = 1;
  textShowAll = 'Show All';
  constructor(http) {
    this.http = http;
  }

O método mais importante em ninjas.js é retrieveNinjas, que chama a API da Web:

retrieveNinjas() {
  return this.http.createRequest(
    "/ninjas/?page=" + this.currentPage +
    "&pageSize=100&query=" + this.searchEntry)
    .asGet().send().then(response => {
      this.ninjas = response.content;
    });
  }

Em outro lugar do aplicativo da Web, configurei a URL de base que Aurelia vai encontrar e incorporar à URL da solicitação:

x.withBaseUrl('http://localhost:46534/api');

Vale a pena observar que o arquivo ninjas.js é apenas JavaScript. Se você já usou o Knockout, deve se lembrar de que é preciso configurar o modelo de exibição usando a notação do Knockout para que o Knockout saiba o que fazer quando o objeto estiver vinculado à marcação. O mesmo não acontece com o Aurelia.

A resposta agora inclui a lista de Ninjas e que defini para a matriz ninja do modelo de exibição, que é retornado para a página ninjas.html que disparou a solicitação. Não há nada na marcação que identifique o modelo — isso já foi solucionado porque o modelo está emparelhado com o HTML. Na verdade, a maior parte da página tem padrão HTML e uma parte, JavaScript, com poucos comandos especiais que o Aurelia vai localizar e manipular.

Vinculação de dados, interpolação e formatação de cadeia de caracteres

A parte mais interessante de ninjas.html é o div, que é usado para exibir a lista de ninjas:

<div class="row">
  <div  repeat.for="ninja of ninjas">
    <a href="#/ninjas/${ninja.Id}" class="btn btn-default btn-sm" >
      <span class="glyphicon glyphicon-pencil" />  </a>
    <a click.delegate="$parent.deleteView(ninja)" class="btn btn-default btn-sm">
      <span class="glyphicon glyphicon-trash" />  </a>
    ${ninja.Name}  ${ninja.ServedInOniwaban ? '[Oniwaban]':''}
    Birthdate:${ninja.DateOfBirth | dateFormat}
  </div>
</div>

A primeira marcação específica do Aurelia no código é repeat.for “ninja do ninjas”, que segue um paradigma de ES6 para executar um loop. Isso acontece porque o Aurelia compreende o modelo de exibição e compreende que “ninjas” é a propriedade definida como matriz. A variável “ninja” pode ser qualquer nome, por exemplo “foo”. Ela simplesmente representa cada item na matriz ninja. Agora, é apenas uma questão de iterar ao longo dos itens na matriz ninja. Vá para a marcação em que as propriedades são exibidas, por exemplo “${ninja. Name}”. Esse é um recurso de ES6 que o Aurelia aproveita, chamado de interpolação de cadeia de caracteres. A interpolação de cadeia de caracteres facilita compor cadeias de caracteres com variáveis inseridas, em vez de, por exemplo, por concatenação. Assim, com uma variável name=“Julie”, eu posso escrever em JavaScript:

`Hi, ${name}!`

e ela será processada como “Oi, Julie!” O Aurelia se aproveita da interpolação de cadeia de caracteres do ES6 e infere a associação de dados unidirecional quando encontra essa sintaxe. Sendo assim, a última linha do código, começando com ${ninja.Name} produzirá as propriedades ninja junto com o restante do texto HTML. Se você codificar em C# ou Visual Basic, vale a pena observar que a interpolação de cadeia de caracteres é um novo recurso do C# 6.0 e do Visual Basic 14.

Eu tinha que aprender um pouco mais sobre a sintaxe JavaScript ao longo do caminho, como, por exemplo, a avaliação condicional do booliano ServedInOniwaban, que por acaso tem a mesma sintaxe C# — condition? true: false.

A formatação de data que apliquei à propriedade DateOfBirth é outro recurso do Aurelia que você deve conhecer se já usou XAML. O Aurelia usa conversores de valor. Eu gosto de usar a biblioteca de JavaScript do momento para ajudar com formatação de data e hora, e estou me aproveitando disso na classe date-format.js:

import moment from 'moment';
export class dateFormatValueConverter {
  toView(value) {
  return moment(value).format('M/D/YYYY');
  }
}

Lembre-se de que você precisa de “ValueConverter” no nome da classe.

Na parte superior da página HTML, logo abaixo do elemento <template> inicial, tenho uma referência a esse arquivo:

<template>
  <require from="./date-format"></require>

Agora a interpolação de cadeia de caracteres é capaz de localizar o dateFormat-[ValueConverter] em minha marcação e aplicar na saída, conforme mostrado na Figura 3.

Como exibir todos os Ninjas com propriedades de vinculação unidirecional usando o Aurelia por meio de interpolação de cadeia de caracteres
Figura 3 Como exibir todos os Ninjas com propriedades de vinculação unidirecional usando o Aurelia por meio de interpolação de cadeia de caracteres

Quero destacar outra instância da vinculação no div, mas é vinculação de eventos, e não vinculação de dados. Observe que na primeira marca de hiperlink eu uso uma sintaxe comum, inserindo uma URL no atributo href. Na segunda, no entanto, eu não uso href. Em vez disso, uso click.delegate. O delegate não é um novo comando, mas o Aurelia o trata de uma forma especial que é muito mais eficiente do que um manipulador de eventos onclick padrão. Leia mais sobre isso em bit.ly/1Jvj38Z. Continuarei a me concentrar na vinculação relacionada a dados aqui.

Os ícones Editar levam a uma URL que inclui a ID do ninja. Eu já instruí o mecanismo de roteamento do Aurelia para rotear para uma página chamada Edit.html. Isso está ligado a um modelo de exibição em uma classe denominada Edit.js.

Os métodos mais importantes de Edit.js servem para recuperar e salvar o ninja selecionado. Vamos começar com retrieveNinja:

retrieveNinja(id) {
  return this.http.createRequest("/ninjas/" + id)
    .asGet().send().then(response => {
      this.ninja = response.content;
    });
  }

Isso cria uma solicitação semelhante para minha API da Web, como antes, mas desta vez acrescentando a id à solicitação.

Na classe ninjas.js, vinculei os resultados a uma propriedade de matriz ninja do meu modelo de exibição. Aqui, estou definindo os resultados, um único objeto, como a propriedade ninja do modelo de exibição atual.

Este é o método de API da Web que será chamado, por causa da id que foi anexada ao URI:

public Ninja Get(int id)
  {
    return _repo.GetNinjaWithEquipmentAndClan(id);
  }

Os resultados desse método são muito mais sofisticados do que os retornados para a lista ninja. A Figura 4 mostra o JSON retornado de uma das solicitações.

Resultados JSON de solicitação de API da Web para um único Ninja
Figura 4 Resultados JSON de solicitação de API da Web para um único Ninja

Depois de colocar os resultados em meu modelo de exibição, eu posso vincular as propriedades do ninja aos elementos na página HTML. Desta vez vou usar o comando .bind. O Aurelia infere se algo deveria ser vinculado unidirecional ou bidirecionalmente, ou outra forma. Na verdade, como você viu em ninjas.html, o Aurelia usou o fluxo de trabalho de vinculação subjacente quando apresentado à interpolação de cadeia de caracteres. Aqui, a escolha foi vinculação única e unidirecional. Como estou usando o comando .bind e estou vinculando a elementos de entrada, o Aurelia infere que desejo fazer uma ligação bidirecional. Essa é a opção padrão, que eu poderia substituir usando .one-way ou outro comando em vez de .bind.

Para resumir, vou extrair apenas a marcação relevante, em vez dos elementos adjacentes.

Aqui temos um elemento de entrada associado à propriedade Name da propriedade ninja no modelo que passei de volta à classe modelview:

<input value.bind="ninja.Name" />

E aqui temos outro, desta vez associado ao campo DateOfBirth. Adoro o fato de poder reutilizar facilmente o conversor de valor date-format até mesmo nesse contexto, usando a sintaxe que aprendi anteriormente:

<input  value.bind="ninja.DateOfBirth | dateFormat"  />

Também quero listar os equipamentos na mesma página, da mesma forma como listei os ninjas para que pudessem ser editados e excluídos. Como demonstração, cheguei a exibir a lista como cadeias de caracteres, mas ainda não implementei os recursos Editar e Excluir recursos, nem uma forma de adicionar equipamentos:

<div repeat.for="equip of ninja.EquipmentOwned">
  ${equip.Name} ${equip.Type}
</div>

A Figura 5 mostra o formulário com a vinculação de dados.

Página Editar exibindo a lista de equipamentos
Figura 5 Página Editar exibindo a lista de equipamentos

O Aurelia também tem um recurso chamado vinculação adaptável, que permite adaptar seus recursos de vinculação com base nos recursos do navegador disponíveis ou até mesmo nos objetos que são passados. Este recurso é excelente e foi projetado para ser capaz de trabalhar junto com navegadores e bibliotecas à medida que vão evoluindo. Você pode ler mais sobre vinculação adaptativa em bit.ly/1GhDCDB.

No momento, você pode editar apenas o nome ninja, a data de nascimento e o indicador Oniwaban. Quando um usuário desmarca Served in Oniwaban e pressiona o botão Save, essa ação chama o método salvar de meu modelo de exibição, que faz algo interessante antes de enviar os dados de volta à minha API da Web. No momento, como vimos na Figura 4, o objeto ninja é um gráfico de profundidade. Não preciso enviar tudo isso de volta para salvamento, apenas as propriedades relevantes. Como estou usando o EF na outra extremidade, quero ter certeza de que as propriedades não editadas também serão enviadas de volta, para que não sejam substituídas por valores nulos no banco de dados. Portanto, estou criando um DTO de sistema em funcionamento chamado ninjaRoot. Já declarei ninjaRoot como propriedade de meu modelo de exibição. Mas a definição de ninjaRoot será indicada por como eu o compilei em meu método Save (ver Figura 6). Fui cuidadosa ao usar os mesmos nomes de propriedade, inclusive maiúsculas e minúsculas, que minha API da Web espera para poder desserializá-los no tipo de Ninja conhecido na API.

Figura 6 Método Save na exibição Editar Modelo

save() {
        this.ninjaRoot = {
          Id: this.ninja.Id,
          ServedInOniwaban: this.ninja.ServedInOniwaban,
          ClanId: this.ninja.ClanId,
          Name: this.ninja.Name,
          DateOfBirth: this.ninja.DateOfBirth,
          DateCreated: this.ninja.DateCreated,
          DateModified: this.ninja.DateModified
        };
        this.http.createRequest("/ninjas/")
          .asPost()
          .withHeader('Content-Type', 'application/json; charset=utf-8')
          .withContent(this.ninjaRoot).send()
          .then(response => {
            this.myRouter.navigate('ninjas');
          }).catch(err => {
            console.log(err);
          });
    }

Observe o método "asPost" nessa chamada. Isso garante que a solicitação vá para o método Post em minha API da Web:

public void Post([FromBody] object ninja)
{
  var asNinja =
    JsonConvert.DeserializeObject<Ninja>
    (ninja.ToString());
  _repo.SaveUpdatedNinja(asNinja);
}

O objeto JSON é desserializado em um objeto Ninja local e, em seguida, passado para o meu método de repositório, que sabe como atualizar esse objeto no banco de dados.

Quando eu retornar à lista de Ninjas em meu site da Web, a alteração será refletida na saída.

Mais do que uma mera vinculação de dados

Lembre-se que o Aurelia é uma estrutura que tem oferece muito mais do que vinculação de dados, mas, em respeito à minha natureza, preferi me concentrar nos dados em meus primeiros passos para explorar essa ferramenta. Você pode aprender muito mais no site do Aurelia, e também na vibrante comunidade encontrada em gitter.im/Aurelia/Discuss.

Gostaria agradecer muito ao site tutaurelia.net, uma série de blog sobre como combinar a API Web ASP.NET e o Aurelia. Recorri à parte 6 da autor Bart Van Hoey para minha primeira passagem ao exibir a lista de ninjas. Talvez, no momento em que este artigo seja publicado, haverá mais postagens adicionadas à série.

O download deste artigo inclui a solução de API da Web e o site do Aurelia. Você pode usar a solução de API da Web no Visual Studio. Ela foi criada no Visual Studio 2015 usando o Entity Framework 6 e o Microsoft .NET Framework 4.6. Se quiser executar o site da Web, você precisará visitar a página Aurelia.io para aprender a instalar Aurelia e executar o site da Web. Você também pode ver minha demonstração do aplicativo em meu curso de Pluralsight, “Getting Started with Entity Framework 6” [disponível somente em inglês] em bit.ly/PS-EF6Start.


Julie Lerman* é MVP da Microsoft, mentora e consultora do .NET, e reside nas colinas de Vermont. Você pode encontrá-la em apresentações sobre acesso de dados ou sobre outros tópicos .NET em grupos de usuários e conferências em todo o mundo. Ela escreve no blog em thedatafarm.com/blog e é a autora de “Programming Entity Framework” (2010), bem como uma edição do Code First (2011) e uma edição do DbContext (2012), todos da O’Reilly Media. Siga Julie no Twitter em twitter.com/julielerman e confira seus cursos da Pluralsight em juliel.me/PS-Videos.*


Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Rob Eisenberg (Durandal Inc.)
Rob Eisenberg tem dez anos de experiência em arquitetura de interface de usuário e engenharia. Ele é o criador de várias estruturas de interface do usuário, como Caliburn.Micro e Durandal, que são usadas por milhares de desenvolvedores em todo o mundo. Ex-participante da equipe do Angular 2.0, Rob atualmente lidera o projeto Aurelia, uma estrutura de cliente JavaScript de última geração para celular, área de trabalho e Web que utiliza convenções simples para que o usuário possa dar asas à sua criatividade. Saiba mais em http://aurelia.io/