E aí galera, beleza?

Hoje falaremos um pouco sobre as rotas (ngRoute) do AngularJS. Nos posts anteriores falamos sobre controladores e serviços, se você ainda não está familiarizado com o framework recomendo a leitura.

Rotas – Para que servem?

Em aplicações que utilizam o conceito de single page, a navegação de uma página para outra é crucial. Quando a aplicação cresce e se torna mais complexa, precisamos encontrar uma maneira de gerenciar as páginas pelas quais o usuário vai navegar através da aplicação.

Poderíamos fazer toda a aplicação em um único arquivo e gerenciar o modo que a tela se comporta fazendo um gerenciamento de estados (por exemplo, escondendo e exibindo componentes), porém, já sabemos que fazer isso é completamente inviável para manutenção e saúde da aplicação (e da nossa querida saúde também!).

O módulo ngRoute é a solução que precisávamos. Ele nos permite gerenciar os templates a serem inseridos na view de acordo com a navegação do usuário. Ou seja, quando há uma ação de mudança de página, o módulo é capaz de “injetar” o template correspondente (desce mais um pouquinho que te mostro como faz).

Como configurar?

O serviço $routeProvider, que faz parte do módulo ngRoute, é utilizado para realizar a configuração das rotas. Ele facilita a conexão do controlador com o template (view) e a URL que será mapeada com a rota. Com esse recurso, podemos utilizar o histórico do navegador e os favoritos.

Nota: $routeProvider é o provider do serviço $route. Por se tratar de um provider, ele só pode ser injetado dentro da função config. Portanto, não podemos utilizar $routeProvider dentro de um controlador.

Instalação

Para utilizar o angular-route em nossa aplicação, precisamos adicionar a referência do javascript após a referência do próprio AngularJS.

<script src="//code.angularjs.org/1.4.4/angular-route.js"></script>

Depois, devemos adicionar a dependência do módulo ngRoute a nossa app.

angular.module('feira-app',['ngRoute'])

Template

Para utilizar um template, é necessário combinar a diretiva ng-view (linha 10) com a rota. Essa diretiva nos permite especificar exatamente onde no DOM nós queremos renderizar o template de acordo com a rota atual.

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
  

<!DOCTYPE html>
<html ng-app="feira-app">
<head>
  <meta charset="UTF-8">
  <title>Blog do Gabriel Feitosa > AngularJS: Rotas (ngRoute)</title>
</head>
<body>
  <h1>Animais para Adoção</h1>
  
  <div ng-view style="border: 1px solid"></div>
  
  <div>
    <p><b>Vamos ver o que está acontecendo?</b></p>
    <pre>Path ($location.path()) = {{$location.path()}}</pre>
    <pre>Template ($route.current.templateUrl) = {{$route.current.templateUrl}}</pre>
    <pre>Controlador ($route.current.controller) = {{$route.current.controller}}</pre>
    <pre>Titulo da Página ($route.current.scope.titulo) = {{$route.current.scope.titulo}}</pre>
    <pre>Parâmetros da URL ($routeParams) = {{$routeParams}}</pre>
  </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.4/angular.min.js"></script>
  <script src="//code.angularjs.org/1.4.4/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/config.js"></script>
  <script src="js/animal.lista.controller.js"></script>
  <script src="js/animal.detalhe.controller.js"></script>
  <script src="js/animal.factory.js"></script>
</body>
</html>  
 

Por enquanto não vamos nos preocupar com o restante do código, principalmente os que estão entre as linhas 14 e 18, explicarei na sessão de eventos.

A diretiva ngView segue os seguintes passos:

  • Toda vez que o evento $routeChangeSucess é disparado, a view é atualizada;
  • Se houver um template associado com a rota atual, um novo escopo é criado;
  • A última _view _é removida e, consequentemente, o último escopo é limpo;
  • O novo escopo é vinculado ao novo template;
  • O controlador é vinculado ao escopo, caso haja um controlador para a rota;
  • O evento $viewContentLoaded é disparado.

Configurando as Rotas

A configuração das rotas se dá através dos métodos whenotherwise do serviço $routeProvide. Na nossa aplicação, ele está sendo injetado dentro da função config (linha 6).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
(function() {
  'use strict';

  angular.module('feira-app')
    .config(function($routeProvider) {
      $routeProvider
        .when('/animais', {
          templateUrl: 'lista.html',
          controller: 'AnimalListaController'
        })
        .when('/animais/:id', {
          templateUrl: 'detalhe.html',
          controller: 'AnimalDetalheController'
        }).otherwise({
          redirectTo: '/animais'
        });
    });
})();
 

O método when (linha 7), recebe dois parâmetros (path e route):

O primeiro parâmetro é o path da rota, que é comparado ao $location.path da URL atual. Podemos passar um parâmetro na URL utilizando o :param, como na nossa rota /animais:id. O resgate desse parâmetro é feito no service $routeparams (linha 18 - index.html).

O segundo parâmetro é o objeto de configuração da rota. É neste momento que definimos qual template e controlador serão injetados. Outras configurações como resolve, redirectTo e reloadOnSearch também são válidas. Os detalhes sobre essas configurações podem ser vistos aqui.

O método otherwise é utilizado para definir uma rota padrão. Assim, quando nenhuma rota for encontrada o AngularJS redirecionará a aplicação para essa rota padrão.

Eventos

O serviço $route dispara eventos em diferentes estágios do fluxo de uma rota. Esses eventos são importantes quando queremos manipular as rotas e são particularmente importantes quando desejamos detectar se um usuário está logado ou não na aplicação.

$routeChangeStart

É disparado antes da mudança da rota. É nesta etapa que todas as dependências necessárias são resolvidas para que a rota mude com sucesso. Uma vez que todas as dependências tenham sido resolvidas o evento $routeChangeSucess é disparado.

Parâmetros:

  • angularEvent: objeto de evento;
  • next: informação sobre a futura rota;
  • current: informação sobre a rota atual;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
(function() {
  'use strict';
  angular.module('feira-app', ['ngRoute']);

  angular.module('feira-app')
    .run(function($rootScope, $route, $routeParams, $location) {
      
      $rootScope.$on('$routeChangeStart',function(evt,next,current){
        console.log('Nome do Evento:'+evt.name);
        console.log('Próxima Rota:'+ angular.toJson(next));
        console.log('Rota Atual:'+ angular.toJson(current));
      });

      $rootScope.$route = $route;
      $rootScope.$location = $location;
      $rootScope.$routeParams = $routeParams;
    });
})();
 

Aquela explicação que fiquei devendo lá em cima vem agora.

Na função run (linhas 6) estamos setando os services $route, $routeparams e $location no $rootScope. Isso está sendo feito para que possamos utilizar esses serviços na view (linhas 14 a 18 do index.html). Através desses serviços, podemos identificar os parâmetros da URL, qual template e controlador estão sendo injetados na rota atual, entre outras informações.

$routeChangeSuccess

É disparado após a mudança bem sucedida da rota.

Parâmetros:

  • angularEvent: o objeto de evento;
  • current: informação sobre a rota atual;
  • previous: informações da rota anterior, ou undefined se for a primeira rota a ser invocada.

$routeChangeError

Se alguma mudança de rota for rejeitada, esse evento será disparado.

Parâmetros:

  • angularEvent: o objeto de evento;
  • current: informação sobre a rota atual;
  • previous: informações da rota anterior;
  • rejection: a _promise _que foi rejeitada.

$routeUpdate

Este evento é disparado se a propriedade reloadOnSearch estiver setada com false e se estivermos reutilizando a mesma instância do controlador.

Parâmetros:

  • angularEvent: o objeto de evento;
  • current: informação sobre a rota atual/anterior;

Ainda há configurações que não terei espaço para abordar aqui, como o hashbang mode (html5Mode), o hashPrefix e os detalhes do serviço $location.

Existe um outro módulo de rotas chamado AngularUI Router, que utiliza uma abordagem diferente. Particularmente o considero melhor que o ngRoute. Ele é baseado em estados (states) e não em URL. Falaremos mais sobre ele em um futuro post. 😉

Então é isso pessoal, espero que tenham curtido.

Os arquivos do nosso exemplo podem ser visualizados na íntegra no gist ou no repositório de código do blog. Aqui você pode ver a aplicação rodando.

Tem dúvidas, críticas ou sugestões? Deixe um comentário aí que terei o maior prazer em responder.

Abraços e até a próxima!