Artigos

Porque utilizar AngularJS no seu próximo projeto

Vamos começar pela definição: AngularJS é um framework JavaScript para construção de aplicações web dinâmicas, comumente referenciado como um framework MVC client side. Essa definição não está errada, mas o fato é que MVC é um padrão para dividir uma aplicação em diferentes partes (modelo, visão e controle), cada uma com suas respectivas responsabilidades. O AngularJS  não implementa um MVC na sua forma tradicional, mas algo próximo de um MVVM (Model – View – ViewModel). Logo, seu próprio time de desenvolvimento resolveu batizá-lo, carinhosamente, de um framework MVW  – Model View Whatever. Bem, o time de desenvolvimento do Angular dispensa apresentação, ele nasceu e é mantido por uma empresa que entende de web, o Google.

“AngularJS – Superheroic JavaScript MVW Framework”

A principal característica do AngularJS é que ele funciona como uma extensão do HTML, diferente de outros frameworks JavaScript, não é necessário manipular o DOM. Isso possibilita a criação de um front end mais organizado e sem a necessidade de manipular “html + dados”.

Suas principais características são:

  • Two-way Data binding
  • Injeção de Dependências
  • Criação de diretivas (Extensão do HTML)
  • Modularização e reuso (Controllers, Services e Filters)
  • Testabilidade

Basicão

<html ng-app>
 <head>
     <title>AngularJS</title>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
 </head>
 <body>Meu nome é: {{ nome }}

<input type="text" ng-model="nome">
 </body>
</html> 

Este é o exemplo mais básico em AngularJS. A diretiva ng-app na tag <html> inicializa a aplicação e define esta tag como o elemento root da aplicação, tudo a partir dela pode ser gerenciado pelo Angular. Ao digitar algo no input o nome será atualizado em realtime na tela. Essa mágica é chamada de Two-way data binding. Qualquer interação na view será refletida no modelo e vice-versa, isso é feito através do objeto $scope, criado automaticamente pelo angular. A propriedade ng-model é responsável por fazer o canal de comunicação e a sintaxe utilizada é o “bigode-bigode”: {{ atributo }}. Note que até aqui não escrevemos nenhum código JavaScript.

Controllers

Para entender como é feito o vínculo da view com o modelo, e como o angular permite organizamos nossa aplicação com a chamada de Controllers, segue o código a seguir:

cervejas.html

&lt;div ng-controller=&quot;CervejaController&quot;&gt;
 &lt;form name=&quot;form&quot;&gt;
     &lt;input type=&quot;text&quot; placeholder=&quot;Cerveja&quot; ng-model=&quot;cerveja.nome&quot;&gt;
     &lt;input type=&quot;text&quot; placeholder=&quot;Cervejaria&quot; ng-model=&quot;cerveja.cervejaria&quot;&gt;
     &lt;button ng-click=&quot;adicionar()&quot;&gt;Adicionar&lt;/button&gt;
 &lt;/form&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cerveja&lt;/th&gt;
&lt;th&gt;Cervejaria&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr ng-repeat=&quot;cerveja in cervejas&quot;&gt;
&lt;td&gt;{{ cerveja.nome }}&lt;/td&gt;
&lt;td&gt;{{ cerveja.cervejaria }}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

CervejaController.js

function CervejaController($scope) {
 $scope.cervejas = [ { nome : 'Cerpinha', cervejaria : 'Cerpa'},
                     { nome : 'Tijuca', cervejaria : 'Cerpa' } ];

 $scope.adicionar = function() {
     $scope.cervejas.push({nome : $scope.cerveja.nome,
                           cervejaria : $scope.cerveja.cervejaria});
     $scope.limpar();
 };

 $scope.limpar = function() {
     $scope.cerveja = {nome: '', cervejaria: ''};
 };
}

A diretiva ng-controller vincula um elemento html com um Controller JavaScript, no caso CervejaController. Tudo a partir desta div pode ser controlado por este Controller. O button chama através da diretiva ng-click a função adicionar(), definida dentro do nosso Controller. Esta função adiciona uma nova cerveja na listagem e limpa os campos do formulário. Ao iniciar o controller, por padrão é adicionada duas cervejas na listagem, também poderíamos usar a diretiva ng-init neste caso. Note que a variável $scope foi recebida como argumento do Controller. Este é um exemplo de injeção de dependência feito automaticamente pelo AngularJS, um dos principais recursos do framework.

Outra diretiva interessante presente neste exemplo é a ng-repeat, que permite iterar em listas de forma bem simples e intuitiva, no formato <elemento> in <coleção>.

Filters

Outro recurso muito interessante e nativo do framework é a aplicação de filters. Os filtros servem para transformar o resultado de uma expressão, aplicando algum tipo de formatação ou restrição nos dados. Segue o exemplo:

&lt;h2&gt;Busca&lt;/h2&gt;
Cerveja: &lt;input ng-model=&quot;search.nome&quot;&gt;
Cervejaria: &lt;input ng-model=&quot;search.cervejaria&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href=&quot;&quot;&gt;Cerveja&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;Cervejaria&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr ng-repeat=&quot;cerveja in cervejas | filter:search | orderBy:'nome'&quot;&gt;
&lt;td&gt;{{ cerveja.nome | uppercase }}&lt;/td&gt;
&lt;td&gt;{{ cerveja.cervejaria }}&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

A sintaxe dos filtros funciona com a inclusão de um pipeline “|” dentro da expressão. No exemplo foi adicionado dois inputs, para filtrar por nome e por cervejaria, através do objeto search, incorporado na diretiva ng-repeat: “filter:search”. Os filtros também suportam ordenação através da propriedade orderBy. Adicionalmente existem alguns filtros padrão no Angular, como uppercase, lowercase e date. Há também a possibilidade de criar filtros customizados da seguinte forma:

App.filter('meuFiltro', function() {
    return function(data) {
        return data;
    };
});

Services

O conceito de separação de responsabilidades está presente no AngularJS através da aplicação de Services. Basicamente, os serviços são funções javascript que devem executar funções específicas, bem definidas, de fácil manutenção e testáveis. Dessa forma é possível modularizar e reaproveitar serviços em diferentes controladores, por meio da injeção de dependências, da seguinte forma:

//app.js
var app = angular.module(&quot;app&quot;, []);

app.service('CervejaService', function(){
    this.remover = function(cervejas, cerveja) {
        var index = cervejas.indexOf(cerveja);
        cervejas.splice(index, 1);
    };
});

//CervejaController.js
function CervejaController($scope, CervejaService) {
    //...
    $scope.remover = function(cerveja) {
        CervejaService.remover($scope.cervejas, cerveja);
    };
}

//cervejas.html
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href=&quot;&quot;&gt;Cerveja&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;Cervejaria&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr ng-repeat=&quot;cerveja in cervejas | filter:search | orderBy:'nome'&quot;&gt;
&lt;td&gt;{{ cerveja.nome | uppercase }}&lt;/td&gt;
&lt;td&gt;{{ cerveja.cervejaria }}&lt;/td&gt;
&lt;td&gt;&lt;button ng-click=&quot;remover(cerveja)&quot;&gt;Remover&lt;/button&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

Na primeira linha, é iniciado o módulo principal da aplicação, o primeiro argumento é o nome do módulo, o segundo é um lista de dependências externas que podem ser injetadas na aplicação, como plug-ins de controle de rotas, componentes de view, etc. Em seguida é definido o serviço CervejaService, nele implementamos uma função para remover uma cerveja de uma listagem, essa função pode ser usada em qualquer controller que injete este serviço. Para chamar a função de remoção, basta uma chamada via ng-click para a função remover(),definida no controller, para que o serviço seja invocado.

Jasmine

Graças a organização do código que o AngularJS nos proporciona, testá-lo se torna uma tarefa fácil. Para isso vamos usar o framework de teste Jasmine. Aliás, o próprio código do Angular é testado utilizando Jasmine. Precisamos importar apenas jasmine.js e o módulo de mocks do angular.

beforeEach(module('app'));
describe('CervejaController', function () {

    it('deve definir uma cerveja padrao com nome e cervejaria vazias', inject(function ($controller) {
        var scope = {};
        var ctrl = $controller('CervejaController', { $scope: scope });

        expect(scope.cerveja.nome).toBe('');
        expect(scope.cerveja.cervejaria).toBe('');
    }));

    it('deve iniciar com duas cervejas carregadas', inject(function ($controller) {
        var scope = {};
        var ctrl = $controller('CervejaController', { $scope: scope });

        expect(scope.cervejas.length).toBe(2);
    }));

    it('deve limpar uma cerveja', inject(function ($controller) {
        var scope = {};
        var ctrl = $controller('CervejaController', { $scope: scope });

        scope.cerveja = {nome: 'Cerpinha', cervejaria: 'Cerpa'};
        scope.limpar(); 

        expect(scope.cerveja.nome).toBe('');
        expect(scope.cerveja.cervejaria).toBe('');
    }));
});

A função beforeEach carrega o módulo antes de iniciarmos o teste do CervejaController. Basicamente foram definidos três testes, os dois primeiros testam o estado inicial do controller, o terceiro testa a função limpar. A função expect é responsável por fazer a asserção de nossos testes.

Daria para escrever um artigo só para os detalhes da implementação de testes com Jasmine e suas diferentes abordagens de cobertura dentro de aplicações com AngularJS.

Conclusões

As possibilidades do AngularJS não se esgotam aqui, existem vários recursos que não foram abordados. Mas o que foi apresentado serve de base pra você considerar utilizar este poderoso framework no seu próximo projeto.

Todo código apresentado aqui está disponível no github.

4 Comentários

  • Alexandre Cardoso (@accbel)

    Post legal, Kaio. Parabéns. Angular é uma boa ferramenta. Mas aí ficou uma pergunta. Quando vc apresenta motivos para utilizar Angular em um próximo projeto, você consegue apontar projetos onde os fatores time/cultura/requisitos/negócio são mais ou menos favoráveis ao uso do Angular?

    Deixo esse link aqui (http://www.fse.guru/2-years-with-angular) para alimentar um pouco a discussão. Eu mesmo já usei Angular em algumas coisas e penso que ele se encaixa bem “nas mãos certas”.

    Abraços!

  • Kaio Valente

    Valeu Alexandre.

    Sobre a questão que levantaste, acho que a utilização do Angular se encaixa bem em times onde os papéis de desenvolvedores de front e backend são claros. A separação que o angular proporciona, até mesmo para mockar o serviços de backend, facilita um pouco o processo de desenvolvimento. Outro ponto é o que o Marcelio comentou, como tudo acaba sendo orientado a uma API Rest, fica fácil reutilizar. Contudo, nem todo mundo gosta e é adepto de JavaScript e seus callback hell, hehe, logo um time que não tenha afinidade com javascript, eu não recomendaria.

    Interessante o artigo que postaste sobre a utilização do angular. Nunca cheguei a analisar algum benchmark que analise a performance do angular em aplicações críticas. Apenas aqueles que o comparam a outros frameworks com a mesma ideologia, como backbone, ember, knockout, etc… Mas concordo quanto a utilização de style guides e padrões pra se conseguir manter uma organização mínima pensando na manutenção. Enfim, todas as tecnologias têm suas limitações e cenários onde não são adequadas.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

%d blogueiros gostam disto: