Este artigo foi traduzido por máquina.

HTML5

Escrevendo um aplicativo Web JavaScript orientado a negócios

Frank Prößdorf

Dariusz Parys

 

Microsoft está colocando muitos esforços em promoção HTML5 e o JavaScript como chave para desenvolvedores do Windows, e há um grande número de estruturas e bibliotecas de alta qualidade disponível para criação de aplicativos prontos para produção. Neste artigo, vamos criar um aplicativo orientado a negócios básico que pode servir como ponto de partida para se aprofundar opções existentes — e permitem que você enfrenta quanto divertida de qualidade de codificação JavaScript pode ser.

O aplicativo catálogo de produtos e dividi-los em categorias e produtos e categorias podem ser criadas, atualizadas e excluídas (CRUD), conforme mostrado na a Figura 1. Com essas operações CRUD típicas, o aplicativo irá lidar com outras tarefas padrão: internacionalização, validação de entrada e o teclado de controle do aplicativo. Um dos aspectos mais importantes do aplicativo é que ele use armazenamento local de HTML5 para permitir a edição offline. Não falaremos sobre os detalhes de todos este aqui, mas você encontrará o código completo para este aplicativo de exemplo no codeplex.

The Products Overview List
Figura 1 da lista de visão geral de produtos

Introdução

Então, como ir sobre como escrever um aplicativo de JavaScript orientado a negócios? Uma maneira bem estabelecida é usando uma estrutura Model-View-Controller (MVC). Isso foi empregado com êxito em estruturas como Ruby on Rails, Django ou ASP.NET MVC. MVC permite uma estrutura estrita e a separação de preocupações do aplicativo, como o modo de exibição e a lógica comercial. Isso é especialmente importante com JavaScript, pois é realmente fácil escrever código confuso, fazendo tudo em um só lugar. Temos muitas vezes lembrar que nós mesmos para não fazer isso e para tentar e escrever código limpo, legível e reutilizável. Há várias estruturas construídas uma estrutura MVC, mais notavelmente Backbone.js, Eyeballs.js e Sammy.js.

Para este aplicativo de exemplo, usamos Sammy.js, principalmente porque estamos já saiba, mas também porque ele é pequeno e bem escrito, testado e faz tudo, precisamos começar. Ele não fornecerá uma estrutura MVC implícita mas permite criar facilmente na sua base. É a única dependência possui atualmente jQuery e que é uma biblioteca que usamos para manipulação de DOM assim mesmo. A estrutura de diretórios, comecei com esta aparência:

- public
  - js
      app.js
    + controllers
    + models
    + helpers
    + views
  + templates
  - vendor
    - sammy
        sammy.js
    - jquery
        jquery.js

Colocamos todos os arquivos de modelo que podem ser processados por meio do código JavaScript no diretório modelos e todo o código JavaScript relevantes para renderizar esses modelos no diretório modos de exibição.

O arquivo de aplicativo

Criamos o real Sammy.js aplicativos em app.js—here os controladores são carregados e suas rotas são inicializados (consulte a Figura 2). Tendemos a namespace todas as variáveis (controladores, modelos e assim por diante) de criar. Nesse caso, escolhemos chamar esta karhu do namespace, o nome da empresa cujos produtos estamos estiver catalogação.

Figura 2 Karhu.app

karhu.app = $.sammy(function() {
  this.element_selector = '#main';
  this.use(Sammy.Mustache, 'mustache');
  this.use(Sammy.NestedParams);
  this.use(Sammy.JSON); 
  this.helpers(karhu.ApplicationHelper);
  this.helpers({ store: karhu.config.store });
  karhu.Products(this);
});
$(function() {
  karhu.app.run('#/products');
});

A primeira etapa é carregar plug-ins, como Mustache, que é um mecanismo de processamento de modelo.Em seguida, você inicializar auxiliares (karhu.ApplicationHelper) e controladores (karhu.Produtos).Depois que o aplicativo é definido e todos os elementos DOM são carregados, você pode executar o aplicativo e direcioná-lo para a rota inicial, que é o índice de todos os produtos.

Testes de escrita

Antes de mostrando como o controlador do produto funciona e exibe todos os produtos, queremos entrar rapidamente em como a qualidade dos aplicativos do JavaScript pode ser bastante aumentada por meio de testes.À medida que passávamos sobre desenvolvimento do aplicativo de exemplo, antes de cada etapa importante primeiro escrevemos um teste de aceitação para garantir que o código realmente funcionaria.Isso também evita regressões, garantindo que tudo gravados antes também ainda funciona corretamente.Para o código mais complexo, podemos escrever testes de unidade e tentam cobrir a maioria dos casos que podem ocorrer quando a execução do código.Uma das maneiras mais fáceis e mais legíveis para escrever testes de aceitação é usar o Capybara com Selenium, no entanto, uma vez sem periféricos navegadores como PhantomJS estão disponíveis como drivers de Capybara, ele provavelmente fará sentido para usá-los em vez de Selenium, conforme eles são muito mais rápidos.

Para nosso primeiro cenário (a Figura 3), vamos testar se podemos ver uma lista de produtos.

Figura 3 uma situação de teste

Feature: Products
  In order to know which products I have
  As a user
  I want to see a list of products
  Scenario: list products
    Given a category "Trees" with the description "Plants"
      And a product "Oak" with the description "Brown"
                                         and the price "232.00€"
                                         that is valid to "12/20/2027"
                                         and belongs to the category "Trees"
      And a product "Birch" with the description "White"
                                         and the price "115.75€"
                                         that is valid to "03/01/2019"
                                         and belongs to the category "Trees"
    When I go to the start page
    Then I should see "Trees"
      And I should see "Oak"
      And I should see "Brown"
      And I should see "232.00€"
      And I should see "12/20/2027"
      And I should see "Birch"

Para testes de unidade, há muitas possibilidades diferentes. Usamos para trabalhar com jspec, porque é semelhante ao rspec do Ruby, que temos utilizado antes. Agora jspec foi substituída em favor de Jasmine, portanto, usamos que aqui. Ele funciona muito bem e inclui uma tarefa do rake que permite executar facilmente juntamente com os testes de aceitação. Aqui está um dos testes de unidade para o aplicativo de exemplo aparência:

describe("Product", function() {
  describe("attachCategory", function() {
    it("should assign itself its category", function() {
      var categories = [{id: 1, name: 'Papiere'}, {id: 2, name: 'Baeume'}];
      var attributes = {id: 1, name: 'Fichte', category_id: 2};
      var product = new karhu.Product(attributes, categories);
      expect(product.category.
name).toEqual('Baeume');
    });   
  });
});

Definir controladores

Depois de concluir o cenário podemos começar a escrever o controlador, que é muito simple primeiro:

karhu.Products = function(app) {
  app.get('#/products', function(context) {
    context.get('/categories', {}, function(categories) {
      context.get('/products', {}, function(products) {
        products = products.map(function(product) { return new karhu.Product(
          product, categories); });
        context.partial('templates/products/index.mustache', {products: products});
      });
    });
  });
};

No momento há apenas uma rota definida, que é um GET na rota # e produtos.O retorno de chamada será executado uma vez o hash de localização na URL é alterado para /products. Portanto, se você anexar a rota para o seu URL (como http://localhost:4567/index.html#/products), o retorno de chamada anexado será executado. O mesmo acontecerá quando o aplicativo é iniciado pela primeira vez, porque na app.js definimos o caminho inicial aponta para a mesma rota.

Dentro da rota, podemos recuperar as categorias e produtos via auxiliares que faça apenas uma básica AJAX solicitação GET para nosso back-end. Assim que podemos recuperar esses dados, podemos mapeá-lo para objetos JavaScript e depois processar esses objetos dentro do modelo de index.mustache. Isso processará-los a < div id = "main" > Marca HTML, que foi definida como o element_selector de raiz no arquivo app.js.

Definindo modelos

Precisamos mapear os dados como objetos JavaScript que podemos pode associar os produtos da categoria eles pertencem e processam o nome da categoria juntamente com o produto, que tem esta aparência:

karhu.Product = function(attributes, categories) {
  _.extend(this, attributes);
  attachCategory(this, categories);
  function attachCategory(product, categories) {
    product.category = _.find(categories, function(category) {
      return parseInt(category.id, 10) === parseInt(product.category_id, 10);
    });
  }
};

Podemos ampliar o objeto com todos os atributos do produto e, em seguida, podemos anexar a categoria do produto para o objeto. Mantemos attachCategory dentro do feriado para torná-lo uma função privada. Observe nesse código, o uso das funções sublinhado, que são fornecidos pelo Underscore.js. Esta biblioteca define auxiliares para a enumerables e ajuda a escrever código conciso e fácil de ler.

Figura 4 mostra como o modelo será exibida ao usuário.

Interacting with the Product Model in the Web App
Figura 4 interagindo com o modelo de produto em que o aplicativo Web

Modelos de renderização

Com o modelo que acabamos de ver, não precisamos um objeto de camada do modo de exibição adicionais porque a lógica de renderização é muito básica — ele apenas itera sobre os objetos dos produtos criamos e exibe os atributos de cada, incluindo o nome da categoria anexamos antecipadamente. O modelo de mustache livre de lógica que será processado se parece com o que é mostrado na a Figura 5.

Figura 5 O modelo de Mustache

    <h2>Products</h2>
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Description</th>
          <th>Price</th>
          <th>Valid To</th>
          <th>Category</th>
        </tr>
      </thead>
      <tbody>
        {{#products}}
          <tr>
            <td>{{name}}</td>
            <td>{{description}}</td>
            <td>{{unit_price}}</td>
            <td>{{valid_to}}</td>
            <td>{{#category}}{{name}}{{/category}}</td>
          </tr>
        {{/products}}
      </tbody>
    </table>

A saída processada é mostrada na a Figura 6.

Rendered HTML Output from the Mustache Template
Figura 6 renderizado saída HTML a partir do modelo de Mustache

Movendo o código do controlador específico do modelo para o modelo

É uma questão de gosto quanto responsabilidade um controlador é dada e quanto pode ser Refatorada em código do modelo. Se eu quiser escrever o código em a Figura 5 de maneira mais centrada no modelo, eu poderia fazer algo como a Figura 7.

Figura 7 uma abordagem centrada no modelo mais

Controlador

karhu.Products = function(app) {
  app.get('#/products', function(context) {
    karhu.Product.all(function(products) {
      context.partial('templates/products/index.mustache', {products: products});
    });
  });
};

Modelo

karhu.Product.all = function(callback) {
  karhu.backend.get('/categories', {}, function(categories) {
    karhu.backend.get('/products', function(products) {
      products = products.map(function(product) { return new karhu.Product(product, categories); });
      callback(products);
    });
  });
};

Tarefas padrão

Há um número de tarefas que são muito comuns para o desenvolvimento da Web e será repetida quando você trabalha em aplicativos de JavaScript. Queremos explicar como podemos solucionados essas tarefas e resolveu os problemas encontrados. Normalmente, há várias maneiras de abordar um problema.

Autenticação a maioria dos aplicativos, incluindo esta, adicione segurança básica, tornando o usuário efetuar login. Como o HTTP é sem estado, você precisa reenviar a autenticação com cada solicitação. Você pode observar isso salvando um token quando ele faz logon pela primeira vez no e, em seguida, usar esse token para cada solicitação daí em diante. O que eu optei por fazer era para salvar um token no armazenamento local depois que o usuário tivesse conectado com êxito e, em seguida, enviar esse token como um cabeçalho anexado para o XMLHttpRequest. O código para fazer isso é mostrado na a Figura 8. Esse código é armazenado em um modelo de back-end que é usado por auxiliares que mencionamos anteriormente.

Figura 8 salvando um Token quando um usuário efetua login

this.get = function(url, data, success, error) {
  sendRequest('get', url, data, success, error);
};
function authenticate(xhr) {
  var token = '';
  if(karhu.token) {
    token = karhu.token;
  } else if(karhu.user && karhu.password) {
    token = SHA256(karhu.user + karhu.password);
  }
 karhu.token = token;
xhr.setRequestHeader("X-Karhu-Authentication", 
'user="' + karhu.user + '", token="' + karhu.token + '"');
};
function sendRequest(verb, url, data, success, error) {
  $.ajax({
    url: url,
    data: data,
    type: verb,
    beforeSend: function(xhr) {
      authenticate(xhr);
    },
    success: function(result) {
      success(result);
    }
  });
}

Figura 9 mostra um token de usuário que foram salvos.

X-Karhu-Authentication Included in an HTTP Request
Figura 9-Karhu-autenticação x incluído em uma solicitação HTTP

Se o usuário conectado apenas, você deve ter um nome de usuário e uma senha disponível. Se o usuário conectado anteriormente, você terá um token salvo. De qualquer forma, você anexar a combinação de token ou senha do usuário como um cabeçalho e se a solicitação for bem-sucedida, você sabe que o usuário é autenticado com êxito. Caso contrário, o back-end apenas retornará um erro. Essa abordagem era relativamente simples de implementar e o único problema que encontramos era que o código se tornou um pouco complexo e ilegível. Para corrigir isso, podemos Refatorada auxiliares em um modelo separado. Abstrair as solicitações em um modelo de back-end é bastante comum, como, por exemplo, com a biblioteca de Backbone.js onde ele é uma parte principal da biblioteca. Código de autenticação geralmente é exclusivo para cada aplicativo e sempre depende do back-end e o que ele espera que o front-end para enviar.

Internacionalização (I18n) internacionalização é uma tarefa comum para aplicativos Web, e jquery.global.js é freqüentemente usado para fazer isso com em aplicativos de JavaScript. Esta biblioteca fornece métodos para formatação de números e datas e permite a tradução de cadeias de caracteres usando um dicionário para a localidade atual. Depois de carregar esse dicionário, que é um objeto JavaScript simples com chaves e valores traduzidos, a única coisa que você precisa prestar atenção está formatando números e datas. É um local adequado para fazer isso nos modelos antes de processar os objetos para os modelos. No modelo de produto ele teria esta aparência:

var valid_to = Date.parse(product.valid_to);
product.valid_to = $.global.format(valid_to, "d");

Figura 10 mostra o alemão como o idioma de exibição.

Switching the Language to German
Figura 10 troca do idioma alemão

Validação uma das vantagens do desenvolvimento em JavaScript é que você pode fornecer os comentários do usuário em tempo real. Faz sentido usar esse potencial para validar os dados antes de serem enviado ao back-end. Observe que ainda é necessário validar os dados no back-end, bem como pode haver solicitações que não usam o front-end. A biblioteca jQuery jquery.validate.js é usado freqüentemente para validação. Ele fornece um conjunto de regras em um formulário e mostra os erros em campos de entrada apropriados se o conteúdo não coincide com as regras. Faz sentido para estruturar as regras de validação para os modelos já temos, portanto, cada modelo tem uma função de validações que retorna as regras. Eis como seria a validação para nosso modelo da categoria:

karhu.Category = function() {
  this.validations = function() {
    return {
      rules: {
        'category[name]': {
          required: true,
          maxlength: 100
        }
      }
    };
  };
};

Figura 11 mostra como pode exibir o erro.

A Validation Error on Creation of a New Category
Figura 11 uma erro de validação na criação de uma nova categoria

Validação vai mais além. Navegação longe dos formulários não enviados é proibida. O usuário deve realmente envie dados válidos ou cancelar proativamente a entrada de dados (consulte a Figura 12).

A Red Flash Notification Warns User of Unsubmitted Form Data
Figura 12 A vermelha notificação Flash avisa o usuário com dados de formulário

O cache de objetos para edição off-line os usuários às vezes serão necessário trabalhar off-line. Permitindo a isso é a parte mais central e complexa do aplicativo. Todos os objetos precisam ser armazenados em cache com antecedência para que assim que o aplicativo estiver off-line podem ser corretamente classificados, paginados e filtrados. Deve haver uma fila para todas as ações que ocorrem antes que os objetos são armazenados em cache, para que essas ações podem ser aplicadas aos objetos, assim que elas estão armazenadas em cache. Também deve haver uma segunda fila que é preenchida quando na verdade, estamos off-line, para que quando estamos online novamente, tudo o que foi feito offline pode ser corrigido por meio ao back-end. Figura 13 mostra o aplicativo off-line.

The Application Response When Taken Offline
Figura 13 A resposta do aplicativo quando colocada off-line

Há um número de questões que precisarão ser atendidos além do processo de enfileiramento de mensagens e o cache já complicado. Por exemplo, quando um objeto é criado quando estiver offline, ele não pode ser atualizado ou excluído sem código adicional porque não possui uma identificação. Trabalhamos em torno do que por enquanto, simplesmente não permitindo que essas ações para objetos criados enquanto off-line. Por essa mesma razão, as categorias criadas enquanto off-line não pode ser usado para a criação de produtos. Eu simplesmente não exibem essas categorias na lista de categorias disponíveis para a criação de um produto. Esses tipos de problemas poderão ser resolvidos por trabalhar com ids temporários e reorganizando fila offline.

Além disso, o termo disponíveis e o modelo precisam ser armazenada em cache. Isso pode ser feito por meio de um manifesto de cache conforme definido em HTML5 se o grupo-alvo navegador compatível, ou simplesmente por meio de carregar o termo e colocá-los em armazenamento local. Isso é bastante simple com Sammy.js e ter esta aparência:

context.load('templates/products/index.mustache', {cache: true});

Integração com Windows

Internet Explorer 9 é ótimo para execução de aplicativos de HTML5. Além disso, ele permite aplicativos da Web para integrar a barra de tarefas do Windows 7, aprimorar o aplicativo com a possibilidade de Mostrar notificações, integrar a navegação e oferecer suporte à lista de salto nativamente. A integração das listas de saltos é simples, apenas uma declaração de atributos de marca meta de formulário em sua forma mais simples. Isso é exatamente a abordagem usada no Karhu, o que torna fácil para os usuários que precisam de acesso. Você pode direcionar o salto da lista de tarefas para ir para os modos de exibição, adicionar produtos, adicionar categorias, visão geral dos produtos e visão geral de categorias (consulte a Figura 14). O código a seguir demonstra como declarar uma tarefa de lista simples de salto:

    <meta name="msapplication-task"
          content="name=Products;
          action-uri=#/products;
          icon-uri=images/karhu.ico" />

Jump List Tasks
Figura 14 Salto da lista de tarefas

Você pode aprender tudo sobre fixação e integração do Windows 7 na Criar Meu Site fixado, que tem idéias adicionais para aplicativos da Web, como notificações de navegador e listas de saltos dinâmicas usando JavaScript.  É fácil adicionar mais funcionalidade ao seu aplicativo da Web com apenas um pouco mais de esforço. Outros pontos de partida para obter uma melhor compreensão sobre o assunto são o Referência da linguagem JavaScript MSDN e as documentações da estrutura e as bibliotecas já mencionadas.

Conclusão

Neste ponto, implementamos todos os requisitos básicos para o aplicativo de exemplo. Com tempo suficiente, podemos também poderia cuidar dos problemas mencionados anteriormente. Tarefas, como autenticação, internacionalização e tratamento de lógica comercial precisará ser codificado independentes das estruturas e bibliotecas, que são realmente apenas um ponto de partida.

Se você escreve testes e preste atenção na estrutura do aplicativo, a escrita de aplicativos de JavaScript pronto para produção que podem continuar a evoluir sempre é, em nossa opinião, não só é possível, mas vale a pena investir em dos objetivos. É fácil começar, mas é importante manter Verificando uma base de código limpo e refatoração se necessário. Se esses requisitos forem atendidos, o JavaScript lhe dará a oportunidade para escrever aplicativos muito elegantes e fácil manutenção.

Frank Prößdorf é um freelance Web developer e co-fundadora da NotJustHosting quem gosta de trabalhar com Ruby e JavaScript. Sua paixão é descobrindo e brincar com novas tecnologias. Ele suporta regularmente open source software, conforme ele é animado com a oportunidade de contribuir e compartilhar idéias e código.  Além de seu trabalho, ele gosta de viajar, sailing e reprodução de Tênis.  Familiarize-se Prößdorf via his github perfil ou his site da.

Dariusz Parys é um desenvolvedor divulgador Microsoft Alemanha, fazendo o desenvolvimento da Web com um foco forte na futuras tecnologias. Atualmente, ele está escrevendo muito JavaScript e HTML5. Você pode contatar Parys pelo seu blog downtocode.net

Graças ao seguir técnico especialista para revisar este artigo: Aaron Quint