E se a aplicação começar a crescer?

Até agora, construímos aplicações AngularJS que mostravam dados na tela, através da camada view. Mas e se a aplicação for crescendo e precisar exibir mais informações na tela? Anexar mais pedaços de código a uma view pode transformá-la numa bagunça rapidamente.

A melhor solução é usar múltiplos templates (views) que mostram diferentes partes dos dados, tendo como base a URL que o usuário está visitando. Podemos fazer isso com as chamadas rotas de aplicação do AngularJS. Vamos precisar criar vários arquivos e pastas para este projeto (Obs: os códigos de cada arquivo estarão no final deste capítulo):

  • O arquivo app.js em js/;
  • Os arquivos HomeController.js e PhotoController em js/controllers/;
  • photos.js em js/services/;
  • A pasta js/views, que guardará os arquivos photo.html e home.html;

Vamos ver um exemplo de como isso pode ser feito. Na camada index.html, vamos adicionar as tags <div ng-view></div> num lugar específico (sugerido pelo tutorial CodeCademy). Dentro do arquivo app.js, escrevemos isso:

    var app = angular.module('GalleryApp', ['ngRoute']);
    app.config(function ($routeProvider) { 
        $routeProvider 
        .when('/', { 
            controller: 'HomeController', 
            templateUrl: 'views/home.html' 
        }) 
        .otherwise({ 
            redirectTo: '/' 
        }); 
    });

No método app.config() usamos o $routeProvider, que define as rotas de aplicação. Depois, usamos .When() para direcionar a URL para um controlador (no nosso caso, HomeController) e um template home.html. O HomeController usa o serviço js/services/photos.js, que pega um array externo e guarda na $scope.photos, dentro de HomeController. O template home.html usa a diretiva ng-repeat para exibir cada item daquele array de fotos que o serviço acessa.

Caso o usuário acidentalmente visite outra URL, .Otherwise() o redireciona para a URL /. Incluindo home.html dentro de <div ng-view> </ div>, em index.html, o usuário será redirecionado para lá.

Agora é hora de adicionar mais templates ao projeto. O que acontece quando o usuário clicar nas fotos? Até agora, nada. Mas vamos mudar isso, adicionando mais uma condição .when() à nossa app.js, assim:

    var app = angular.module('GalleryApp', ['ngRoute']);
    app.config(function ($routeProvider) { 
        $routeProvider 
        .when('/', { 
            controller: 'HomeController', 
            templateUrl: 'views/home.html' 
        })
        .when('/photos/:id', {
            controller: 'PhotoController',
        templateUrl: 'views/photo.html'
        })
        .otherwise({ 
            redirectTo: '/' 
        });
    });

Agora, se o usuário clicar em alguma das fotos do array que recebemos do serviço, um novo template se mostrará. Mas como fizemos isso?

Nós simplesmente mapeamos uma URL à PhotoController e a photo.html, para que pudéssemos acessar outras templates do projeto. No controlador PhotoController, usamos o $routeParams.id para recuperar os dados da URL. Note que incluímos $routeParams e o serviço Photos dentro de PhotoController, para que pudéssemos usá-los dentro deste controlador. O PhotoController tem o código abaixo:

    app.controller('PhotoController', ['$scope', 'photos', '$routeParams', function($scope, photos, $routeParams) {
        photos.success(function(data) {
        $scope.detail = data[$routeParams.id];
        });
    }]);

Para acessar cada foto, primeiro usamos o serviço que recebe os dados de um servidor externo (as fotos), e as acessamos pelo indice de cada uma das fotos através de $routeParams.id.

Assim como no exemplo anterior, qualquer propriedade anexada a $scope se torna disponível para acesso. Isto significa que, em photo.html, podemos usar expressões para exibir detalhes ($scope.detail, que está em PhotoController) de cada foto. Em photo.html, encontramos esse código:

    <div class="photo">
        <div class="container">
            <img ng-src="{{ detail.url }}">
            <h2 class="photo-title"> </h2>
            <p class="photo-author"> </p>
            <p class="photo-views"> </p>
            <p class="photo-upvotes"> </p>
            <p class="photo-pubdate"> </p>
        </div>
    </div>

Note que ainda não estamos exibindo informações sobre as fotos quando clicamos nelas. É porque não usamos as expressões para mostrá-las ainda. Dentro do template photo.html, temos que adicionar expressões que mostram os detalhes das fotos, assim:

    <div class="photo">
        <div class="container">
            <img ng-src="{{ detail.url }}">
            <h2 class="photo-title"> </h2>
            <p class="photo-author"> </p>
            <p class="photo-views"> {{photo.views|number}}</p>
            <p class="photo-upvotes"> {{photo.upvotes|number}}</p>
            <p class="photo-pubdate"> {{photo.pubdate|date}}</p>
        </div>
    </div>

Como prometido, abaixo estão os códigos completos de cada arquivo que usamos no tutorial. Porém, um aviso: é provável que alguma parte faça com que o projeto não funcione, pois os arquivos fonte de alguns deles estão em servidores externos.

Código da index.html:

    <!doctype html>
    <html>
      <head>
        <link href="https://s3.amazonaws.com/codecademy-content/projects/bootstrap.min.css" rel="stylesheet" />
        <link href='https://fonts.googleapis.com/css?family=Roboto:400,500,300' rel='stylesheet' type='text/css'>
        <link href="css/main.css" rel="stylesheet" />

        <!-- Include the core AngularJS library -->
        <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>

        <!-- Include the AngularJS routing library -->
        <script src="https://code.angularjs.org/1.2.28/angular-route.min.js"></script>
      </head>
      <body ng-app="GalleryApp">

        <div class="header">
          <div ng-view></div>
          <div class="container">
            <a href="/#/">
              <img src="img/logo.svg" width="80" height="80"> &#12501; &#65387; &#12488; &#12501; &#65387; &#12488;
            </a>
          </div>
        </div>



        <!-- Modules -->
        <script src="js/app.js"></script>

        <!-- Controllers -->
        <script src="js/controllers/HomeController.js"></script>
        <script src="js/controllers/PhotoController.js"></script>

        <!-- Services -->
        <script src="js/services/photos.js"></script>
      </body>
    </html>

Código de home.html

    <div class="main">
      <div class="container">
        <h2>Recent Photos</h2>
        <div class="row">
          <div class="item col-md-4" ng-repeat="photo in photos">
            <a href="#/photos/{{$index}}">
              <img class="img-responsive" ng-src="{{ photo.url }}">
              <p class="author">by {{ photo.author }}</p>
            </a>
          </div>
        </div>
      </div>
    </div>

Código de photo.html

    <div class="photo">
      <div class="container">
        <img ng-src="{{ detail.url }}">
        <h2 class="photo-title"> </h2>
        <p class="photo-author"> </p>
        <p class="photo-views"> {{ detail.views | number }} </p>
        <p class="photo-upvotes"> {{ detail.upvotes | number }} </p>
        <p class="photo-pubdate"> {{ detail.pubdate | date }}</p>
      </div>
    </div>

Código de HomeController.js

    app.controller('HomeController', ['$scope', 'photos', function($scope, photos) {
      photos.success(function(data) {
        $scope.photos = data;
      });
    }]);

Código de PhotoController.js

    app.controller('PhotoController', ['$scope', 'photos', '$routeParams', function($scope, photos, $routeParams) {
      photos.success(function(data) {
        $scope.detail = data[$routeParams.id];
      });
    }]);