En el anterior post sobre AngularJS vimos por encima el concepto de templates, como un mecanismo para separar fragmentos de código HTML.

En este post vamos a profundizar en el concepto de template, y vamos a explorar los mecanismos que proporciona Angular para inyectar código en nuestro layout, gracias a sus capacidades de routing. El tutorial que proponemos está basado en este fantástico tutorial de scotch.io.

Contenido de la demo

Vamos a crear una simple web con páginas home, about y contact (todo ello en modo single-page-view gracias a Angular).

Las ventajas:

  • Aplicación single-page
  • No hay que refrescar la página al cambiar de vista
  • Cada página contiene diferente información

Estructura de archivos

  • script.js: Aquí meteremos todo nuestro código Angular
  • index.html: Layout principal
  • pages/
    • home.html
    • about.html
    • contact.html

Layout

Empezando por la parte más simple, vamos a crear en nuestro index.html un layout simple con una barra de navegación, e incluiremos Bootstrap como front-end framework y Font Awesome para disponer de iconos SVG escalables. Además, incluiré la etiqueta ng-app=”demoApp” en nuestro html, ya que así es como llamaré al módulo Angular para esta aplicación, y ng-controller=”mainCtrl” en nuestro body, dado que así llamaré al controlador.

<!-- index.html -->
    <!DOCTYPE html>
    <html ng-app="demoApp">
    <head>
      <!-- SCROLLS -->
      <!-- load bootstrap and fontawesome via CDN -->
      <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" />
      <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.0/css/font-awesome.css" />

      <!-- SPELLS -->
      <!-- load angular via CDN -->
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
      <script src="script.js"></script>
    </head>

    <body ng-controller="mainCtrl">
        <!-- HEADER AND NAVBAR -->
        <header>
            <nav class="navbar navbar-default">
            <div class="container">
                <div class="navbar-header">
                    <a class="navbar-brand" href="/">Angular Routing Example</a>
                </div>

                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#"><i class="fa fa-home"></i> Home</a></li>
                    <li><a href="#about"><i class="fa fa-shield"></i> About</a></li>
                    <li><a href="#contact"><i class="fa fa-comment"></i> Contact</a></li>
                </ul>
            </div>
            </nav>
        </header>

        <!-- MAIN CONTENT AND INJECTED VIEWS -->
        <div id="main">

            <!-- angular templating -->
            <!-- this is where content will be injected -->

        </div>

        <!-- FOOTER -->
        <footer class="text-center">
            <hr>
            <strong>- KaiKreations -</strong>
        </footer>

    </body>

    </html>

DETALLE: Observar que cuando, en nuestra barra de navegación, ponemos enlaces a las otras páginas, son enlaces internos (#, #about, #contact). Esto es por que no queremos que el navegador intente acceder a otra página, recordemos que nuestra aplicación es single-page-view.

Como vemos en el código, estamos incluyendo Bootstrap, Font Awesome y Angular a partir de sus CDNs, así como nuestro archivo script.js.
Acto seguido, definimos un header, con enlaces a las otras vistas, el cuadro de contenido central (main) y un footer.

Cabe destacar el uso que hacemos de font awesome, añadiendo los iconos de clases “fa fa-home”, “fa fa-shield” y “fa fa-comment” a nuestros enlaces.

Aplicación y controlador Angular

Abrimos nuestro script.js y:

  • Creamos un módulo Angular que llamaremos demoApp
  • Añadimos un controlador a nuestro módulo, llamado mainCtrl
  • Creamos el mensaje “Hello world!” a través del controlador.
// script.js
    // create the module and name it demoApp
    var demoApp = angular.module('demoApp', []);

    // create the controller and inject Angular's $scope
 demoApp.controller('mainCtrl', function($scope) {

        // create a message to display in our view
        $scope.message = 'Hello world!';
    });

Probando que la estructura funciona

Añadimos el mensaje en el main div de index.html, del siguiente modo:

    <div id="main">
        {{ message }}

        <!-- angular templating -->
        <!-- this is where content will be injected -->
    </div>

Y ejecutamos un servidor para ver el resultado. Desde Ubuntu, lo más sencillo es movernos al directorio donde tenemos los archivos, ejecutar por consola:

python -m SimpleHTTPServer

Y acceder a la url y puerto que nos indíque, que típicamente será: http://localhost:8000/

Angular routing Example

Utilizando rutas

Veamos como utilizar las herramientas de routing de Angular.

En primer lugar, necesitaremos indicarle a Angular donde queremos que ponga el contenido que va a ir cambiando en función de la página. Lo indicaremos con ng-view.

ng-view: Inyección de páginas

Angular nos proporciona la directiva ng-view, que permite inyectar contenido desde templates situados en una ruta determinada (en este caso “/home”, “/about” y “/contact”) en nuestro layout.

¿Como funciona? Debemos añadir la etiqueta

allí donde queremos que se rendericen nuestras páginas, es decir, abrimos index.html y en el main, añadimos:

 

    <div id="main">

        <!-- angular templating -->
        <!-- this is where content will be injected -->
        <div ng-view></div>

    </div>

Providers: configurando las vistas a través de rutas

Utilizaremos el provider $routeProvider de Angular para gestionar nuestras rutas. De este modo, Angular se encargará de obtener los templates e inyectarlos en nuestro layout.

DETALLE: Los providers, son objetos que crean (proporcionan) instancias de servicios y exponen APIs de configuración que pueden usarse para controlar la creación y el comportamiento de un servicio en tiempo de ejecución.

Por este motivo, los providers solo pueden inyectarse en funciones config, es decir, no podemos inyectar un provider en un controlador.

$routeProvider

Es un provider del servicio $route que expone una API que nos permite definir rutas para nuestra aplicación.
Cabe destacar que desde Angular 1.2, ya no está incluido en el módulo core, y para usarlo hay que incluir en la aplicación, el módulo ngRoute. Por este motivo, abriremos nuestro index.html y añadiremos la siguiente linea en el head:

<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular-route.js"></script>

 

Dicho esto, vamos a incluir $routeProvider en nuestro código, actualizando nuestro script.js del siguiente modo:

// script.js


    // include ngRoute for all our routing needs
    var demoApp = angular.module('demoApp', ['ngRoute']);

    // configure our routes
    demoApp.config(function($routeProvider) {
  $routeProvider

            // route for the home page
            .when('/', {
                templateUrl : 'pages/home.html',
                controller  : 'mainCtrl'
            })

            // route for the about page
            .when('/about', {
                templateUrl : 'pages/about.html',
                controller  : 'aboutCtrl'
            })

            // route for the contact page
            .when('/contact', {
                templateUrl : 'pages/contact.html',
                controller  : 'contactCtrl'
            });
    });

    // create the controller and inject Angular's $scope
 demoApp.controller('mainCtrl', function($scope) {
        // create a message to display in our view
        $scope.message = 'Hello world, this is the home page!';
    });

    demoApp.controller('aboutCtrl', function($scope) {
  $scope.message = 'Hi! This is the about page.';
    });

    demoApp.controller('contactCtrl', function($scope) {
  $scope.message = 'Would you like to contact us?';
    });

Explicando el código:

  • demoApp.config(function($routeProvider) {…}: Como ya se ha comentado, los providers deben inyectarse como funciones config de nuestro módulo.
  • $routeProvider.when(‘url’, {…}): El método when de $routeProvider nos permite decirle a angular que template cargar (templateUrl) y que controlador utilizar (controller) al seleccionar un enlace a una cierta url.

Creando nuestros templates:

Creamos el directorio pages, y los archivos home.html, about.html y contact.html. Les damos el siguiente contenido:

home.html:

<!-- home.html -->
    <div class="jumbotron text-center">
        <h1>Home Page</h1>
        <p>{{ message }}</p>
    </div>

about.html:

<!-- about.html -->
    <div class="jumbotron text-center">
        <h1>About Page</h1>
        <p>{{ message }}</p>
    </div>

contact.html:

<!-- contact.html -->
    <div class="jumbotron text-center">
        <h1>Contact Page</h1>
        <p>{{ message }}</p>
    </div>

Probando los resultados

Volvemos a ejecutar nuestro servidor, y podemos ver como ahora se carga el jumbotron con Hello world!, y además, los enlaces para cambiar de vista funcionan como era de esperar:

angular routing example II

DETALLE: Podemos observar que nuestra URL tiene un aspecto ligeramente desagradable (url/#/pagina). El hashtag lo incluye por defecto Angular, pero podemos deshacernos de el configurando el provider $locationProvider, y definiendo un path base para nuestras rutas relativas.

Mejorando el aspecto de nuestras URLs

Utilizando $locationProvider

Este provider sirve para configurar el servicio $location, que parsea la URL en la barra de direcciones, para efectuar cambios sobre la aplicación, y viceversa.

A través de $locationProvider, estableceremos el modo Html5 a true.

¿Qué es la API de Historial HTML5?

Es una forma estandarizada de manipular el historial del navegador utilizando un script. Esto le permite a Angular cambiar las rutas y URLs de nuestras páginas sin refrescar la página.

Veamos como afectan los cambios a nuestro script.js:

var demoApp = angular.module('demoApp', ['ngRoute']);

    // configure our routes
    demoApp.config(function($routeProvider, $locationProvider) {
        $routeProvider

            // route for the home page
            .when('/', {
                templateUrl : 'pages/home.html',
                controller  : 'mainCtrl'
            })

            // route for the about page
            .when('/about', {
                templateUrl : 'pages/about.html',
                controller  : 'aboutCtrl'
            })

            // route for the contact page
            .when('/contact', {
                templateUrl : 'pages/contact.html',
                controller  : 'contactCtrl'
            });

        $locationProvider.html5Mode(true);
    });

 

Estableciendo el directorio base para nuestros links relativos

Lo único que debemos hacer es incluir la etiqueta en nuestro de index.html:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">

    <base href="/">
</head>

Comprobamos de nuevo los resultados. Si nuestro navegador soporta HTML5, deberíamos ver como han cambiado las URLs:

Angular Routing Example III

 

Eso es todo. Espero que el tutorial haya sido de ayuda.

¡Saludos!