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/
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:
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:
Eso es todo. Espero que el tutorial haya sido de ayuda.
¡Saludos!