Olá pessoal, beleza?

No post anterior, Introdução ao AngularJS, fizemos uma explanação sobre algumas características importantes do AngularJS. Hoje vamos falar sobre os controladores e as boas práticas que podem ser adotadas em sua utilização.

Entendendo os Controladores

Os Controladores (Controllers), como o próprio nome diz, são responsáveis pelo controle da aplicação. É neles que gerenciamos o fluxo de dados apresentados na view. Quando um controlador é anexado ao DOM, via ng-controller, um novo objeto do controlador será instanciado, de acordo com a especificação do construtor. Na sequência, um novo escopo ($scope) filho será criado e disponibilizado como um parâmetro injetável no construtor do controlador.

[Boa Prática]

  1. O controlador deve ser utilizado apenas para a lógica de negócio da aplicação. É nele que devemos setar o estado inicial do $scope e/ou adicionar comportamento.
  2. Não é recomendado usá-lo para:
    • Manipular o DOM: para a manipulação do DOM é aconselhável utilizar diretivas, com o intúito de encapsular esse comportamento. Ademais, com essa prática o código fica mais manutenível e testável.
    • Formatar Inputs: para isso use os controladores de formulários.
    • Filtrar Dados: o AngularJS dispõe de filtros padrão, além da opção de escrever filtros customizados.
    • Compartilhar código: para compartilhar estados ou dados com outros controladores devemos utilizar as factories/services.

Vamos pegar o exemplo do post Introdução ao AngularJS e adicionar o controlador, aqui você pode vê-lo rodando:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<!--Declaração do módulo da aplicação-->
<html ng-app="app">

<head>
    <meta charset="utf-8">
    <title>Exemplo 1 - Blog do Gabriel Feitosa</title>
</head>

<body>
    <h1>Entendendo os Controladores</h1>
    <!--Declaração do nosso MeuController-->
    <div ng-controller="MeuController">
        <input type="text" ng-model="nome" placeholder="Diz teu nome aí" />
        <h1>Eita , o controlador setou o estado inicial do $scope.nome!</h1>
    </div>
    <footer>
        <hr>
        <a href="http://www.gabrielfeitosa.com"> Blog do Gabriel Feitosa</a>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
    <script>
        //Declarando o módulo da aplicação	
        var app = angular.module('app', []);
        //Declarando o construtor do MeuController
        app.controller('MeuController', ['$scope', function($scope) {
            $scope.nome = 'Gabriel';
        }]);
    </script>
</body>

</html>

Neste exemplo, criamos o controlador (MeuController) no módulo app, utilizando o método controller(). Esse método o mantém fora do escopo global. O objeto $scope é inserido dentro do MeuController através do mecanismo de injeção de dependência. Depois utilizamos a diretiva ng-controller, na div, para anexar o nosso controlador ao DOM.

Adicionando comportamento ao $scope

Quando queremos executar alguma ação na view, precisamos adicionar comportamento ao escopo. Antes disso, no entanto, é necessário anexar os métodos ao $scope. Assim, eles ficarão disponíveis para a camada de visão.

O código abaixo pode ser visto rodando aqui.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html ng-app="app">

<head>
    <meta charset="utf-8" />
    <title>Exemplo 2 - Blog do Gabriel Feitosa</title>
</head>

<body>
    <h1>Entendendo os Controladores</h1>
    <div ng-controller="MeuController">
        <form>
            <input type="text" ng-model="item" placeholder="Informe um item" />
            <button ng-click="addItem()">Adicionar</button>
        </form>
        <br/>
        <h1> Itens</h1>
        <ul>
            <li ng-repeat="i in itens"></li>
        </ul>
    </div>
    <footer>
        <hr/>
        <a href="http://www.gabrielfeitosa.com"> Blog do Gabriel Feitosa</a>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
    <script>
        var app = angular.module('app', []);
        app.controller('MeuController', ['$scope', function($scope) {
            $scope.item = '';
            $scope.itens = [];
            $scope.addItem = function() {
                $scope.itens.push($scope.item);
                $scope.item = '';
            }
        }]);
    </script>
</body>

</html>

Para adicionar um item a lista de ítens, foi anexado ao $scope o método addItem ($scope.addItem). Já para utilizar o método addItem, utilizamos a diretiva ng-click. Sempre que houver o evento de click no botão o método será chamado.

Para exibir os itens adicionados utilizamos a diretiva ng-repeat. Quando um item for adicionado um novo elemento <li> será criado.

Nota: Para evitar o erro Error: ngRepeat:dupes na diretiva ng-repeat quando adicionamos ítens duplicados, é necessário usar o track by. O uso é indispensável porque o AngularJS utiliza uma chave única para ligar o nó do DOM ao elemento que está iterando. Mais detalhes sobre o assunto aqui.

Herança de $scope

Quando adicionamos os controllers em hierarquias, um dentro do outro, temos uma herança de escopos. Isso acontece porque a diretiva ng-controller cria um novo $scope filho, então o $scope que cada controlador recebe terá acesso aos métodos e atributos do controlador acima na hierarquia.

O código abaixo pode ser visto rodando aqui

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<!--Declaração do módulo da aplicação-->
<html ng-app="app">
    <head>
        <meta charset="utf-8"/>
        <title>Exemplo 3 - Blog do Gabriel Feitosa</title>
        <style type="text/css">
            div.heranca div {
                padding: 5px;
                border: solid 2px #000;
            }
        </style>
    </head>
    <body>
        <h1>Entendendo os Controladores - Herança de $scope</h1>
        <!--Declaração do nosso MeuController-->
        <div class="heranca">
            <div ng-controller="PaiController">
                <p>Eu sou  !</p>
                <div ng-controller="FilhoController">
                    <p>Eu sou  </p>
                    <div ng-controller="NetoController">
                        <p>Eu sou  </p>
                    </div>
                </div>
            </div>
        </div>
        <footer>
            <hr/>
            <a href="http://www.gabrielfeitosa.com"> Blog do Gabriel Feitosa</a>
        </footer>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
        <script>
            var app = angular.module('app',[]);
            
            app.controller('PaiController',['$scope',function($scope){
                $scope.nome = 'Gabriel';
                $scope.sobrenome='Feitosa';
            }]); 
            
            app.controller('FilhoController',['$scope',function($scope){
                $scope.nome = 'Lampião';
            }]); 	
            
            app.controller('NetoController',['$scope',function($scope){
                $scope.sobrenome='Junior';
            }]); 	
        </script>
    </body>
</html>

Como resultado desse aninhamento de controladores, são criados quatro objetos $scope (o root scope e um $scope para cada controlador). Para acessar uma propriedade do escopo pai, nós usamos o $parent.

Ainda há propriedades dos controladores que podem ser exploradas como, por exemplo, o uso do controller as ao invés do $scope. Vocês podem ver nesse plunker um exemplo utilizando o controller as e outras boas práticas, como o conceito de responsabilidade única (single responsability) para os arquivos javascript.

Por hoje é isso pessoal, espero que tenham gostado.

Abraços e até a próxima!