Ken Williamson
Novatec
Authorized Portuguese translation of the English edition of titled Learning AngularJS, ISBN 9781491916759 © 2015 Ken Williamson. This translation is published and sold by permission of O'Reilly Media, Inc., the owner of all rights to publish and sell the same. Tradução em português autorizada da edição em inglês da obra Learning AngularJS, ISBN 9781491916759 © 2015 Ken Williamson. Esta tradução é publicada e vendida com a permissão da O'Reilly Media, Inc., detentora de todos os direitos para publicação e venda desta obra. © Novatec Editora Ltda. [2015]. Todos os direitos reservados e protegidos pela Lei 9.610 de 19/02/1998. É proibida a reprodução desta obra, mesmo parcial, por qualquer processo, sem prévia autorização, por escrito, do autor e da Editora. Editor: Rubens Prates Tradução: Lúcia A. Kinoshita Revisão gramatical: Mari Kumagai Assistente editorial: Priscila A. Yoshimatsu Editoração eletrônica: Carolina Kuwabata ISBN: 978-85-7522-430-4 IG20150423 Histórico de impressões: Maio/2015
Primeira edição
Novatec Editora Ltda. Rua Luís Antônio dos Santos 110 02460-000 – São Paulo, SP – Brasil Tel.: +55 11 2959-6529 Email: novatec@novatec.com.br Site: www.novatec.com.br Twitter: twitter.com/novateceditora Facebook: facebook.com/novatec LinkedIn: linkedin.com/in/novatec
capítulo 1
Introdução ao AngularJS
O AngularJS do Google é um framework JavaScript MVC (Model-View-Controller, ou Modelo-Visão-Controlador) completo, que facilita bastante a criação rápida de aplicações que executem adequadamente em qualquer plataforma desktop ou móvel. Em um período de tempo muito curto, o AngularJS passou de uma opção de código aberto desconhecida para um dos frameworks JavaScipt do lado cliente mais conhecido e mais amplamente utilizado. O AngularJS 1.3 e as versões mais recentes, combinados com a jQuery e o Twitter Bootstrap, oferecem tudo que é necessário para implementar rapidamente frontends HTML5 de aplicações JavaScript que usem web services REST para os processos em backend. Este livro mostrará como usar todos os três componentes do frontend para ter domínio sobre a eficácia dos serviços REST no backend e implementar rapidamente aplicações móveis e desktop eficientes.
Frameworks JavaScript do lado cliente As aplicações JavaScript do lado cliente executam no dispositivo do usuário ou em um PC e, desse modo, deslocam a carga de trabalho para o hardware do usuário, distante do servidor. Até bem recentemente, os frameworks MVC para web do lado do servidor como o Struts, o Spring MVC e o ASP.NET eram as opções preferidas da maioria dos projetos de desenvolvimento de software baseados em web. Os frameworks JavaScript do lado cliente, porém, são modelos sustentáveis, que oferecem muitas vantagens em relação aos frameworks web convencionais como simplicidade, desenvolvimento rápido, agilidade na operação, testabilidade e 20
Capítulo 1 ■ Introdução ao AngularJS
21
a capacidade de empacotar toda a aplicação e implantá-la em todos os dispositivos móveis e na Web com relativa facilidade. Você pode implementar sua aplicação uma só vez, implantá-la e executá-la em qualquer lugar ou plataforma, sem fazer modificações. É bastante eficiente. O AngularJS torna esse processo ainda mais rápido e fácil. Ele ajuda a criar aplicações de frontend em dias em vez de meses, além de oferecer suporte completo para testes de unidade que ajudam a reduzir o tempo de QA (Quality Assurance, ou Garantia de qualidade). O AngularJS tem um conjunto rico de documentação de usuário e um ótimo suporte da comunidade para ajudar a responder perguntas durante o seu processo de desenvolvimento. Os modelos e as visões do AngularJS são muito mais simples do que aqueles que você encontra na maioria dos frameworks JavaScript do lado cliente. Os controladores, geralmente ausentes em outros frameworks JavaScript do lado cliente, são componentes funcionais básicos do AngularJS. A figura 1.1 mostra um diagrama de uma aplicação AngularJS e todos os componentes MVC relacionados. Depois que a aplicação AngularJS é iniciada, o modelo, a visão, o controlador e todos os documentos HTML são carregados no dispositivo móvel do usuário ou no desktop e executam totalmente no hardware desse usuário. Como podemos ver, chamadas são feitas aos serviços REST no backend; nesse local, estão toda a lógica e os processos de negócios. Os serviços REST do backend podem estar em um servidor web privado ou na nuvem (que é o que ocorre com mais frequência). Os serviços REST na nuvem podem escalar, passando de um punhado de usuários para milhões deles, com relativa facilidade. Parciais (partials) HTML5 Backend Visão Modelo
Serviço REST
Controlador
Figura 1.1 – Diagrama de uma aplicação AngularJS MVC.
22
Introdução ao AngularJS
Aplicação single-page O AngularJS é usado com mais frequência para criar aplicações que estejam de acordo com o conceito de SPA (Single-Page Application). As SPAs são aplicações que têm somente uma página HTML como ponto de entrada; todo o conteúdo da aplicação é adicionado e removido dinamicamente dessa página única. Você pode ver o ponto de entrada de nossa SPA no código de index.html que se segue. A tag <div ng-view></div> é o local em que todo o conteúdo dinâmico será inserido em index.html: <!-- chapter1/index.html --> <!DOCTYPE html> <html lang="en" ng-app="helloWorldApp"> <head> <title>AngularJS Hello World</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script <script <script <script
src="js/libs/angular.min.js"></script> src="js/libs/angular-route.min.js"></script> src="js/libs/angular-resource.min.js"></script> src="js/libs/angular-cookies.min.js"></script>
<script src="js/app.js"></script> <script src="js/controllers.js"></script> <script src="js/services.js"></script> </head> <body> <div ng-view></div> </body> </html>
À medida que o usuário clicar nos links da aplicação, o conteúdo existente associado à tag será removido e novos conteúdos dinâmicos serão associados à mesma tag. Em vez de o usuário esperar que uma nova página seja
Capítulo 1 ■ Introdução ao AngularJS
23
carregada, o novo conteúdo será dinamicamente exibido em uma fração do tempo que seria necessário para carregar uma nova página web HTML. Você pode fazer download do código-fonte da aplicação “Hello, World” do capítulo 1 a partir do GitHub (http://bit.ly/lajs-github).
Bootstrapping da aplicação Bootstrapping (inicialização) do AngularJS é o processo de carregar o AngularJS quando uma aplicação é iniciada. Carregar as bibliotecas do AngularJS em uma página dará início ao processo de bootstrap. O arquivo index.html será analisado e o parser procurará a tag ng-app. A linha <html lang="en" ng-app="helloWorldApp"></html> mostra como ng-app está definida. O código a seguir apresenta o JavaScript iniciado por essa linha no arquivo index.html. Como podemos ver, app.js é o local em que a aplicação AngularJS helloWorldApp está definida como um módulo do AngularJS, e esse é o ponto de entrada da aplicação. A variável helloWorldApp nesse arquivo poderia ter qualquer nome.Entretanto vou chamá-la de helloWorldApp por questões de uniformidade: /* excerto de chapter1/app.js */ 'use strict'; /* Módulo da aplicação */ var helloWorldApp = angular.module('helloWorldApp', [ 'ngRoute', 'helloWorldControllers' ]);
Dependency Injection (Injeção de dependências) A DI (Dependency Injection, ou Injeção de dependências) é um padrão de projeto em que as dependências são definidas em uma aplicação como parte da configuração. A injeção de dependências ajuda a evitar a neces-
24
Introdução ao AngularJS
sidade de criar dependências da aplicação manualmente. O AngularJS utiliza a injeção de dependências para carregar as dependências dos módulos quando uma aplicação é iniciada. O código de app.js da seção anterior mostra como as dependências do AngularJS são definidas. Como podemos ver, duas dependências estão definidas conforme necessárias à aplicação helloWorldApp na inicialização. As dependências estão definidas em um array na definição do módulo. A primeira dependência é o módulo ngRoute do AngularJS, que disponibiliza o roteamento à aplicação. A segunda dependência corresponde ao nosso módulo controlador helloWorldControllers. Discutiremos os controladores em detalhes posteriormente, mas, por enquanto, basta entender que eles são necessários às nossas aplicações no momento da inicialização. A injeção de dependências não é um conceito novo. Ela foi introduzida há mais de dez anos e tem sido usada consistentemente em diversos frameworks de aplicações; a DI estava presente no núcleo do popular framework Spring, escrito em Java. Uma das principais vantagens é que ela reduz a necessidade de código boilerplate, cuja escrita, normalmente, é um processo que consome tempo de uma equipe de desenvolvimento. A injeção de dependências também ajuda a deixar uma aplicação mais testável. Essa é uma das principais vantagens de usar o AngularJS para criar aplicações JavaScript. As aplicações AngularJS são muito mais fáceis de testar do que as aplicações criadas com a maioria dos frameworks JavaScript. Com efeito, há um framework de teste que foi criado especificamente para facilitar os testes de aplicações AngularJS. Falaremos mais sobre testes no final deste capítulo.
Rotas no AngularJS As rotas no AngularJS são definidas por meio da API $routeProvider. As rotas são dependentes do módulo ngRoute, e é por isso que esse módulo é um requisito quando a aplicação é iniciada. O código de app.js a seguir mostra como definimos as rotas em uma aplicação AngularJS. Duas rotas estão definidas – a primeira é / e a segunda é /show:
Capítulo 1 ■ Introdução ao AngularJS
25
/* excerto de chapter1/app.js */ helloWorldApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider){ $routeProvider. when('/', { templateUrl: 'partials/main.html', controller: 'MainCtrl' }). when('/show', { templateUrl: 'partials/show.html', controller: 'ShowCtrl' });
As duas rotas definidas estão diretamente mapeadas aos URLs definidos na aplicação. Se um usuário clicar em um link na aplicação especificada como www.nomeDeAlgumDominio/show, a rota /show será seguida e o conteúdo associado a esse URL será exibido. Se o usuário clicar em um link especificado como www.nomeDeAlgumDominio/, a rota / será seguida e esse conteúdo será exibido.
Modo HTML5 O arquivo app.js completo será mostrado a seguir. A última linha em app.js ($locationProvider.html5Mode(false).hashPrefix('!');) utiliza o serviço locationProvider. Essa linha de código desabilita o modo HTML5 e habilita o modo hashbang do AngularJS. Se você habilitar o modo HTML5 passando true, a aplicação usará a API HTML5 History. O modo HTML5 também oferece URLs elegantes à aplicação, como /nomeDeAlgumaApp/blogPost/5 no lugar dos URLs padrões do AngularJS como /nomeDeAlgumaApp/#!/blogPost/5 que usam o #!, conhecido como hashbang. /* Arquivo chapter1/app.js completo */ 'use strict'; /* Módulo da aplicação */ var helloWorldApp = angular.module('helloWorldApp', [ 'ngRoute', 'helloWorldControllers'
26
Introdução ao AngularJS ]); helloWorldApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { $routeProvider. when('/', { templateUrl: 'partials/main.html', controller: 'MainCtrl' }).when('/show', { templateUrl: 'partials/show.html', controller: 'ShowCtrl' }); $locationProvider.html5Mode(false).hashPrefix('!'); }]);
O modo HTML5 pode oferecer URLs elegantes, porém exige mudanças na configuração do servidor web na maioria dos casos. As mudanças são diferentes para cada servidor web em particular, e também podem apresentar divergências em diferentes instalações de servidor. Além disso, o modo HTML5 trata mudanças em URL de maneira diferente, usando a API HTML History para navegação. Usar o modo HTML5 exige somente uma mudança de configuração no AngularJS; neste livro, não discutiremos as mudanças necessárias no servidor, pois nosso foco está no AngularJS. O site do AngularJS contém documentação a respeito das mudanças necessárias em todos os servidores web modernos quando o modo HTML5 estiver habilitado. Usar esse modo oferece algumas vantagens, porém ficaremos com o modo hashbang nos exercícios de nosso capítulo. O modo hashbang é usado para suportar ferramentas de pesquisa convencionais, que não tenham a capacidade de executar JavaScript em sites Ajax como aqueles criados com o AngularJS. Quando uma ferramenta de pesquisa convencional fizer uma pesquisa em um site que use hashbangs, criado com o AngularJS, a ferramenta substituirá #! por ?_escaped_fragment_=. As ferramentas de pesquisa convencionais esperam que o servidor tenha snapshots HTML no local em que _escaped_fragment_= estiver apontando. Os snapshots HTML são simplesmente cópias da versão renderizada de HTML do site ou da aplicação.
Capítulo 1 ■ Introdução ao AngularJS
27
Ferramentas de pesquisa modernas Felizmente, as ferramentas de pesquisa modernas têm a capacidade de executar JavaScript, conforme anunciado pelo Google em uma nota de imprensa divulgada no dia 23 de maio de 2014 (http://bit.ly/1EWcX3P). O modo hashbang também permite que aplicações AngularJS armazenem páginas solicitadas por Ajax no histórico do navegador. Geralmente, esse processo simplifica o uso de marcadores (bookmarks) nos navegadores.
Templates do AngularJS As parciais (partials) do AngularJS, também chamadas de templates, são seções de código que contêm código HTML associado à tag <div ng-view></div></div> mostrada no arquivo index.html anteriormente neste capítulo. Se você observar o arquivo app.js completo novamente, poderá ver que valores diferentes de templateUrl estão definidos para cada rota. Os arquivos main.html e show.html listados a seguir mostram as duas parciais definidas (templates). Os templates contêm somente código HTML e não têm nada em especial por enquanto. Mais tarde, usaremos a linguagem integrada de templates do AngularJS para exibir dados dinâmicos em nossos templates: <!-- chapter1/main.html --> <div>Hello World</div> <!-- chapter1/show.html --> <div>Show The World</div>
À medida que o usuário clicar nos diferentes links, o valor atribuído a <div ng-view> será substituído pelo conteúdo dos arquivos de template associados. O valor do controlador definido para cada rota referencia o componente associado ao controlador (do padrão MVC) definido para cada rota em particular. As próximas seções oferecem uma visão geral concisa de cada componente MVC do AngularJS e como esses componentes são usados, permitindo uma melhor compreensão de como o AngularJS funciona. De
28
Introdução ao AngularJS
modo diferente da maioria dos frameworks JavaScript do lado cliente, o AngularJS oferece componentes para modelo, visão e controlador, que poderão ser usados em todas as aplicações. Geralmente, isso ajuda os desenvolvedores que tenham familiaridade com padrões de projeto a dominar rapidamente os conceitos do AngularJS.
Visões do AngularJS (MVC) Muitos frameworks JavaScript do lado cliente exigem que você realmente defina as classes de visão (view) em JavaScript, e essas classes podem conter de algumas a centenas de linhas de código. Não é isso que ocorre com o AngularJS. O AngularJS extrai todos os templates definidos para uma aplicação e cria as visões no DOM (Document Object Model, ou Modelo de objetos de documentos) para você. Sendo assim, seu único trabalho ao criar as visões será criar os templates. Criar as visões no AngularJS é um processo simples, em que se utiliza principalmente HTML e CSS. A simplicidade das visões do AngularJS permite economizar bastante tempo na criação de aplicações AngularJS. Discutiremos a criação de templates com mais detalhes no capítulo 5.
Modelos do AngularJS (MVC) Muitos frameworks JavaScript do lado cliente também exigem que você crie classes de modelos (model) em JavaScript. Isso também não ocorre com o AngularJS. O AngularJS tem um objeto $scope, usado para armazenar o modelo da aplicação. Os escopos são associados ao DOM. O modelo é acessado por meio de propriedades de dados atribuídas ao objeto $scope. O $scope do AngularJS ajuda a simplificar consideravelmente as aplicações JavaScript. Outros frameworks JavaScript geralmente incentivam a inserção de grandes quantidades de lógica de negócios nas classes do modelo para o framework em particular. Infelizmente, essa prática normalmente resulta em uma lógica de negócios duplicada. Em um projeto de grande porte, isso pode resultar em milhares de linhas de código inúteis. Discutiremos os modelos com mais detalhes no capítulo 7.
Capítulo 1 ■ Introdução ao AngularJS
29
Controladores do AngularJS (MVC) Os controladores (controllers) do AngularJS correspondem às fitas adesivas que mantêm os modelos e as visões conectados. O controlador é o local em que você deve colocar toda a lógica de negócios específica de uma visão em particular quando não for possível colocá-la em um serviço REST. A lógica de negócios quase sempre deve ser colocada em serviços REST no backend sefor possível; isso ajuda a simplificar as aplicações AngularJS. Quando a lógica de negócios inserida em uma aplicação for usada por vários controladores, ela deverá ser colocada em serviços AngularJS que não sejam REST. Esses serviços podem então ser injetados em qualquer controlador que precise acessar a lógica. Discutiremos os serviços que não são REST no capítulo 8 com mais detalhes.
Lógica de negócio dos controladores O código a seguir mostra o conteúdo do arquivo controllers.js. No início do arquivo, definimos o módulo helloWorldController. Então definimos dois novos controladores – MainCtrl e ShowCtrl – e os associamos ao módulo helloWorldController. A lógica de negócios específica do controlador MainCtrl é definida nesse controlador. Da mesma maneira, a lógica de negócios específica do controlador ShowCtrl é definida no controlador ShowCtrl. Observe que $scope é injetado em ambos os controladores. O $scope injetado em cada controlador é específico desse controlador, e não será visível aos demais controladores: /* chapter1/controllers.js */ 'use strict'; /* Controladores */ var helloWorldControllers = angular.module('helloWorldControllers', []); helloWorldControllers.controller('MainCtrl', ['$scope', function MainCtrl($scope) { $scope.message = "Hello World"; }]);
30
Introdução ao AngularJS helloWorldControllers.controller('ShowCtrl', ['$scope', function ShowCtrl($scope) { $scope.message = "Show The World"; }]);
Como podemos ver, estamos usando o modelo para preencher as mensagens a serem exibidas nos templates. O código a seguir mostra os templates modificados que usam os valores do modelo recém-criados. A linha $scope.message = "Hello World" no controlador MainCtrl é usada para criar uma propriedade chamada message, adicionada ao escopo (que armazena os atributos do modelo). Então usamos a marcação com chaves duplas ({{}}) no template main.html para obter acesso e exibir o valor atribuído a $scope.message: <!-- chapter1/main.html --> <div>{{message}}</div>
O uso de chaves duplas é a maneira de o AngularJS exibir propriedades do escopo na visão. A sintaxe de chaves duplas, na verdade, faz parte da linguagem integrada de template do AngularJS. De modo semelhante, usamos o valor atribuído à propriedade message com a linha $scope.message = "Show The World" no controlador ShowCtrl para preencher a mensagem exibida no template show.html. Usamos a marcação de chaves duplas no template show.html como fizemos anteriormente para obter acesso e exibir a propriedade do modelo: <!-- chapter1/show.html --> <div>{{message}}</div>
Integrando o AngularJS a outros frameworks O AngularJS pode ser integrado a aplicações existentes que usem outros frameworks. Podem ser outros frameworks JavaScript do lado cliente ou frameworks web como Spring MVC ou CakePHP. Você pode partir de uma aplicação implementada em Java e adicionar novas funcionalidades do lado cliente muito facilmente usando o AngularJS, reduzindo consideravelmente o tempo de desenvolvimento.
Capítulo 1 ■ Introdução ao AngularJS
31
Adicionar um novo carrinho de compras do AngularJS em uma aplicação Java existente é um bom exemplo a ser considerado. A aplicação Java existente poderia estar implementada com o framework Spring e usar o Spring MVC como framework web. Adicionar um carrinho de compras implementado em Java usando o Spring MVC pode ser um processo demorado. Isso, porém, não é o que ocorre com o AngularJS. Você pode criar um carrinho de compras rapidamente com o AngularJS e ter o código pronto para funcionar em algumas horas, integrando facilmente o carrinho na aplicação Java existente. Será possível não só criar o carrinho de compras como também adicionar testes de unidade rapidamente para aumentar a cobertura do código testado e reduzir os defeitos da aplicação. O AngularJS foi concebido para ser testável desde o início; esse é um dos recursos fundamentais do AngularJS e um dos motivos principais para selecioná-lo em relação a outros frameworks JavaScript do lado cliente. Discutiremos mais sobre os testes de aplicações AngularJS na próxima seção.
Testando aplicações AngularJS Nos últimos anos, as ferramentas de geração de versões com CI (Continuous Integration, ou Integração contínua) como o Travis CI, o Jenkins e outras ganharam popularidade e passaram a ser mais usadas. As ferramentas de CI têm a capacidade de executar scripts de teste durante um processo de geração de versão e fornecer feedback imediato na forma de resultados de testes. As ferramentas de CI ajudam a automatizar o processo de testar softwares e, com frequência, podem alertar os desenvolvedores a respeito de defeitos no software assim que eles ocorrerem. Há dois tipos de testes no AngularJS que se integram bem às ferramentas de CI. O primeiro tipo de testes é o teste de unidade (unit testing). A maioria dos desenvolvedores tem familiaridade com os testes de unidade; geralmente, esses testes podem identificar defeitos de software com antecedência no processo de desenvolvimento ao testar pequenas unidades de código. O segundo tipo de testes é o teste E2E (End-to-End, ou Fim a fim). Os testes E2E ajudam a identificar defeitos de software ao testar de que modo os componentes de software se conectam e interagem.
32
Introdução ao AngularJS
Há várias ferramentas de testes usadas para executar testes de unidade em aplicações AngularJS. Duas das mais populares são o Karma e o JS Test Driver. O Karma, porém, está se tornando rapidamente a principal opção das equipes de desenvolvimento AngularJS .A ferramenta de teste E2E mais popular para testes fim a fim de aplicações AngularJS é uma nova ferramenta chamada Protractor. Ambas as ferramentas se integram bem às ferramentas de CI. As equipes de desenvolvimento AngularJS de grande porte descobrirão que testar aplicações AngularJS com ferramentas de integração contínua proporcionará uma enorme economia de tempo. Com frequência, um teste de CI que falhe será a primeira indicação de um defeito para as equipes de grande porte. Equipes pequenas também perceberão muitas vantagens nos testes baseados em CI. Os desenvolvedores AngularJS devem desenvolver testes de unidade e testes fim a fim sempre que for possível. Ao longo deste livro, abordaremos tanto os testes de unidade quanto os testes fim a fim. Usaremos o Karma e o JsTestDrive para testes de unidade, e o Protractor para testes E2E.
Conclusão Discutiremos modelos, visões e controladores com mais detalhes nos próximos capítulos, usando esses componentes para criar aplicações funcionais que demonstrem a eficácia do AngularJS. Mostraremos como todos os três componentes funcionam em conjunto para simplificar a tarefa de criar aplicações JavaScript do lado cliente. Também discutiremos a criação de testes de unidade e de testes fim a fim para aplicações AngularJS. O capítulo 2 focará em ajudar você a configurar um ambiente de desenvolvimento para HTML5. Também faremos download das versões mais recentes do AngularJS, da jQuery e do Twitter Bootstrap, e as adicionaremos ao nosso projeto de exemplo.