AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf ·...

218

Transcript of AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf ·...

Page 1: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un
Page 2: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

AngularJs Paso a PasoLa primera guía completa en español para adentrarse paso a paso enel mundo de AngularJS

Maikel José Rivero Dorta

Este libro está a la venta en http://leanpub.com/angularjs-paso-a-paso

Esta versión se publicó en 2016-02-02

This is a Leanpub book. Leanpub empowers authors and publishers with the LeanPublishing process. Lean Publishing is the act of publishing an in-progress ebook usinglightweight tools and many iterations to get reader feedback, pivot until you have theright book and build traction once you do.

© 2014 - 2016 Maikel José Rivero Dorta

Page 3: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

¡Twitea sobre el libro!Por favor ayuda a Maikel José Rivero Dorta hablando sobre el libro en Twitter!

El tweet sugerido para este libro es:

”AngularJS Paso a Paso” un libro de @mriverodorta para empezar desde cero. Adquieretu copia en http://bit.ly/AngularJSPasoAPaso

El hashtag sugerido para este libro es #AngularJS.

Descubre lo que otra gente está diciendo sobre el libro haciendo click en este enlacepara buscar el hashtag en Twitter:

https://twitter.com/search?q=#AngularJS

Page 4: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un
Page 5: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Dedicado aEn primer lugar este libro esta dedicado a todos los que de alguna forma u otra me han apoyadoen llevar a cabo la realización de este libro donde plasmo mis mejores deseos de compartir mi

conocimiento.

En segundo lugar a toda la comunidad de desarrolladores de habla hispana que en múltiplesocasiones no encuentra documentación en su idioma, ya sea como referencia o para aprender

nuevas tecnologías.

v

Page 6: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Índice general

Dedicado a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v

Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i

Traducciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ii

Prólogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iiiPara quien es este libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iiiQue necesitas para este libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iiiEntiéndase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iiiFeedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ivErrata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ivPreguntas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ivRecursos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv

Alcance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viCapítulo 1: Primeros pasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viCapítulo 2: Estructura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viCapítulo 3: Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viCapítulo 4: Servicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viCapítulo 5: Peticiones al servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiCapítulo 6: Directivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiCapítulo 7: Filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiCapítulo 8: Rutas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiCapítulo 9: Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiCapítulo 10: Recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiCapítulo 11: Formularios y Validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiiExtra: Servidor API RESTful . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viii

Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix

Segunda Edición . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x

Entorno de desarrollo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1Seleccionando el editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Page 7: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

ÍNDICE GENERAL

Preparando el servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2Gestionando dependencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

AngularJS y sus características . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Estructura MVC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Vinculación de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7Directivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7Inyección de dependencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Capítulo 1: Primeros pasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Vías para obtener AngularJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Incluyendo AngularJS en la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Atributos HTML5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10La aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10Tomando el Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17Bind Once Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18Observadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19Observadores para grupos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20Controladores como objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22Controladores Globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

Capítulo 2: Estructura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25Estructura de ficheros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25Estructura de la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Capítulo 3: Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30Creando módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30Minificación y Compresión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Inyectar dependencias mediante $inject . . . . . . . . . . . . . . . . . . . . . . . . . . . 32Inyección de dependencia en modo estricto . . . . . . . . . . . . . . . . . . . . . . . . . 33Configurando la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33Método run . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

Capítulo 4: Servicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40Provider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42Constant y Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45$provide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45Promesas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46Varias promesas a la vez . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

Page 8: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

ÍNDICE GENERAL

El constructor de las promesas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51Desplazamiento con $anchorScroll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56Log . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59Manejando Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61Retrasando funcionalidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62Creando repeticiones con intervalos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64Anotaciones en el DOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

Capítulo 5: Peticiones al servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67Objeto de configuración del servicio $http . . . . . . . . . . . . . . . . . . . . . . . . . . 68Métodos de acceso rápido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71Provider del servicio $http . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

Capítulo 6: Directivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76ng-class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76ng-style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79ng-list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79ng-non-bindable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80ng-repeat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80ng-if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85ng-include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86ng-cloak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86ng-href . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87ng-src y ng-srcset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87ng-blur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87ng-copy, ng-cut y ng-paste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87ng-dblclick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88ng-keydown, ng-keypress y ng-keyup . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88Eventos del mouse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88ng-change . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89ng-checked . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90ng-disabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90ng-readonly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91ng-selected . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91ng-submit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91ng-focus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92ng-strict-di . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92ng-model-options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92Creando las directivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Capítulo 7: Filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109Currency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

Page 9: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

ÍNDICE GENERAL

Number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110Uppercase y Lowercase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110limitTo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111OrderBy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112Creando filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

Capítulo 8: Rutas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116El módulo ngRoute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116Definiendo las rutas con $routeProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . 117Uniendo los componentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121Plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131Plantillas en cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133Precargando plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134El servicio $route . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136Cambio de parámetros en la ruta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141El servicio $location . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

Capítulo 9: Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148Propagando eventos hacia los scopes padres . . . . . . . . . . . . . . . . . . . . . . . . . 148Propagando eventos hacia los scopes hijos . . . . . . . . . . . . . . . . . . . . . . . . . . 149Escuchando eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150Objeto Evento de Angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

Capítulo 10: Recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152Obteniendo ngResource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152Primera petición al servidor REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153Parámetros del servicio $resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155El objeto de respuesta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157Instancia de un recurso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163Trailing Slash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

Capítulo 11: Formularios y Validación . . . . . . . . . . . . . . . . . . . . . . . . . . 167Reglas de Validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167Creando una regla de validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168Mejoras creando reglas de validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170Ejecutando validación asíncrona . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172El formulario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174Estados del formulario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174Estilos en el formulario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175Mostrando errores de validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177Estado de los elementos de formulario . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180Mostrando errores con ngMessages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

Page 10: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

ÍNDICE GENERAL

Reusando mensajes de validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184Soporte para nuevos elementos de HTML5 . . . . . . . . . . . . . . . . . . . . . . . . . . 185Validación de HTML5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187Otras formas de validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187Resetear elementos de formulario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191Nombre de elementos interpolables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

Servidor API RESTful . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195Requerimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195Instalando dependencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196Configurando el servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196Iniciando el servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196Uso del servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197

Page 11: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

AgradecimientosQuisiera agradecer a varias personas que me han ayudado en lograr este proyecto.Primero que todo a Jasel Morera por haber revisado el libro y corregido mucho de loserrores de redacción ya que no soy escritor y en ocasiones no sé cómo expresarme yllegar a las personas de una manera correcta. También agradecer a Anxo Carracedo porla foto de los pasos que aparece en la portada. A Wilber Zada Rosendi @wil63r¹ por eldiseño de la portada. También a todos los demás que de una forma u otrame han ayudadoa hacer realidad esta idea de escribir para la comunidad.

¹http://twitter.com/wil63r

i

Page 12: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

TraduccionesSi te gustaría traducir este libro a otro lenguaje, por favor escríbeme a @mriverodortacon tus intenciones. Ofreceré el 35% de las ganancias por cada libro vendido en tutraducción, la cual será vendida al mismo precio que el original. Además de una páginaen el libro para la presentación del traductor.

Nótese que el libro ha sido escrito en formato markdown con las especificaciones deLeanpub, las traducciones deberán seguir los mismos pasos.

ii

Page 13: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

PrólogoAngularJs paso a paso cubre el desarrollo de aplicaciones con el framework AngularJs.En este libro se tratarán temas esenciales para el desarrollo de aplicaciones web dellado del cliente. Además, trabajaremos con peticiones al servidor, consumiendo serviciosREST y haciendo que nuestro sistema funcione en tiempo real sin tener que recargar lapágina de nuestro navegador.

Para quien es este libro

Está escrito para desarrolladores de aplicaciones que posean un modesto conocimientode Javascript, así como de HTML5 y que necesiten automatizar las tareas básicas en eldesarrollo de una aplicaciónweb, específicamente en sistemas de una sola página,manejode rutas, modelos, peticiones a servidoresmediante Ajax, manejo de datos en tiempo realy otros.

Que necesitas para este libro

Para un correcto aprendizaje de este libro es necesario una serie de complementos que tepermitirán ejecutar los ejemplos y construir tu propia aplicación. Si estaremos hablandosobre el framework AngularJS es esencial que lo tengas a tu alcance, lo mismo usandoel CDN de Google o mediante una copia en tu disco duro. También necesitarás unnavegador para ver el resultadode tu aplicación, recomiendoGoogleChromepor su gransoporte de HTML5 y sus herramientas para el desarrollo. Además de lo anteriormentemencionado necesitarás un editor de código. Más adelante estaremos hablando sobrealgunas utilidades que harían el desarrollo más fácil pero que no son estrictamentenecesarias.

Entiéndase

Se emplearán diferentes estilos de texto, para distinguir entre los diferentes tipos deinformación. Aquí hay algunos ejemplos de los estilos y explicación de su significado.

Lo ejemplos de los códigos serán mostrado de la siguiente forma:

iii

Page 14: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Prólogo iv

1 <!DOCTYPE html>2 <html lang="es" ng-app="MiApp">3 <head>4 <meta charset="UTF-8">5 <title>Titulo</title>6 </head>7 <body>8 <div ng-controller="MiCtrl">Hola Mundo!</div>9 </body>

10 </html>

Feedback

El feedback de los lectores siempre es bienvenido. Me gustaría saber qué piensas acercade este libro que te ha gustado más y que no te ha gustado. Lo tendré presente parapróximas actualizaciones. Para enviar un feedback envía un tweet a@mriverodorta.

Errata

Este es el primer libro que escribo así que asumo que encontraran varios errores. Túpuedes ayudarme a corregirlos enviándome un tweet con el error que has encontrado a@mriverodorta junto con los detalles del error.

Los errores serán solucionados a medida que sean encontrados. De esta forma estaránarreglados en próximas versiones del libro.

Preguntas

Si tienes alguna pregunta relacionada con algún aspecto del libro puedes hacerla a@mriverodorta con tus dudas.

Recursos

AngularJS posee una gran comunidad a su alrededor además del equipo de Googleque trabaja dedicado a este framework. A continuación, mencionaré algunos de lossitios donde puedes encontrar recursos y documentación relacionada al desarrollo conAngularJS.

Page 15: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Prólogo v

Sitios de referencia

• Sitio web oficial http://www.angularjs.org²• Google+ https://plus.google.com/u/0/communities/115368820700870330756³• Proyecto en Github https://github.com/angular/angular.js⁴• Grupo de Google [email protected]• Canal en Youtube http://www.youtube.com/user/angularjs⁵• Twitter @angularjs

Extensiones

La comunidad alrededor de AngularJS ha desarrollado gran cantidad de librerías yextensiones adicionales que agregan diferentes funcionalidades al framework y tienensitio en: http://ngmodules.org⁶.

IDE y Herramientas

Si eres un desarrollador web, para trabajar con AngularJS no es necesario que utilicesalgo diferente de lo que ya estés acostumbrado, puedes seguir usandoHTML y Javascriptcomo lenguajes y si estás dando tus primeros pasos en este campo podrás utilizar uneditor de texto común. Aunque te recomendaría usar un ⁷IDE que al comienzo te será demucha ayuda con alguna de sus funciones como el auto-completamiento de código, hastaque tengas un mayor entendimiento de las propiedades y funciones. A continuación,recomendare algunos:

• WebStorm: Es un potente IDE multiplataforma que podrás usar lo mismo enMac,Linux o Windows. Además, se le puede instalar un Plugin para el trabajo conAngularJS que fue desarrollado por la comunidad.

• SublimeText: También multiplataforma y al igual posee un plugin para AngularJSpero no es un IDE es sólo un editor de texto.

• Espreso: Sólo disponible en Mac enfocado para su uso en el frontend.

Navegador

Nuestra aplicación de AngularJS funciona a través de los navegadores más popularesen la actualidad (Google Chrome, Safari, Mozilla Firefox). Aunque recomiendo GoogleChrome ya que posee una extensión llamada Batarang para inspeccionar aplicacionesAngularJS y la misma puede ser instalada desde Chrome Web Store.

²http://www.angularjs.org³https://plus.google.com/u/0/communities/115368820700870330756⁴https://github.com/angular/angular.js⁵http://www.youtube.com/user/angularjs⁶http://ngmodules.org⁷Integrated Development Environment

Page 16: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

AlcanceEste libro abarcará la mayoría de los temas relacionados con el framework AngularJS.Está dirigido a aquellos desarrolladores que ya poseen conocimientos sobre el uso deAngularJS y quisieran indagar sobre algún tema en específico. A continuación, describirépor capítulos los temas tratados en este libro.

Capítulo 1: Primeros pasos

En este capítulo se abordarán los temas iniciales para el uso del framework, sus prin-cipales vías para obtenerlo y su inclusión en la aplicación. Además de la definiciónde la aplicación, usos de las primeras directivas y sus ámbitos. La creación del primercontrolador y su vinculación con la vista y el modelo. Se explicarán los primeros pasospara el uso del servicio $scope.

Capítulo 2: Estructura

Este capítulo se describirá la importancia de tener una aplicación organizada. La es-tructura de los directorios y archivos. Comentarios sobre el proyecto angular-seedpara pequeñas aplicaciones y las recomendaciones para aquellas de estructura medianaso grandes. Además de analizar algunos de los archivos esenciales para hacer que elmantenimiento de la aplicación sea sencillo e intuitivo.

Capítulo 3: Módulos

En este capítulo comenzaremos por aislar la aplicación del entorno global con la creacióndel módulo. Veremos cómo definir los controladores dentro del módulo. Tambiénveremos cómo Angular resuelve el problema de la minificación en la inyección dedependencias y por último los métodos de configuración de la aplicación y el espaciopara tratar eventos de forma global con el método config() y run() del módulo.

Capítulo 4: Servicios

AngularJS dispone de una gran cantidad de servicios que hará que el desarrollo de laaplicación seamás fácil mediante la inyección de dependencias. También comenzaremosa definir servicios específicos para la aplicación y se detallarán cada una de las vías paracrearlos junto con sus ventajas.

vi

Page 17: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Alcance vii

Capítulo 5: Peticiones al servidor

Otra de las habilidades de AngularJS es la interacción con el servidor. En este capítulotrataremos lo relacionado con las peticiones a los servidores mediante el servicio $http.Como hacer peticiones a recursos en un servidor remoto, tipos de peticiones y más.

Capítulo 6: Directivas

Las directivas son una parte importante de AngularJS y así lo reflejará la aplicación quecreemos con el framework. En este capítulo haremos un recorrido por las principalesdirectivas, con ejemplos de su uso para que sean más fáciles de asociar. Además, secrearán directivas específicas para la aplicación.

Capítulo 7: Filtros

En este capítulo trataremos todo lo relacionado con los filtros, describiendo los queproporciona angular en su núcleo. También crearemos filtros propios para realizaracciones específicas de la aplicación. Además de su uso en las vistas y los controladoresy servicios.

Capítulo 8: Rutas

Una de las principales características de AngularJS es la habilidad que tiene para crearaplicaciones de una sola página. En este capítulo estaremos tratando sobre el módulongRoute, el tema del manejo de rutas sin recargar la página, los eventos que se procesanen los cambios de rutas. Además, trataremos sobre el servicio $location.

Capítulo 9: Eventos

Realizar operaciones dependiendo de las interacciones del usuario es esencial para lasaplicaciones hoy en día. Angular permite crear eventos y dispararlos a lo largo de laaplicación notificando todos los elementos interesados para tomar acciones. En estecapítulo veremos el proceso de la propagación de eventos hacia los $scopes padres ehijos, así como escuchar los eventos tomando acciones cuando sea necesario.

Capítulo 10: Recursos

En la actualidad existen cada vez más servicios RESTful en internet, en este capítulocomenzaremos a utilizar el servicio ngResource de Angular. Realizaremos peticiones aun API REST y ejecutaremos operaciones CRUD en el servidor a través de este servicio.

Page 18: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Alcance viii

Capítulo 11: Formularios y Validación

Hoy en día la utilización de los formularios en la web es masiva, por lo general todaslas aplicaciones web necesitan al menos uno de estos. En este capítulo vamos a vercomo emplear las directivas para validar formularios, así como para mostrar erroresdependiendo de la información introducida por el usuario en tiempo real.

Extra: Servidor API RESTful

En el Capítulo 10 se hace uso de una API RESTful para demostrar el uso del servicio$resource. En este extra detallaré el proceso de instalación y uso de este servidor que ala vez viene incluido con el libro y estará disponible con cada compra. El servidor estacreado utilizandoNodeJs, Express.js yMongoDB.

Page 19: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

IntroducciónA lo largo de los años hemos sido testigo de los avances y logros obtenidos en el desarrolloweb desde la creación deWorld Wide Web. Si comparamos una aplicación de aquellosentonces con una actual notaríamos una diferencia asombrosa, eso nos da una ideade cuan increíble somos los desarrolladores, cuantas ideas maravillosas se han hechorealidad y en la actualidad son las que nos ayudan a obtener mejores resultados en lacreación de nuevos productos.

A medida que el tiempo avanza, las aplicaciones se hacen más complejas y se necesitansoluciones más inteligentes para lograr un producto final de calidad. Simultáneamentese han desarrollado nuevas herramientas que ayudan a los desarrolladores a lograr finesenmenor tiempo y conmayor eficiencia. Hoy en día las aplicacionesweb tienen una granimportancia, por la cantidad de personas que utilizan Internet para buscar informaciónrelacionada a algún tema de interés, hacer compras, socializar, presentar su empresa onegocio, en fin, un sin número de posibilidades que nos brinda la red de redes.

Una de las herramientas que nos ayudará mucho en el desarrollo de una aplicación webesAngularJS, un framework desarrollado porGoogle, lo que nos da una idea de las basesy el soporte del framework por la reputación de su creador. En adición goza de unacomunidad a su alrededor que da soporte a cada desarrollador con soluciones a todotipo de problemas.

Por estos tiempos existen una gran cantidad de frameworks que hacen un increíbletrabajo a la hora de facilitar las tareas de desarrollo. Pero AngularJS viene siendo comoel más popular diría yo, por sus componentes únicos, los cuales estaremos viendo másadelante.

En este libro estaremos tratando el desarrollo de aplicaciones web con la ayuda deAngularJS y veremos cómo esta obra maestra de framework nos hará la vida más fácil ala hora de desarrollar aplicaciones web.

ix

Page 20: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Segunda EdiciónEn esta segunda edición se cubrirán los cambios y nuevas funcionalidades de la versión1.3 de AngularJS en adelante. Esta nueva versión del framework tiene gran cantidad decambios en las funcionalidades ya existentes. Además, tiene algunos cambios que debesconsiderar antes de cambiar de versión ya que podría poner en riesgo la cobertura de tuaplicación con respecto a los navegadores.

En esta revisión del libro encontrarás la información necesaria para sacar un mejorprovecho de las nuevas funcionalidades. Si estas a punto de comenzar a crear una nuevaaplicaciónpuedes hacer uso del contenido sin preocupaciones. Si ya tienes una aplicacióny deseas migrar a la nueva versión de Angular, antes de hacerlo debes estar conscientede los problemas que podría presentar.

En esta segunda edición del libro describiré los cambios relacionados en cada capítulodel libro donde hablare al detalle sobre lasmodificaciones del framework para esta nuevaversión.

En la versión 1.3 de AngularJS hay grandes mejoras en el rendimiento. Con solo cambiarde una versión anterior a la nueva versión, sin hacer cambios en el código de la aplicación,el rendimiento serámuchomejor. Esta versión incluyemejoras en el procesamiento y enel manejo de operaciones con el DOM. Además, se incluyen nuevas funcionalidades conun API más sencillo que permitirá utilizar las nuevas funcionalidades de forma más fácily con menos código.

Estas nuevas funcionalidades brindan más control sobre los elementos como los formu-larios, mensajes de validación, modelos, controladores y directivas. Todos estos cambiosestán orientados a hacerte más productivo con la nueva versión del framework.

Aunque tienemuchas partes buenas podría tener algunos inconvenientes. En esta versiónAngularJS ha retirado el soporte para la versión 8 de Internet Explorer. Esto quiere decirque si tu aplicación está enfocada para usuarios deWindows XP no sería una buena ideahacer un cambio a esta nueva versión sin considerar la pérdida de usuarios. Aunque estees uno de los cambios que nos hace pensar en cambiar de versión, es uno de los que hahecho que el rendimiento de angular se haya mejorado considerablemente además de lareducción del código base del framework.

Otro de los cambios importantes es que el framework ha dejado el soporte de jQuery conversiones menores a la 2.1.1, esto afecta a los desarrolladores que hacen uso del jQueryen sus aplicaciones en sustitución a la versión jqLite.

Para finalizar con los cambios inconvenientes debemos agregar que en esta versión seha eliminado la posibilidad de utilizar funciones globales como controladores. Aunque

x

Page 21: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Segunda Edición xi

Este último cambio no debería afectarte ya que es una mala práctica el uso de funcionesglobales como controladores y deberías evitar su uso, aunque uses una versión anteriora la 1.3 de Angular.

Page 22: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Entorno de desarrolloEs esencial que para sentirnos cómodos con el desarrollo tengamos a la mano ciertavariedad de utilidades para ayudarnos a realizar las tareas de una forma más fácil y enmenor tiempo. Esto lo podemos lograr con un buen editor de texto o un IDE. No senecesita alguno específicamente, podrás continuar utilizando el que estás acostumbradosi ya has trabajado Javascript anteriormente.

Seleccionando el editor

Existen una gran variedad de editores e IDE en el mercado hoy en día, pero hay algunosque debemos prestar especial atención. Me refiero a editores como Visual Studio Codeo Sublime Text 2/3 y al IDE JetBrains WebStorm, los tres son multi plataforma.Personalmente uso Visual Studio Code para mi desarrollo de día a día, con este editorpodremos escribir código de una formamuy rápida gracias a las posibilidades que brindael uso de las referencias a los archivos de definición.

Visual Studio Code

Para Sublime Text existen plugins que te ayudarán a aumentar la productividad. Elprimer plugin esAngularJs desarrollado por el grupo deAngular-UI, solo lo uso para elauto completamiento de las directivas en las vistas así que en sus opciones deshabilito elauto completamiento en el Javascript. El segundo plugin es AngularJS Snippets el cualuso para la creación de controladores, directivas, servicios y más en el Javascript. Estosdos plugins aumentan en gran cantidad la velocidad en que escribes código.

1

Page 23: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Entorno de desarrollo 2

Sublime Text

Por otra parte WebStorm es un IDE con todo tipo de funcionalidades, auto comple-tamiento de código, inspección, debug, control de versiones, refactorización y ademástambién tiene un plugin para el desarrollo con AngularJS que provee algunas funciona-lidades similares a los de Sublime Text.

WebStorm

Preparando el servidor

Habiendo seleccionado ya el editor o IDE que usarás para escribir código el siguientepaso es tener listo un servidor donde poder desarrollar la aplicación. En esta ocasióntambién tenemos varias opciones, si deseas trabajar online Plunker⁸ es una buena opcióny Cloud9⁹ es una opción aún más completa donde podrás sincronizar tu proyecto

⁸http://plnkr.co⁹http://cloud9.io

Page 24: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Entorno de desarrollo 3

mediante git y trabajar en el pc local o en el editor online.

En caso de que quieras tener tu propio servidor local para desarrollo puedes usarNodeJscon ExpressJS para crear una aplicación. Veamos un ejemplo.

Archivo: App/server.js

1 var express = require('express'),

2 app = express();

3

4 app.use(express.static(__dirname+'/public'))5 .get('*', function(req, res){

6 res.sendFile('/public/index.html', {root:__dirname});

7 }).listen(3000);

Después de tener este archivo listo ejecutamos el comando node server.js y podremosacceder a la aplicación en la maquina local por el puerto 3000 (localhost:3000). Todas laspeticiones a la aplicación serán redirigidas a index.html que se encuentra en la carpetapublic. De esta forma podremos usar el sistema de rutas de AngularJS con facilidad.

Otra opción es usar el servidor Apache ya sea instalado en local en el pc como servidorhttp o por las herramientas AMP. Para Mac MAMP, windows WAMP y linux LAMP.Con este podremos crear un host virtual para la aplicación. En la configuración de lossitios disponibles de apache crearemos un virtualhost como el ejemplo siguiente.

1 <VirtualHost *:80>2 # DNS que servirá a este proyecto

3 ServerName miapp.dev

4 # La direccion de donde se encuentra la aplicacion

5 DocumentRoot /var/www/miapp

6 # Reglas para la reescritura de las direcciones

7 <Directory /var/www/miapp>8 RewriteEngine on

9 # No reescribir archivos o directorios.

10 RewriteCond %{REQUEST_FILENAME} -f [OR]

11 RewriteCond %{REQUEST_FILENAME} -d

12 RewriteRule ^ - [L]

13

14 # Reescribir todo lo demás a index.html para usar el modo de rutas HTML5

15 RewriteRule ^ index.html [L]

16 </Directory>17 </VirtualHost>

Page 25: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Entorno de desarrollo 4

Después de haber configurado el host virtual para la aplicación necesitamos crear eldns local para que responda a nuestra aplicación. En Mac y Linux esto se puede lograren el archivo /etc/hosts y en Windows está en la carpeta dentro de la carpeta delsistema C:Windows\system32Drivers\etc\hosts. Escribiendo la siguiente línea al finaldel archivo.

1 127.0.0.1 miapp.dev

Después de haber realizado los pasos anteriores reiniciamos el servicio de apache paraque cargue las nuevas configuraciones y podremos acceder a la aplicación desde elnavegador visitando http://miapp.dev.

Gestionando dependencias

En la actualidad la comunidad desarrolla soluciones para problemas específicos cada vezmás rápido. Estas soluciones son compartidas para que otros desarrolladores puedanhacer uso de ellas sin tener que volver a reescribir el código. Un ejemplo es jQuery,LoDash, Twitter Bootstrap, Backbone e incluso el mismo AngularJS. Sería un pocoengorroso si para la aplicación que fuéramos a desarrollar necesitáramos un númeroconsiderado de estas librerías y tuviéramos que buscarlas y actualizarlas de formamanual.

Con el objetivo de resolver este problema Twitter desarrolló una herramienta llamadabower que funciona como un gestor de dependencias y a la vez nos da la posibilidadde compartir nuestras creaciones con la comunidad. Esta herramienta se encargará deobtener todas las dependencias de la aplicación y mantenerlas actualizada por nosotros.

Para instalar bower necesitamos tener instalado previamente npm y NodeJs en el pc.Ejecutando el comando npm install -g bower en la consola podremos instalar bower deforma global en el sistema. Luego de tenerlo instalado podremos comenzar a gestionarlas dependencias de la aplicación. Lo primero que necesitamos es crear un archivobower.json donde definiremos el nombre de la aplicación y las dependencias. El archivotiene la siguiente estructura.

Page 26: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Entorno de desarrollo 5

Archivo: App/bower.json

1 {

2 "name": "miApp",

3 "dependencies": {

4 "angular": "~1.2.*"

5 }

6 }

De esta forma estamos diciendo a bower que nuestra aplicación se llamamiApp y quenecesita angular para funcionar. Una vez más en la consola ejecutamos bower install enla carpeta que tiene el archivo bower.json. Este creará una carpeta bower_componentsdonde incluirá el framework para que lo podamos usar en la aplicación.

La creacióndel archivobower.json lo podemos lograr de forma interactiva. En la consolavamos hasta el directorio de la aplicación y ejecutamos bower init. Bower nos hará unaserie de preguntas relacionadas con la aplicación y luego creará el archivo bower.jsoncon los datos que hemos indicado. Teniendo el archivo listo podemos proceder a instalardependencias de la aplicación ejecutando bower install --save angular lo que instalaráAngularJS como la vez anterior. El parámetro –save es muy importante porque es elque escribirá la dependencia en el archivo bower.json de lo contrario AngularJS seríainstalado pero no registrado como dependencia.

Una de las principales ventajas que nos proporciona Bower es que podremos distribuirla aplicación sin ninguna de sus dependencias. Podremos excluir la carpeta de lasdependencias sin problemas ya que en cada lugar donde se necesiten las dependenciaspodremos ejecutar bower install y bower las gestionará por nosotros. Esto es muy útil ala hora de trabajar en grupo con sistemas de control de versiones como Github ya queen el repositorio solo estaría el archivo bower.json y las dependencias en las maquinaslocales de los desarrolladores.

Para saber más sobre el uso de Bower puedes visitar su página oficial y ver la documen-tación para conocer acerca de cada una de sus características.

Page 27: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

AngularJS y sus característicasCon este framework tendremos la posibilidad de escribir una aplicación demanera fácil,que con solo leerla podríamos entender qué es lo que se quiere lograr sin esforzarnosdemasiado. Además de ser un framework que sigue el patrón MVC¹⁰ nos brinda otrasposibilidades como la vinculación de datos en dos vías y la inyección de dependencia.Sobre estos términos estaremos tratando más adelante.

Plantillas

AngularJS nos permite crear aplicaciones de una sola página, o sea podemos cargar di-ferentes partes de la aplicación sin tener que recargar todo el contenido en el navegador.Este comportamiento es acompañado por un motor de plantillas que genera contenidodinámico con un sistema de expresiones evaluadas en tiempo real.

El mismo tiene una serie de funciones que nos ayuda a escribir plantillas de una formaorganizada y fácil de leer, además de automatizar algunas tareas como son: las iteracionesy condiciones paramostrar contenido. Este sistema es realmente innovador y usaHTMLcomo lenguaje para las plantillas. Es suficientemente inteligente como para detectarlas interacciones del usuario, los eventos del navegador y los cambios en los modelosactualizando solo lo necesario en el DOM¹¹ y mostrar el contenido al usuario.

Estructura MVC

La idea de la estructura MVC no es otra que presentar una organización en el código,donde el manejo de los datos (Modelo) estará separado de la lógica (Controlador) dela aplicación, y a su vez la información presentada al usuario (Vistas) se encontrarátotalmente independiente. Es un proceso bastante sencillo donde el usuario interactúacon las vistas de la aplicación, éstas se comunican con los controladores notificando lasacciones del usuario, los controladores realizan peticiones a los modelos y estos gestio-nan la solicitud según la información brindada. Esta estructura provee una organizaciónesencial a la hora de desarrollar aplicaciones de gran escala, de lo contrario sería muydifícilmantenerlas o extenderlas. Es importante aclararmencionar que en esta estructurael modelo se refiere a los diferentes tipos de servicios que creamos con Angular.

¹⁰(Model ViewController) Estructura deModelo, Vista y Controlador introducido en los 70 y obtuvo su popularidad en el desarrollo de aplicacionesde escritorio.

¹¹Doccument Object Model

6

Page 28: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

AngularJS y sus características 7

Vinculación de datos

Desde que el DOM pudo ser modificado después de haberse cargado por completo,librerías como jQuery hicieron que la web fuera más amigable. Permitiendo de estamanera que en respuesta a las acciones del usuario el contenido de la página puedeser modificado sin necesidad de recargar el navegador. Esta posibilidad de modificarel DOM en cualquier momento es una de las grandes ventajas que utiliza AngularJS paravincular datos con la vista.

Pero eso no es nuevo, jQuery ya lo hacía antes, lo innovador es, ¿Que tan bueno sería sipudiéramos lograr vincular los datos que tenemos en nuestros modelos y controladoressin escribir nada de código? Seria increíble verdad, pues AngularJS lo hace de unamanera espectacular. En otras palabras, nos permite definir que partes de la vistaserán sincronizadas con propiedades de Javascript de forma automática. Esto ahorraenormemente la cantidad de código que tendríamos que escribir para mostrar los datosdel modelo a la vista, que en conjunto con la estructuraMVC funciona de maravillas.

Directivas

Si vienes del dominio de jQuery esta será la parte donde te darás cuenta que el desarrolloavanza de forma muy rápida y que seleccionar elementos para modificarlos posterior-mente, como ha venido siendo su filosofía, se va quedando un poco atrás comparándolocon el alcance de AngularJS. jQuery en si es una librería que a lo largo de los añosha logrado que la web en general se vea muy bien con respecto a tiempos pasados. Asu vez tiene una popularidad que ha ganado con resultados demostrados y posee unacomunidad muy amplia alrededor de todo el mundo.

Uno de los complementos más fuertes de AngularJS son las directivas, éstas vienen aremplazar lo que en nuestra web haría jQuery. Más allá de seleccionar elementos delDOM, AngularJS nos permite extender la sintaxis de HTML. Con el uso del frameworknos daremos cuenta de una gran cantidad de atributos que no son parte de las especifi-caciones de HTML.

AngularJS tiene una gran cantidad de directivas que permiten que las plantillas seanfáciles de leer y a su vez nos permite llegar a grandes resultados en unas pocas líneas.Pero todo no termina ahí, AngularJS nos brinda la posibilidad de crear nuestras propiasdirectivas para extender el HTMLy hacer que nuestra aplicación funcionemuchomejor.

Inyección de dependencia

AngularJS está basado en un sistema de inyección de dependencias donde nuestroscontroladores piden los objetos que necesitan para trabajar a través del constructor.

Page 29: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

AngularJS y sus características 8

Luego AngularJS los inyecta de forma tal que el controlador puede usarlo como seanecesario. De esta forma el controlador no necesita saber cómo funciona la dependenciani cuáles son las acciones que realiza para entregar los resultados.

Así estamos logrando cada vez más una organización en nuestro código y logrando loque es una muy buena práctica: “Los controladores deben responder a un principio deresponsabilidad única”. En otras palabras, el controlador es para controlar, o sea recibepeticiones y entregar respuestas basadas en estas peticiones, no genera el mismo lasrespuestas. Si todos nuestros controladores siguen este patrón nuestra aplicación serámuy fácil de mantener incluso si su proceso de desarrollo es retomado luego de unapausa de largo tiempo.

Si no estás familiarizado con alguno de los conceptos mencionados anteriormente o note han quedado claros, no te preocupes, todos serán explicados en detalle más adelante.Te invito a que continúes ya que a mi modo de pensar la programación es más de códigoy no de tantos de conceptos. Muchas dudas serán aclaradas cuando lo veas en la práctica.

Page 30: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasosEn este capítulo daremos los primeros pasos para el uso deAngularJS. Debemos entenderque no es una librería que usa funciones para lograr un fin, AngularJS está pensadopara trabajar por módulos, esto le brida una excelente organización a nuestra aplicación.Comenzaremos por lo más básico como es la inclusión de AngularJS y sus plantillas enHTML.

Vías para obtener AngularJS

Existen varias vías para obtener el framework, mencionaré tres de ellas:

La primera forma es descargando el framework de forma manual desde su web ofi-cial http://www.angularjs.org¹² donde tenemos varias opciones, la versión normal y laversión comprimida. Para desarrollar te recomiendo que uses la versión normal ya quela comprimida está pensada para aplicaciones en estado de producción además de nomostrar la información de los errores.

La segunda vía es usar el framework directamente desde el CDN de Google. Tambiénencontrará la versión normal y la comprimida. La diferencia de usar una copia local ola del CDN se pone en práctica cuando la aplicación está en producción y un usuariovisita cualquier otra aplicación que use la misma versión de AngularJS de tu aplicación,el CDN no necesitará volver a descargar el framework ya que ya el navegador lo tendráen cache. De esta forma tu aplicación iniciará más rápido.

En tercer lugar, es necesario tener instalado en el pc npm y Bower. Npm es el ges-tor de paquetes de NodeJS que se obtiene instalando Nodejs desde su sitio oficialhttp://nodejs.org¹³. Bower es un gestor de paquetes para el frontend. No explicaré estavía ya que está fuera del alcance de este libro, pero esta opción esta explicada en varioslugares en Internet, así que una pequeña búsqueda te llevara a obtenerlo.

Nosotros hemos descargado la versión normal desde el sitio oficial y la pondremos enun directorio /lib/angular.js para ser usado.

Incluyendo AngularJS en la aplicación

Ya una vez descargado el framework lo incluiremos simplemente como incluimos unarchivo Javascript externo:

¹²http://www.angularjs.org¹³http://nodejs.org

9

Page 31: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 10

1 <script src="lib/angular.js"></script>

Si vamos a usar el CDN de Google seria de la siguiente forma:

1 <script2 src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.js">3 </script>

De esta forma ya tenemos el framework listo en nuestra aplicación para comenzar ausarlo.

Atributos HTML5

ComoAngularJS tiene un gran entendimiento del HTML, nos permite usar las directivassin el prefijo data por ejemplo, obtendríamos el mismo resultado si escribiéramos el có-digo data-ng-app que si escribiéramos ng-app. La diferencia está a la hora de que el códigopase por los certificadores que al ver atributos que no existen en las especificaciones deHTML5 pues nos darían problemas.

La aplicación

Después de tener AngularJS en nuestra aplicación necesitamos decirle donde comenzary es donde aparecen las Directivas. La directiva ng-app define nuestra aplicación. Es unatributo de clave=”valor” pero en casos de que no hayamos definido un módulo no seránecesario darle un valor al atributo.Más adelante hablaremos de losmódulos ya que seríael valor de este atributo, por ahora solo veremos lo más elemental.

AngularJS se ejecutará en el ámbito que le indiquemos, es decir abarcará todo el entornodonde usemos el atributo ng-app. Si lo usamos en la declaración de HTML entoncesse extenderá por todo el documento, en caso de ser usado en alguna etiqueta como porejemplo en el body su alcance se verá reducido al cierre de la misma. Veamos el ejemplo.

Page 32: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 11

1 <!DOCTYPE html>2 <html lang="en">3 <head>4 <meta charset="UTF-8">5 <title>{{Respuesta}}</title>6 </head>7 <body ng-app>8 <div class="container">9 Entiendes el contenido de este libro?

10 <input type="checkbox" ng-model="respuesta">11 <div ng-hide="respuesta">12 <h2>Me esforzare más!</h2>13 </div>14 <div ng-show="respuesta">15 <h2>Felicidades!</h2>16 </div>17 </div>18 <script src="lib/angular.js"></script>19 </body>20 </html>

En este ejemplo encontramos varias directivas nuevas, pero no hay que preocuparse,explicaremos todo a lo largo del libro. Podemos observar lo que analizábamos del ámbitode la aplicación en el ejemplo anterior, en la línea 5 donde definimos el título de la páginahay unos {{ }}, en angular se usa para mostrar la información del modelo que declaramosen la línea 10 con la directiva ng-model. Vamos a llamarlo variables para entenderlomejor, cuando definimos un modelo con ng-model creamos una variable y en el títuloestamos tratando de mostrar su contenido con la notación {{ }}.

Podemos percatarnos que no tendremos el resultado esperado ya que el título está fueradel ámbito de la aplicación, porque ha sido definida en la línea 7 que es el body. Lo quequiere decir que todo lo que esté fuera del body no podrá hacer uso de nuestra aplicación.Prueba mover la declaración de ng-app a la etiqueta de declaración de HTML en la línea2 y observa que el resultado es el correcto ya que ahora el título está dentro del ámbitode la aplicación.

Cuidado.Sólo se puede tener una declaración de ng-app por página, sin importar que losámbitos estén bien definidos.

Ya has comenzado a escribir tu primera aplicación con AngularJS, a diferencia de losclásicos Hola Mundo! esta vez hemos hecho algo diferente. Se habrán dado cuenta lo

Page 33: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 12

sencillo que fue interactuar con el usuario y responder a los eventos del navegador,y ni siquiera hemos escrito una línea de Javascript, interesante verdad, pues lo queacabamos de hacer es demasiado simple para la potencia de AngularJS, veremos cosasmás interesantes a lo largo del Libro.

A continuación, se analizará las demás directivas que hemos visto en el ejemplo anterior.Para entender el comportamiento de la directiva ng-model necesitamos saber qué sonlos scopes en AngularJS. Pero lo dejaremos para último ya que en ocasiones es un pococomplicado explicarlo por ser una característica única de AngularJS y si vienes de usarotros frameworks como Backbone o EmberJS esto resultará un poco confuso.

En el ejemplo anterior hemos hecho uso de otras dos directivas, ng-show y ng-hidelas cuales son empleadas como lo dice su nombre para mostrar y ocultar contenidosen la vista. El funcionamiento de estas directivas es muy sencillo muestra u oculta unelemento HTML basado en la evaluación de la expresión asignada al atributo de ladirectiva. En otras palabras, evalúa a verdadero o falso la expresión para mostrar uocultar el contenido del elemento HTML. Hay que tener en cuenta que un valor falso seconsiderara cualquiera de los siguientes resultados que sean devueltos por la expresión.

• f• 0• false• no• n• []

Preste especial atención a este último porque nos será de gran utilidad a la hora demostrar u ocultar elementos cuando un arreglo esté vacío.

Esta directiva logra su función, pero no por arte de magia, es muy sencillo, AngularJStiene un amplio manejo de clases CSS las cuales vienen incluidas con el framework. Unejemplo es .ng-hide, que tiene la propiedad display definida como none lo que indicaa CSS ocultar el elemento que ostente esta clase, además tiene una marca !importantpara que tome un valor superior a otras clases que traten de mostrar el elemento. Lasdirectivas que muestran y ocultan contenido aplican esta clase en caso que quieranocultar y la remueven en caso que quieran mostrar elementos ya ocultos.

Aquí viene una difícil, Scopes y su uso en AngularJS. Creo que sería una buena idea irviendo su comportamiento y su uso a lo largo del libro y no tratar de definir su conceptoahora, ya que solo confundiría las cosas. Se explicará de forma sencilla según se vayautilizando. En esencia el scope es el componente que une las plantillas (Vistas) con loscontroladores, creo que por ahora será suficiente con esto. En el ejemplo anterior en lalínea 10 donde utilizamos la directiva ng-model hemos hecho uso del scope para definiruna variable, la cual podemos usar como cualquier otra variable en Javascript.

Page 34: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 13

Realmente la directiva ng-model une un elemento HTML a una propiedad del $scopeen el controlador. Si esta vez $scope tiene un $ al comienzo, no es un error de escritura,es debido a que $scope es un servicio de AngularJS, otro de los temas que estaremostratando más adelante. En resumen el modelo respuesta definido en la línea 10 del ejem-plo anterior estaría disponible en el controlador como $scope.respuesta y totalmentesincronizado en tiempo real gracias a el motor de plantillas de AngularJS.

Tomando el Control

Veamos ahora un ejemplo un poco más avanzado en el cual ya estaremos usandoJavascript y definiremos el primer controlador.

Esta es la parte de la estructura MVC que maneja la lógica de nuestra aplicación.Recibe las interacciones del usuario con nuestra aplicación, eventos del navegador, y lastransforma en resultados para mostrar a los usuarios.

Veamos el ejemplo:

1 <body ng-app>2 <div class="container" ng-controller="miCtrl">3 <h1>{{ mensaje }}</h1>4 </div>5 <script>6 function miCtrl ($scope) {

7 $scope.mensaje = 'Mensaje desde el controlador';

8 }

9 </script>10 <script src="lib/angular.js"></script>11 </body>

En este ejemplo hemos usado una nueva directiva llamada ng-controller en la línea2. Esta directiva es la encargada de definir que controlador estaremos usando para elámbito del elemento HTML donde es utilizada. El uso de esta etiqueta sigue el mismopatrón de ámbitos que el de la directiva ng-app. Como has podido notar el controladores una simple función de Javascript que recibe un parámetro, y en su código sólo defineuna propiedad mensaje dentro del parámetro.

Esta vez no es un parámetro lo que estamos recibiendo, AngularJS interpretará el códigocon la inyección de dependencias, como $scope es un servicio del framework, crearáuna nueva instancia del servicio y lo inyectará dentro del controlador haciéndolo asídisponible para vincular los datos con la vista. De esta forma todas las propiedades queasignemos al objeto $scope estarán disponibles en la vista en tiempo real y completa-mente sincronizado. El controlador anterior hace que cuando usemos {{ mensaje }} en la

Page 35: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 14

vista tenga el valor que habíamos definido en la propiedad con el mismo nombre del$scope.

Habrán notado que al recargar la página primero muestra la sintaxis de {{ mensaje }} ydespués muestra el contenido de la variable del controlador. Este comportamiento esdebido a que el controlador aún no ha sido cargado en el momento que se muestra esaparte de la plantilla. Lomismo que pasa cuando tratas demodificar elDOM y este aún noestá listo. Los que vienen de usar jQuery saben a quéme refiero, es que en elmomento enque se está tratando de mostrar la variable, aún no ha sido definida. Ahora, si movemoslos scripts hacia el principio de la aplicación no tendremos ese tipo de problemas ya quecuando se trate de mostrar el contenido de la variable, esta vez si ya ha sido definido.

Veamos el siguiente ejemplo:

1 <body ng-app>2 <script src="lib/angular.js"></script>3 <script>4 function miCtrl ($scope) {

5 $scope.mensaje = 'Mensaje desde el controlador';

6 }

7 </script>8 <div class="container" ng-controller="miCtrl">9 <h1>{{ mensaje }}</h1>

10 </div>11 </body>

De esta forma el problema ya se ha resuelto, pero nos lleva a otro problema, que pasasi tenemos grandes cantidades de código y todos están en el comienzo de la página. Lesdiré que pasa, simplemente el usuario tendrá que esperar a que termine de cargar todoslos scripts para que comience a aparecer el contenido, en muchas ocasiones el usuario seva de la página y no espera a que termine de cargar. Claro, no es lo que queremos paranuestra aplicación, además de que es una mala práctica poner los scripts al inicio de lapágina. Como jQuery resuelve este problema es usando el evento ready del Document,en otras palabras, el estará esperando a que el DOM esté listo y después ejecutará lasacciones pertinentes.

Con AngularJS podríamos hacer lo mismo, pero esta vez usaremos algo más al estilode AngularJS, es una directiva: ng-bind=”expresion”. Esencialmente ng-bind hace queAngularJS remplace el contenido del elemento HTML por el valor devuelto por laexpresión. Hace lo mismo que ** {{ }} ** pero con la diferencia de que es una directivay no se mostrara nada hasta que el contenido no esté listo.

Veamos el siguiente ejemplo:

Page 36: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 15

1 <body ng-app>2 <div class="container" ng-controller="miCtrl">3 <h1 ng-bind="mensaje"></h1>4 </div>5 <script>6 function miCtrl ($scope) {

7 $scope.mensaje = 'Mensaje desde el controlador';

8 }

9 </script>10 <script src="lib/angular.js"></script>11 </body>

Como podemos observar en el ejemplo anterior ya tenemos los scripts al final y notenemos el problema demostrar contenido no deseado. Al comenzar a cargarse la páginase crea el elemento H1 pero sin contenido, y no es hasta que Angular tenga listo elcontenido en el controlador y vinculado al $scope que se muestra en la aplicación.

Debo destacar que con el uso de la etiqueta ng-controller estamos creando un nuevoscope para su ámbito cada vez que es usada. Lo anterior, significa que cuando existantres controladores diferentes cada uno tendrá su propio scope y no será accesible a laspropiedades de uno al otro. Por otra parte, los controladores pueden estar anidados unosdentro de otros, de esta forma también obtendrán un scope nuevo para cada uno, con ladiferencia de que el scope del controlador hijo tendrá acceso a las propiedades del padreen caso de que no las tenga definidas en sí mismo.

Veamos el siguiente ejemplo:

1 <body ng-app>2 <div class="container">3 <div ng-controller="padreCtrl">4 <button ng-click="logPadre()">Padre</button>5 <div ng-controller="hijoCtrl">6 <button ng-click="logHijo()">Hijo</button>7 <div ng-controller="nietoCtrl">8 <button ng-click="logNieto()">Nieto</button>9 </div>

10 </div>11 </div>12 </div>13 <script>14 function padreCtrl ($scope) {

15 $scope.padre = 'Soy el padre';

16 $scope.logPadre = function(){

Page 37: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 16

17 console.log($scope.padre);

18 }

19 }

20 function hijoCtrl ($scope) {

21 $scope.hijo = 'Soy el primer Hijo';

22 $scope.edad = 36;

23 $scope.logHijo = function(){

24 console.log($scope.hijo, $scope.edad);

25 }

26 }

27 function nietoCtrl ($scope) {

28 $scope.nieto = 'Soy el nieto';

29 $scope.edad = 4;

30 $scope.logNieto = function(){

31 console.log($scope.nieto, $scope.edad, $scope.hijo);

32 }

33 }

34 </script>35 <script src="lib/angular.js"></script>36 </body>

Ops, quizás se haya complicado un poco el código, pero lo describiremos a continuación.Para comenzar veremos que hay una nueva directiva ng-click=”“. Esta directiva notiene nada de misterio, por si misma se explica sola, es la encargada de especificar elcomportamiento del evento Click del elemento y su valor es evaluado. En cada uno delos botones se le ha asignado un eventoClickpara ejecutar una función en el controlador.

Como han podido observar también cada uno de los controladores están anidados unodentro de otros, el controlador nietoCtrl dentro de hijoCtrl y este a su vez dentro depadreCtrl. Veamos el contenido de los controladores. En cada uno se definen propiedadesy una función que posteriormente es llamada por el evento Click de cada botón de lavista. En el padreCtrl se ha definido la propiedad padre en el $scope y ésta es impresa a laconsola al ejecutarse la función logPadre.

En el hijoCtrl se ha definido la propiedad hijo y edad que igualmente serán impresas ala consola. En el nietoCtrl se han definido las propiedades nieto y edad, de igual formase imprimen en la consola. Pero en esta ocasión trataremos de imprimir también lapropiedad hijo la cual no está definida en el $scope, así que AngularJS saldrá delcontrolador a buscarla en el $scope del padre.

El resultado de este ejemplo se puede ver en el navegador con el uso de las herramientasde desarrollo en su apartado consola.

Quizás te habrás preguntado si el $scope del padreCtrl tiene un scope padre. Pues larespuesta es si el $rootScope. El cual es también un servicio que puede ser inyectado

Page 38: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 17

en el controlador mediante la inyección de dependencias. Este rootScope es creado conla aplicación y es único para toda ella, o sea todos los controladores tienen acceso a esterootScope lo que quiere decir que todas las propiedades y funciones asignadas a estescope son visibles por todos los controladores y este no se vuelve a crear hasta la páginano es recargada.

Estarás pensando que el rootScope es la vía de comunicación entre controladores. Puedeser usado con este fin, aunque no es una buena práctica, para cosas sencillas no estaríanada mal. Pero no es la mejor forma de comunicarse entre controladores, ya veremos dequé forma se comunican los controladores en próximos capítulos.

Bindings

El uso del $scope para unir la vista con el controlador y tener disponibilidad de losdatos en ambos lugares es una de las principales ventajas que tiene Angular sobre otrosframeworks. Aunque no es un elemento único de Angular si es destacable que en otroses mucho más complicado hacer este tipo de vínculo. Para ver lo sencillo que seríarecoger información introducida por el usuario, y a la vez mostrarla en algún lugar de laaplicación completamente actualizada en tiempo real, veamos el siguiente ejemplo.

1 <body ng-app>2 <div ng-controller="ctrl">3 <p ng-bind="mensaje"></p>4 <input type="text" ng-model="mensaje">5 </div>6 <script src="lib/angular.js"></script>7 <script>8 function ctrl($scope) {

9 $scope.mensaje = '';

10 }

11 </script>12 </body>

En el ejemplo anterior podemos observar que amedida que escribimos en la caja de texto,automáticamente se va actualizando en tiempo real en el controlador como en la vista.Como todas las cosas esta funcionalidad viene con un costo adicional, y es que ahoraAngular estará pendiente de los cambios realizados por el usuario. Esto significa que encada interacción del usuario angular ejecutara un $digest para actualizar cada elementonecesario.

En cada ocasión que necesitemos observar cambios en algún modelo, Angular colocaraun observador ($watch) para estar al tanto de algún cambio y poder actualizar la vista

Page 39: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 18

correctamente. Esta funcionalidad es especialmente útil cuando estamos pidiendo datosa los usuarios o esperando algún tipo de información desde un servidor remoto.

También podremos colocar nuestros propios observadores ya que $watch es uno de losmétodos del servicio $scope. Más adelante explicare como establecer observadores ytomar acciones cuando estos se ejecuten.

Elmétodo $digest procesa todos los observadores ($watch) declarados en el $scope y sushijos. Debido a que algún $watch puede hacer cambios en el modelo, $digest continuaráejecutando los observadores hasta que se deje de hacer cambios. Esto quiere decir quees posible entrar en un bucle infinito, lo que llevaría a un error. Cuando el númerode iteraciones sobrepasa 10 este método lanzara un error ‘Maximum iteration limitexceeded’.

En la aplicación mientras más modelos tenemos más $watch serán declarados y a la vezmás largo será el proceso de $digest. En grandes aplicaciones es importante mantenerel control de los ciclos ya que este proceso podría afectar de manera sustancial elrendimiento de la aplicación.

Bind Once Bindings

Una de las nuevas funcionalidades de la versión 1.3 del framework es la posibilidadde crear bind de los modelos sin necesidad de volver a actualizarlos. Es importantemencionar que el uso de esta nueva funcionalidad debe utilizarse cuidadosamente yaque podría traer problemas para la aplicación. Como explique anteriormente en cadaocasión que esperamos cambios en el modelo, es registrado un nuevo $watch para serejecutado en el $digest.

Con el nuevo método de hacer binding al modelo Angular simplemente imprimirá elmodelo en la vista y se olvidará que tiene que actualizarlo. Esto quiere decir que no estarápendiente de cambios en el modelo para ejecutar el $digest. De esta forma la aplicaciónpodría mejorar en rendimiento drásticamente. Esto es de gran utilidad ya que muchasde las ocasiones donde utilizamos el modelo no tienen cambios después de que se cargala vista, y aun así Angular está observando los cambios en cada uno de ellos.

Es importante que esta funcionalidad se utilice de manera sabia en los lugres que estásseguro que no es necesario actualizar. Por lo general esta funcionalidad tendrá mejorutilidad en grandes aplicaciones donde el $digest ralentiza la ejecución dado la grancantidad de modelos y ciclos que necesita en las actualizaciones.

Para hacer “one time binding” esmuy sencillo solo necesitas poner ‘::’ delante delmodelo.Vamos a verlo en una nueva versión del ejemplo anterior.

Page 40: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 19

1 <body ng-app='app'>2 <div ng-controller="ctrl">3 <p ng-bind="::mensaje"></p>4 <input type="text" ng-model="mensaje">5 </div>6 <script src="bower_components/angular/angular.js"></script>7 <script>8 angular.module('app', [])

9 .controller('ctrl', function($scope){

10 $scope.mensaje = 'Primer mensaje';

11 });

12 </script>13 </body>

Al hacer cambios en la caja de texto podrás notar que en la parte superior no se actualizael valor. Como podrás darte cuenta esta nueva funcionalidad es muy útil. Existen otroslugares donde podemos hacer uso de esta funcionalidad, como son dentro de la directivang-repeat para transformar una colección en ‘one time binding’. Algo que destacar enel uso con la directiva ng-repeat es que los elementos de la colección no se convertiránen ‘one time binding’. En otro de los lugares donde podemos hacer uso es dentro de lasdirectivas propias que crees para tu aplicación.

Observadores

Es muy sencillo implementar nuestros propios observadores para actuar cuando secambia el modelo de alguno de los elementos que observamos. Primero, el servicio $scopetiene un método $watch que es el que utilizaremos para observar cambios. Este métodorecibe varios parámetros, primero es una cadena de texto especificando el modelo al quese quiere observar. El segundo parámetro es una función que se ejecutara cada vez que elmodelo cambie, esta recibe el nuevo valor y el valor anterior. Y existe un tercer parámetroque es utilizado para comprobar referencias de objetos, pero este no lo utilizaremosmuya menudo.

Vamos a crear un ejemplo con una especie de validación muy sencilla a través deluso de $watch. Crearemos un elemento input de tipo password y comprobaremos sila contraseña tiene un mínimo de 6 caracteres. De no cumplir con esa condición semostrará un mensaje de error al usuario. Para empezar, crearemos el HTML necesario.

Page 41: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 20

1 <div ng-controller="Controlador">2 <form action="#">3 Contraseña: <input type="password" ng-model="password">4 <p ng-show="errorMinimo">Error: No cumple con el mínimo de caracteres (6)</p>5 </form>6 </div>

Con la directiva ng-model estamos vinculando el password con el $scope para poderobservarlo. No te preocupes por la directiva que se muestra a continuación ng-show yaque esta se explicará más adelante en el libro, solo necesitas saber que será la encargadade mostrar y ocultar el mensaje de error. Ahora necesitamos crear el controlador paraobservar los cambios.

1 angular.module('app', [])2 .controller('Controlador', function ($scope) {

3 $scope.errorMinimo = false;4 $scope.$watch('password', function (nuevo, anterior) {

5 if (!nuevo) return;6 if (nuevo.length < 6) {

7 $scope.errorMinimo = true;8 } else {

9 $scope.errorMinimo = false;10 }

11 })

12 });

En el controlador inyectamos el servicio $scope y le asignamos una variable errorMinimoque será la encargada de definir si se muestra o no el error de validación. Acto seguidoimplementamos el observador mediante el método $watch del $scope. Como primerparámetro le pasaremos la cadena que definimos comomodelo con la directiva ng-modelen el HTML. Como segundo parámetro será una funciona anónima que recibirá comoparámetros el valor nuevo y el valor anterior. Dentro comprobamos si existe un valornuevo, de lo contrario salimos de la función. En caso de que exista un valor nuevocomprobamos que este tenga 6 o más caracteres, y definimos el valor de la variableerrorMinimo.

Ahora podremos ver el ejemplo en funcionamiento. Cuando comencemos a escribir enel veremos que el error aparece mientras no tenemos un mínimo de 6 caracteres en él.

Observadores para grupos

En la versión 1.3 de Angular se añadió una nueva opción para observar grupo demodelos. En esencia el funcionamiento es elmismoalmétodo $watchpero en esta ocasión

Page 42: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 21

observará un grupo de modelos y ejecutará la misma acción para cualquier cambioen estos. El nuevo método watchGroup recibe como primer parámetro un arreglo decadenas de texto con el nombre de cada uno de los elementos que se quieren observar.

Como segundo parámetro una función que se ejecutara cuando cualquiera de los ele-mentos observados tenga un cambio. Como con el método watch esta función tambiénrecibe los valores nuevos y los anteriores, pero en esta ocasión es un arreglo con losnuevos y otro con los antiguos. Es importante mencionar que el orden en que aparecenlos valores en el arreglo es el mismo en el que se especificaron en el primer parámetrode watchGroup.

Para ver un ejemplo de su uso, vamos a crear algo similar al ejemplo realizado parawatch pero en esta ocasión validaremos dos elementos password y comprobaremos queel valor de uno coincida con el otro. De no coincidir los valores, mostraremos un erroranunciando al usuario que los valores no coinciden.

Primero comenzaremos creando el HTML necesario para mostrar dos elementos pass-word y el mensaje de error. A cada uno de los elementos le daremos un modelo con ladirectiva ng-model, los cuales serán los mismos que observaremos más adelante en elcontrolador.

1 <div ng-controller="Controlador">2 <form action="#">3 Contraseña: <input type="password" ng-model="password"><br><br>4 Rectificar: <input type="password" ng-model="password2">5 <p ng-hide="coincidencia">Error: Las contraseñas no coinciden</p>6 </form>7 </div>

Ahora crearemos el controlador para observar los cambios en el modelo. Primero in-yectamos el servicio $scope y le asignamos una variable coincidencia que será la encargadade mostrar o no el error de validación. Después observaremos el grupo de elementospasándole como primer parámetro al método $watchGroup, un arreglo con los nombresde los modelos que queremos observar.

Como segundo parámetro pasaremos una función anónima que recibirá los valoresnuevos y anteriores. Dentro comprobamos que existan valores nuevos, de lo contrariosalimos de la función. En caso de que haya valores nuevos, comprobaremos el primervalor del arreglo nuevos contra el segundo valor. Si los valores coinciden marcaremos lacoincidencia como verdadero de lo contrario pasaremos un valor falso.

Page 43: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 22

1 angular.module('app', [])2 .controller('Controlador', function ($scope) {

3 $scope.coincidencia = false;4 $scope.$watchGroup(['password', 'password2'], function (nuevos, anteriores) {

5 if (!nuevos) return;6 if (nuevos[0] === nuevos[1]) {

7 $scope.coincidencia = true;8 } else {

9 $scope.coincidencia = false;10 }

11 })

12 });

Ahora que el ejemplo está completo puedes ponerlo en práctica y probar escribiendo enlos dos elementos password para comprobar su funcionalidad. En versiones anteriores,para lograr un comportamiento similar a este, era necesario observar cada uno de loselementos de forma individual.

Controladores como objetos

Debido a la herencia del $scope cuando tratamos con controladores anidados, en ocasio-nes terminamos remplazando elementos por error. Esto podría traer comportamientosno deseados e inesperados en la aplicación, en muchas ocasiones costaría un poco detrabajo encontrar el motivo de los errores. Para solucionar este tipo de problemas ycolisiones innecesarias podemos utilizar la sintaxis controller as. De esta forma noestaremos utilizando el objeto $scope para exponer elementos de la vista, si no que seutilizara el controlador como un objeto.

Esta sintaxis está disponible desde la versión 1.1.5 de Angular como beta y se hizo estableen versiones posteriores. Para utilizar los controladores por esta vía debemos exponerlos elementos como propiedades del mismo controlador utilizando la palabra this. Deesta forma cuandonecesitamos utilizar algún elemento del controlador lo haremos comomismo accedemos a una propiedad de un objeto JavaScript. Veamos un ejemplo.

Page 44: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 23

1 <body ng-app>2 <div ng-controller="ctrl as lista">3 {{ lista.elementos }}4 </div>5 <script src="lib/angular.js"></script>6 <script>7 function ctrl() {

8 this.elementos = 'uno, dos, tres, cuatro.';

9 }

10 </script>11 </body>

Como podrás observar en la línea dos del ejemplo se utiliza la directiva ng-controllery la sintaxis controller as de la que hablamos anteriormente. A este controlador leasignamos un nombre lista para poder utilizarlo como objeto. En la línea tres delejemplo interpolamos la propiedad elementos del controlador. Después en la línea ochoexponemos la propiedad elementos del controlador con una cadena de texto. De estaforma la vista está conectada al controlador al igual que si utilizáramos el $scope.

Utilizando este tipo de sintaxis ganamos algunas posibilidades, pero a la vez tambiénperdemos. Si usamos el controlador comounobjeto ganamos en cuanto a la organizacióndel código, ya que siempre sabremos de donde proviene el elemento que estamosaccediendo. Pero a la vez perdemos la herencia ya que no estaremos accediendo apropiedades del $scope sino del objeto que exponemos en el controlador. Orta punto atener en cuenta es que al no usar el $scope para unir el controlador con la vista, perderásla posibilidad de utilizar las demás bondades que brinda el objeto $scope en sí.

Controladores Globales

Si estas utilizando una versión de Angular 1.3.X los ejemplos anteriores no te funcio-narán, ya que desde esa versión en adelante esta deshabilitado el uso de controladorescomo funciones globales. Aunque no es recomendado utilizar este tipo de sintaxis paradefinir los controladores, esta puede ser activada nuevamentemediante la configuraciónde la aplicación. Para lograrlo debemos primero definir un módulo, en el código acontinuación se definirá un módulo para poder configurar la aplicación, este contenidoestará detallado en el capítulo tres, si no lo entiendes, no te preocupes, continua y másadelante entenderás a la perfección.

Page 45: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 1: Primeros pasos 24

1 <body ng-app="app">2 <div class="container" ng-controller="miCtrl">3 <h1>{{ mensaje }}</h1>4 </div>5 <script src="lib/angular-1.3.js"></script>6 <script>7 var app = angular.module('app',[]);

8 app.config(function($controllerProvider){

9 $controllerProvider.allowGlobals();

10 });

11 function miCtrl ($scope) {

12 $scope.mensaje = 'Mensaje desde el controlador';

13 }

14 </script>15 </body>

En el ejemplo anterior se ha utilizado el mismo controlador del primer ejemplo, peroen esta ocasión se ha incluido el archivo de Angular 1.3 el cual no permite utilizarcontroladores como funciones globales por defecto. Pero a través de la configuraciónde la aplicación podemos re activar este comportamiento gracias al método allowGlobals

del $controllerProvider. Si no entiendes el código anterior no te preocupes, te parecerámucho más fácil en el futuro cuando expliquemos los módulos y configuración de laaplicación.

Habiendo definido esta configuración ya podemos continuar utilizando funciones glo-bales como controladores. Esta forma de definir controladores es considerada una malapráctica y puede traer problemas graves a tu aplicación debido a la colisión de nombresentre otros.

Page 46: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 2: EstructuraAngularJs no define una estructura para la aplicación, tanto en la organización de losficheros como en los módulos, el framework permite al desarrollador establecer unaorganización donde mejor considere y más cómodo se sienta trabajando.

Estructura de ficheros.

Antes de continuar con el aprendizaje del framework, creo que es importante desde unprincipio, tener la aplicaciónorganizada ya que cuando se trata de ordenar una aplicacióndespués de estar algo avanzado, se tiene que parar el desarrollo y en muchas ocasioneshay que reescribir partes del código para que encajen con la nueva estructura que sequiere usar.

Con respecto a este tema es recomendado organizar la aplicación por carpetas temáticas.Los mismos desarrolladores de Angular nos proveen de un proyecto base para iniciarpequeñas aplicaciones. Este proyecto llamado angular-seed está disponible para todosen su página de Github: https://github.com/angular/angular-seed y a continuación veremosuna breve descripción de su organización.

A lo largo del tiempo que se ha venido desarrollando este proyecto, angular-seed hacambiado mucho en su estructura. En este momento en que estoy escribiendo estecapítulo la estructura es la siguiente.

App

├── components

│ ├── version

│ │ ├── interpolate-filter.js

│ │ ├── version-directive.js

│ │ ├── version.js

├── view1

│ ├── view1.html

│ ├── view1.js

├── view2

│ ├── view2.html

│ ├── view2.js

├── app.css

├── app.js

├── index.html

25

Page 47: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 2: Estructura 26

Al observar la organización de angular-seed veremos que en su app.js declaran laaplicación y además requieren como dependencias cada una de las vistas y la directiva. Sila aplicación tomara un tamaño considerable, la lista de vistas en los requerimientos delmódulo principal sería un poco incómoda de manejar. Dentro de cada carpeta de vistaexiste un archivo paramanejar todo lo relacionado con lamisma. En éste crean un nuevomódulo para la vista con toda su configuración y controladores.

Realmente es una semilla para comenzar a crear una aplicación. Un punto de partidapara tener una idea de la organización que esta puede tomar. A medida que la aplicaciónvaya tomando tamaño se puede ir cambiando la estructura. Si es una pequeña aplicaciónpodrás usar angular-seed sin problemas. Si tu punto de partida es una aplicaciónmediana o grande más adelante se explican otras opciones para organizar tu aplicación.

A continuación, hablaremos sobre una de las posibles la organización que se puedenseguir para las medianas aplicaciones. De esta forma los grupos de trabajo podránlocalizar las porciones de código de forma fácil.

App

├── css

├── img

├── index.html

├── js

│ ├── app.js

│ ├── Config

│ ├── Controllers

│ ├── Directives

│ ├── Filters

│ ├── Models

│ ├── Services

├── partials

En esencia ésta es la estructura de directorios para una aplicación mediana. En caso deque se fuera a construir una aplicación grande es recomendable dividirla por módulos,para ello se usaría esta estructura por cada módulo:

Page 48: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 2: Estructura 27

App

├── css

├── img

├── index.html

├── js

│ ├── app.js

│ ├── Registro

│ │ ├── Registro.js

│ │ ├── Config

│ │ ├── Controllers

│ │ ├── Directives

│ │ ├── Filters

│ │ ├── Models

│ │ ├── Services

│ ├── Blog

│ │ ├── Blog.js

│ │ ├── Config

│ │ ├── Controllers

│ │ ├── Directives

│ │ ├── Filters

│ │ ├── Models

│ │ ├── Services

│ ├── Tienda

│ │ ├── Tienda.js

│ │ ├── Config

│ │ ├── Controllers

│ │ ├── Directives

│ │ ├── Filters

│ │ ├── Models

│ │ ├── Services

│ ├── Ayuda

│ │ ├── Ayuda.js

│ │ ├── Config

│ │ ├── Controllers

│ │ ├── Directives

│ │ ├── Filters

│ │ ├── Models

│ │ ├── Services

├── partials

│ ├── Registro

│ ├── Blog

│ ├── Tienda

Page 49: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 2: Estructura 28

│ ├── Ayuda

Como podemos observar al establecer esta estructura en nuestro proyecto, no importacuánto este crezca, siempre se mantendrá organizado y fácil de mantener. En este libroutilizaremos la estructura para una aplicación mediana, está disponible en el repositoriode Github https://github.com/mriverodorta/ang-starter y viene con ejemplos.

Al observar el contenido de la estructura nos podemos percatar de lo que significa cadauno de sus archivos. Ahora analizaremos algunos de ellos.

En el directorio app/js es donde se guarda todo el código de nuestra aplicación, conexcepción de la página principal de entrada a la aplicación y las plantillas (partials), queestarán a un nivel superior junto a los archivos de estilos y las imágenes. En el archivoapp.js es donde declararemos la aplicación (módulo) y definiremos sus dependencias. Sihas obtenido ya la copia de ang-starter desde https://github.com/mriverodorta/ang-starter,veras varios archivos con una configuración inicial para la aplicación. A continuación,describiré el objetivo de cada uno de ellos.

Estructura de la aplicación

Por lo general en términos de programación al crear una aplicación y ésta ser iniciadaen muchas ocasiones, necesitamos definir una serie de configuraciones para garantizarun correcto funcionamiento en la aplicación en general. En muchos casos se necesitaque estén disponibles desde el mismo inicio de la aplicación, para que los componentesinternos que se cargan después puedan funcionar correctamente.

AngularJS nos permite lograr este tipo de comportamiento mediante el método run()de los módulos. Este método es esencial para la inicialización de la aplicación. Solorecibe una función como parámetro o un arreglo si utilizamos inyección de dependencia.Este método se ejecutará cuando la aplicación haya cargado todos los módulos. Parael uso de esta funcionalidad se ha dispuesto el archivo Bootstrap.js, donde podremosdefinir comportamientos al inicio de la aplicación. En caso de que se necesite aislaralgún comportamiento del archivo Bootstrap.js se puede hacer perfectamente ya queAngularJS permite la utilización del método run() del módulo tantas veces como seanecesario.

Un ejemplo del aislamiento lo veremos en el archivo Security.js, donde haremos uso delmétodo run() para configurar la seguridad de la aplicación desde el inicio de la misma.

En la mayoría de las aplicaciones se necesitan el uso de las constantes. Los módulosde AngularJS proveen un método constant() para la declaración de constantes y sonun servicio con una forma muy fácil de declarar. Este método recibe dos parámetros,nombre y valor, donde el nombre es el que utilizaremos para inyectar la constante encualquier lugar que sea necesario dentro de la aplicación, el valor puede ser una cadena

Page 50: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 2: Estructura 29

de texto, número, arreglo, objeto e incluso una función. Las constantes las definiremosen el archivo Constants.js.

Como he comentado en ocasiones, AngularJS tiene una gran cantidad de servicios quelos hace disponibles mediante la inyección de dependencias. Muchos de estos serviciospueden ser configurados antes de ser cargando el módulo, para cuando este esté listo yalos servicios estén configurados. Para esto existe el método config() de los módulos. Estemétodo recibe comoparámetro un arreglo o función para configurar los servicios. Comoestamos tratando con aplicaciones de una sola página, el manejo de rutas es esencialpara lograrlo. La configuración del servicio $routeProvider donde se definen las rutasdebe ser configurado con el método config() del módulo, ya que necesita estar listo paracuando el módulo este cargado por completo. Estas rutas las podremos definir en elarchivo Routes.js del cual hablaremos más adelante.

Las aplicaciones intercambian información con el servidor mediante AJAX, por lo quees importante saber qué AngularJS lo hace a través del servicio $http. El mismo puedeser configuradomediante su proveedor $httpProvider para editar los headers enviadosal servidor en cada petición o transformar la respuesta del mismo antes de ser entregadapor el servicio. Este comportamiento puede ser configurado en el archivoHTTP.js.

En esencia, éste es el contenido de la carpeta App/Config de igual forma se puedecontinuar creando archivos de configuración según las necesidades de cada aplicación y amedida que se vayanusando los servicios. Las configuraciones de losmódulos de tercerosdeben estar situados en App/Config/Packages para lograr una adecuada estructura.

Los directorios restantes dentro de la carpeta App tienen un significado muy simple:Controllers,Directives y Filters serán utilizados para guardar los controladores, direc-tivas y filtros respectivamente. La carpeta de Services será utilizada para organizar todala lógica de nuestra aplicación que pueda ser extraída de los controladores, logrando deesta forma tener controladores con responsabilidades únicas. Y por último en la carpetaModels se maneja todo lo relacionado con datos en la aplicación.

Si logramos hacer uso de esta estructura obtendremos como resultado una aplicaciónorganizada y fácil de mantener.

Page 51: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 3: MódulosHasta ahora hemos estado declarando el controlador como una función de Javascripten el entorno global, para los ejemplos estaría bien, pero no para una aplicación real. Yasabemos que el uso del entorno global puede traer efectos no deseados para la aplicación.AngularJS nos brinda una forma muy inteligente de resolver este problema y se llamaMódulos.

Creando módulos

Los módulos son una forma de definir un espacio para nuestra aplicación o parte dela aplicación ya que una aplicación puede constar de varios módulos que se comunicanentre sí. La directiva ng-app que hemos estado usando en los ejemplos anteriores es elatributo que define cual es el módulo que usaremos para ese ámbito de la aplicación.Aunque si no se define ningún módulo se puede usar AngularJS para aplicacionespequeñas, no es recomendable.

En el siguiente ejemplo definiremos el primer módulo y lo llamaremos miApp, acontinuación, haremos uso de él.

1 <body ng-app="miApp">2 <div class="container" ng-controller="miCtrl">3 {{ mensaje }}4 </div>5 <script src="lib/angular.js"></script>6 <script>7 angular.module('miApp', [])

8 .controller('miCtrl', function ($scope) {

9 $scope.mensaje = 'AngularJS Paso a Paso';

10 });

11 </script>12 </body>

En el ejemplo anterior tenemos varios conceptos nuevos. Comencemos por mencionarque al incluir el archivo angular.js en la aplicación, éste hace que esté disponible el objetoangular en el entorno global o sea como propiedad del objeto window, lo podemoscomprobar abriendo la consola del navegador en el ejemplo anterior y ejecutandoconsole.dir(angular) o console.dir(window.angular)

30

Page 52: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 3: Módulos 31

A través de este objeto crearemos todo lo relacionado con la aplicación.

Para definir un nuevo módulo para la aplicación haremos uso del método module delobjeto angular como se puede observar en la línea 7. Este método tiene dos funciona-lidades: crear nuevos módulos o devolver un módulo existente. Para crear un nuevomódulo es necesario pasar dos parámetros al método. El primer parámetro es el nombredel módulo que queremos crear y el segundo una lista de módulos necesarios para elfuncionamiento del módulo que estamos creando. La segunda funcionalidad es obtenerun módulo existente, en este caso sólo pasaremos un primer parámetro al método, queserá el nombre del módulo que queremos obtener y este será devuelto por el método.

Minificación y Compresión

En el ejemplo anterior donde creábamos el módulo comenzamos a crear los controla-dores fuera del espacio global, de esta forma no causará problemas con otras libreríaso funciones que hayamos definido en la aplicación. En esta ocasión el controlador escreado por un método del módulo que recibe dos parámetros. El primero es una cadenade texto definiendo el nombre del controlador, o un objeto de llaves y valores dondela llave sería el nombre del controlador y el valor el constructor del controlador. Elsegundo parámetro será una función que servirá como constructor del controlador, estesegundo parámetro lo usaremos si hemos pasado una cadena de texto como primerparámetro.

Hasta este punto todo marcha bien, pero en caso de que la aplicación fuera a serminificada¹⁴ tendríamos un problema ya que la dependencia $scope seria reducida yquedaría algo así como:

1 .controller('miCtrl',function(a){

AngularJS no podría inyectar la dependencia del controlador ya que a no es un serviciodeAngularJS. Este problema tiene una soluciónmuy fácil porque AngularJS nos permitepasar un arreglo como segundo parámetro del método controller. Este arreglo conten-drá una lista de dependencias que son necesarias para el controlador y como últimoelemento del arreglo la función de constructor. De esta forma al ser minificado nuestroscript no se afectarán los elementos del arreglo por ser solo cadenas de texto y quedaríade la siguiente forma:

1 .controller('miCtrl',['$scope',function(a){

¹⁴Minificar es el proceso por el que se someten los scripts para reducir tamaño y así aumentar la velocidad de carga del mismo.

Page 53: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 3: Módulos 32

AngularJS al ver este comportamiento inyectará cada uno de los elementos del arregloa cada uno de las dependencias del controlador. En este caso el servicio $scope seráinyectado como a en el constructor y la aplicación funcionará correctamente.

Es importante mencionar que el orden de los elementos del arreglo será el mismoutilizado por AngularJS para inyectarlos en los parámetros del constructor. En caso deequivocarnos a la hora de ordenar las dependencias podría resultar en comportamientosno deseados. En lo adelante para evitar problemas de minificación el código será escritocomo en el siguiente ejemplo.

1 <script>2 angular.module('miApp', [])

3 .controller('miCtrl', ['$scope', function ($scope) {

4 $scope.mensaje = 'AngularJS Paso a Paso';

5 }]);

6 </script>

Inyectar dependencias mediante $inject

Hasta el momento hemos visto como inyectar dependencias mediante la notación delarreglo como se explicó en el apartado de la minificación. Existe otra vía la cual nospermitirá escribir código más fácil de leer e interpretar. Haciendo uso de la propiedad$inject de las funciones que utilizaremos, podremos especificar que necesitamos inyec-tar en estas. Para ver su funcionamiento vamos a ver el siguiente ejemplo.

1 angular.module('app', [])2 .controller('AppCtrl', AppCtrl);

3

4 AppCtrl.$inject = ['$scope', '$interval', '$http', '$log'];5 function AppCtrl($scope, $interval, $http, $log){

6 // Contenido del controlador7 }

Como habrás podido comprobar es mucho más fácil de entender el código si lo creamosespecificando funciones separadas. Hay varias ventajas que nos permite separar elcontrolador a su propia función nombrada y no en una función anónima. La primeraes que es mucho más descriptivo el código a la hora de interpretarlo. La más importanteventaja es la de poder especificar una propiedad $inject con todas las dependencias quenecesita el controlador.

Esta versión de la inyección de dependencia es la más utilizada por los desarrolladores.Por este motivo en lo adelante esteremos intercambiando entre esta vía para inyectarlas dependencias y la que ya sabias anteriormente. De esta forma te será más fácilrecordarlas.

Page 54: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 3: Módulos 33

Inyección de dependencia en modo estricto

En ocasiones puede ocurrir que olvidemos poner la anotación de alguna de las de-pendencias que necesita la aplicación. En este caso cuando vallamos a producción y elcódigo seaminificado podríamos tener graves problemas. Para solucionar este problemaen Angular 1.3 incluye una nueva directiva ng-strict-di que impedirá que la aplicaciónfuncione hasta que todas las dependencias sean anotadas correctamente. Esta directivadebe ser utilizada en el mismo elemento HTML donde definimos la aplicación con ng-app.

1 <html lang="en" ng-app="miApp" ng-strict-di>

Este no es uno de los cambios más importantes de esta versión, pero para los desarrolla-dores que utilizan las dependencias anotadas les es de gran utilidad.

Configurando la aplicación

En el ciclo de vida de la aplicación AngularJS nos permite configurar ciertos elementosantes de que los módulos y servicios sean cargados. Esta configuración la podemoshacer mediante el módulo que vamos a utilizar para la aplicación. El módulo poseeun método config() que aceptará como parámetro una función donde inyectaremos lasdependencias y configuraremos. Estemétodo es ejecutado antes de que el propiomódulosea cargado.

A lo largo del libro estaremos haciendo uso de este método para configurar variosservicios. Es importante mencionar que un módulo puede tener varias configuraciones,estas serán ejecutadas por orden de declaración. En lo adelante también mencionamosvarios servicios que pueden ser configurados en el proceso de configuración del móduloy será refiriendo a ser configurado mediante este método.

La inyección de dependencia en esta función de configuración solo inyectará dos tiposde elementos. El primero serán los servicios que sean definidos con el método provider.El segundo son las constantes definidas en la aplicación. Si tratáramos de inyectar algúnotro tipo de servicio o value obtendríamos un error. La sintaxis de la configuración esla siguiente.

1 angular.module('miApp')

2 .config(['$httpProvider', function ($httpProvider) {

3 // Configuraciones al servicio $http.4 }]);

Page 55: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 3: Módulos 34

Método run

En algunas ocasiones necesitaremos configurar otros servicios que no hayan sido decla-rados con elmétodo provider delmódulo. Para esto elmétodo config delmódulo no nosfuncionará ya que los servicios aún no han sido cargados, incluso ni siquiera el módulo.AngularJS nos permite configurar los demás elementos necesarios de la aplicación justodespués de que todos los módulos, servicios han sido cargados completamente y estánlistos para usarse.

El método run() del módulo se ejecutará justo después de terminar con la carga de todoslos elementos necesarios de la aplicación. Estemétodo también acepta una función comoparámetro y en esta puedes hacer inyección de dependencia. Como todos los elementoshan sido cargados puedes inyectar lo que sea necesario. Este método es un lugar idealpara configurar los eventos ya que tendremos acceso al $rootScope donde podremosconfigurar eventos para la aplicación de forma global.

Otro de los usos más comunes es hacer un chequeo de autenticación con el servidor,escuchar para si el servidor cierra la sesión del usuario por tiempo de inactividad cerrarlatambién en la aplicación cliente. Escuchar los eventos de cambios de la ruta y del servicio$location. La Sintaxis es esencialmente igual a la del método config.

1 angular.module('miApp')

2 .run(['$rootScope', function ($rootScope) {

3 $rootScope.$on('$routeChangeStart', function(e, next,current){

4 console.log('Se comenzará a cambiar la ruta hacia' + next.originalPath);

5 })

6 }]);

Después de haber visto como obtener AngularJS, la manera de insertarlo dentro de laaplicación, la forma en que este framework extiende los elementos HTML con nuevosatributos, la definición, la aplicación con módulos y controladores considero que hasdado tus primeros pasos. Pero no termina aquí, queda mucho por recorrer. Esto tan soloes el comienzo.

Page 56: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: ServiciosEn la estructura MVC debemos seguir unos patrones que nos indican como debe serla organización interna de la aplicación. Las Vistas son las encargadas de mostrar lainformación al usuario. Los modelos se encargan de almacenar la información y hacerladisponible cuando sea necesaria. Y por último los controladores son los encargadosde obtener las Peticiones del usuario y transformarlas en Respuestas. De esta formapareciera que no tenemos lugar donde escribir la lógica de la aplicación. Es donde vienea tomar lugar los Servicios. Estos son los encargados de llevar toda la lógica de laaplicación que no debe ser de interés para el controlador.

Un ejemplo clásico de lo mencionado anteriormente es cuando un usuario entra ala aplicación y se le requiere que se identifique. El usuario escribe el usuario y lacontraseña en la vista y envía el formulario pidiendo ser comprobado sus credenciales.El controlador recibe la petición y aquí es donde comienza el proceso de identificación.Podríamos simplemente comprobar pidiendo información al modelo para saber si susdatos son los correctos y permitir al usuario entrar en la aplicación. Pero desde elmomento en que realicemos esta operación, el controlador estará realizando tareas queno le corresponden ya que su única responsabilidad es recibir peticiones y entregarrespuestas.

En este lugar es donde los Servicios deberían hacer su trabajo. Continuando con lahipótesis anterior, el controlador al recibir la petición del usuario entrega los datos alservicio de identificación. Éste comprueba con el modelo si los datos de identificaciónson correctos e indica al controlador que el usuario puede entrar o no en la aplicación.El controlador devuelve una respuesta al usuario con un mensaje de error o redireccio-nandolo hacia donde debe ir después de identificarse.

Logrando este nivel de extracción, los controladores siempre deberán tener una respon-sabilidad única y delegar en los servicios todo tipo de lógica de la aplicación. Obteniendocomo resultado una aplicación bien organizada y fácil de pasar por pruebas (Test’s).

Los servicios enAngularJS son singleton lo que quiere decir que son objetos instanciadosuna vez y las demás ocasiones que se trate de instanciarlos se obtendrá el mismo objeto.El uso de los servicios nos permitirá intercambiar información entre diferentes partesde la aplicación ya que al ser creados y modificados todos los que accedan al obtendránel mismo resultado.

AngularJS trae en su núcleo muchos servicios que nos permiten ahorrarnos grancantidadde código ya quenos proveende funcionalidades básicas de cualquier aplicaciónweb. Además de los que nos proporciona el framework, este nos permite crear serviciospara satisfacer las necesidades específicas de la aplicación que estas creando.

35

Page 57: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 36

Existen tres formas de definir los servicios en AngujarJS pero todas forman partedel módulo. En algunas aplicaciones podrás observar que se crean módulos solo paraalmacenar servicios con funcionalidades específicas de la aplicación. Ya que con el uso delos servicios podemos crear bloques de códigos que sean reutilizables en varios lugares dela aplicación e incluso a través de diferentes aplicaciones si estos son menos específicos.

Factory

Las tres formas de definir servicios en elmódulo son con losmétodos service(), factory()y provider(), en este orden de complejidad. Comenzaré por los factory() con el siguienteejemplo. Teniendo en cuenta la estructura de directorios descrita en el Capítulo 2 elarchivo index.html posee el siguiente contenido.

Archivo: App/index.html

1 <body>2 <div class="container" ng-controller="PlaylistCtrl">3 <ul> <li ng-repeat="titulo in playlist"> {{ titulo }} </li> </ul>4 </div>5 <div ng-controller="PlaylistMetodosCtrl">6 <ul>7 <li ng-repeat="titulo in playlist">8 {{ titulo }}9 <a class="button" href="#" ng-click="borrar($index)">&times;</a>

10 </li>11 </ul>12 </div>13 <script src="lib/angular.js"></script>14 <script src="js/app.js"></script>15 <script src="js/Services/Playlist.js"></script>16 <script src="js/Controllers/PlaylistCtrl.js"></script>17 <script src="js/Controllers/PlaylistMetodosCtrl.js"></script>18 </body>

El archivo App/js/app.js posee la declaración del módulo y sus dependencias. Aunquepor ahora no tiene ninguna dependencia.

Page 58: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 37

Archivo: App/js/app.js

1 'use strict';

2 angular.module('miApp', []);

Dentro de la carpeta Servicios crearemos un nuevo archivoPlaylist.js que será el primerservicio de tipo factory.

Archivo: App/js/Services/Playlist.js

1 angular.module('miApp')

2 .factory('Playlist', [function () {

3 var playlist = [

4 'The Miracle (Of Joey Ramone)',

5 'Raised By Wolves',

6 'Every Breaking Wave',

7 'Cedarwood Road',

8 'California (There Is No End to Love)',

9 'Sleep Like a Baby Tonight',

10 'Song for Someone',

11 'This Is Where You Can Reach Me Now',

12 'Iris (Hold Me Close)',

13 'The Troubles',

14 'Volcano'

15 ];16 var listar = function(){return playlist;};

17 var borrar = function(id){playlist.splice(id,1);};18 return {

19 listar: listar,

20 borrar: borrar

21 };

22 }])

La definición de un servicio de tipo factory es muy sencilla además es la más usada. Unfactory se declara con el método factory() del modelo y este recibe como parámetroel nombre del servicio y un arreglo con las dependencias y el constructor del servicioque será utilizado para crear la instancia del servicio. Algo muy importante a tener encuenta es que los servicios de tipo factory siempre tienen que devolver una respuestacon la palabra return. Este comportamiento nos permite crear cualquier tipo de objetoscomplejos privados y solo devolver un objeto con losmétodos visibles para el usuario, deesta manera toda la lógica quedaría privada y accesible al usuario solo un API para llevara cabo las acciones que permite el servicio.

Page 59: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 38

El servicio que ha sido creado esmuy sencillo, posee una lista de canciones y dosmétodospara interactuar con la misma. La lista está directamente dentro del constructor demanera que si tratamos de acceder a ella fuera del servicio el resultado será undefinedya que el servicio solo devuelve un objeto con los métodos públicos.

Ahora que ya tenemos el servicio creado vamos con el controlador PlaylistCtrl.js en lacarpeta Controllers

Archivo: App/js/Controllers/PlaylistCtrl.js

1 angular.module('miApp')

2 .controller('PlaylistCtrl', ['$scope', 'Playlist',

3 function ($scope, Playlist) {

4 $scope.playlist = Playlist.listar();

5 }]);

Como han podido observar hemos inyectado el servicio Playlist como dependencia denuestro controlador y hemos asignado la lista de canciones al$scopemediante la funciónlistar() definida por el servicio para hacerlo disponible en la vista. Ahora iteraremossobre el con el uso de la directiva ng-repeat.

Archivo: App/index.html

1 <div class="container" ng-controller="PlaylistServiceCtrl">2 <ul> <li ng-repeat="titulo in playlist"> {{ titulo }} </li> </ul>3 </div>

Después de haber incluido el controlador en el index.html. Con estas líneas de códigoel contenido del servicio se mostrará al usuario con la ayuda de la directiva ng-repeat lacual es detallada a fondo en el Capítulo 5. De esta forma el controlador PlaylistCtrl soloha tenido la responsabilidad de gestionar la información que será mostrada al usuario.El servicio Playlist fue el encargado de obtener esa información y hacerla disponible.Esto ha sido un ejemplo muy sencillo donde el servicio solo ha devuelto un objeto conla funcionalidad necesaria para manejar la información, pero la lógica donde obtenemosesos datos queda fuera de alcance.

Ahora haremos uso de ese servicio en otro controlador para ejecutar el otro método delservicio para borrar canciones de la lista. De esta forma también podrás observar comolos servicios son singleton y cuando su contenido es modificado en un lugar de nuestraaplicación, a su vez este cambio es reflejado a lo largo de la aplicación.

Page 60: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 39

Archivo: App/js/Controllers/PlaylistMetodosCtrl.js

1 angular.module('miApp')

2 .controller('PlaylistMetodosCtrl', ['$scope', 'Playlist', function ($scope, Play\

3 list) {

4 $scope.playlist = Playlist.listar();

5 $scope.borrar = function(id){Playlist.borrar(id);};

6 }]);

En el controlador hemos expuesto a la vista el método borrar, este método recibe comoparámetro el índice que queremos borrar del arreglo. Este índice es obtenido de lavariable $index que hace disponible ng-repeat dentro del bucle. Veamos la vista comoquedaría.

Archivo: App/index.html

1 <body>2 <div class="container" ng-controller="PlaylistCtrl">3 <ul> <li ng-repeat="titulo in playlist"> {{ titulo }} </li> </ul>4 </div>5 <div ng-controller="PlaylistMetodosCtrl">6 <ul>7 <li ng-repeat="titulo in playlist">8 {{ titulo }}9 <a class="button" href="#" ng-click="borrar($index)">&times;</a>

10 </li>11 </ul>12 </div>13 <script src="lib/angular.js"></script>14 <script src="js/app.js"></script>15 <script src="js/Services/Playlist.js"></script>16 <script src="js/Controllers/PlaylistMetodosCtrl.js"></script>17 </body>

Como puedes observar en la segunda lista hay un vínculo al final de cada canción el cualeliminará ese índice de la lista. Al eliminar un elemento del arreglo podemos comprobarque efectivamente este es eliminado pero que también es actualizada la lista mostradapor el primer controlador. De esta forma podríamos intercambiar información a travésde los controladores ya que los servicios pueden ser inyectados tantas veces como seannecesarios y siempre existirá una sola instancia de los mismos.

Page 61: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 40

Service

Ahora hablaremos de otra de las formas de declarar servicios en AngularJS es específi-camente con el método service() de los módulos. Esencialmente se declaran de la mismaforma que los factory() y podemos obtener los mismos resultados de los mismos. Pero loque lo hace diferente es que los services() van a declarar una nueva instancia de una clasecuando son utilizados. Vamos a hacer el ejemplo del factory() pero esta vez con service()para ver la diferencia.

Archivo: App/js/Services/PlaylistService.js

1 angular.module('miApp')

2 .service('PlaylistService', [function () {

3 var playlist = [

4 'The Miracle (Of Joey Ramone)',

5 'Raised By Wolves',

6 'Every Breaking Wave',

7 'Cedarwood Road',

8 'California (There Is No End to Love)',

9 'Sleep Like a Baby Tonight',

10 'Song for Someone',

11 'This Is Where You Can Reach Me Now',

12 'Iris (Hold Me Close)',

13 'The Troubles',

14 'Volcano'

15 ];16 this.listar = function(){return playlist;};

17 this.borrar = function(id){playlist.splice(id,1);};18 }])

Comopuedes observar ahora el servicio no devuelve ningún objeto con la palabra returnesta vez la misma función es el objeto que ha sido instanciada connew y losmétodos sonexpuestos a través de this como haríamos con una clase Javascript. Veamos su uso en elcontrolador.

Page 62: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 41

Archivo: App/js/Controllers/PlaylistServiceCtrl.js

1 angular.module('miApp')

2 .controller('PlaylistServiceCtrl', ['$scope', 'PlaylistService',

3 function ($scope, PlaylistService) {

4 $scope.playlist = PlaylistService.listar();

5 console.log(PlaylistService.playlist);

6 }]);

En la línea 4 he tratado de acceder a la variable privada playlist y escribir su contenidoa la consola para comprobar que no es accesible. El contenido restante del controladores esencialmente el mismo. Veamos la vista.

Archivo: App/index.html

1 <body>2 <div class="container" ng-controller="PlaylistServiceCtrl">3 <ul> <li ng-repeat="titulo in playlist"> {{ titulo }} </li> </ul>4 </div>5 <script src="lib/angular.js"></script>6 <script src="js/app.js"></script>7 <script src="js/Services/PlaylistService.js"></script>8 <script src="js/Controllers/PlaylistServiceCtrl.js"></script>9 </body>

Si abrimos la consola del navegador esta mostrará el mensaje undefined ya que lapropiedad playlist es privada dentro del servicio.

Aun así, los service no son muy diferentes de los factory, he aquí la mejor parte paralos que están acostumbrados a crear clases Javascript. Veamos el ejemplo anterior de unaforma diferente y utilizaremos otra forma de declarar servicios con service().

Archivo: App/js/Services/PlaylistServiceClass.js

1 var PlaylistServiceClass = function(){2 var playlist = [3 'The Miracle (Of Joey Ramone)',

4 'Raised By Wolves',

5 'Every Breaking Wave',

6 'Cedarwood Road',

7 'California (There Is No End to Love)',

8 'Sleep Like a Baby Tonight',

9 'Song for Someone',

Page 63: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 42

10 'This Is Where You Can Reach Me Now',

11 'Iris (Hold Me Close)',

12 'The Troubles',

13 'Volcano'

14 ];15 this.listar = function(){return playlist;};

16 this.borrar = function(id){playlist.splice(id,1);};17 }

18

19 angular.module('miApp')

20 .service('PlaylistService', PlaylistServiceClass);

De esta forma podemos crear los servicios como clases comunes de Javascript y luegousarlas como servicios en AngularJS.

Provider

La tercera vía y la más compleja de declarar servicios en AngularJS es mediante elmétodo provider() de los módulos. Primero veamos el ejemplo y después lo describiré.

Archivo: App/js/Services/PlaylistProvider.js

1 angular.module('miApp')

2 .provider('Playlist', [function () {

3 var playlist = [

4 'The Miracle (Of Joey Ramone)',

5 'Raised By Wolves',

6 'Every Breaking Wave'

7 ];8 var listar = function(){return playlist;};

9 var borrar = function(id){playlist.splice(id,1);};10 return {

11 agregar: function(data){12 playlist = playlist.concat(data);

13 },

14 $get: function(){15 return {

16 listar: listar,

17 borrar: borrar

18 };

19 }

Page 64: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 43

20 };

21 }]);

A primera vista parece un poco raro, con ese $get que no de donde habrá salido. Una vezmás este servicio tiene la misma funcionalidad que los que hemos creado hasta ahoracon factory y service. Lo que hace diferente este a los dos anteriores es que los providerpermiten ser configurados en el momento en que se está configurando la aplicación.Mediante el método config del módulo podremos pre configurar el servicio antes deque este sea inyectado.

Comencemos pormencionar que se puede declarar exponiendo los métodos con returno creando una clase completamente aislada como con los servicios, exponiendo losmétodos con this y pasándola como segundo parámetro en la declaración del provider.Algo muy importante y que es requerido por los provider es que se exponga el método$get que será una función que devolverá los métodos públicos del servicio. Realmentelo que sea devuelto por la función de $get es lo que obtendremos cuando inyectemos elservicio en los controladores u otros servicios. Los demás métodos que se expongan enel provider serán solo accesibles desde el método config() del módulo. Para configurarhe creado un archivo en la carpeta Config con el nombre de PlaylistProvider.js paramantener la organización de la aplicación.

Archivo: App/js/Config/Playlist.js

1 angular.module('miApp').

2 config(['PlaylistProvider', function (PlaylistProvider) {

3 var canciones = [

4 'Cedarwood Road',

5 'California (There Is No End to Love)',

6 'Sleep Like a Baby Tonight',

7 'Song for Someone',

8 'This Is Where You Can Reach Me Now',

9 'Iris (Hold Me Close)',

10 'The Troubles',

11 'Volcano'

12 ];13

14 PlaylistProvider.agregar(canciones);

15 }])

En el método config del módulo inyectamos como dependencia el PlaylistProvider. Yasé que no existe, cuando creamos el servicio con provider lo llamamos solo Playlist. Elmotivo por lo que solo lo llamamos Playlist fue porque AngularJS cuando ve la palabra

Page 65: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 44

Provider detrás de un servicio automáticamente busca el servicio con el nombre queprecede la palabra provider e inyecta el provider de ese servicio en vez de inyectar el$get. De esta forma tendremos acceso a los métodos expuestos por el servicio para suconfiguración. El servicio después de ser configurado será inyectado donde quiera quese necesite, pero ya con los cambios realizados.

En este archivo de configuración solo agregamos las canciones restantes a la lista decanciones. Ya que en el servicio inicialmente tiene solo 3. Esto lo hacemos mediante elmétodo agregar que expusimos en el provider cuando lo creamos.

Constant y Value

Existen otras dos formas de declarar servicios, pero aún más sencillas ya que estosresponden a funcionalidades muy simples. En la programación las constantes siemprehan sido una clase de variable con un contenido definido que no puede ser alteradodespués de su definición. En AngularJS ese concepto de constante no es del todovalido. El framework nos permite declarar constantes con el método constant(). Esteacepta como primer parámetro el nombre y como segundo parámetro una cadena detexto, numero, arreglo, objeto o función que será devuelta cuando se utilice. Estasconstantes pueden ser inyectadas desde la configuración delmódulo, en otros servicios ocontroladores donde pueden ser modificadas asignándole un nuevo valor a la constante.

Archivo: App/js/Config/Constant.js

1 angular.module('miApp')

2 .constant('CSRF_TOKEN', '94a08da1fecbb6e8b46990538c7b50b2')

3 .constant('API_TOKEN', {

4 _public: 'a2d10a3211b415832791a6bc6031f9ab',

5 _secret: '5ebe2294ecd0e0f08eab7690d2a6ee69'

6 });

Los value son una forma sencilla de registrar un servicio como con provide donde supropiedad $get no recibe parámetros y es devuelta. Estos no pueden ser inyectados en laconfiguración del módulo, pero pueden ser modificados por un decorator de Angular.Llevado a la práctica realmente no tienenmucha diferencia de las constantes. Se declarande la misma forma con el método value() del módulo.

Archivo: App/js/Config/Values.js

1 angular.module('miApp')

2 .values('API_URL', 'api.example.com/v1/');

Page 66: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 45

¿Cuándo debemos usar una constant o un value?. Deberíamos usar los values cuandonecesitemos registrar un servicio. Las constant cuando necesitamos incluirlas en laconfiguración del módulo ya que los values producirían un error si son inyectados en laconfiguración.

Decorators

Cuando necesitamos agregar cierta funcionalidad a un servicio sin modificar su código,ya sea unopropio o de una librería de terceros. Angular nos provee elmétododecorator()del servicio $provide. Este método intercepta la creación del servicio que queremosdecorar permitiéndonos modificar el comportamiento del servicio antes de que seacreado. El objeto que devuelva el decorator debe ser el mismo servicio o un nuevoservicio que remplace el original.

Este método recibe dos parámetros. El primero es el nombre del servicio que queremosdecorar. El segundo es una función que será ejecutada cuando el servicio necesite serinstanciado y necesita devolver una instancia del servicio ya decorado. Esta funciónse le inyectara la instancia original del servicio para ser decorada. Veamos un ejemplodecorando uno de los servicios anteriores para obtener una cadena de texto separadapor comas de lista de canciones del servicio.

Archivo: App/js/Decorators/Playlist.js

1 angular.module('miApp')

2 .config(['$provide', function ($provide) {

3 $provide.decorator('Playlist', ['$delegate', function($delegate) {

4 $delegate.texto = function(){

5 return $delegate.listar().join(', ');

6 };

7 return $delegate;

8 }]);9 }]);

De esta forma ahora tenemos disponible un nuevo método en el servicio llamado textoque devuelve una cadena con todas las canciones separadas por comas.

$provide

Hasta el momento hemos estado usando los métodos provider(), constant(), value(),factory() y service() del módulo para declarar los servicios en angular. Todos estos noson más que accesos directos a los métodos del servicio $provide de AngularJS. Este

Page 67: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 46

servicio es el encargado de registrar los componentes con el $injector que a su vez es elencargado de devolver las instancias de los servicios definidos por $provide.

AngularJS nos provee varios servicios para resolver tareas específicas dentro de laaplicación, a medida que vayamos haciendo uso de estos iré explicándolos al detalle.Ahora solo detallaré el servicio $q ya que lo utilizaremos en el próximo capítulo.

Promesas

En el desarrollo de una aplicación en ocasiones necesitamos mostrar información alusuario y puede que esta no esté disponible. En la mayoría de los casos la aplicaciónpara su curso de ejecución hasta que esos datos estén disponibles para ser mostradosy continuar con la ejecución. Estos comportamientos no deseados podrían afectargrandemente a la aplicación. Situaciones como estas se agudizanmás aun cuando se tratade realizar peticiones a un servidor remoto donde tenemos que esperar una respuestaque puede tardar períodos de tiempo diferentes en cada petición. También al recibir larespuesta puede ser de un error o de un resultado satisfactorio para la aplicación.

Para resolver este tipo de situaciones necesitaríamos lograr que cuando la aplicaciónllegue a la ejecución de una de estas peticiones, las hiciera de forma paralela para que laaplicación siga su curso de carga. Para cuando la petición termine de realizarse tambiénnecesitaríamos que nuestra aplicación sea notificada y hacer los trabajos necesarios conla respuesta del servidor.

Para resolver problemas como este AngularJS nos provee un servicio llamado $q quees una implementación de las promesas en Javascript. Este servicio está basado en lalibrería Q de Kris Kowal’s. Y AngularJS hace un uso extensivo de las promesas paraentregar información cuando esté disponible.

$q puede ser inyectado como los demás servicios deAngular en controladores y serviciospara ejecutar tareas asíncronas y permitir tomar decisiones dependiendo de si la promesaes resuelta o no. Comencemos por explicar cómo funciona. Para comenzar a usarlas promesas necesitamos crear un objeto para aplazar alguna tarea. Esto es logradomediante $q.defer(), de esta forma obtenemos una nueva instancia del objeto defer listopara ser utilizado. Este objeto tiene tres métodos, resolve, reject y notify. Veamos unejemplo.

Page 68: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 47

Archivo: App/js/Controllers/PromiseCtrl.js

1 angular.module('miApp')

2 .controller('PromiseCtrl', ['$scope', '$q', function ($scope, $q) {

3 var checkServer = function(){

4 var def = $q.defer();

5 setTimeout(function(){

6 def.resolve('Online');

7 }, 2000);

8 return def.promise;

9 };

10

11 var checkHTTP = function(){

12 var def = $q.defer();

13 setTimeout(function(){

14 if ( Math.floor(Math.random()*100) > 50 ) {

15 def.resolve('Online');

16 } else {

17 def.reject('El servicio no está disponible');

18 };

19 }, 5000)

20 return def.promise;

21 }

22

23 var checkDb = function(){

24 var def = $q.defer();

25 setTimeout(function(){

26 if ( Math.floor(Math.random()*100) > 50 ) {

27 def.resolve('Online');

28 } else {

29 def.reject('El servicio no está disponible');

30 };

31 }, 3000)

32 return def.promise;

33 }

34

35 var checkSsl = function(){

36 var def = $q.defer();

37 setTimeout(function(){

38 def.notify('Comprobación de conexión segura iniciada.');

39 if ( Math.floor(Math.random()*100) > 50 ) {

40 def.notify('Las conexiones seguras están habilitadas');

41 def.resolve('SSL');

Page 69: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 48

42 } else {

43 def.notify('Las conexiones seguras están desactivadas');

44 def.reject('Desactivadas');

45 };

46 }, 4000)

47 return def.promise;

48 }

49

50 checkServer().then(function(result){

51 $scope.status = result;

52 });

53 checkHTTP().then(function(result){

54 $scope.http = result;

55 }, function(err){

56 $scope.http = err;

57 });

58 checkDb().then(function(result){

59 $scope.db = result;

60 }, function(err){

61 $scope.db = err;

62 });

63 checkSsl().then(function(result){

64 $scope.ssl = result;

65 }, function(err){

66 $scope.ssl = err;

67 }, function(notif){

68 console.log(notif);

69 });

70

71 }])

En el ejemplo anterior he simulado una comprobación de estados de un servidorutilizando setTimeout para demorar las respuestas y observar el comportamiento delas promesas. Hay que tener en cuenta que toda esta lógica la he escrito en el controladorpara propósitos del ejemplo, en una aplicación real estos métodos de chequeo deben serextraídos a su propio servicio. Ya que el controlador no necesita saber cómo es que secomprueba el estado del servidor sino cual es el estado de los servicios para mostrarlosal usuario. Observemos los resultados en el navegador.

Page 70: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 49

Archivo: App/index.html

1 <body>2 <div class="container" ng-controller="PromiseCtrl">3 <p>Estado del servidor: {{ status }}</p>4 <p>Estado del servicio HTTP: {{ http }}</p>5 <p>Estado del servicio de Base de Datos: {{ db }}</p>6 <p>Estado de las conexiones seguras: {{ ssl }}</p>7 </div>8 <script src="lib/angular.js"></script>9 <script src="js/app.js"></script>

10 <script src="js/Controllers/PromiseCtrl.js"></script>11 </body>

Como se ha podido observar todos los procesos se comienzan a ejecutar almismo tiempoy los resultados se vanmostrando amedida que se van resolviendo por $q. De esta formala aplicación no ha parado para esperar a que el primer resultado sea obtenido paracontinuar su ejecución. Ahora describiré el código del ejemplo anterior

En el controlador se ha inyectado como dependencia el servicio $q para hacer usode las promesas. También se han creado una función para comprobar cada uno delos servicios del servidor comencemos por la primera. Esta función se encargará decomprobar la disponibilidad del servidor. Para esta función he creado el objeto defmediante $q.defer() que devuelve una nueva instancia del objeto defer y representauna tarea que se finalizará en el futuro. Ahora el objeto def tiene varios métodos. Elprimero que usamos es el método resolve() que recibe como parámetro el valor que seráentregado por la promesa cuando sea utilizada en el futuro. En este ejemplo es la cadenaOnline porque hemos obligado a que siempre muestre que el servidor está online. Luegodevolvemos la promesa con return def.promise para ser usada posteriormente.

Como el resultado ha sido obligado, esta promesa siempre devolverá Online comoresultado de su ejecución. Ya tenemos la función lista para ser ejecutada asíncrona.Ahora necesitamos ejecutar acciones cuando esta haya terminado si ejecución y tengalos resultados listos. El objeto promise que se ha devuelto de la función tiene unmétodothen para ejecutar acciones dependiendo del resultado de la promesa. El método thenrecibe tres funciones como parámetros, el primero se ejecutará si la promesa se haresuelto, el segundo si ha sido rechazada y la tercera es una función que se ejecutarátantas veces como se haya usado el método notify del objeto defer. Cada una de estosmétodos recibe como parámetro una función que a su vez recibe como parámetro elresultado de la promesa.

Para la comprobación del estado del servidor solo se ha pasado la primera función deparámetro al método then por qué dispuesto en el código de la promesa que siempreserá resuelta. En esta función asignamos el resultado de la promesa a la propiedad status

Page 71: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 50

del $scope para hacerlo disponible en la vista desde el momento en que el resultado estélisto.

Para la comprobación del servicio HTTP he creado otra función donde esta vez lapromesa será resuelta o rechazada dependiendo de un valor aleatorio obtenido con laclaseMath de Javascript. El resultado es mostrado en la vista dependiendo si se resuelveo no la promesa, porque esta vez hemos pasado el segundo parámetro a al método thenque se ejecutará si la promesa es rechazada.

En la comprobación de conexiones seguras he utilizado el método notify para indicarel estado de la comprobación y el resultado de la misma. De esta forma podremos verreflejado en la consola del navegador, el proceso de comprobación cada vez que senotifique.

Varias promesas a la vez

Existen ocasiones donde tenemos varias promesas que se resolverán en diferente tiempo,pero necesitamos esperar a que todas se resuelvan para tomar acciones cuando todashayan finalizado. Para solucionar este tipo de necesidad, el servicio $q tiene un métodoque acepta un arreglo de promesas en el cual podremos tomar acciones cundo todashayan finalizado.

Para ver este comportamiento en acción vamos a crear un ejemplo con tres promesasutilizando la función setTimeout de Javascrip, cuando cada una de ellas se resuelvaimprimiremos en la consola un mensaje.

1 .controller('AppCtrl', function ($q) {

2 var promesa1 = $q.defer();

3 var promesa2 = $q.defer();

4 var promesa3 = $q.defer();

5

6 promesa1.promise.then(completado);

7 promesa2.promise.then(completado);

8 promesa3.promise.then(completado);

9

10 function completado(data) {

11 console.log(data);

12 }

13

14 setTimeout(function () {

15 promesa1.resolve('Promesa #1 resuelta');

16 }, Math.random() * 1000);

17 setTimeout(function () {

Page 72: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 51

18 promesa2.resolve('Promesa #2 resuelta');

19 }, Math.random() * 1000);

20 setTimeout(function () {

21 promesa3.resolve('Promesa #3 resuelta');

22 }, Math.random() * 1000);

23 });

Como puedes observar, cuando se ejecuta este controlador, en la consola aparecenlos mensajes de respuesta de cada promesa. Estas son resueltas en orden aleatoriodependiendo del tiempo que demore el setTimeout. Pero ahora necesitamos una víapara tomar acciones cuando todas se hayan resuelto. Esta funcionalidad la podemosobtener mediante el método all del servicio $q. Para ejecutar una acción cuando todaslas promesas han sido resueltas, pasaremos como parámetro un arreglo con todas laspromesas a la función all y luego ejecutaremos la acción necesaria.

1 var todas = $q.all([promesa1.promise, promesa2.promise, promesa3.promise]);2 todas.then(function (data) {

3 console.log(data);

4 })

En esta ocasión cuando ejecutamos el método then y recibimos los datos, estos serán unarreglo con el resultado de cada una de las promesas. Es importante mencionar que elorden en que vienen los resultados de las promesas es el mismo en que le pasamos laspromesas a la función all, sin importar el orden en que estas hayan sido resueltas.

El constructor de las promesas

En la versión 1.3 Angular se introdujo una nueva forma de crear promesas. Esta vezmás acorde a lo que nos entregara la nueva versión de Javascript ECMAScript 6. Ahoratendremos la posibilidad de utilizar las promesasmediante el constructor del servicio $q.Para ver la nueva vía vamos a crear un ejemplo simple con la versión antigua y luego latransformaremos a la nueva forma de utilizar promesas en Angular 1.3.

Para comenzar crearemos una vista con dos botones, uno para que resuelva la promesay otro para que la rechace. Estos botones ejecutaran una acción en el controladorpasándole un valor verdadero o falso como parámetro para resolver o no la promesa.Además, mostraremos el resultado de la promesa o el error de la misma.

Page 73: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 52

1 <body ng-controller="AppCtrl">2 <button ng-click="accion(false)">Resolver</button>3 <button ng-click="accion(true)">Rechazar</button>4 <div>{{resuelta}}</div>5 <div>{{rechazada}}</div>6 <script src="bower_components/angular/angular.js"></script>7 <script src="app.js"></script>8 </body>

Ahora en el controlador crearemos una función tarea que será la encargada de crearla promesa y ejecutarla. Esta se ejecutará de forma asíncrona utilizando la funciónsetTimeout de Javascript. Dependiendo del valor verdadero o falso que se le pasa comoparámetro a esta función, se resolverá o rechazará la promesa.

1 angular.module('app', [])2 .controller('AppCtrl', function ($scope, $q) {

3

4 function tarea(comprobar){

5 var dfd = $q.defer();

6 setTimeout(function() {

7 if (!comprobar) {

8 dfd.resolve('Promesa resuelta');

9 } else {

10 dfd.reject('Promesa rechazada');

11 }

12 }, 1000);

13 return dfd.promise;

14 }

15 });

Para terminar solo nos queda crear la función que ejecutara la promesa. Crearemos lafunción ejecutar y la asignamos al $scope para que los botones de la vista puedan accedera ella. Esta función asignará al scope los valores de la promesa en dos variables, resuelta yrechazada.

Page 74: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 53

1 ...

2 $scope.accion = ejecutar;

3

4 function ejecutar(comprobar){

5 tarea(comprobar).then(function (data) {

6 $scope.resuelta = data;

7 }, function (error) {

8 $scope.rechazada = error;

9 })

10 }

11 ...

Para convertir la promesa anterior a la nueva vía para crear las promesas, solo nece-sitaremos hacer algunos cambios en la función tarea que creamos anteriormente. Paracomenzar ya no tendremos que crear un objeto defer sino devolver el resultado delconstructor del servicio $q. Al constructor le pasamos como parámetro una funciónanónima que recibirá dos parámetros, estos son dos funciones, resolve que la utilizaremospara resolver las promesas y reject que utilizaremos para rechazarlas. De esta forma elnuevo código quedaría como aparece a continuación.

1 function tarea(comprobar) {

2 return $q(function (reject, resolve) {

3 setTimeout(function () {

4 if (!comprobar) {

5 resolve('Promesa resuelta');

6 } else {

7 reject('Promesa rechazada');

8 }

9 }, 1000);

10 });

11 }

Como habrás podido comprobar, el resultado es el mismo, pero esta nueva versión estámás acorde a la nueva interfaz de promesas que trae la nueva versión de Javascript

Ahora solo nos queda ejecutar la aplicación en el navegador y ver su funcionamiento.

Esencialmente este es el comportamiento de las promesas enAngularJS. Poner una tareaasíncrona a la ejecución de la aplicación y tomar acciones cuando esté lista.

Desplazamiento con $anchorScroll

Cuando creamos aplicaciones, en ocasiones queremos dirigir al usuario a cierto lugardentro de la página. Usualmente esto es logrado mediante la asignación de id a ciertos

Page 75: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 54

elementos en el código y haciendo uso de vínculos con la propiedad href apuntando a laid a la que queremos ir. Esta acción hará que en la dirección del navegador aparezca la idy el navegador se dirija a esa nueva posición. Pero después de haber dirigido el usuarioa cierto lugar, si este se mueve hacia otro lugar de la página la dirección del navegadorno se cambiará de forma automática. Si el usuario vuelve a dar click en el vínculo que loenvía a la posición del id, en esta ocasión el navegador no tomara ninguna acción porqueya está en esa posición la dirección.

Con el nuevo servicio$anchorScroll introducido en la versión1.3 deAngular, podremosresolver este problema. Este servicio nos permitirá desplazarnos hacia cualquier id de lapágina incluso cuando la dirección del navegador ya este apuntando a esa id. Para ver unejemplo vamos a crear una vista con varios vínculos y varios id para desplazarnos haciaellos.

1 <head>2 <meta charset="UTF-8">3 <title>Document</title>4 <style>5 .contenido {height: 800px;}

6 #contenido1 {background-color: #1ABC9C}

7 #contenido2 {background-color: #3498DB}

8 #contenido3 {background-color: #9B59B6}

9 #contenido4 {background-color: #E74C3C}

10 </style>11 </head>12 <body ng-controller="AppCtrl">13 <a href="" ng-click="irA(1)">Contenido 1</a>14 <a href="" ng-click="irA(2)">Contenido 2</a>15 <a href="" ng-click="irA(3)">Contenido 3</a>16 <a href="" ng-click="irA(4)">Contenido 4</a>17 <div class="contenido" id="contenido1">Contenido 1</div>18 <div class="contenido" id="contenido2">Contenido 2</div>19 <div class="contenido" id="contenido3">Contenido 3</div>20 <div class="contenido" id="contenido4">Contenido 4</div>21

22 <script src="bower_components/angular/angular.js"></script>23 <script src="app.js"></script>24 </body>

En la vista se han agregado unos estilos para diferenciar cada uno de los contenidos.Todos los vínculos tienen una directiva ng-click apuntando a una función irA quedefiniremos en el controlador. Esta función será la encargada de movernos dentro dela página hacia el contenido que especifiquemos como parámetro.

Page 76: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 55

1 angular.module('app', [])2 .controller('AppCtrl', function ($scope, $anchorScroll) {

3 $scope.irA = function (id) {

4 var nuevaId = 'contenido' + id;

5 $anchorScroll(nuevaId);

6 }

7 })

En el controlador inyectamos como dependencia el nuevo servicio $anchorScroll.Dentro de la función irA que definimos en el $scope, hacemos una llamada al servicio$anchorScroll pasándole como parámetro el id al que queremos desplazarnos.

Como podrás observar en el ejemplo, después de haber sido desplazado hacia uncontenido específico, puedes ir hacia el inicio de la página y volver a desplazarte hacia elmismo contenido sin problemas. Con el nuevo servicio se desplazará, aunque no se hayacambiado la dirección en el navegador.

Hay que tener en cuenta que el servicio $anchorScroll no reflejara la id en la dirección.Si necesitamos reflejar el id en la dirección para que esta posición pueda ser guardadacomo marcador, podemos inyectar el servicio $location y utilizar su método hash paraque este refleje la posición en la dirección del navegador. Una vez utilizado el serviciolocation, no es necesario pasar la id al servicio anchorScroll ya que este navegara hacia lanueva posición gracias a la dirección.

1 angular.module('app', [])2 .controller('AppCtrl', function ($scope, $anchorScroll, $location) {

3 $scope.irA = function (id) {

4 var nuevaId = 'contenido' + id;

5 $location.hash(nuevaId);

6 $anchorScroll();

7 }

8 });

Este servicio tiene una propiedad que podemos utilizar para dejar un margen superiorcuando hacemos el desplazamiento. Esta propiedad la podremos configurar en el bloquerun de la aplicación para de esta forma este definida para toda la aplicación. Veamos unejemplo.

1 angular.module('app', [])2 .run(function ($anchorScroll) {

3 $anchorScroll.yOffset = 50;

4 });

Page 77: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 56

Ahora cuandoutilizamos cualquiera de los enlaces paramovernos dentro de la aplicaciónynos desplazamos, veremos que tendremos unmargen superior de 50px comodefinimosen el bloque run anteriormente. Este comportamiento es muy útil para cuando tenemosbarras de navegación con una posición fija en la parte superior.

El valor de esta propiedad puede ser un número como el ejemplo anterior, este será lacantidad de pixeles que se dejará como margen en la parte superior. Además, este valorpuede ser una función que será llamada en cada ocasión que se utilice el servicio. Estafunción siempre debe devolver un número, así podremos realizar cálculos para saber lacantidad demargen que necesitamos en la parte superior. Y por último ese valor tambiénpuede ser un elemento jqLite o jQuery. Se tomará comomargen la distancia desde la partesuperior de la página hasta la parte inferior del objeto jqLite/jQuery. Es importante queeste elemento tenga una posición fija, de lo contrario no se tomara en cuenta.

Cache

Si queremos obtener un buen rendimiento en la aplicación que estemos desarrollando,es importante tener en cuenta no repetir operaciones innecesarias. Hay muchos casosen los que cuando vamos de una página a otra necesitamos recalcular datos de la páginaanterior, esto conlleva a repetir las mismas operaciones de cálculo una y otra vez. Pararesolver este problema, Angular nos provee con un servicio de cache para guardar datosy reutilizarlos en cualquier momento.

Con este servicio tendremos la posibilidad de almacenar todo tipo de datos para suposterior uso. El servicio $cacheFactory es muy fácil de utilizar y acelerará el procesa-miento de la aplicación. Este servicio tiene un APImuy sencilla e intuitiva que describiréa continuación.

Para hacer uso de este servicio lo podemos inyectar en cualquier lugar como los demásservicios de Angular, y utilizarlo para crear objetos de cache. Los datos que deseasalmacenar en la cache, son asociados a un objeto de cache que hayas creado previamente.Estos objetos de cache tienenmétodos para agregar y eliminar datos de la cache. Cuandoun objeto de cache ha sido creado con el servicio $cacheFactory, este puede ser utilizadodesde cualquier otra parte de la aplicación para obtener sus datos.

Para crear nuevos objetos de cache con el servicio $cacheFactory, solo necesitamos pasarel nombre del nuevo objeto de cache que queremos crear como parámetro al constructordel servicio.

1 var info = $cacheFactory('infoCache');

Después de creado el objeto de cache, tendremos una serie de métodos para ejecutar lasacciones con la cache. Ahora vamos a describir cada una de ellas y como utilizarlas.

Page 78: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 57

• put: Es el método que utilizaremos para depositar nuevos elementos dentro de lacache. Acepta dos parámetros, el primero es una cadena de texto que será la llavepor la cual llamaremos a este elemento desde la cache. El segundo parámetro eslo que necesitamos guardar en la cache, esto puede ser un arreglo, objeto, texto,número o booleano.

• get: Este método será el encargado de extraer un elemento de la cache y devol-verlo como resultado. Acepta como único parámetro el nombre del objeto que seencuentra en la cache. Si el elemento que se está solicitando no existe, se devolveráundefined.

• remove: Utilizaremos este método para eliminar elementos del objeto de cache.Solo acepta un parámetro y es el nombre del elemento que queremos eliminar.

• removeAll: Cuando necesitamos eliminar todos los elementos del objeto de cache,ejecutaremos este método sin pasar ningún parámetro.

• info: Este método devolverá información básica del objeto de cache como son elnombre o la cantidad de elementos que posee.

• destroy: Si necesitamos eliminar el objeto de cache del servicio $cacheFactorypodremos hacerlo mediante esta acción.

Con losmétodos que exponen los objetos creados por el servicio $cacheFactory podremosrealizar todas las acciones necesarias para manejar la cache de nuestra aplicación. Esteservicio además tiene otrométodo info, este nos devolverá un objeto con la informaciónde cada unode los objetos de cache que existen en el servicio de cache. Para ver los objetosde la cache podemos ejecutar lo siguiente.

1 console.log($cacheFactory.info());

Si revisas en la consola del navegador, podrás observar los objetos de cache, así como lacantidad de elementos que posee cada uno de estos. Como te habrás podido dar cuentaexisten dos objetos de cache que crea angular, uno es $http y el otro es templates. Másadelante hablare sobre estos objetos de cache, pero por ahora solomencionar que en ellosse guardan las peticiones que hagamos con el servicio $http y las plantillas que se carganen la aplicación.

Para ver el servicio en funcionamiento crearemos un ejemplo sencillo donde tendremosdos controladores. En uno pondremos un elemento input de tipo texto donde podremosescribir un valor para guardarlo en la cache. En el otro controlador tendremos un botónque cargará el contenido que hayamos escrito en el controlador anterior y lo imprimiráen la consola. Para comenzar crearemos la vista con los elementos necesarios.

Page 79: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 58

1 <body>2 <div ng-controller="PrimerCtrl as uno">3 <input type="text" ng-model="uno.texto">4 <button ng-click="uno.guardar()">Guardar en la Cache</button>5 </div>6 <div ng-controller="SegundoCtrl as dos">7 <button ng-click="dos.imprimir()">Imprimir desde la Cache</button>8 </div>9

10 <script src="bower_components/angular/angular.js"></script>11 <script src="app.js"></script>12 </body>

En la vista anterior los controladores están en elmismonivel, de esta forma unono puedeacceder a los elementos del otro ya que no están anidados. Ahora crearemos el primercontrolador e inyectaremos el servicio $cacheFactory para crear un objeto de cache conel nombre de cachePrincipal.

1 angular.module('app', [])2 .controller('PrimerCtrl', PrimerCtrl);

3

4 PrimerCtrl.$inject = ['$cacheFactory'];5 function PrimerCtrl($cacheFactory){

6 var vm = this;7 var cachePrincipal = $cacheFactory('cachePrincipal');

8 }

Si después de crear el objeto de cache pedimos imprimimos la información del servicio$cacheFactory en la consola, podremos observar que hay un nuevo elemento con elnombre que acabamos de crear.

1 ...

2 var cachePrincipal = $cacheFactory('cachePrincipal');

3 console.log($cacheFactory.info());

4 ...

Ahora que tenemos listo el objeto de cache, necesitamos escribir una función paraguardar el contenido del input dentro de la cache.

Page 80: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 59

1 ...

2 vm.guardar = function () {

3 cachePrincipal.put('mensaje', vm.texto);

4 }

5 ...

Con esta función, al hacer clic en el botón del primer controlador, se guardará elcontenido del input dentro de la cache en una llave con el nombre mensaje. Ahoranecesitamos crear en el segundo controlador la función que imprimirá el mensaje enla consola.

1 angular.module('app')

2 .controller('SegundoCtrl', SegundoCtrl);

3

4 SegundoCtrl.$inject = ['$cacheFactory'];5 function SegundoCtrl($cacheFactory){

6 var vm = this;7 var cachePrincipal = $cacheFactory.get('cachePrincipal');

8

9 vm.imprimir = function () {

10 console.log(cachePrincipal.get('mensaje'));

11 }

12 }

Ahora que el ejemplo está completo, puedes ejecutarlo en el navegador y ver el resultado.Si escribes unmensaje en el elemento input y lo guardas, lo podrás imprimir desde el otrocontrolador. También puedes implementar funcionalidades como la de borrar, limpiar lacache e incluso eliminar el objeto de cache ahora que ya tienes el conocimiento de cómohacerlo.

Aunque el ejemplo que se utilizó para demostrar su funcionamiento no tiene mucha uti-lidad, el servicio en si es muy útil para cuando se realizan series de cálculos repetidos endiferentes lugares. Estos pueden ser guardados en la cache una vez y después utilizadosdentro de la aplicación donde sean necesarios. Realmente el mayor uso que le darás aeste servicio es cuando comiences a utilizarlo en conjunto con el servicio $http, ya queahorrara mucho tiempo de espera en peticiones a servidores remotos.

Log

Durante el proceso de desarrollo de una aplicación, con frecuencia hacemos uso de laconsola para imprimir información mediante el método log. Angular posee un servicio

Page 81: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 60

pensado para utilizado en reemplazo del clásico console. El servicio $log no es más queun acceso a las principales funcionalidades de console pero con algunos cambios lo hacenmás útil en algunos casos.

Este servicio incluye cinco métodos las cuales podemos utilizar para manejar diferentessituaciones que necesiten ser imprimidas en la consola. Los métodos son los que serelacionan a continuación.

1. log(): Escribe un mensaje.2. info(): Escribe un mensaje de información (Icono Info)3. warn(): Escribe un mensaje de cuidado (Icono Warning)4. error(): Escribe un mensaje de error (Icono Error)5. debug(): Escribe un mensaje se el servicio está en modo debug

A simple vista el servicio $log no tiene gran utilidad sobre el uso del objeto console pero,este servicio nos permite configurar si deseamos o no mostrar los mensajes de debug.Esta utilidad puede ser utilizada como remplazo del simple log del objeto console. A lolargo de la aplicaciónpodremos especificarmensajes de debugpara que facilite el procesode desarrollo. Al terminar la aplicación podremos configurar el servicio para que nomuestre los mensajes de debug, de esta forma no tendremos que borrar todas las líneasde código que imprimen mensajes en la consola.

Para configurar el servicio necesitamos inyectar el $logProvider en la configuración dela aplicación y pasar un valor falso al método ** debugEnabled** del servicio. De estaforma el servicio no imprimirá ninguno de los mensajes de tipo debug. Esto deberemoshacerlo para todas las aplicaciones que vallan a pasar a producción.

1 angular.module('app', [])2 .config(Log);

3

4 Log.$inject = ['$logProvider'];5 function Log($logProvider) {

6 $logProvider.debugEnabled(false);7 }

Page 82: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 61

Muestra del servicio $log en la consola de Google Chrome

Manejando Excepciones

Angular posee una formamuy sencilla de manejar los errores que devuelve la aplicación.Estos errores son imprimidos a la consola por un servicio llamado $exceptionHandler,que a su vez utiliza el servicio $log. En cada ocasión que se produzca una excepción,angular lo procesara automáticamente a través de este servicio. Para poder llevar unprocesamiento de errores más profundo, podríamos reemplazar el servicio por unonuestro que cumpla los requisitos de la aplicación.

Para cambiar el funcionamiento por defecto de este servicio tendremos que redefinirlomediante la creación de un Factory con el nombre $exceptionHandler. Este Factorydebe devolver una función que acepte dos parámetros, el primero es la excepción y elsegundo la causa. Para ver su funcionamiento vamos a crear el servicio, pero utilizaremosel servicio $log con su método debug. De esta forma cuando la aplicación entre enproducción, los errores no sean imprimidos en la consola.

Lo primero que necesitamos hacer es definir un Factory con el nombre exceptionHandlerque devuelva una función y acepte los parámetros del error. Inyectamos el servicio$log y hacemos debug en la consola de cualquier excepción que no haya sido manejadapreviamente. Después en el controlador lanzamos un error para simular el uso del nuevoservicio.

1 angular.module('app', [])2 .factory('$exceptionHandler', ExceptionHandler)

3 .controller('AppCtrl', AppCtrl);

4

5 ExceptionHandler.$inject = ['$log'];6 function ExceptionHandler($log){

7 return function (exception, cause) {

8 $log.debug.apply($log, arguments);

9 }

10 }

Page 83: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 62

11

12 AppCtrl.$inject = ['$scope'];13 function AppCtrl($scope) {

14 throw new Error('Error grave.');

15 }

Como habrás podido observar los mensajes de la consola ahora salen a través delservicio que creamos anteriormente. Haciendo uso del método debug del servicio $log,podremos deshabilitar que muestre los errores en la consola cuando estamos en modode producción. El ejemplo anterior no tiene gran utilidad, pero haciendo un correcto usodel servicio podremos enviar los errores a una base de datos para analizarlos.

Retrasando funcionalidades

En muchas ocasiones necesitamos retrasar la ejecución de alguna funcionalidad en laaplicación. Usualmente en Javascript este comportamiento es realizado mediante lafunción setTimeout. Para este tipo de necesidades, Angular dispone de un servicio llamado$timeout. En esencia este servicio realizará lo que estamos acostumbrados a hacer consetTimeout pero desde el punto de vista del framework.

El servicio $timeout tiene algunas diferencias con respecto al nativo de javascript y es loque lo hará más útil al utilizarlo en el framework sobre el método nativo. Para comenzarla función que se retrasará será rodeada por un bloque try/catch, cualquier excepción quesea lanzada será delegada al servicio $exceptionHandler que explicamos anteriormente.

Lo que realmente hace más útil este servicio sobre el nativo de Javascript es que estábasado en promesas. Siendo así tendremos una serie de funcionalidades que serían muyútil a la hora de implementarlo en la aplicación. Otra de las capacidades es que este estárelacionado directamente con el ciclo digest de la aplicación. Esto facilita que las accionesque realicemos con este servicio serán interpretadas por Angular correctamente.

El primer parámetro que pasaremos al constructor será la función que necesitamosretrasar en su ejecución. El segundo parámetro es el tiempo que será retrasado, estetiempo es especificado en milisegundos. El tercer parámetro es un valor booleano quede ser false obviará el chequeo de los modelos, de lo contrario invocara $applay. Apartir del cuarto parámetro en adelante serán pasados como parámetros a la funciónque especificamos como primer parámetro.

Cuando creamos un nuevo objeto con el constructor de $timeout y lo guardamos en unavariable, este puede ser cancelado antes de que se ejecute la función. Para cancelarloel servicio posee un método cancel donde pasaremos como parámetro la promesa quenecesitamos cancelar.

Ahora veremos un ejemplo completo del uso del servicio $timeout. Primero crearemosuna vista con un botón que nos permita cancelar el $timeout desde la vista. En el

Page 84: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 63

controlador crearemos un objeto timeout con el nombre retraso el cual ejecutara lafunción Accion después de tres segundos. A esta función le pasaremos dos parámetrosadicionales que imprimiremos por la consola. Luego de que la promesa se ha ejecutadoimprimiremos en la consola el mensaje de devuelto por la ejecución de la función. Sicancelamos el temporizador, se disparará la acción catch de la promesa y por ultimodefinimos el método cancelar para poder ejecutarlo en la vista.

Vista

1 <body ng-controller="AppCtrl as vm">2 <button ng-click="vm.cancelar()">Cancelar</button>3 </body>

Controlador

1 angular.module('app', [])2 .controller('AppCtrl', AppCtrl);

3

4 AppCtrl.$inject = ['$timeout'];5 function AppCtrl($timeout) {

6 var vm = this;7 var retraso = $timeout(Accion, 3000, true, 'Uno', 'Dos');

8

9 function Accion(param1, param2) {

10 console.log('Ejecutado después de dos segundos.');

11 console.log('Parámetros: ', param1, param2);

12 return 'Mensaje devuelto por el temporizador.';

13 }

14

15 retraso.then(function (msg) {

16 console.log(msg);

17 console.log('Retraso finalizado');

18 });

19

20 retraso.catch(function () {

21 console.log('Retraso cancelado.');

22 })

23

24 vm.cancelar = function () {

25 $timeout.cancel(retraso);

26 }

27 }

Page 85: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 64

Con este ejemplo hemos podido observar todas las características de este servicio y loque lo hace más útil sobre el clásico setTimeout de Javascript.

Creando repeticiones con intervalos

Cuando queremos que una tarea específica se ejecute cada cierto tiempo, usualmenteutilizamos la función serInterval de Javascript. Al igual que para setTimeout AngularJSdispone de un servicio que te ayudará a ejecutar tareas repetidamente cada un tiempoespecífico. El servicio $interval es muy similar al servicio $timeout.

El servicio $timeout se diferencia en $interval solo en que este realizará la tarea unacantidad especificada de ocasiones. Los dos servicios utilizan exactamente el mismoAPI para trabajar con ellos, el único cambio es a la hora de especificar la cantidad deveces que se ejecutara. La cantidad de veces que se repetirá la tarea se especificará comotercer parámetro en la invocación del servicio. Para ver un ejemplo crearemos un conteoregresivo de 5 segundos e imprimiremos en la consola cada uno de los segundos. Alterminar el conteo regresivo enviaremos una alerta al usuario.

1 angular.module('app', [])2 .controller('AppCtrl', AppCtrl);

3

4 AppCtrl.$inject = ['$scope', '$interval'];5 function AppCtrl($scope, $interval){

6 var conteo = $interval(imprimirConteo, 1000, 5);

7 var i = 4;

8 function imprimirConteo() {

9 if ( i > 0 ) {

10 console.log('Quedan ' + i + ' segundos.');

11 i--;

12 } else {

13 console.log('Conteo finalizado.');

14 }

15 }

16 conteo.then(function () {

17 alert('Ya han pasado 5 segundos.');

18 });

19 }

Es importante mencionar que los intervalos creados por este servicio no son canceladosautomáticamente después de finalizados. Estos deben ser cancelados de forma manualuna vez que hayan terminado o antes de que el *$scope** sea destruido. Para lograrlo loprimero que necesitamos es cancelarlo después que haya terminado.

Page 86: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 65

1 conteo.then(function () {

2 alert('Ya han pasado 5 segundos.');

3 $interval.cancel(conteo);

4 });

La otra precaución que necesitamos tomar es para cuando el intervalo aún no hafinalizado. Para ello podremos escuchar el evento$destroy del $scope y entonces cancelarel intervalo cuando este ocurra.

1 $scope.$on('$destroy', function () {

2 $interval.cancel(conteo);

3 });

De esta forma evitaremos problemas de rendimiento al cambiar desde un $scope haciaotro.

Anotaciones en el DOM

Como habrás podido notar a lo largo del desarrollo con el framework, en el DOMde la aplicación, Angular escribe una serie de anotaciones que son innecesarias parael funcionamiento de esta. Las anotaciones se muestran en la imagen que aparece acontinuación.

Anotaciones en el DOM

En la nueva versión del framework existe una vía para eliminar esas anotaciones.Para lograrlo necesitamos configurar el servicio $compiler en la configuración de laaplicación.

Page 87: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 4: Servicios 66

1 angular.module('app', [])2 .config(['$compileProvider', function ($compileProvider) {

3 $compileProvider.debugInfoEnabled(false);4 }]);

Unavez configuradopodremosobservar queAngular ha eliminado las anotaciones comose muestra en la imagen a continuación.

Anotaciones en el DOM

Esto implica un ligero aumento en el rendimiento general de la aplicación ya queAngularno tiene que escribir todas esas anotaciones en el DOM para el funcionamiento de laaplicación.

Page 88: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidorHasta ahora podemos comprobar que con AngularJS podemos crear una aplicacióncompleta del lado del cliente, pero solo con la información que cargamos al inicio.Claro que con solo el lado del cliente no se puede lograr muchas cosas. Por este motivoAngularJS trae un servicio que nos ayudará a intercambiar información con el servidor.Otro de los servicios del núcleo del framework es $http que será el encargado deinteractuar con el servidor remoto mediante el objeto XMLHttpRequest.

Este servicio solo aceptará un argumento que será un objeto de configuración paradependiendo de este, generar las peticiones al servidor remoto. Como comentamos enel capítulo anterior este servicio siempre devolverá una promesa. Lo que quiere decirque podemos usar el método then para manejar la respuesta. Pasándole la primerafunción como parámetro para si la promesa ha sido resuelta y una segunda para sí hasido rechazada. Estos dos métodos reciben como parámetro un objeto que representala respuesta. Además del método then $http nos proporciona dos métodos de accesorápido para gestionar la promesa. El primero será el método success() y el segundo seráerror().

Si el código de la respuesta es un número entre 200 y 299 la respuesta se seconsiderará como resuelta, de lo contrario será tratada como error y el métodoerror() será ejecutado.

Los parámetros que recibirán losmétodos success() y error() serándata, status,headers,config, statusText.Mientras que elmétodo then solo recibirá un objeto de respuesta queune el contenido anterior.

data Puede ser de tipo objeto o texto. Son los datos retornados por el servidor despuésde haber sido transformados por las funciones de transformación.

statusDe tipo número. Será el código de la respuesta que ha enviado el servidor.

headersEs una función para obtener las cabeceras de la respuesta.

configDe tipo objeto. Es el objeto de configuración que fue usado para generar la petición.

statusTextCadena de texto con el mensaje de estado HTTP de la respuesta.

67

Page 89: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 68

Los parámetros antes mencionados son los que recibirán los métodos success y error.No es necesario usarlos todos, por lo general solo se usan los dos primeros. Los datosde la respuesta y el código para tomar acciones en la aplicación correspondiente a lorecibido.

Objeto de configuración del servicio $http

Como he dicho antes el servicio $http obtiene un objeto de configuración ahora descri-biré cuales son las propiedades que puede tener este objeto.

methodCadena de texto que describe el método HTTP que se usará para la petición (‘GET’,‘POST’, ‘PUT’, ‘PATCH’, ‘DELETE’, etc.).

url Dirección absoluta o relativa a la que se hará la petición.

paramsObjeto de llaves: valor que será enviadodespués de la url (?llave=valor&llave2=valor2).Si el valor no es una cadena de texto será convertido a JSON.

data Cadena de texto u objeto que será enviado como datos de la petición.

headersObjeto de cadenas de texto, o funciones que devuelven cadenas de texto que re-presenten cabeceras HTTP para ser enviadas al servidor. Si alguna de las funcionesdevuelve null esa cabecera no será enviada.

xsrfHeaderNameCadena de texto con el nombre de la cabeceraHTTPque será utilizada para el tokenXSRF.

xsrfCookieNameCadena de texto con el nombre de la cookie que contiene el token XSRF.

transformRequestFunciónde transformacióno arreglo de funciones de transformación. Estas funcio-nes reciben el cuerpo de la petición y las cabeceras como parámetro y las devuelventransformadas.

transformResponseEl mismo funcionamiento que transformRequest pero para transformar las res-puestas.

Page 90: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 69

cacheSi recibe un valor verdadero se hará cache de la petición, si es una instancia delservicio $cacheFactory esta será usada para hacer cache de la petición.

timeoutTiempo de espera en mili segundos o una promesa que aborte la petición cuandose resuelva.

withCredentialsValor verdadero o falso para ser indicado en el objeto XHR

responseTypeCadena de texto con el tipo de respuesta solicitada.

Veamos un ejemplo de cómo se utiliza el servicio $http. Para este ejemplo he creado unarchivo JSON que simulará una respuesta del servidor.

Archivo: App/usuarios.json

1 [{

2 "nombre": "Maikel",

3 "apellidos": "Rivero Dorta",

4 "email": "[email protected]",

5 "lenguajes": ["en", "es"]

6 },

7 {

8 "nombre": "john",

9 "apellidos": "Doe",

10 "email": "[email protected]",

11 "lenguajes": ["en"]

12 },

13 {

14 "nombre": "Jane",

15 "apellidos": "Doe",

16 "email": "[email protected]",

17 "lenguajes": ["en","es"]

18 }]

Ahora utilizaremos un controlador para hacer una petición a este archivo y mostrar elresultado en la vista.

Page 91: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 70

Archivo: App/js/Controllers/UsersCtrl.js

1 angular.module('miApp')

2 .controller('UsersCtrl', ['$scope', '$http', function ($scope, $http) {

3 var usuarios = $http({

4 method: 'GET',

5 url: 'usuarios.json'

6 }).success(function(data, status){

7 $scope.usuarios = data;

8 }).error(function(data, status){

9 console.log(data, status);

10 });

11 }])

El ejemplo anterior si lo ejecutas fuera de un servidorHTTP no te va a funcionar, si estástrabajando local te recomiendo que uses los AMP osea para MacMAMP para WindowsWAMP y para Linux LAMP. Estas aplicaciones son servidores muy fáciles de usar paradesarrollo local y vienen pre-cargados con servicioHTTP y base de datosMySQL. Si notienes experiencia con servidores, los anteriores son muy fáciles de hacerlos funcionar,en suweb explican paso a paso comoutilizarlos. Otras de las opciones que puedes utilizares crear tu propio servidor con node.js o instalar de forma dedicadaApache oNginx entu pc.

En el ejemplo anterior configuramos el servicio $http para hacer una petición de tipoGET al archivo usuarios.json. Cuando su promesa ha sido resuelta ejecutará el métodosuccess, donde asignamos la respuesta al $scope para hacerlos disponible en la vista. Sila promesa no se resuelve se ejecutará el método error donde enviamos la respuesta a laconsola. Veamos el código para la vista.

Archivo: App/index.html

1 <body>2 <div class="container" ng-controller="UsersCtrl">3 <hr>4 <div ng-repeat="usuario in usuarios">5 <p><strong>Nombre:</strong> {{ usuario.nombre }}</p>6 <p><strong>Apellidos:</strong> {{ usuario.apellidos }}</p>7 <p><strong>Email:</strong> {{ usuario.email }}</p>8 <p><strong>Lenguajes:</strong> |

9 <span ng-repeat="lenguaje in usuario.lenguajes">10 {{ lenguaje }} |

11 </span></p>12 <hr>

Page 92: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 71

13 </div>14 </div>15 <script src="lib/angular.js"></script>16 <script src="js/app.js"></script>17 <script src="js/Controllers/UsersCtrl.js"></script>18 </body>

En el código anterior no hay nada nuevo, simplemente usamos la directiva ng-repeatpara mostrar los usuarios y sus datos.

De esta forma comenzamos a hacer peticiones al servidor. En esta ocasión lo hemoshecho a un archivo en nuestro propio servidor, pero esta es la vía más rápida de hacerlas peticiones. En la propiedad url del objeto de configuración que le pasamos al serviciohttp es donde decidiremos a donde haremos la petición. Existen variasAPI públicas conlas que podrías usar este servicio. Ejemplo de estas son Twitter, Github, IMDB, todasestas tienen su ayuda donde explican su funcionamiento.

Métodos de acceso rápido

Este servicio nos brinda varios métodos de acceso rápido para ejecutar acciones con losmétodos HTTP.

$http.get(url, config)Este método realiza una petición get a la url que recibirá como primer parámetroy en caso de que necesitemos especificar alguna otra configuración lo recibirácomo segundo parámetro, pero no es necesario. Este método reduciría el códigodel ejemplo anterior a

1 $http.get('usuarios.json')

2 .success(function(data, status){

3 $scope.usuarios = data;

4 }).error(function(data, status){

5 console.log(data, status);

6 });

$http.head(url, config)Este método nos permite hacer una petición head a la url especificada. Comosegundo parámetro recibe un objeto de configuración.

Page 93: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 72

$http.post(url, data, config)Este método es el que por lo general usamos para enviar peticiones al servidorcon un cuerpo, ya sea para enviar datos de identificación, o para la creación denuevos recursos en el servidor. Se creará una petición de tipo post a la url que sepasará como primer parámetro, en el segundo parámetro pasaremos el cuerpo dela petición, y opcional como tercer parámetro el objeto de configuración.

$http.put(url, data, config)El método put es usado para hacer las peticiones de actualización, los parámetrosque recibe son los mismos que las peticiones de tipo post.

$http.delete(url, config)Con este método podremos realizar una petición de tipo delete para eliminarrecursos en el servidor. El primero parámetro es la url a la que se realizará lapetición, por lo general sería algo así www.api.com/contactos/52 donde se eliminaráel contacto 52 si el servidor tiene implementado este tipo de peticiones. El segundoparámetro es opcional, un objeto de configuración.

$http.patch(url, data, config)Realiza una petición de tipo patch esencialmente es como el método put.

$http.jsonp(url, config)Realiza una petición tipo jsonp al servidor donde el nombre del callback debe serla cadena de texto JSON_CALLBACK. El primer parámetro es la url que especificala dirección a donde se hará la petición, el segundo parámetro es un objeto deconfiguración.

Provider del servicio $http

El servicio $http está registrado como provider lo que quiere decir que puede serconfigurado en el proceso de creación del módulo. En esta configuración podemosdefinir varios parámetros para que nuestra aplicación siempre que use el servicio $httplos tenga disponibles. En esta configuración podemos cambiar las cabeceras para cadatipo de petición y poner otras cabeceras que necesite enviar la aplicación a la hora dehacer la petición al servidor. Supongamos que necesitamos enviar el Token CSRF entodas las peticiones, quedaría de esta forma.

Page 94: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 73

Archivo: App/Config/http.js

1 angular.module('miApp').

2 config(['$httpProvider', function ($httpProvider) {

3 $httpProvider.defaults.headers.common.CSRF_TOKEN = "a2d10a3211b415832791a6bc6";

4 }]);

De esta forma el token CSRF será enviado en todas las peticiones que hagamos alservidor. En el ejemplo anterior he añadido el token al objeto common pero si soloquisiéramos enviar el token en las peticiones post, put, path o delete podríamos hacerloescribiéndolo en cada método por individual de la siguiente forma

Archivo: App/Config/http.js

1 angular.module('miApp').

2 config(['$httpProvider', function ($httpProvider) {

3 $httpProvider.defaults.headers.post.CSRF_TOKEN = "a2d10a3211b415832791a6bc6";

4 }]);

El servicio $http permite transformar las peticiones y las respuestas antes de ser en-tregadas. Automáticamente $http siempre las transforma, las peticiones que se hacen alservidor y tengan una propiedad data en el objeto de configuración y esta sea un objeto,el servicio serializa automáticamente el objeto data en formato JSON para ser entregadoal servidor. En cuanto a las respuestas, si es detectado que el contenido es en formatoJSON es deserializado a un objeto o arreglo Javascript.

La configuración del servicio permite incluir propias funciones de transformación parasi necesitas hacer cambios específicos a tus peticiones o respuestas. Veamos un ejemplode cómo podemos realizar estas transformaciones para utilizarlas en nuestro favor.

Archivo: App/Config/respTransformer

1 angular.module('miApp').

2 config(['$httpProvider', function ($httpProvider) {

3 $httpProvider.defaults.transformResponse.push(function(data){

4 data.push({

5 "nombre": "Junior",

6 "apellidos": "Doe",

7 "email": "[email protected]",

8 "lenguajes": ["es"]9 });

10 return data;

11 })

12 }]);

Page 95: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 74

Si ese archivo de configuración lo cargamos en el archivo index.html del ejemplo anteriorpodremos observar que el cuándo la respuesta es entregada al controlador y este a la vistaya incluye el nuevo usuario que escribimos en el archivo de configuración. De estamismaforma se escriben los transformadores de las peticiones.

Además de esta flexibilidad de transformar las peticiones y las respuestas el servicio$http, nos brinda otra vía de interceptar las respuestas antes de ser entregadas a la aplica-ción y las peticiones antes de ser enviadas al servidor. Para entender correctamente debeshaber entendido el funcionamiento del servicio $q y las promesas. Los Interceptorsserán servicios factory que serán añadidos al arreglo $httpProvider.interceptors. Estosserán llamados y se les inyectará sus dependencias en caso denecesitar alguna y devolveráel interceptor. Veamos otro ejemplo de cómoenviar el tokenCSRFen todas las peticioneso agregar un usuario nuevo a la respuesta, pero esta vez con un interceptor.

Archivo: App/Config/reqInterceptor.js

1 angular.module('miApp')

2 .factory('reqInterceptor', [function () {

3 var interceptor = {

4 request: function(config){

5 config.headers['CSRF_TOKEN'] = 'a2d10a3211b415832791a6bc6';

6 return config;

7 },

8 response: function(response){9 response.data.push({

10 nombre: 'Lorem',

11 apellidos: 'Ipsum Dolor',

12 email: '[email protected]',

13 lenguajes: ['en', 'es']14 })

15 return response;

16 }

17 }

18 return interceptor;

19 }])

20 .config(['$httpProvider', function ($httpProvider) {

21 $httpProvider.interceptors.push('reqInterceptor');

22 }])

En el ejemplo anterior he declarado el factory reqInterceptor que devolverá un objetocon dos propiedades, una es request y la otra es response. La primera recibirá comoparámetro el objeto de configuración del servicio $http antes de enviar la petición.Este lo usamos para agregarle la cabecera CSRF_TOKEN y siempre tendremos que

Page 96: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 5: Peticiones al servidor 75

devolver el objeto de configuración o un objeto de configuración nuevo. La segundarecibirá como parámetro un objeto de respuesta, que es el mismo que recibe el métodosuccess del servicio $http pero antes de ser entregado a este. Teniendo la posibilidadde modificarlo he añadido un nuevo usuario a la propiedad data de la respuesta paraser entregado en el controlador a la vista. En este caso también tendremos que devolverese objeto de respuesta después de haberlo modificado. Por último, se agrega el servicioreqInterceptor al arreglo de interceptors de la configuración de $httpProvider.

Para comprobar que funcionen correctamente podemos ir a las herramientas de desarro-llo del navegador y en la pestaña Red buscamos la petición que se hace a usuarios.jsony en los headers de la petición podemos observar que se a añadido el CSRF_TO-KEN:a2d10a3211b415832791a6bc6 y que al retornar la respuesta tenemos el nuevousuario Lorem en la lista que muestra la vista si has incluido este archivo en el in-dex.html del ejemplo anterior.

Como habrás podido observar el servicio $http esmuy útil y muy flexible si necesitamoshacer peticiones al servidor en la aplicación.

Page 97: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: DirectivasComo hemos podido observar hasta ahora, las directivas son una parte importante deAngularJS. Con ellas podemos manipular el DOM de una forma muy fácil y lograrbloques de código que de otra forma sería un poco complicado. Otra de las ventajas delas directivas es que nos permite reutilizar partes de la aplicación sin tener que volver aescribir el mismo código en diferentes partes. Las directivas no se rigen solo a atributosde los elementos HTML, estas pueden ser elementos e incluso clases CSS.

CuidadoLas directivas declaradas como elementos puede que no funcionen en InternetExplorar, solo funcionarán en navegadores como Google Chrome, Safari, Fi-refox, Opera y otros. Por este motivo debes restringir tus directivas a clases oatributos.

Angular trae en su núcleo definido una gran cantidad de directivas que te ayudarán adesarrollar tu aplicación con un código más limpio y efectivo. Pero también te permitedeclarar tus propias directivas que sea más específicas para tu aplicación. Hasta ahora heexplicado el funcionamiento de algunas a lo largo de los ejemplos. Antes de comenzara crear las directivas específicas de la aplicación veamos otras de las que vienen en elnúcleo de Angular.

ng-class

AngularJS nos permite cambiar o añadir clases a los elementos HTML. Definiendouna expresión que represente las clases que serían añadidas o removidas del elemento.Este comportamiento lo realiza mediante la directiva ng-class. Esta directiva funcionade tres formas diferentes dependiendo del resultado de la evaluación de la expresiónproporcionada como valor.

La primera es si la expresión es evaluada a una cadena de texto, el texto debe ser unnombre de clase o varios separados por espacios.

76

Page 98: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 77

1 <head>2 <meta charset="UTF-8">3 <title>ng-class</title>4 <style>5 .flotar {float: right; padding: 0 10px;}

6 .fondoRojo {background-color: red; }

7 .bordesRedondeados {border: 2px solid black; border-radius: 10px; }

8 </style>9 <script src="../lib/angular.js"></script>

10 </head>11 <body ng-app="miApp" ng-controller="miCtrl">12 <div ng-class="generarClases()">13 <h1>Ejemplo de los usos de ng-class</h1>14 </div>15 <script>16 angular.module('miApp', [])

17 .controller('miCtrl', ['$scope', function ($scope) {

18 var clases = ['flotar', 'fondoRojo', 'bordesRedondeados'];

19 $scope.generarClases = function(){

20 return clases.join(' ');

21 }

22 }])

23 </script>24 </body>

La segunda es si la expresión es evaluada a un arreglo donde cada uno de sus elementossea una cadena de texto de uno o varias clases separadas por espacios.

1 <head>2 <meta charset="UTF-8">3 <title>ng-class</title>4 <style>5 .flotar {float: right; padding: 0 10px;}

6 .fondoRojo {background-color: red; }

7 .bordesRedondeados {border: 2px solid black; border-radius: 10px; }

8 </style>9 <script src="../lib/angular.js"></script>

10 </head>11 <body ng-app="miApp" ng-controller="miCtrl">12 <div ng-class="generarClases()">13 <h1>Ejemplo de los usos de ng-class</h1>14 </div>

Page 99: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 78

15 <script>16 angular.module('miApp', [])

17 .controller('miCtrl', ['$scope', function ($scope) {

18 var clases = ['flotar', 'fondoRojo bordesRedondeados'];

19 $scope.generarClases = function(){

20 return clases;

21 }

22 }])

23 </script>24 </body>

La tercera es la más compleja ya que podemos tenemos la opción de poner condiciones.Esta forma es si la expresión se evalúa a un objeto, donde por cada par llave-valor conun valor verdadero, la llave será usada como clase.

1 <head>2 <meta charset="UTF-8">3 <title>Test</title>4 <style>5 .flotar {float: right; padding: 0 10px;}

6 .fondoRojo {background-color: red; }

7 .bordesRedondeados {border: 2px solid black; border-radius: 10px; }

8 </style>9 <script src="../lib/angular.js"></script>

10 </head>11 <body ng-app="miApp">12 <input type="checkbox" ng-model="flotar">Flotar13 <input type="checkbox" ng-model="fondoRojo">Fondo Rojo

14 <input type="checkbox" ng-model="bordesRedondeados">Bordes Redondeados

15 <div ng-class="{

16 'flotar':flotar,

17 'fondoRojo': fondoRojo,

18 'bordesRedondeados': bordesRedondeados}">19 <h1>Ejemplo de los usos de ng-class</h1>20 </div>21 <script>22 angular.module('miApp', []);

23 </script>24 </body>

En el ejemplo anterior hemos hecho uso de ng-model para obtener un valor verdadero ofalso proporcionado por el input. Como han podido observar esta última forma de usarla directiva nos damuchas posibilidades para obtener resultados de una formamuy fácil.

Page 100: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 79

Existen otras tres directivas para alterar las clases de los elementos, dos de ellas son ng-class-even y ng-class-odd, estas funcionan en conjunto con la directiva ng-repeat quetrataremos más adelante. Las dos directivas funcionan de la misma forma que ng-classpero solo tienen efecto en las filas pares e impares de ng-repeat. Ahora hablaremos de latercera.

ng-style

Esta directiva no será muy usada ya que con ng-class podemos lograr lo que con esta.ng-style permite que cambies el estilo del elemento condicionalmente. Digo que no serámuy usada por que por lo general no usamos el atributo style de los elementos HTMLregularmente, en su lugar usamos clases definidas en nuestros archivos de css.

1 <body ng-app="miApp" ng-controller="miCtrl">2 <div ng-style="clases">3 <h1>Ejemplo de los usos de ng-class</h1>4 </div>5 <script>6 angular.module('miApp', [])

7 .controller('miCtrl', ['$scope', function ($scope) {

8 var clases = {

9 'float': 'right',

10 'padding': '0 10px',

11 'background-color': 'red',

12 'border': '2px solid black',

13 'border-radius': '10px'

14 };

15 $scope.clases = clases;

16 }]);

17 </script>18 </body>

ng-list

En ocasiones en las aplicaciones necesitamos obtener una lista indicada por el usuario,un ejemplo de esto es la lista de etiquetas o categorías de un post en un blog. Para estospropósitos AngularJS posee la directiva ng-list.

Page 101: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 80

1 <body ng-app="miApp" ng-controller="miCtrl">2 Etiquetas: <input ng-model="etiquetas" ng-list><br>3 Debug: {{ etiquetas }}4 <script>5 angular.module('miApp', [])

6 .controller('miCtrl', ['$scope', function($scope) {

7 $scope.etiquetas = ['Actualidad', 'Finanzas', 'Tecnología'];

8 }]);

9 </script>10 </body>

ng-non-bindable

En caso de que en la aplicación quisiéramos que AngularJS no ejecute ninguna de susacciones o no evalué ninguna expresión podemos usar la directiva ng-non-bindable.Cuando AngularJS encuentre esta directiva en el código de la aplicación, pasará por altoese bloque y continuara con la ejecución de la aplicación.

1 <body ng-app="miApp" ng-controller="miCtrl">2 <div>{{ mensaje }}</div>3 <div ng-non-bindable>{{ mensaje }}</div>4 <script>5 angular.module('miApp', [])

6 .controller('miCtrl', ['$scope', function ($scope) {

7 $scope.mensaje = 'Hola desde el controlador.';

8 }])

9 </script>10 </body>

ng-repeat

Unade las directivasmás importantes deAngularJS viene a serng-repeat que con su ven-taja de repetir una plantilla por cada elemento de una colección. Este comportamientonos da una gran ventaja a la hora de hacer listas o tablas. Hay varias utilidades que nosbrinda el ng-repeat, las iré describiendo a continuación.

Page 102: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 81

1 <body ng-app="miApp" ng-controller="miCtrl">2 <div class="container">3 Listado de compra.

4 <ul>5 <li ng-repeat="compra in musica">6 <strong>Artista:</strong> {{ compra.artista }}7 <strong>CD</strong> {{ compra.cd }}8 </li>9 </ul>

10 </div>11 <script>12 angular.module('miApp', [])

13 .controller('miCtrl', ['$scope', function ($scope) {

14 var musica = [

15 {artista: 'U2', cd: 'Songs of Innocence'},

16 {artista: 'Afrojack', cd: 'Forget the World'},

17 {artista: 'Alexandra Stan', cd: 'Unlocked'},

18 {artista: 'Avicii', cd: 'True'},

19 {artista: 'Dash Berlin', cd: 'The New Daylight'},

20 {artista: 'David Guetta', cd: 'Lovers on the Sun'},

21 {artista: 'Echosmith', cd: 'Talking Dreams'},

22 {artista: 'La Roux', cd: ' Trouble in paradise'}

23 ];

24 $scope.musica = musica;

25 }]);

26 </script>27 </body>

En el controlador se ha creado un arreglo de elementos para ser posteriormente asignadoal $scope y hacerlos disponibles en la vista. Por otra parte, en la vista se ha utilizado unalista desordenada para mostrar los elementos. La directiva ng-repeat está situada en elelemento <li> ya que será el que queremos que se repita por cada elemento de la lista decompra.

Esta directiva evalúa su valor de dos formas. La primera es la que hemos usado en elejemplo anterior. Se evalúa la expresión de la siguiente forma variable in coleccióndonde la variable es la que tomará un valor de la colección en cada vez que se repitay valdrá solo hasta el final de ese ciclo donde comenzará nuevamente con el siguientevalor de la colección.

La segunda forma es en esencia igual solo que esta vez podremos obtener también la llavey no solo el valor (llave, valor) in colección como veremos en el siguiente ejemplo.

Page 103: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 82

1 <body ng-app="miApp" ng-controller="miCtrl">2 <div class="container">3 <h3>U2 - Songs of Innocence</h3>4 <table>5 <thead> <tr> <th>Titulo</th><th>Duración</th> </tr> </thead>6 <tbody>7 <tr ng-repeat="(titulo, tiempo) in playlist">8 <td>{{ titulo }}</td><td>{{ tiempo }}</td>9 </tr>

10 </tbody>11 </table>12 </div>13 <script>14 angular.module('miApp', [])

15 .controller('miCtrl', ['$scope', function ($scope) {

16 var playlist = {

17 'The Miracle (Of Joey Ramone)': '4:15',

18 'Raised By Wolves': '4:12',

19 'Every Breaking Wave': '3:59',

20 'Cedarwood Road': '3:46',

21 'California (There Is No End to Love)': '5:19',

22 'Sleep Like a Baby Tonight': '3:14',

23 'Song for Someone': '4:05',

24 'This Is Where You Can Reach Me Now': '4:25',

25 'Iris (Hold Me Close)': '5:01',

26 'The Troubles': '5:05',

27 'Volcano': '4:45'

28 };

29 $scope.playlist = playlist;

30 }]);

31 </script>32 </body>

Existen otros parámetros que puede ser incluido en la expresión que le pasamos a ladirectiva. Este es muy útil para buscar dentro de listas, filter nos permite filtrar elcontenido de la colección mediante una variable.

Page 104: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 83

1 <body ng-app="miApp" ng-controller="miCtrl">2 <div class="container">3 <h1>Biblioteca de CD's.</h1>4 <input type="search" ng-model="buscar">5 <ul>6 <li ng-repeat="cd in cds | filter:buscar">{{ cd }}</li>7 </ul>8 </div>9

10 <script>11 angular.module('miApp', [])

12 .controller('miCtrl', ['$scope', function ($scope) {

13 var cds = [

14 'Songs of Innocence',

15 'Forget the World',

16 'Unlocked', 'True',

17 'The New Daylight',

18 'Lovers on the Sun',

19 'Talking Dreams',

20 'Trouble in paradise'

21 ];

22 $scope.cds = cds;

23 }]);

24 </script>25 </body>

Para especificar el filtro debemos separarlo con el caracter | y a continuación la palabrafilter seguido de : y el nombre de la variable que servirá de filtro. En el ejemplo anteriorusamos un elemento input para hacer la búsqueda dinámica en el navegador.

La directivang-repeat además nos provee de una serie de variables útiles que nos puedenservir muy bien para realizar varias operaciones con la lista. Estas variables solo estarándisponibles dentro del ciclo que recorre ng-repeat

$indexNosdevuelve el númerode la iteraciónpor la que vamos en esemomento, comienzaen 0.

$firstTiene valor verdadero si es el primer elemento del ciclo.

$middleTiene valor verdadero si el elemento no es ni el primero ni el último del ciclo.

Page 105: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 84

$last Tiene valor verdadero si el elemento es el último del ciclo.

$evenTiene valor verdadero si la variable $index tiene un valor par.

$oddTiene valor verdadero si la variable $index tiene un valor impar.

A continuación, un ejemplo combinando todas las particularidades de ng-repeat unidoa otras directivas ya estudiadas.

1 <head>2 <meta charset="UTF-8">3 <title>ng-repeat</title>4 <script src="../lib/angular.js"></script>5 <style>6 .container {width: 600px; margin: auto;}

7 ul {list-style: none; width: 100%; padding: 0; margin: 0;}

8 input {width: 100%; padding: 5px;}

9 .primera {background-color: #FF7676; }

10 .medio {background-color: #4AB300;}

11 .ultima {background-color: #43539C;}

12 .par { text-decoration: underline;}

13 .impar {font-weight: bold;}

14 </style>15 </head>16 <body ng-app="miApp" ng-controller="miCtrl">17 <div class="container">18 <h1>Biblioteca de CD's.</h1>19 <input type="search" ng-model="buscar" placeholder="Buscar...">20 <ul>21 <li ng-repeat="cd in cds | filter:buscar"

22 ng-class="{

23 'primera':$first,

24 'medio':$middle,

25 'ultima':$last,

26 'par':$even,

27 'impar':$odd}">28 {{ $index+1 }} - {{ cd }}29 </li>30 </ul>31 </div>32

Page 106: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 85

33 <script>34 angular.module('miApp', [])

35 .controller('miCtrl', ['$scope', function ($scope) {

36 var cds = [

37 'Songs of Innocence',

38 'Forget the World',

39 'Unlocked', 'True',

40 'The New Daylight',

41 'Lovers on the Sun',

42 'Talking Dreams',

43 'Trouble in paradise'

44 ];

45 $scope.cds = cds;

46 }]);

47 </script>48 </body>

Como has podido observar en el ejemplo anterior se combinan todas las bondades de ladirectiva ng-repeat con las de ng-class. Pero eso no es todo, hay un sin fin de utilidadespara esta directiva, ya lo verás en próximos capítulos.

ng-if

Esta directiva basada en la evaluación de una expresión que si resulta un valor falsoelimina por completo los elementos del DOM, de lo contrario inserta un clon de loselementos. Su uso es parecido a la de ng-show/ng-hide pero con la diferencia de que loselementos no son alterados con la propiedad display de css.

1 <body ng-controller="miCtrl">2 <div class="container">3 <input type="checkbox" ng-model="mostrar"> Mostrar bienvenida.

4 <p ng-if="mostrar">Bienvenido al mundo de <strong>AngularJS</strong></p>5 </div>6 <script>7 angular.module('miApp', [])

8 .controller('miCtrl', ['$scope', function ($scope) {

9 $scope.mostrar = true;

10 }])

11 </script>12 </body>

Page 107: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 86

Algo a tener en cuenta es el comportamiento de que siempre es insertado un clon delelemento y no el que existía antes de ser eliminado. Si el elemento había sidomodificadocon jQuery este perderá las modificaciones

ng-include

A la hora de hacer la maqueta de la aplicación quizás necesites tener parte de laaplicación que se usarán en varias plantillas. La directiva ng-include puede solucionarteese problema permitiendo que extraigas la porción de la plantilla que será reutilizada aun archivo diferente y luego con el uso de la directiva, incluirla en varios lugares de tuaplicación. La directiva debe recibir el URL de la plantilla para ser cargada.

RestriccionesHay que tener en cuenta que las plantillas deben estar en el mismo dominioy protocolo que nuestra aplicación, para cargar plantillas fuera de nuestrodominio necesitamos registrarlo en la lista blanca configurando en el servicio$sce.getTrustedResourceUrl. Además, hay que tener en cuenta las restriccionesdel navegador con respecto a los recursos compartidos a través de dominios. Entodos los navegadores no funciona esta directiva cuando se llama a las plantillasdesde *file://*, será mejor ejecutar la aplicación desde un servidor para obtenerlos resultados.

Además, esta directiva permite el uso de otras dos propiedades. Una es una expresiónque será evaluada cuando la plantilla sea cargada onload=”“ y la otra es autoScroll=”“que si no está presente deshabilita el scrolling, si está presente, pero sin valor habilita elscrolling o habilita el scrolling si la expresión es evaluada a verdadero.

ng-cloak

Esta directiva es usada para prevenir que Angular muestre partes de la plantilla sin sercompiladas previamente. Puede ser aplicada al elemento body, pero se trata de una malapráctica ya que es bueno que la aplicación vaya siendo visualizada a medida que se vayacargando. Para ello se pueden usar varias veces la directiva para reducir la cantidad decontenido que no será mostrado hasta que no sea compilado por Angular. Esta directivahace su función mediante CSS y la propiedad display de cada elemento donde se aplica.Esta directiva es solo el atributo, no necesita valor.

Debido a que trabaja conCSS sería necesario que el framework sea incluido al comienzode la página o de lo contrario no existirán las clases CSS que este utiliza para ocultar loscontenidos.De otra forma la clase puede ser incluida en un archivoCSS y el framework alfinal de la página. La clase que deberíamos escribir en nuestro archivoCSS es la siguiente.

Page 108: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 87

1 [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {

2 display: none !important;3 }

Estas directivas que se han expuesto hasta ahora pueden ser usadas en varios elementos.Ahora trataremos sobre directivas específicas de algunos elementos HTML.

ng-href

Cuando utilizamos la etiqueta <a> tenemos un atributo href que nos permite definirla referencia del vínculo a donde se quiere llegar. En AngularJS quizás necesites usarvínculos como http://www.miapp.com/perfiles/{{ usuario.id }}. En esencia funcionarán, perosolo si el usuario da clic después de que Angular haya tenido la oportunidad de cambiarla sintaxis de la plantilla por la id del usuario. Este problema lo resuelve el ng-href yaque este hará que el vínculo solo funcione cuando esté completamente construido y listopara ser usado.

ng-src y ng-srcset

Al igual que ng-href esta directiva resuelve el problema de que las llamadas a las URLgeneradas por el motor de plantillas se hagan en el momento en que la plantilla está lista.

Ahora mencionaré las directivas relacionadas con eventos. Cada una de estas directivashacen disponible un objeto $event con el evento.

ng-blur

Esta directiva dispara un evento en el momento que el elemento pierde el foco. Tienevarios usos como por ejemplo en un formulario de registro para ejecutar una compro-bación si el nombre de usuario existe.

ng-copy, ng-cut y ng-paste

Estas directivas se explican solas por el significado de su nombre. Cada una evalúa unaexpresión dada cuando se realiza alguna de las acciones de copiar, cortar o pegar sobreun elemento.

Page 109: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 88

ng-dblclick

Al igual que la directiva ng-click esta evalúa una expresión al hacer doble clic en elelemento.

ng-keydown, ng-keypress y ng-keyup

Estas directivas evalúan una expresión al ejecutarse la acción de presionar una tecla,cuando la tecla esta presionada y cuando la tecla es liberada. Además, en el objeto eventose les puede extraer el código de la tecla.

1 <body ng-controller="miCtrl">2 <input type="text"

3 ng-keydown="keydown($event)"

4 ng-keypress="keypress($event)"

5 ng-keyup="keyup($event)">6 <script>7 angular.module('miApp', [])

8 .controller('miCtrl', ['$scope', function ($scope) {

9 $scope.keydown = function(e){

10 console.log('Key down - Key Code: '+e.keyCode, 'altKey: '+e.altKey);

11 };

12 $scope.keypress = function(e){

13 console.log('Key press - Key Code: '+e.keyCode, 'altKey: '+e.altKey);

14 };

15 $scope.keyup = function(e){

16 console.log('Key up - Key Code: '+e.keyCode, 'altKey: '+e.altKey);

17 };

18 }])

19 </script>20 </body>

Eventos del mouse

Angular maneja seis eventos con el mouse, cada uno de ellos evalúa una expresión alproducirse el evento. Lo describiremos con un ejemplo.

Page 110: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 89

1 <body ng-controller="miCtrl">2 <div ng-style="style"

3 ng-mousedown="down()"

4 ng-mouseup="up()"

5 ng-mouseenter="enter()"

6 ng-mouseleave="leave()"

7 ng-mousemove="move($event)"

8 ng-mouseover="over()">9 {{ pos }}

10 </div>11 <script>12 angular.module('miApp', [])

13 .controller('miCtrl', ['$scope', function ($scope) {

14 $scope.style = {

15 'border': '2px solid black',

16 'width': '200px',

17 'height': '200px',

18 'background-color': '#56A5F3'

19 };

20 $scope.down = function(e){console.log('Ejecutado el evento Mousedown'); };

21 $scope.up = function(e){console.log('Ejecutado el evento Mouseup'); };

22 $scope.enter = function(e){console.log('Ejecutado el evento Mouseenter');};

23 $scope.leave = function(e){console.log('Ejecutado el evento Mouseleave');};

24 $scope.move = function(e){$scope.pos = 'x: '+e.x + 'y: '+ e.y;};

25 $scope.over = function(e){console.log('Ejecutado el evento Mouseover');};

26 }])

27 </script>28 </body>

Ahora describiré las directivas relacionadas con los formularios.

ng-change

Esta directiva evalúa una expresión cuando se modifica el contenido del control delformulario por el usuario. Si la modificación viene desde el modelo esta acción no tieneefecto.

Page 111: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 90

1 <body ng-controller="miCtrl">2 <input type="text"

3 ng-model="input"

4 ng-change="cambio()"

5 placeholder="Escribe aquí">6 {{ texto }}7 <script>8 angular.module('miApp', [])

9 .controller('miCtrl', ['$scope', function ($scope) {

10 $scope.cambio = function(){

11 $scope.texto = $scope.input;

12 }

13 }])

14 </script>15 </body>

ng-checked

Nos permite evaluar una expresión y definir el valor checked de un elemento checkboxdependiendo del resultado.

ng-disabled

Ladirectivang-disablednos permite deshabilitar un elementode formulario dependien-do de la evaluación de una expresión. Si la expresión es evaluada a verdadero Angularpone el atributo disabled en el elemento. Hay que tener en cuenta que esta directiva nofuncionará en Internet Explorer y navegadores antiguos.

1 <body ng-controller="miCtrl">2 Deshabilitar el elemento

3 <input type="checkbox" ng-model="habilitado"> <br>4 <input type="text" ng-disabled="habilitado" placeholder="Escribe aquí">5 <script>6 angular.module('miApp', [])

7 .controller('miCtrl', ['$scope', function ($scope) {

8 $scope.habilitado = false;

9 }])

10 </script>11 </body>

Page 112: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 91

ng-readonly

En esencia esta directiva funciona de la misma forma que ng-disabled con la diferenciade que no tiene problemas con los navegadores. Evalúa una expresión y si el resultado esverdadero pone el atributo readonly en el elemento.

ng-selected

Esta directiva funciona de la misma forma que lo hace la directiva ng-checked. Evalúauna expresión y si el resultado es verdadero pone el atributo selected en el elemento.

ng-submit

Cuando tratamos de hacer submit en un formulario, por defecto el navegador enviarálos datos del formulario y recargará la página. Pero si estamos haciendo una aplicaciónde una sola página, la directiva ng-submit nos ayudara a tomar acciones previniendoeste comportamiento de recargar la página. Hay que tener en cuenta que para que estadirectiva tenga un correcto funcionamiento el formulario nodebe tener el atributo action,data-actiono x-actiondefinidos. Esta directiva evalúa una expresión al ser ejecutada y hacedisponible un objeto $event.

1 <body ng-controller="miCtrl">2 <div class="container">3 <form ng-submit="login()">4 Nombre: <input type="text" ng-model="usuario.nombre"><br>5 Contraseña: <input type="password" ng-model="usuario.contrasena"> <br>6 <input type="submit" value="Aceptar">7 </form>8 </div>9 <script>

10 angular.module('miApp', [])

11 .controller('miCtrl', ['$scope', function ($scope) {

12 $scope.usuario = { nombre: '', contrasena: ''}

13 $scope.login = function(){

14 //Ejecutar el proceso de login

15 console.log($scope.usuario);

16 }

17 }])

18 </script>19 </body>

Page 113: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 92

Esta directiva debe ser llamada en el elemento <form> si se desea usar ng-click en su lugarng-submit no debe estar presente y ng-click debe estar presente en el primer elementode tipo submit del formulario.

ng-focus

Este es uno de los eventos de formulario. Evalúa una expresión cuando el elementoobtiene el foco.

ng-strict-di

Esta directiva se explica detalladamente en el Capítulo 3: Inyección de dependencia enmodo estricto.

ng-model-options

Esta directiva se explica detalladamente en el Capítulo 11: Otras formas de validación

Creando las directivas

Como has podido observar el framework tiene una gran cantidad que resuelvenmuchosde las necesidades básicas de una aplicación. Ahora es tiempo de crear las directivasmás específicas de la aplicación que estamos desarrollando. Para esto necesitamos sabercuál es el tratamiento que da Angular a las directivas. Cuando Angular es iniciado enla aplicación este recorre todo el DOM aplicando comportamientos específicos a loselementos. En este momento es donde las directivas son aplicadas, cuando angularencuentra la llamada a una directiva en el DOM este aplica la funcionalidad definidapor esa directiva.

Para comenzar veamos una directiva muy simple.

1 angular.module('miApp', [])

2 .directive('primeraDirectiva', [function () {

3 return {

4 restrict: 'E',

5 template: '<p>Esta es la primera directiva</p>'

6 };

7 }])

Podemos hacer uso de esta directiva en el HTML de esta forma.

Page 114: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 93

1 <body>2 <primera-directiva></primera-directiva>3 </body>

Lo que veremos es que cuando Angular inicie e incluirá en el elemento <primera-

directiva> el elemento <p> con el texto que definimos en la directiva. Las directivas sedefinen con el método directive() del módulo. Este recibe como primer parámetro elnombre de la directiva, Hay que tener en cuenta que en la declaración de la directivael nombre tiene que ser definido en notación de camello (Camel Case). Pero a la horade hacer uso de esta en la vista, debe ser usada con el nombre dividido por -.‘Comosegundo parámetro recibe un arreglo con las dependencias y la función que será elcomportamiento de la directiva. Esta función siempre debe devolver un objeto con lasopciones que serán compiladas en la directiva.

Es una buena práctica que al nombrar la directiva se le incluya un prefijo para evitarproblemas con futuras especificaciones de HTML. Por ejemplo, si creas la directivabreadcrumb y en HTML6 es creado ese elemento ocasionaría un conflicto. Por estemotivo deberíamos poner un prefijomi-breadcrumb.

En el ejemplo anterior usamos la propiedad restrict. De esta forma indicamos a angularde que tipo es la directiva. E es para elementos, A para atributos y C para nombre declases. Las directivas pueden ser declaradas con más de un tipo, veamos la directivaanterior como atributo y elemento.

1 angular.module('miApp', [])

2 .directive('primeraDirectiva', [function () {

3 return {

4 restrict: 'EA',

5 template: '<p>Esta es la primera directiva</p>'

6 };

7 }])

Y su uso en la vista.

1 <body>2 <primera-directiva></primera-directiva>3 <div primera-directiva></div>4 </body>

El Ejemplo anterior producirá una vista con el siguiente código.

Page 115: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 94

1 <body>2 <primera-directiva><p>Esta es la primera directiva</p></primera-directiva>3 <div primera-directiva=""><p>Esta es la primera directiva</p></div>4 </body>

Como se ha restringido el uso de la directiva a Elementos y Atributos esta podrá serusada de cualquiera de las dos formas al mismo tiempo en la aplicación. Deberíamostratar de siempre restringir las directivas a Atributos ya que Internet Explorer tieneproblema con los elementos fuera de las especificaciones. Por otra parte, la otra pro-piedad que usamos en la declaración de la directiva es template que será un bloque decódigo que será insertado en el DOM donde se llame a la directiva. Una buena prácticaes siempre separar el código de la vista o sea del template, para eso podemos usar lapropiedad templateUrl en vez de la anterior. Esta propiedad funciona de lamisma formaque la directica ng-include que he explicado antes. Así que solo tendremos que darlecomo valor la url del template que queremos incluir.

Deben haber notado que en los dos usos de la directiva del ejemplo anterior el elemento<p> es insertado dentro de donde se llama la directiva. Este comportamiento puede sercambiado si necesitamos que el template de la directiva remplace la llamada. Haciendouso de la propiedad replace y dándole un valor verdadero (true).

1 angular.module('miApp', [])

2 .directive('primeraDirectiva', [function () {

3 return {

4 restrict: 'EA',

5 template: '<p>Esta es la primera directiva</p>',

6 replace: true7 };

8 }])

De esta forma el elemento <primera-directiva></primera-directiva> desaparecerá dejan-do en su lugar solo el template de la directiva que es el elemento <p>. Como estamoscreando nuevos elementos HTML los validadores no reconocerán las directivas ya queno están en las especificaciones. AngularJS tiene una solución para este problema y esque desde la llegada de HTML5 se permitió hacer uso de atributos personalizados conel prefijo data- de esta forma pueden ser ejecutadas todas las directivas y angular lasreconocerá perfectamente.

Page 116: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 95

1 <body>2 <div data-primera-directiva></div>3 <div x-primera-directiva></div>4 <div data-primera:directiva></div>5 <div x-primera_directiva></div>6 </body>

El ejemplo anterior produce en todos sus casos el mismo resultado que es mostrarelemento <p>. Funciona de esta forma ya que Angular al encontrar una directiva eliminael prefijo x- o data- del inicio del nombre y los :, -, _ los convierte en notación de camellopara su coincidencia con el nombre de la directiva que declaramos.

Ahora veamos otro ejemplo de la directiva, pero esta vez haciendo uso del scope.

1 angular.module('miApp')

2 .controller('UsuariosCtrl', ['$scope', function ($scope) {

3 $scope.usuario = {

4 nombre: 'john',

5 apellido: 'Doe',

6 email: '[email protected]'

7 };

8 }])

1 angular.module('miApp')

2 .directive('info', [function () {

3 var plantilla = '<a href="mailto:{{usuario.email}}">';

4 plantilla += '{{usuario.nombre}} {{usuario.apellido}}</a>';

5 return {

6 restrict: 'A',

7 template: plantilla,

8 remplace: true9 };

10 }])

1 <body data-ng-controller="UsuariosCtrl">2 <div data-info></div>3 </body>

De esta forma podemos mostrar toda la información del usuario con un solo elementoHTML, pero si tenemos varios usuarios en el scope no podríamos usar la directiva yaque no tenemos forma de decirle que usuario es el que queremos mostrar. Para estoAngular nos permite aislar el scope de la directiva y decirle solo que usará del scopedel controlador. Mediante la propiedad scope del objeto que se devuelve en la directivapodremos relacionar ambos scopes de la siguiente forma.

Page 117: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 96

1 angular.module('miApp')

2 .controller('UsuariosCtrl', ['$scope', function ($scope) {

3 $scope.usuario1 = {

4 nombre: 'john',

5 apellido: 'Doe',

6 email: '[email protected]'

7 };

8 $scope.usuario2 = {

9 nombre: 'Jane',

10 apellido: 'Doe',

11 email: '[email protected]'

12 };

13 }])

1 angular.module('miApp')

2 .directive('info', [function () {

3 var plantilla = '<a href="mailto:{{usuario.email}}">';4 plantilla += '{{usuario.nombre}} {{usuario.apellido}}</a>';5 return {

6 restrict: 'A',

7 scope: {

8 usuario: '=usuario'

9 },

10 template: plantilla,

11 remplace: true

12 };

13 }])

1 <body data-ng-controller="UsuariosCtrl">2 <div data-info data-usuario="usuario1"></div>3 <div data-info data-usuario="usuario2"></div>4 </body>

Como puedes observar en el ejemplo anterior en la vista damos un nuevo atributo a ladirectiva el cual tendrá el nombre de la variable de scope que queremos vincular al scopede la directiva. En la directiva la propiedad scope tendrá como valor un objeto con loselementos del scope del controlador que serán vinculados, la llave será la que tendremosdisponible en el scope de la directiva y el valor será el tipo de vínculo y el nombre delatributo al que vincularemos.

En la directiva anterior puedes observar que cuando vinculamos en el scope el usuarioañadimos un caracter = lo que será el tipo de vínculo que tendremos de ese atributo

Page 118: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 97

en el scope de la directiva. El = vincula directamente los datos haciéndolos visible en elscope, en caso de que seanmodificados por el controlador o en lamisma vista, la directivamostrará los cambios de forma instantánea. Otro de los tipos de vínculos es@ que harávisible el valor de uno de los atributos de la directiva directamente como propiedad delscope, veamos un ejemplo.

1 angular.module('miApp')

2 .directive('miMensaje', [function () {

3 var plantilla = '<div class="alert-{{tipo}}">';4 plantilla += '<h2>{{titulo}}</h2><p>{{mensaje}}</p></div>';5 return {

6 restrict: 'EA',

7 scope: {

8 tipo: '@',

9 titulo: '@',

10 mensaje: '@'

11 },

12 template: plantilla,

13 replace: true

14 };

15 }])

TiposSi la llave de la propiedaddel scope tiene elmismonombre que el valor podremosindicar solo el tipo de vínculo como en el ejemplo anterior.

1 <body>2 <mi-mensaje3 titulo="Error"

4 tipo="warning"

5 mensaje="Error 404, El contenido que usted busca no ha sido encontrado.">6 </mi-mensaje>7 </body>

En el ejemplo anterior utilizamos los atributos del nuevo elemento mi-mensaje paradefinir las propiedades del scope. Aunque estas estuviesen definidas en el scope por elcontrolador, la directiva solo vincularía los atributos del elemento como propiedadesdel scope. Si dentro del valor del atributo se hace la llamada al modelo, el modelo tendráacceso a modificar el valor dentro de la directiva en caso de que sea alterado, pero si esalterado dentro de la directiva no se reflejará en el scope del controlador, es un vínculode una sola vía, desde el scope del controlador hacia la directiva, veamos otro ejemplopara que quede claro.

Page 119: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 98

1 <body>2 Mensaje: <input type="text" data-ng-model="mensaje">3 <mi-mensaje4 titulo="Error"

5 tipo="warning"

6 mensaje="{{ mensaje }}">7 </mi-mensaje>8 </body>

Al cambiar el texto en el input automáticamente se cambiará dentro de la directiva, perosi dentro de la directiva ponemos otro input como este.

1 angular.module('miApp')

2 .directive('miMensaje', [function () {

3 var plantilla = '<div class="alert-{{tipo}}">';4 plantilla += '<h2>{{titulo}}</h2><p>{{mensaje}}</p>';5 plantilla += '<input type="text" data-ng-model="mensaje"></div>';6 return {

7 restrict: 'EA',

8 scope: {

9 tipo: '@',

10 titulo: '@',

11 mensaje: '@'

12 },

13 template: plantilla,

14 replace: true

15 };

16 }])

Al modificar el texto en el input dentro de la directiva este lo modificará en el scopeaislado de la directiva, pero no lo modificará fuera de la directiva como podemosobservar.

Existe un tercer tipo de vínculo & que se utilizará para delegar funciones. Este es el tipomás complicado de los tres veamos un ejemplo para explicarlo con detalles.

Page 120: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 99

1 angular.module('miApp')

2 .controller('LogCtrl', ['$scope', function ($scope) {

3 $scope.log = function(elem) {

4 console.log(elem);

5 };

6 }])

El controlador solo tendrá la función que vincularemos a la directiva, nada especial enél.

1 <body data-ng-controller="LogCtrl">2 <mi-contacto log="log(msg)"></mi-contacto>3 </body>

En la vista hacemos uso de la función log que creamos en el controlador pasándole unmsg como parámetro ya que de esta forma es como se ejecutará la función dentro de ladirectiva.

1 angular.module('miApp')

2 .directive('miContacto', [function () {

3 return {

4 restrict: 'E',

5 scope: {

6 log: '&'

7 },

8 templateUrl: '_vistas/contacto.html',

9 replace: true10 };

11 }])

En el objeto scope definimos la propiedad log con el valor & para hacer referencia a lafunción declarada en el controlador. En esta ocasión usaremos la propiedad templateUrlpara no incluir la plantilla en la misma directiva. La plantilla de la directiva se encuentraen un archivo contacto.html.

Page 121: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 100

1 <form name="contacto">2 <label for="titulo">Titulo:</label>3 <input type="text" id="titulo" ng-model="titulo"><br>4 <label for="mensaje">Mensaje:</label>5 <textarea6 ng-model="mensaje" id="mensaje"

7 cols="30" rows="10">8 </textarea><br>9 <button ng-click="log({msg: 'Titulo: '+titulo+' Mensaje: '+mensaje})">

10 Enviar

11 </button>12 </form>

En la plantilla de la directiva tenemos un formulario conTitulo yMensaje yunbotónquehará uso de la función log del controlador a través de la directiva. Hay que destacar quelos parámetros a la función no se le pasarán directamente sino como un objeto dondecada una de las propiedades del objeto es cada uno de los parámetros que recibirá lafunción del controlador, En esta ocasión solo enviamos un parámetro que es msg porlo enviamos en un objeto con una propiedadmsg donde su valor será en entregado a lafunción del controlador. Hay que tener en cuenta que los nombres sonmuy importantes,si en vez de enviarmsg en el objeto se envía alguna propiedad que no coincide con la queespera en el uso de la directiva, esta no funcionara correctamente.

El uso del scope aislado dentro de la directiva solo puede comunicarse con el scope padrede las tres formas anteriores. Es importante que se Entienda cada una de ellas ya que si sedeclara la propiedad scope dentro de la directiva no se tendrá acceso al scope de padre ano ser que se empleen estas vías. Por otra parte, cada vez que se necesite usar una directivaen varios lugares diferentes de la aplicación será necesario aislar el scope porque siempreel padre no tendrá las mismas características.

Comomismo podemos usar solo el símbolo del tipo de vínculo (@, = y &) si la propiedadtiene elmismo nombre, también podemos vincular propiedades con nombres diferentes.

1 <mi-mensaje titulo="Error" tipo="warning" mensaje="{{ mensaje }}"></mi-mensaje>

1 scope: {

2 tipo: '@',

3 titulo: '@',

4 texto: '@mensaje'

5 }

De esta forma vinculamos el atributo mensaje al scope de la directiva con el nombretexto.

Page 122: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 101

Hasta ahora las directivas parecen solo bloques estáticos en la aplicación. En caso deque quisiéramos modificar el comportamiento de del DOM al estilo jQuery claro quepodemos hacerlo. Anteriormente comente que Angular nos brinda una versión reducidade jQuery con las utilidadesmás usadas. En una directiva podremos utilizar la propiedadlink del objeto que devuelve la directiva para tomar acciones que modifiquen el DOMal estilo jQuery.

Esta propiedad link tomará como valor una función anónima que recibirá tres pará-metros. El primero es el scope pero no es el scope de la inyección de dependenciasen esta ocasión, es el scope de la directiva. En segundo lugar tomara el elemento, estees el objeto jqLite con el que tendremos las funcionalidades jQuery para modificar elelemento que será el template de la directiva, resumiendo $('<template>') para ejecutaracciones sobre él. El tercer parámetro es un objeto con los atributos que definimos en ladirectiva. Veamos un ejemplo.

1 angular.module('miApp')

2 .directive('miTitular', [function () {

3 return {

4 restrict: 'E',

5 template: '<div><h1>{{texto}}</h1></div>',6 replace: true,

7 scope: {

8 texto: '@'

9 },

10 link: function (scope, iElement, iAttrs) {

11 console.log(scope.texto);

12 iElement.css('cursor', 'pointer')

13 .on('mouseenter', function(e){

14 iElement.css('opacity', 0.6);

15 })

16 .on('mouseleave', function(e){

17 iElement.css('opacity', 1)

18 }).append('<p>-- '+iAttrs.especial+' --</p>')19 }

20 };

21 }])

En el ejemplo anterior declaramos la directiva miTitular de tipo elemento con untemplatemuy sencillo, indicamos que queremos remplazar el contenido del template porel elemento de la directiva. Obtenemos el texto como propiedad en el scope para enviarloa la consola y mostrarlo en la plantilla. Acto siguiente hacemos uso de la propiedad linkque recibe los tres parámetros antes detallados. Los nombres de los parámetros no tienenimportancia ya que no serán inyectados como dependencias.

Page 123: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 102

Como podemos observar en la función link, primero se envía el texto a la consola através del scope, luego se procede a usar el elemento con jqLite, cambiamos el cursor, yañadimos dos eventos al elemento y por último insertamos el atributo especial despuésdel elemento. Notar que este último elemento que es accesible a través del objeto iAttrsno está definido en el scope y aun así es accesible desde la función link. Para usar ladirectiva lo haremos de la siguiente forma.

1 <body>2 <mi-titular texto="Lorem ipsum dolor sit amet, consectetur adipisicing elit, s\

3 ed do eiusmod

4 tempor incididunt ut labore et dolore magna aliqua." especial="Atributo Especi\

5 al"></mi-titular>6 </body>

El ejemplo anterior no es la mejor forma de realizar esa funcionalidad, pero para elpropósito de demostrar cómo funciona la propiedad link del objeto devuelto por ladirectiva está bien. En el inicio de Angular el proceso de compilación de la vista ejecutaráesta función link individualmente en cada una de las directivas que encuentre.

En la directiva miTitular la función link se ejecutará individualmente en cada unade las directivas aplicando comportamientos. Angular provee otra propiedad que seejecutará antes que link pero se ejecutará una vez en todas las instancias demiTitular almismo tiempo. Esta propiedad será útil para cuando necesites aplicar comportamientosa todas las instancias pero que no requiera datos del scope de cada una. La mayoríade las directivas no necesitarán esta propiedad ya que en la función compile estarásmodificando el DOM de todas las instancias y en la función link estarás modificandouna directiva específica, esperando por modificaciones, agregando eventos y demás.

Esta función recibe tres parámetros, el primero es el elemento, es la plantilla en si paraser modificada en todas las directivas a la vez. El segundo parámetro es un arreglo conlos atributos de la directiva. El tercero es una función transclude la cual creará un clondel elemento para modificar el DOM. Como puedes observar no hay parámetro scopepara poder manipular los datos de la directiva de forma individual. En esta ocasión nohay inyección de dependencia por lo tanto los nombres de los parámetros pueden variar.Veamos un ejemplo de la sintaxis.

Page 124: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 103

1 angular.module('miApp')

2 .directive('miCita', [function () {

3 return {

4 restrict: 'E',

5 compile: function (iElement, iAttrs) {

6 var plantilla = angular.element('<blockquote></blockquote>');7 plantilla.append(iElement.contents());

8 iElement.replaceWith(plantilla);

9 }

10 };

11 }])

1 <body>2 <mi-cita>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur\

3 adipisci eaque blanditiis minus explicabo voluptas, quo corrupti velit debitis \

4 quidem necessitatibus ab rem. Quaerat nisi quod sapiente tenetur, perspiciatis d\

5 ucimus!</mi-cita>6 <mi-cita>Esta es una segunda cita.</mi-cita>7 </body>

Comopuedes observar en el ejemplo anterior se ejecutará la función compile y converti-rá las directivasmi-cita en elementos <blockquote>. Para esto he hecho uso de la funciónde angular element que no es más que un alias del jqLite, en este caso le pasamos comoparámetro una cadena HTML y nos devuelve el objeto jqLite para tomar acciones conél.

Cuando la propiedad compile está presente en el objeto de la directiva, la propiedadlink es ignorada por completo. En casos de que necesitemos definir las dos propiedadespara tomar acciones diferentes dependiendo de las posibilidades de cada una, la funcióncompile deberá devolver la función link. Veamos el ejemplo siguiente.

1 angular.module('miApp')

2 .directive('miCita', [function () {

3 return {

4 restrict: 'E',

5 compile: function (iElement, iAttrs) {

6 var plantilla = angular.element('<blockquote></blockquote>');7 plantilla.append(iElement.contents());

8 iElement.replaceWith(plantilla);

9 return function(scope, iElement, iAttrs){

10 iElement.css('text-align', 'right');

11 if (iAttrs.autor) {

Page 125: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 104

12 iElement.append('<br><span>Por: '+iAttrs.autor+'</span>')13 };

14 };

15 }

16 };

17 }])

1 <body>2 <mi-cita>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consequatur\

3 adipisci eaque blanditiis minus explicabo voluptas, quo corrupti velit debitis \

4 quidem necessitatibus ab rem.</mi-cita>5 <mi-cita data-autor="Maikel Rivero">Esta es una segunda cita.</mi-cita>6 </body>

De esta forma es como usaremos compile y link en la misma directiva para lograr unafuncionalidad específica con cada una de ellas.

Además de las dos funciones antes mencionadas que añaden comportamientos a ladirectiva, también disponemos de un controlador para la directiva. Esto quiere decir quepodremos crear todo tipo de funciones para tomar acciones dentro de nuestra directivapor individual. Esta propiedad puede recibir uno de dos valores, el primero es una cadenade texto que será el nombre de un controlador definido en la aplicación. El otro valorposible es una función anónima para hacer de constructor del controlador. Como lapropiedad link provee control aislado dentro de la directiva la propiedad controllerbrinda la posibilidad de declarar funcionamientos compartidos entre las directivas. Estopuede lograrse ya que una directiva puede requerir el controlador de otra, estaremoshablando sobre este tema más adelante.

El controlador recibe como parámetros el $scope que es el asociado directamentecon la directiva, el $element que es el elemento de la directiva en sí, los $attrs queson los atributos definidos en la directiva y $transclude que es la función que crearáel clon del elemento para manipular el DOM. Hacer uso de este último dentro delcontrolador es una mala práctica ya que los controladores no deben ser utilizados paramodificar el DOM, pero aun así tendremos la posibilidad a través de $transclude. Es unabuena práctica solo usar $transclude dentro de la función compile. Este controladorfuncionará de igual forma que si utilizáramos la directiva ng-controller en la directivaque estamos creando.

El controlador dentro de la directiva es una buena idea solo cuando necesitamos exponerfuncionalidades para ser utilizadas en otras directivas. De lo contrario deberíamosutilizar la función link para realizar las tareas individuales de la directiva.

Anteriormente mencionamos que podemos hacer uso de los métodos de un controla-dor en otra directiva. Para esto debemos entender el funcionamiento de la propiedad

Page 126: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 105

require. Esta recibirá como parámetro una cadena de texto o un arreglo de cadenas detexto, las cuales serán el nombre de la directiva que queremos utilizar el controlador.Este nombre de directiva que utilizamos para incluir el controlador puede tener variosprefijos. Si no especificamos ningún prefijo la se buscará el controlador de una directivaque exista en el mismo elemento veamos un ejemplo.

1 restrict: 'A',

2 require: 'otraDirectiva',

3 link: function(scope, iElemen, iAttrs, ctrl){//...}

1 <div mi-directiva otra-directiva></div>

En el ejemplo anterior en la directivamiDirectiva buscaremos el controlador de otra-Directiva que deberá estar en el mismo elemento. En caso que otraDirectiva estuvieseen otro elemento tendremos que poner el prefijo � en el nombre.

1 restrict: 'A',

2 require: '^otraDirectiva',

3 link: function(scope, iElemen, iAttrs, ctrl){//...}

1 <div otra-directiva>2 <div mi-directiva></div>3 </div>

De esta forma el controlador será buscado en el elemento padre. En caso de que nose encuentre el controlador ocurrirá un error. Para evitar estos errores podremosrequerir un controlador de forma opcional especificando el prefijo ?. En caso de queel controlador no sea encontrado se pasará el valor null.

1 restrict: 'A',

2 require: '?otraDirectiva',

3 link: function(scope, iElemen, iAttrs, ctrl){//...}

1 <div mi-directiva></div>

También podremos hacer una combinación de los dos prefijos para requerir un con-trolador en el elemento padre opcionalmente. Estos controladores serán pasados comocuarto parámetro a la función link para ser usado.

Page 127: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 106

1 angular.module('miApp')

2 .directive('miDirectiva', [function () {

3 return {

4 restrict: 'A',

5 controller: function () {

6 this.log = function(){

7 console.log('Método de la directiva: mi-directiva');

8 };

9 }

10 };

11 }])12 .directive('otraDirectiva', [function () {

13 return {

14 restrict: 'A',

15 require: 'miDirectiva',

16 link: function (scope, iElement, iAttrs, ctrls) {

17 ctrls.log();

18 }

19 };

20 }])

1 <body>2 <div mi-directiva otra-directiva></div>3 </body>

En el ejemplo anterior se imprimirá en la consola el mensaje definido en el controladorde la directivamiDirectiva a través de la función link de la directiva otraDirectiva. Estofunciona de forma correcta por que las dos directivas están en el mismo elemento, perosi la directivami-directiva es movida a el elemento <body> se producirá un error al noser encontrado el controlador ya que no especificamos el prefijo �. Como habrás podidonotar en el controlador se ha definido la función en el objeto this ya que si se hiciera enel $scope este no estaría disponible para las demás directivas por ser el scope privado decada directiva. Mediante this podemos exponer los métodos que serán utilizados por lafunción link de otras directivas.

Cuando usamos this para exponer contenido en el controlador al no ser declarado en el$scope este no podrá ser utilizado directamente en la propia directiva a no ser que se usela propiedad controllerAs. Utilizando esta propiedad damos un alias al controlador y lohacemos accesible con ese nombre dentro del scope. Veamos un ejemplo.

Page 128: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 107

1 angular.module('miApp')

2 .directive('miEjemplo', [function () {

3 return {

4 restrict: 'A',

5 template: '<div>{{ctrl.mensaje}}</div>',6 controllerAs: 'ctrl',

7 controller: function () {

8 this.mensaje = 'La variable mensaje está expuesta para ser utilizada '

9 this.mensaje += 'fuera del controlador y a la vez dentro de la misma dir\

10 ectiva.';

11 }

12 };

13 }])

1 <body>2 <div mi-ejemplo></div>3 </body>

De esta forma el controlador se expondrá para ser utilizado en otras directivas ymedian-te la propiedad controllerAs lo definimos dentro del scope interno de la directiva.

Hasta el momento hemos creado varias directivas que cumplen diferentes funcionalida-des, debes haber notado que ninguna tiene contenido dentro de sí misma. En muchasde las ocasiones el contenido de la directiva es importante, por lo que necesitamosencargarnos de que cuando la plantilla de la directiva sea intercambiada en su lugar setenga en cuenta el contenido que tiene la misma. Esto se posible mediante la propiedadtransclude y la directivang-transclude. La propiedad transclude debe tener como valortrue cuando la necesitemos usar ya que en su ausencia angular la declara automáticamen-te false. Veamos un ejemplo de cómo funciona.

1 angular.module('miApp')

2 .directive('comentario', [function () {

3 var plantilla = '<div><div><img ng-src="{{imagenSrc}}">Por: {{por}}</div>';4 plantilla += '<blockquote ng-transclude></blockquote></div>';5 return {

6 restrict: 'E',

7 scope: {

8 por: '@',

9 imagenSrc: '@'

10 },

11 replace: true,

12 transclude: true,

Page 129: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 6: Directivas 108

13 template: plantilla

14 };

15 }])

1 <body>2 <comentario por="Maikel Rivero" imagen-src="camino/hacia/el/avatar.jpg">3 Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nemo, sed qui sint\

4 , vitae repellendus sit deleniti fuga voluptate maxime ut eius numquam pariatur \

5 dolorum, quos nostrum? Sequi voluptatibus tempora labore.

6 </comentario>7 </body>

En el ejemplo anterior damos el valor verdadero a la propiedad transclude y luego en eltemplate definimos la directiva ng-transclude dentro del elemento <blockquote> que esdonde se pondrá el contenido.

Otro de las propiedades que se puede definir en el objeto de la directiva es la prioridadcon que se ejecuta la directiva en el mismo elemento. Por lo general esta propiedad esomitida y por defecto obtiene valor 0. Esta propiedad es necesario definirla con unmayorvalor en casode que quisiéramos que la directiva se ejecute primeroqueotras en elmismoelemento. En un elemento que tenga dos directivas se ejecutará primero la que mayorprioridad posea, en caso de que las dos tengan la misma prioridad se ejecutará por ordende declaración. Esta propiedad debe ser definida con un valor numérico.

Hasta ahora hemos ya detallado como crear directivas propias que sean específicaspara la aplicación que estés creando. Con todos los temas tratados y las directivasque proporciona el mismo framework tienes en las manos las herramientas para crearpotentes bloques de código que sean reutilizables incluso para otras aplicaciones quedesarrolles en el futuro.

Page 130: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 7: FiltrosSi AngularJS tiene muchas directivas en su núcleo como vimos en el capítulo anterior,no es así con los filtros. En esta ocasión tiene una cantidad muy reducida, pero de igualforma puedes crear los tuyos propios, y es lo que trataremos en este capítulo. Primerocomenzaremos detallando los filtros que nos brinda el núcleo del framework y luegocomenzaremos a crear filtros propios.

EnAngularJS los filtros pueden ser usados en cualquier parte de la aplicaciónmediante elservicio $filter que puede ser inyectado en controladores y servicios. En la vista puedenser usadosmediante el caracter | como ya pudimos observar en el capítulo anterior dondese explicaba la directiva ng-repeat. Para ver un ejemplo vamos a ver el primer filtro.

Currency

El filtro currency da formato a los números como moneda poniendo el símbolo de lamoneda que se está usando delante del número y separándolo por comas y punto condecimales. Veamos el ejemplo.

1 <body>2 <div>Costo: {{ 1412.99 | currency }}</div>3 <div>Costo: {{ 728.99 | currency: "€" }}</div>4 </body>

En la vista podremos especificar el filtro currency solo o podremos pasarle unparámetrode tipo cadena de texto con el símbolo en que queremos que se convierta el número. Encaso de que no se especifique el símbolo se tomará el definido local para moneda en elsistema. En los controladores y servicios se utilizan los filtros a través del servicio $filterveamos el ejemplo.

109

Page 131: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 7: Filtros 110

1 angular.module('miApp')

2 .controller('FiltroCtrl', ['$scope', '$filter', function ($scope, $filter) {

3 var costo = 1453.50;

4 $scope.costo = $filter('currency')(costo);

5 $scope.costoEuro = $filter('currency')(costo, '€');

6 console.log($scope.costo,$scope.costoEuro);

7 }])

Como puedes observar es muy sencillo usarlo en el controlador, el servicio $filter esuna función que recibe como parámetro una cadena de texto con el nombre del filtroque se quiere utilizar. El filtro es devuelto por el servicio filter así que podremos usarloen cadena pasándole como primer valor el número que se quiere pasar por el filtro, yde forma opcional el segundo parámetro será una cadena de texto con el símbolo que sequiere mostrar.

En la versión 1.3 de Angular se añadió un nuevo parámetro que puedes especificar a lahora de mostrar la moneda, este es la cantidad de decimales que deseas mostrar despuésdel .. Para utilizarlo debes primero especificar el tipo de moneda y luego como segundoparámetro la cantidad de decimales.

1 <div>Costo: {{ 728.42963 | currency:"€":3 }}</div>

Number

Como el filtro anterior interpreta números a monedas poniendo las comas y los puntosen los decimales de forma correcta, el filtro number lo hace, pero sin especificar unamoneda. Además, es posible especificar la cantidad de decimales que deseamos que semuestren con el número, en caso de no ser especificado será obtenido por defecto de laconfiguración del sistema, normalmente 3 dígitos.

1 <body>2 {{ 2432.44*332.91 | number }}3 {{ 2432.44*332.91 | number:5 }}4 </body>

Uppercase y Lowercase

Para convertir una cadena de texto en mayúsculas o minúsculas tenemos dos filtros queresolverán en cualquier tipo de situación.

Page 132: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 7: Filtros 111

1 <body>2 {{ "Prueba de texto" | uppercase }}3 {{ "PRUEBA De TeXtO" | lowercase }}4 </body>

limitTo

Otro de los filtros es limitTo que funcionará para arreglos o para cadenas de textodevolviendo solo la parte a la que se ha limitado. Este filtro recibe un parámetro quees la cantidad a la que se limitará la cadena o arreglo. Si el número proporcionado comolímite es negativo, se devolverá la cantidad de caracteres o elementos comenzando desdeel final.

1 <script>2 function ctrl ($scope) {

3 $scope.arreglo = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];

4 $scope.texto = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Do\

5 lorum maxime eum perspiciatis hic corporis sapiente. Ab, provident modi deleniti\

6 assumenda nobis ratione, accusantium porro, necessitatibus similique beatae dol\

7 oremque perferendis tempora!';

8 }

9 </script>10 <div data-ng-controller="ctrl">11 {{ texto | limitTo: 150 }}12 {{ texto | limitTo: -135 }}13 {{ arreglo | limitTo: 6 }}14 {{ arreglo | limitTo: -3 }}15 </div>

Date

Uno de los filtros más útiles que nos brinda Angular es date ya que por lo generallas fechas son almacenadas en el servidor con un formato como este 1409323723006.Difícilmente podremos saber qué fecha de ese número a primera vista, para eso el filtrodate la convertirá a una fecha que podamos interpretar.

1 <body>2 {{ 1409323723006 | date }}3 </body>

Page 133: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 7: Filtros 112

Hay casos en que el formato de la fecha no es el que deseamos, por ese motivo podremospasarle el formato como parámetro como una cadena de texto para que sea formateadacomo deseemos.

1 <body>2 {{ 1409323723006 | date: "dd/MM/y hh:mm:ss a" }}3 </body>

Esta cadena de formato puede obtener cualquiera de los valores de fecha usados normal-mente en programación como los que están demostrados en el ejemplo anterior, ademásunas cadenas con formatos predefinidos que facilitan la escritura del formato.

• ‘medium’: es equivalente a ‘MMM d, h:mm:ss a’. ej: Aug 29, 2014 10:48:43 AM• ‘short’: es equivalente a ‘M/d/yy h:mm a’. ej: 8/29/14 10:48 AM• ‘fullDate’: es equivalente a ‘EEEE, MMMM d,y’. ej: Friday, August 29, 2014• ‘longDate’: es equivalente a ‘MMMM d, y’. ej: August 29, 2014• ‘mediumDate’: es equivalente a ‘MMM d, y’. ej: Aug 29, 2014• ‘shortDate’: es equivalente a ‘M/d/yy’. ej: 8/29/14• ‘mediumTime’: es equivalente a ‘h:mm:ss a’. ej: 10:48:43 AM• ‘shortTime’: es equivalente a ‘h:mm a’. ej: 10:48 AM

En caso de no especificar ninguno de los formatos, Aungular utilizarámediumDate pordefecto. El filtro date se puede utilizar de igual forma a través del servicio $filter encontroladores y servicios pasando como primer parámetro la fecha y como segundo elformato de forma opcional.

En la versión 1.3 de Angular se añadió un nuevo formato para mostrar en la fecha.Además de todos los anteriores ahora tienes disponible el número de la semana en elaño. Para mostrar el número de la semana de una fecha puedes utilizar el valor ww sideseas que este se muestre con dos dígitos (Ej. 04), o una simple w para que se muestrecon un solo digito (Ej. 4).

OrderBy

El filtro orderBy es muy útil a la hora de mostrar arreglos de datos ya que los puedeordenar de forma alfabética si son cadenas de texto o de forma numérica si son números.Este filtro funciona muy bien combinado con la directiva ng-repeat ya que permiteordenar la lista que se muestra iterando sobre un arreglo.

Podemos pasar tres parámetros al filtro, el primero es el arreglo si lo estamos usandoa través del servicio $filter ya que si se usa en la directiva ng-repeat no es necesario.

Page 134: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 7: Filtros 113

El segundo parámetro es la expresión por la que se ordenará el arreglo. Este parámetropuede ser una función que devolverá la expresión por la que se ordenará, también puedeser una cadena de texto con el nombre de una de las propiedades de un objeto dentro delarreglo, esta puede tener un prefijo + o - para indicar orden ascendente o descendente.El tercer valor que puede obtener el segundo parámetro es un arreglo de cadenas detexto o funciones para ser evaluadas en caso de que el primer elemento del arreglo hagaque coincidan dos elementos de la lista se ejecutará la siguiente expresión del arreglo. Eltercer parámetro que recibe el filtro es uno de tipo booleano que indica si el arreglo seráordenado de forma reversa.

1 <body data-ng-controller="ctrl">2 <table border="1">3 <thead> <tr>4 <th><a href="#" data-ng-click="campo = 'nombre'; reverso=!reverso">5 Nombre

6 </a></th>7 <th><a href="#" data-ng-click="campo = 'apellidos'; reverso=!reverso">8 Apellidos

9 </a></th>10 <th><a href="#" data-ng-click="campo = 'email'; reverso=!reverso">11 Email

12 </a></th>13 <th>Lenguajes</th>14 </tr> </thead>15 <tbody>16 <tr data-ng-repeat="usuario in usuarios | orderBy:campo:reverso">17 <td>{{ usuario.nombre }}</td>18 <td>{{ usuario.apellidos }}</td>19 <td>{{ usuario.email }}</td>20 <td>{{ usuario.lenguajes.join(',') }}</td>21 </tr>22 </tbody>23 </table>24 <script src="lib/angular.js"></script>25 <script src="js/app.js"></script>26 <script>27 function ctrl ($scope) {

28 $scope.campo = 'nombre';

29 $scope.reverso = true;

30 $scope.usuarios = [

31 { nombre: "Maikel",

32 apellidos: "Rivero Dorta",

33 email: "[email protected]",

Page 135: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 7: Filtros 114

34 lenguajes: ["en", "es"] },

35 { nombre: "john",

36 apellidos: "Doe",

37 email: "[email protected]",

38 lenguajes: ["en"] },

39 { nombre: "Jane",

40 apellidos: "Doe",

41 email: "[email protected]",

42 lenguajes: ["en","es"] }

43 ];

44 }

45 </script>46 </body>

Existen otras extensiones de angular que proveen filtros específicos como también haymuchos filtros desarrollados por la comunidad que pueden ser utilizados en la aplicaciónque estés desarrollando. Ahora que ya tienes conocimiento de los filtros que proveeAngular es hora de que crees tus propios filtros.

Creando filtros

Como Angular nos permite crear directivas también nos permite crear filtros, estos sonrelativamente más fáciles de crear que las directivas. Los filtros los definimos con elmétodo filter del módulo, este acepta un primer parámetro que será el nombre del filtroy como segundo parámetro un arreglo con las dependencias y la función del filtro. Estafunción siempre debe devolver otra función que será el filtro en sí.

Te estarás preguntando por que dos funciones, es sencillo, la primera es la funciónque se usará para inyectar las dependencias del filtro y la segunda es el filtro que seejecutará. Esta función del filtro recibirá como primer parámetro el contenido que seráfiltrado, haciéndolo disponible de esta forma para poder alterarlo como sea necesario.En caso de que el filtro necesite recibir algún parámetro de configuración, estos seráninyectados como parámetro en la función del filtro después del contenido. La funcióndel filtro siempre debe devolver el contenidomodificado o un nuevo contenido. Veamosun ejemplo.

Page 136: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 7: Filtros 115

1 angular.module('miApp')

2 .filter('titulo', function(){3 return function(input){4 return input.split(' ')

5 .map(function(elem){6 return elem[0].toUpperCase() + elem.slice(1);

7 })

8 .join(' ');

9 };

10 });

En la vista lo usaremos como los filtros de Angular, especificando el nombre del filtrodespués del caracter |.

1 <body>2 <h1> {{ 'este es el título de una entrada del blog' | titulo }} </h1>3 </body>

El filtro anterior convertirá a mayúsculas todas las primeras letras de cada palabra paraque parezca un título. Este filtro también puede ser utilizado en un servicio o en uncontrolador mediante el servicio $filter.

1 angular.module('miApp')

2 .controller('BlogCtrl', ['$scope', '$filter',

3 function ($scope, $filter) {

4 var titulo = 'este es el título de una entrada del blog';

5 $scope.titulo = $filter('titulo')(titulo);

6 }]);

De esta forma obtendremos el mismo resultado que utilizándolo en la vista con elcaracter |. Además de esa forma de usar los filtros mediante el servicio filter existe otraforma de inyectar los filtros y es inyectando como dependencia el nombre del filtro y acontinuación la palabra Filter ejemplo tituloFilter.

1 angular.module('miApp')

2 .controller('BlogCtrl', ['$scope', '$filter', 'tituloFilter',

3 function ($scope, $filter, tituloFilter) {

4 var titulo = 'este es el título de una entrada del blog';

5 $scope.titulo = tituloFilter(titulo);

6 }])

De esta forma el código queda más limpio que usando el servicio $filter pero elcomportamiento es exactamente el mismo así que deberás escoger cual es la forma queutilizarás en tus aplicaciones.

Page 137: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: RutasHasta el momento hemos estado viendo diferentes usos de AngularJS, todos en la mismapágina, lo que nos detiene un poco a la hora de crear diferentes funcionalidades. Otrode los aspectos importantes del framework es su habilidad para crear aplicaciones deuna sola página. Esto es logrado mediante la observación del cambio de las rutas enel navegador. Para este propósito Angular posee un módulo llamado ngRoute que nosproporciona la habilidad de configurar rutas en la aplicación y responder con diferentescomportamientos para cada una de ellas.

El módulo ngRoute

ElmódulongRouteno está incluido en el núcleo deAngular por lo que debemos incluirloen nuestra aplicación comomismo hicimos con el framework. Estemódulo lo podremosobtener de varias formas, a través delCDNdeGoogle //ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-route.js, descargándolo desde la página oficial de Angular o instalándolo con el gestorde dependencias bower.

1 bower install --save angular-route

Luego de tener el módulo de alguna de las formas anteriores debemos incluirlo ennuestra aplicación después de la línea donde incluimos el framework, nunca antes uobtendremos un error.

1 <script src="lib/angular.js"></script>2 <script src="lib/angular-route.js"></script>

Con incluir el archivo del módulo en la aplicación no es suficiente, tendremos tambiénque inyectarlo como dependencia donde definimos el módulo para que Angular lo hagadisponible.

1 angular.module('miApp', ['ngRoute']);

De esta forma ya podremos comenzar a hacer uso del módulo dentro de la aplicación.ngRoute nos proporciona varios componentes como la directiva ng-view la que seencargará de mostrar el contenido de las plantillas. También hace disponible el $rou-teProvider que utilizaremos para configurar las rutas de la aplicación. Además, dosservicios, $route y $routeParams los cuales explicaremos al detalle más adelante.

116

Page 138: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 117

Definiendo las rutas con $routeProvider

Siguiendo las prácticas de la organización de ficheros del Capítulo 2, configuraremoslas rutas en el archivo app/js/Config/Routes.js para mantener la aplicación organizada.Las rutas de la aplicación se definen mediante el $routeProvider que tiene un APImuysimple con la que relacionaremos plantillas, controladores y resolveremos datos antes demostrar a la vista. Veamos un ejemplo sencillo.

1 angular.module('miApp')

2 .config(['$routeProvider', function ($routeProvider) {

3 $routeProvider

4 .when('/', {templateUrl: 'home.html', controller: 'HomeCtrl'})

5 .when('/contacto', {

6 templateUrl: 'contacto.html',

7 controller: 'ContactoCtrl'

8 })

9 .otherwise({ redirectTo: '/' });

10 }])

La definición de las rutas de la aplicación se hace dentro de un bloque config() delmódulo. De esta forma tendremos acceso al Provider del servicio $route que es el que seencarga de todo el enrutamiento de la aplicación. Este provider tiene dos métodos conlos cuales definiremos las rutas.

El primero es when(), este método es el encargado de añadir nuevas rutas al servicio$route. Acepta dos parámetros, el primero es una cadena de texto con el patrón de laruta que queremos responder. La ruta puede tener parámetros los cuales podremos usaren la aplicación para cambiar el comportamiento dependiendo de cuales parámetros serecibe. Estos parámetros son representados en la ruta comenzando por : y terminandopor /. El nombre especificado en el parámetro estará disponible como propiedad enel servicio $routeParams. Además podremos tener parámetros que sean opcionalesespecificando un ? al final del parámetro, de manera que si este no estuviese presentela ruta se resolvería sin él.

1 $routeProvider

2 .when('/saludo/:mensaje?', {

3 templateUrl: 'saludo.html',

4 controller: 'SaludoCtrl'

5 });

En el ejemplo anterior podríamos utilizar el parámetromensaje de la ruta para enviarloa la vista o como es opcional si no está disponible enviar a la vista unmensaje por defecto.

Page 139: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 118

Si el usuario introduce un caracter / al final de la ruta o le falta alguno para coin-cidir con la ruta que definimos, el servicio $location se encargará de agregarloo eliminarlo para que la ruta coincida. El proceso es realizado por el serviciolocation debido a que todas las rutas son utilizadas por $location.path.

El segundo parámetro que recibe el método when del $routeProvider es un objeto deconfiguración que puede tener varias propiedades, veamos cada una de ellas.

template: Esta propiedad puede tener una cadena de texto o una función como valor. Sies una cadena de texto deberá ser la plantilla HTML que se mostrará para esta ruta. Sies una función esta debe revolver una plantilla HTML para ser usada. Esta función serállamada con un objeto como parámetro el cual tendrá los parámetros de la ruta comopropiedades. La propiedad template no debería ser usada, siempre debemos usar unaplantilla externa en vez de escribir código HTML directamente dentro de la lógica dela aplicación, en su lugar se debe usar la propiedad templateUrl. Veamos un ejemploutilizando una función y los parámetros.

1 $routeProvider

2 .when('/saludo/:mensaje?', {

3 template: function(params){4 return '<p>'+(params.mensaje || 'Hola')+'</p>';

5 }

6 })

templateUrl: Esta propiedad puede tener como valor una cadena de texto o una función.Si es una cadena de texto deberá ser la url de una plantilla que será utilizada paramostraral usuario cuando la ruta se resuelva. Si es una función deberá devolver la ruta de laplantilla, esta función será llamada con un objeto que contendrá los parámetros de laruta como propiedades al igual que la propiedad template. Si está presente la propiedadtemplate no se utilizará templateUrl.

controller: Puede tener una cadena de texto como valor indicando el nombre de uncontrolador previamente definido en la aplicación o una función para definir el con-trolador. Es una mala práctica definir el controlador en la misma ruta. Este controladorserá el utilizado para toda la plantilla que responderá a la ruta. Algunos desarrolladoresprefieren especificar el controlador en la vista con la directiva ng-controller, de maneraque sea fácil de saber cuál controlador es el que utiliza la vista. En mi opinión esresponsabilidad de la ruta especificar la vista y el controlador. También se puede utilizarla sintaxis controller as en esta propiedad.

controllerAs: Es una cadena de texto con un alias para el controlador. Si esta propiedadestá presente el controlador será expuesto en el $scope con ese alias.

resolve: Esta propiedad debe tener como valor un objeto con dependencias que seráninyectadas en el controlador. Si alguna de estas dependencias son promesas se esperará

Page 140: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 119

a que todas sean resueltas o que alguna sea rechazada antes de instanciar el controlador.Si todas las promesas son resueltas los valores de las respuestas son inyectadas en elcontrolador y se disparará el evento $routeChangeSuccess si alguna es rechazada sedisparará el evento $routeChangeError. Más adelante estaremos hablando sobre loseventos así que no te preocupes por esto ahora.

Las propiedades del objeto deberán tener como llave el nombre que será inyectado comodependencia en el controlador.Comovalor podrán tener una cadena de texto la cual debeser el nombre de un servicio previamente definido en la aplicación. El otro valor puedeser una función que será instanciada y el resultado será enviado al controlador comodependencia. Si la función devuelve una promesa se esperará hasta que sea resuelta antesde ser inyectada en el controlador. Es importante saber que si dentro de esta función seutilizará el servicio $routeParams, este todavía tendrá los parámetros de la ruta anteriorno los de la que se está ejecutando. Si se necesita utilizar los parámetros se deberánacceder a ellos mediante el servicio $route.current.params para acceder a los nuevosparámetros.

1 $routeProvider

2 .when('/resolver', {

3 template: '<p>Contenido: {{contenido}} </p>'+

4 '<p>Promesa: {{promesa}} </p>',

5 resolve: {

6 contenido: function(){7 return 'Este es el contenido resuelto';

8 },

9 promesa: function($q){10 var def = $q.defer();

11 setTimeout(function(){12 def.resolve('La promesa ha sido resuelta.');

13 }, 2000);

14 return def.promise;

15 }

16 },

17 controller: function($scope, contenido, promesa){

18 $scope.contenido = contenido;

19 $scope.promesa = promesa;

20 }

21 })

En el ejemplo anterior tenemos dos dependencias que se resolverán, la primera es unafunción que se resolverá de forma instantánea e inyectará su respuesta al controlador. Lasegunda es una promesa que demorará 2 segundos en ser resuelta, así que el controlador

Page 141: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 120

no se instanciará hasta que se haya resuelto. Como uno de los elementos a resolver esuna promesa el controlador no será instanciado hasta que la promesa sea resuelta.

redirectTo: Esta propiedad puede tener como valor una cadena de texto o una función.Si es una cadena de texto se hará una redirección hacia ese valor mediante el servicio$location. Si es una función deberá devolver una cadena de texto con la ruta a la quese hará la redirección. A esta función se le inyectarán tres parámetros, el primero es unobjeto con los parámetros de la ruta, el segundo es el valor path del servicio $locationcuando se hace la llamada a la ruta, el tercero es un objeto con los valores de la búsquedaque existen en la url proporcionados por $location.search().

1 $routeProvider

2 .when('/redirect/:param', {

3 redirectTo: function(param, path, search){

4 console.log(param, path, search);

5 return '/resolver';

6 }

7 })

reloadOnSearch: Esta propiedad recibe un valor de tipo booleano, por defecto esverdadero e indica que se recargara la vista cuando se cambie el valor dela propiedad$location.search() o $location.hash(). Si se declara con un valor false y se cambia la urlen el navegador, se disparará el evento $routeUpdate en el $rootScope para tomar lasacciones necesarias.

caseInsensitiveMatch: Debe tener un valor verdadero o falso. Por defecto esta propie-dad tiene un valor false lo que hace que la url que especifiquemos en el navegador tengaque coincidir exactamente con la declarada incluyendomayúsculas y minúsculas. Si estevalor se define a verdadero (true) se intentarán resolver las direcciones sin importar lasmayúsculas y las minúsculas.

Las anteriormente descritas son todas las propiedades que acepta el objeto de configu-ración de la ruta del métodowhen().

El segundométodo que proporciona el $routeProvider es otherwise() que acepta comoúnico parámetro un objeto de configuración como el del métodowhen(). Por lo generaleste método se utiliza solo para redireccionar a otra ruta.

1 .otherwise({ redirectTo: '/' });

En la nueva versión 1.3 de Angular al método otherwise() se le agrego la opción deaceptar una cadena de texto como parámetro. Esto quiere decir que ya no tendremosque especificar un objeto con la propiedad redirectTo, simplemente pasamos una cadenacon la dirección a la que queremos que nos envíe este método.

Page 142: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 121

1 .otherwise('/');

La única propiedad que tiene $routeProvider es caseInsensitiveMatch. Esta funcionaigual que si lo definiéramos dentro de la configuración de una ruta en específico,pero se aplicará para todas las rutas a la vez. Es importante que esta propiedad seutilice antes de la primera llamada del método when, de lo contrario podríamos obtenercomportamientos no deseados.

1 $routeProvider.caseInsensitiveMatch = true;

2 $routeProvider

3 .when('/', {

4 template: 'Inicio <a href="#/Nosotros">Nosotros</a>'5 })

6 .when('/nosotros', {

7 template: 'Acerca de nosotros'

8 })

9 .otherwise('/');

Uniendo los componentes

Con toda la configuración antes mencionada ya puedes comenzar a crear tu aplicacióncon tus propias rutas, crear las plantillas y los controladores para que respondan aestas rutas y visualizar los resultados en el navegador. Aún quedan más detalles delmódulongRoute que hablaremosmás adelante. Ahora para continuar con el aprendizajecrearemos una pequeña aplicación para almacenar contactos, esto lo haremos paso a pasopara ver cómo se integran todos los componentes que hemos estado aprendiendo hastaahora.

Crearemos un archivo index.html que responderá como la aplicación. Incluiremos elframework y elmódulongRoute. Declararemos la directivang-app con valorContacto-sApp que será el nombre del módulo que crearemos. Utilizaremos la directiva ng-viewque será el contenedor de todas las vistas. También incluiremos el archivo app.js quetendrá la definición de la aplicación y el archivo de rutas que veremos más adelante.

Page 143: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 122

Archivo: App/index.html

1 <!DOCTYPE html>2 <html lang="en" data-ng-app="ContactosApp">3 <head>4 <meta charset="UTF-8">5 <title>Contactos</title>6 </head>7 <body>8 <h1>Contactos</h1>9 <script src="lib/angular/angular.js"></script>

10 <script src="lib/angular-route/angular-route.js"></script>11 <script src="js/app.js"></script>12 <script src="js/Config/rutas.js"></script>13 </body>14 </html>

El archivo de las rutas por ahora solo tendrá definida la página principal que será unalista de los contactos.

Archivo: App/js/Config/rutas.js

1 angular.module('ContactosApp')

2 .config(['$routeProvider', function ($routeProvider) {

3 var vista = function(vista) {

4 return '_vistas/' + vista.split('.').join('/') + '.html';

5 }

6 $routeProvider

7 .otherwise({ redirectTo: '/' })

8 .when('/', {

9 templateUrl: vista('lista'),

10 controller: 'ListaCtrl'

11 })

12 }])

Además, se ha definido una función vista para que sea más fácil indicar las vistas a lapropiedad templateUrl del objeto de configuración de las rutas. Esta función devolverála dirección de una plantilla.html dentro de la carpeta _vistas donde se dividirá por .la ruta y no se necesitará indicar la extensión html. La ruta / tendrá como plantilla elarchivo /_vistas/lista.html y el controlador ListaCtrl. Veamos el controlador.

Page 144: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 123

Archivo: App/js/Controladores/ListaCtrl

1 angular.module('ContactosApp')

2 .controller('ListaCtrl', ['$scope', 'contactos',

3 function ($scope, contactos) {

4 $scope.contactos = contactos.lista();

5 }])

En el controlador ListaCtrl hacemos uso del servicio contactos y exponemos al $scopela lista de contactos mediante el método lista() del servicio contactos. Veamos ahora lavista de esta ruta.

Archivo: App/_vistas/lista.html

1 <div>2 <ul>3 <li data-ng-repeat="contacto in contactos">4 <a ng-href="#/{{$index}}">{{contacto.nombre}}</a>5 </li>6 </ul>7 </div>

En esta vista iteraremos sobre el arreglo de los contactos y mostraremos un vínculo auna nueva ruta para ver contactos con el $index del contacto. Antes de crear esa nuevaruta veamos el servicio contacto.

Archivo: App/js/Servicios/contactos.js

1 angular.module('ContactosApp')

2 .factory('contactos', [function () {

3 var contactos = [

4 { nombre: 'Maikel Rivero Dorta',

5 email: '[email protected]',

6 tel: '123456789', },

7 { nombre: 'john Doe',

8 email: '[email protected]',

9 tel: '543216789', }

10 ];

11 return {

12 lista: function(){13 return contactos;

14 }

15 };

16 }])

Page 145: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 124

En este servicio almacenaremos los contactos, no trataremos con un backend en unservidor remoto por ahora, solo un arreglo con los datos de cada contacto y haremosun API para interactuar con ese arreglo. Se ha expuesto el método listar que devolverá elarreglo con todos los contactos, este método es el utilizado en el controlador ListaCtrlpara mostrar los contactos en la vista.

Ahora vamos a crear la nueva ruta para ver cada contacto de forma independiente, enesta ocasión haremos uso del servicio $routeParams para saber que contacto es el quese quiere ver.

Archivo: App/js/Config/rutas.js

1 $routeProvider

2 .when('/:id', {

3 templateUrl: vista('ver'),

4 controller: 'VerCtrl'

5 })

Crearemos un nuevo controlador llamado VerCtrl para mostrar el contacto.

Archivo: App/js/Controladores/VerCtrl.js

1 angular.module('ContactosApp')

2 .controller('VerCtrl', ['$scope', '$routeParams', 'contactos', '$location',

3 function ($scope, $routeParams, contactos, $location) {

4 if (contactos.ver($routeParams.id)) {

5 $scope.contacto = contactos.ver($routeParams.id);

6 } else {

7 $location.path('/');

8 };

9 }])

En este controlador haremos uso del servicio contactos para obtener la información delcontacto que se va a mostrar y el servicio $location. Comenzaremos por usar el métodover del servicio contacto, si esta devuelve un contacto lo asignaremos al scope, en caso deque el contacto no exista se redireccionará el navegador a la página principal mediante elmétodo path() del servicio $location. Esta comprobación nos dará la posibilidad de quesi el usuario intenta introducir un id que no existe en la URL es redireccionado haciala lista de contactos de forma automática. Antes de crear el método ver en el servicioveamos la vista para esta ruta.

Page 146: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 125

Archivo: App/_vistas/ver.html

1 <div>2 <span data-ng-repeat="(key, value) in contacto">3 <strong>{{key | uppercase}}:</strong> {{value}} <br>4 </span>5 <a href="#/">Volver</a>6 </div>

En esta vista iteraremos sobre cada campo del contacto ymostraremos sus datos, ademástendremos un vínculo para volver a la página principal. Ahora crearemos el método veren el servicio contacto.

1 ver: function(id){2 return contactos[id] || false;3 }

Exponiendo este método ver en el objeto que devuelve el servicio podremos ejecutarla aplicación en el navegador y podremos visitar los dos contactos que pusimos en elservicio. Como puedes comprobar ya tienes una aplicación que maneja rutas y navegassobre los contactos viendo la información sin recargar la página. Ahora agregaremosalgunas funcionalidades extra a la aplicación como borrar, editar y crear contactosademás un buscador para encontrar los contactos de forma fácil.

Comencemos por borrar un contacto que es la más fácil de las acciones. Para lograrlodebemos crear el método borrar en el servicio.

1 borrar: function(id){2 return contactos.splice(id,1).length ? true : false;3 }

Borraremos el índice que se pase como valor a la función y devolveremos verdadero ofalso. En el controlador expondremos un método borrar en el $scope.

Page 147: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 126

1 $scope.borrar = function(){2 if (contactos.borrar($routeParams.id)) {

3 $location.path('/');

4 } else {

5 console.log('No se ha podido eliminar el contacto.');

6 };

7 }

Mediante el método borrar que declaramos en el servicio se borrará el índice y seredireccionará hacia la página principal, en caso de que el índice no pueda ser borradoimprimiremos en la consola un mensaje de error.

1 <a href="#/">Volver</a> <a ng-click="borrar()" href="">Borrar</a>

En la vista agregamos el botón borrar que ejecutará la acción de borrar. Con esto yatendremos el comportamiento de borrar contactos terminados. Ahora para crear nuevoscontactos necesitamos crear una nueva ruta esta la llamaremos /nuevo. Para que estanueva ruta funcione correctamente debemos definirla antes de la ruta de ver contactosya que, si la definimos después, tendríamos un conflicto y siempre respondería la ruta dever y la palabra nuevo pasaría como parámetro de esa ruta. Si la definimos antes Angularpodrá acertar primero en la ruta /nuevo y en los demás casos lo responderá con la ruta/:id.

1 .when('/nuevo', {

2 templateUrl: vista('nuevo'),

3 controller: 'NuevoCtrl'

4 })

5 .when('/:id', {

6 templateUrl: vista('ver'),

7 controller: 'VerCtrl'

8 })

Ahora veamos el controlador.

Page 148: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 127

Archivo: App/js/Controladores/NuevoCtrl.js

1 angular.module('ContactosApp')

2 .controller('NuevoCtrl', ['$scope', 'contactos', '$location',

3 function ($scope, contactos, $location) {

4 var contacto = $scope.contacto = {};

5 $scope.crear = function(){6 if (contactos.crear(contacto)) {

7 $location.path('/');

8 } else {

9 console.log('No se ha podido crear el contacto');

10 };

11 }

12 }]);

Utilizaremos un nuevo método en el servicio contactos para crear nuevos y haremosuna redireccion a / cuando el contacto se haya creado, en caso de error enviaremos unmensaje a la consola. Veamos la vista.

Archivo: App/_vistas/nuevo.html

1 <div>2 <h3>Nuevo contacto</h3>3 <form data-ng-submit="crear()">4 <label for="nombre">Nombre: </label>5 <input type="text" id="nombre" data-ng-model="contacto.nombre"><br>6 <label for="email">Email: </label>7 <input type="email" id="email" data-ng-model="contacto.email"><br>8 <label for="tel">Tel: </label>9 <input type="text" id="tel" data-ng-model="contacto.tel"><br>

10 <input type="submit" value="Crear">11 <a href="#/">Volver</a>12 </form>13 </div>

Tendremos un formulario con la directiva ng-submit apuntando al método crear queexpusimos en el controlador. Esta directiva prevendrá el evento submit por defecto delnavegador y hará un sumbit con el método que le hemos indicado. Hay un campo paracada dato del contacto con la directiva ng-model haciendo estos datos disponibles en elcontrolador para crear el nuevo contacto. Una vez terminado esto ya tendremos lista lafuncionalidad de crear nuevos contactos.

Ahora haremos la de editar un contacto, para lograrlo debemos hacer varias modifica-ciones. En el controlador VerCtrl crearemos un nuevo método para editar el contacto.

Page 149: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 128

Archivo: App/js/Controladores/VerCtrl.js

1 $scope.editar = function(){2 $location.path('/'+ $routeParams.id + '/editar');

3 };

En la vista de ver los contactos crearemos un vínculo que nos lleve a editar el contacto.

Archivo: App/_vistas/ver.html

1 <a data-ng-click="editar()" href="">Editar</a>

Ahora crearemos la nueva ruta para editar. Esta vez debajo de la ruta de ver ya que notendremos colisiones de ningún tipo al ser un patrón diferente.

1 .when('/:id/editar', {

2 templateUrl: vista('editar'),

3 controller: 'EditarCtrl'

4 })

La vista editar.html será esencialmente lo mismo que la vista de nuevos contactos.

Archivo: App/_vistas/editar.html

1 <div>2 <h3>Editando a {{contacto.nombre}}</h3>3 <form data-ng-submit="guardar()">4 <label for="nombre">Nombre: </label>5 <input type="text" id="nombre" data-ng-model="contacto.nombre"><br>6 <label for="email">Email: </label>7 <input type="email" id="email" data-ng-model="contacto.email"><br>8 <label for="tel">Tel: </label>9 <input type="text" id="tel" data-ng-model="contacto.tel"><br>

10 <input type="submit" value="Guardar">11 <a data-ng-click="cancelar()" href="">Cancelar</a>12 </form>13 </div>

En esta vista se utilizarán dos métodos, uno es para guardar el contacto mediante ladirectiva ng-submit y la otra es cancelar que regresa a la vista del contacto. Estos dosmétodos los definiremos en el controlador.

Page 150: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 129

Archivo: App/js/Controladores/EditarCtrl.js

1 angular.module('ContactosApp')

2 .controller('EditarCtrl', ['$scope', 'contactos', '$location', '$routeParams',

3 function ($scope, contactos, $location, $routeParams) {

4 if (!$routeParams.id && !contactos.ver($routeParams.id)) {

5 $location.path('/')

6 };

7 var contacto = $scope.contacto = contactos.ver($routeParams.id);

8 $scope.cancelar = function(){9 $location.path('/'+ $routeParams.id)

10 };

11 $scope.guardar = function(){12 contactos.editar($routeParams.id, contacto)

13 $location.path('/'+ $routeParams.id)

14 }

15 }])

En el controlador lo primero que haremos es hacer una comprobación si nos hanpasado el id de un contacto existente, de lo contrario redireccionamos para la lista decontactos. Exponemos el contacto que se desea editar a la vista rellenando los campos. Elmétodo cancelar regresa a la vista del contactomediante el servicio$location. Elmétodoguardar envía al servicio el id y los nuevos datos del contactomediante el método editary después redirecciona a la vista del contacto. Ahora veremos necesitamos crear esemétodo en el servicio.

Archivo: App/js/Servicios/contactos.js

1 editar: function(id, contacto){

2 contactos[id] = contacto;

3 }

Con estas modificaciones tendremos listo la funcionalidad de editar contactos. Ahorasolo queda para terminar esta pequeña aplicación el buscador. Para agregarlo vamos a lavista lista.html y le agregamos un input para buscar y un filtro en la directiva ng-repeat.

Page 151: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 130

Archivo: App/_vistas/lista.html

1 <div>2 <input type="search" data-ng-model="buscar.nombre">3 <ul>4 <li data-ng-repeat="contacto in contactos | filter:buscar">5 <a ng-href="#/{{$index}}">{{contacto.nombre}}</a>6 </li>7 </ul>8 </div>

De esta forma ya está lista la aplicación, hemos hecho uso de gran cantidad de loselementos explicados hasta ahora en el libro, y de otros elementos que aún no se handetallado como el servicio $location que explicaremos más adelante.

Como habrás podido observar la URL tiene un # antes de todas las rutas de la aplicación.Esto es debido a que no estamos utilizando el modo HTML5 del navegador. Este modopuede ser activado mediante el $locationProvider en su propiedad html5Mode quepor defecto esta con un valor false. Por ejemplo, actualmente en la aplicación una rutaluce de esta forma /#/1/editar y con el html5Mode activado sería de la siguiente forma/1/editar.

Para cambiar almodoHTML5hay que hacer varios ajustes en la aplicación. En el archivode rutas inyectamos el provider $locationProvider y ponemos la propiedadhtml5Modecon un valor true.

Archivo: App/js/Config/rutas.js

1 angular.module('ContactosApp')

2 .config(['$routeProvider', '$locationProvider',

3 function ($routeProvider, $locationProvider) {

4 // ... demás codigo --5 $locationProvider.html5Mode(true);6 }])

Después de activar el modo HTML5 debemos dar una dirección base al archivo in-dex.html ya que este será requerido por Angular para manejar las rutas. Este elementodebe tener en su atributo href la dirección base de nuestra aplicación ya que a partir deesa dirección es que se crearán las rutas. Si tenemos la aplicación en la raíz del servidorbastara solo con poner un / en el atributo, de no ser así debes especificar la ruta paraacceder a la aplicación.

Page 152: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 131

Archivo: App/index.html

1 <head>2 <title>Contactos</title>3 <base href="/">4 </head>

Si usas Apache como tu servidor puedes crear un host virtual como el que se crea en elejemplo detallado en el apartado Entorno de desarrollo en el que se especifica una líneaque hará que todas las peticiones se hagan al archivo index.html.

Si usas NodeJS también puedes usar el servidor descrito en el apartado Entorno dedesarrollo donde se servirá el archivo index.html para todas las peticiones GET.

Lo próximo que debes hacer es eliminar todos los # que hay en los <a href=""> en lasvistas de la aplicación. Después ya podrás visitar la aplicación, en esta ocasión solo severán direcciones amigables.

SoporteNo hay que preocuparse por nuestra aplicación cuando sea ejecutada en unnavegador que no soporte HTML5. Angular es lo suficiente inteligente paracambiar el html5Mode a falso en caso de que los navegadores no tengan soportepara este modo.

Plantillas

Hasta el momento hemos visto cómo utilizar plantillas utilizando las dos propiedadesde la configuración del método when. De la primera forma escribiendo directamentela plantilla como valor de la propiedad template, y la segunda especificando una urlpara que sea cargada cuando se visite la ruta. Como es una mala práctica escribir lasvistas directamente en la lógica de la aplicación, la primera opción queda totalmentedescartada. La segunda opción donde cargamos la vista cuando esta es requerida, vienecon un costo adicional y es que cuando se visite la ruta se hará una petición HTTP paraobtener la plantilla.

Para solucionar problemas como estos, Angular provee una vía para crear las plantillasdentro de la misma vista HTML de la aplicación, la cual es cargada la primera vezque abrimos la página. Esto lo podemos lograr haciendo uso de las etiquetas <script>de HTML. Angular reconocerá todas las etiquetas <script> que sean de tipo text/ng-template y las verá como plantillas. Estas las podremos utilizar en la aplicaciónmediantela directiva ng-include o en la propiedad templateUrl de la configuración de las rutas.

Page 153: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 132

Ahora veremos este funcionamiento en un ejemplo. Para comenzar crearemos unaaplicación con solo dos rutas, no es necesario crear controladores, solo la configuraciónespecificando la plantilla de la ruta.

1 angular.module('app', ['ngRoute'])2 .config(Rutas);

3 Rutas.$inject = ['$routeProvider'];4 function Rutas($routeProvider) {

5 $routeProvider

6 .when('/', {

7 templateUrl: 'plantilla-inicio.html'

8 })

9 .when('/acerca-de', {

10 templateUrl: 'plantilla-acerca-de.html'

11 })

12 }

Ahora que tenemos la configuración para las rutas, solo necesitamos crear las plantillaspara cada una de ellas. Normalmente estas se crearían como archivos separados, peroahora haremos uso de las etiquetas <script> de tipong-template para hacerlas disponiblesen la aplicación tan rápido como esta cargue.

1 <body>2 <ng-view></ng-view>3 <script type="text/ng-template" id="plantilla-inicio.html">4 <h1>Esta es la vista para la ruta de Inicio</h1>5 <p>Esta plantilla está incluida directamente en el contenido de la página pr\

6 incipal de la aplicación mediante las etiquetas script de tipo <strong>ng-templa\7 te</strong></p>8 Desde aquí podrás visitar la página de <a href="#acerca-de">Acerca De</a>9 </script>

10 <script type="text/ng-template" id="plantilla-acerca-de.html">11 <h1>Vista para hablar acerca de nosotros</h1>12 <p>Esta plantilla como la de inicio, es creada mediante las etiquetas &lt;sc\13 ript&gt; de tipo <strong>ng-template</strong></p>14 </script>15 </body>

Como puedes observar las dos plantillas están definidas en el mismo archivo que inclusoes el índice de la aplicación donde reside la etiqueta <ng-view>. La propiedad id de laetiqueta script indica el nombre de la plantilla por la cual Angular la reconocerá y cargaráen el $templateCache que hablaremos más adelante.

Page 154: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 133

Si observas en la pestaña de red del navegador puedes observar que cambiando de unaruta hacia la otra no requiere una petición extra al servidor. Ahora las dos plantillas estáncargadas desde que se carga la página principal.

Es importante mencionar que para que estas plantillas sean correctamente reconocidases necesario que sea definidas dentro del rangode la aplicación.Con esto quiero decir quelas etiquetas script de tipo ng-template tienen que ser descendientes del elemento dondese define la aplicación con la directiva ng-app.

Plantillas en cache

Como explique anteriormente haciendo uso de las etiquetas script para crear las plan-tillas, podría ahorrarnos algunas peticiones al servidor remoto. Existe otra vía porla que podremos tener disponible todas las plantillas desde el momento en que secarga la aplicación. Esta vía que explicaré a continuación es haciendo uso del servicio$templateCache.

Este servicio es utilizado porAngular para elmanejo de la cache de todo tipo de plantillas.Cuando hacemos uso de cualquier tipo de plantilla, la primera ocasión en que se utiliceuna nueva plantilla, Angular la incluirá en la cache. Esta cache esmanejada por el servicio$templateCache, Ahora explicare su funcionamiento y su utilización.

La otra vía para cargar plantillas directamente desde que se carga la aplicación es hacien-do uso del servicio $templateCache. Este servicio lo podremos inyectar en el método runde la aplicación e insertarle las plantillas directamente en ese momento. Para cuando elusuario solicite la aplicación, las plantillas estarán directamente cargadas desde que secorra el método run.

Para ver un ejemplo de su funcionamiento, utilizaremos el ejemplo de las etiquetas scriptcon ng-template y lo convertiremos haciendo uso del $templateCache.

1 angular.module('app', ['ngRoute'])

2 .config(Rutas)

3 .run(Plantillas);

4 Rutas.$inject = ['$routeProvider'];

5 function Rutas($routeProvider) {

6 $routeProvider

7 .when('/', {

8 templateUrl: 'plantilla-inicio.html'

9 })

10 .when('/acerca-de', {

11 templateUrl: 'plantilla-acerca-de.html'

12 })

13 }

Page 155: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 134

14

15 Plantillas.$inject = ['$templateCache'];

16 function Plantillas($templateCache){

17 $templateCache.put('plantilla-inicio.html', '<h1>Esta es la vista para la ru\

18 ta de Inicio</h1><p>Esta plantilla está incluida directamente en el contenido de\

19 la página principal de la aplicación mediante las etiquetas script de tipo <str\20 ong>ng-template</strong></p>Desde aquí podrás visitar la página de <a href="#ace\

21 rca-de">Acerca De</a>');22 $templateCache.put('plantilla-acerca-de.html', '<h1>Vista para hablar acerca\

23 de nosotros</h1><p>Esta plantilla como la de inicio, es creada mediante las eti\

24 quetas &lt;script&gt; de tipo <strong>ng-template</strong></p>');25 }

Como habrás podido observar, con el uso del servicio obtenemos el mismo comporta-miento que utilizando las plantillas de ng-template. Inyectando las plantillas en el métodorun de la aplicación, estas estarán disponibles desde que la aplicación esté lista.

Para poner las plantillas dentro de la cache hemos hecho uso del método put delservicio. Este método acepta dos parámetros, el primero es una cadena de texto con elidentificador de la plantilla. El segundo parámetro es Una cadena de HTML que será laplantilla que será la plantilla para ese identificador.

Este servicio no es más que un acceso directo creado sobre el servicio $cacheFactory.Esto significa que dispondrá de los mismos métodos que los objetos de cache comunes.Además, mediante el servicio $cacheFactory puedes acceder a las plantillas cargadas por$templateCache utilizando el id templates.

Precargando plantillas

A partir de la versión 1.3 de Angular, se incluyó un nuevo servicio para precargarplantillas en el $templateCache. Cuando estamos en una página de la aplicación y nosdesplazamos hacia otra, la aplicación hace una petición XHR para obtener la plantillaque necesitamos mostrar. Este proceso sucede en el momento en que el usuario da clicen la acción para cambiar la página. Si el contenido de la petición es grande, tendremos elusuario esperando a que se termine de obtener el contenido. Con el nuevo servicio cargarlas plantillas de otras páginas antes de que el usuario de clic para cambiar hacia ellas. Deesta forma las peticiones se realizarán mientras el usuario navega por la aplicación y asíevitaremos tiempo de carga al usuario.

Ya hemos visto otras formas de hacer cache de las plantillas, pero esta nos permitirá hacercache de la que necesitamos específicamente. Este proceso de hacer cache solo de lasplantillas que necesitamos utilizar ayudará a mejorar el rendimiento de la aplicación.Para utilizarlo solo tendremos que inyectar el servicio $templateRequest y solicitar laplantilla que queremos guardar en cache.

Page 156: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 135

Ahora veremos un ejemplo de cómo funciona el servicio $templateRequest. Para ellocrearemos una simple aplicación de dos páginas, desde que visitemos la primera páginaautomáticamente se cargara la plantilla de la segunda. Guardando la segunda página enla cache antes de que el usuario la solicite, evitará que tenga que esperar a que se resuelvacuando desee ir hacia ella.

Lo primero que vamos a hacer para el ejemplo es crear una página index.html quecontendrá el cuerpo de la aplicación. Después crearemos otros dos archivos HTML queservirán como plantillas de la página inicio y de la página nosotros.

index.html

1 ...

2 <body>3 <ng-view></ng-view>4 </body>5 ...

inicio.html

1 <h1>Esta es la página de inicio</h1>2 <p>Accediendo a esta página se cargara el contenido de la página <strong>/nosotr\3 os</strong> de forma automática para evitar tiempo de carga cuando el usuario de\

4 click para navegar hacia ella.</p>5 <a href="#/nosotros">Ir a Nosotros</a>

nosotros.html

1 <h1>Nosotros</h1>2 <p>Esta plantilla ha sido cargada previamente mediante el servicio <strong>$temp\3 lateRequest</strong></p>

Ahora necesitamos definir las dos rutas y el controlador para la página principal. Elcontrolador se encargará de cargar la plantilla de la página de nosotros mediante elservicio $templateRequest.

Page 157: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 136

app.js

1 angular.module('app', ['ngRoute'])2 .config(Rutas)

3 .controller('AppCtrl', AppCtrl);

4

5 Rutas.$inject = ['$routeProvider'];6 function Rutas($routeProvider){

7 $routeProvider

8 .when('/', {

9 templateUrl: 'inicio.html',

10 controller: 'AppCtrl'

11 })

12 .when('/nosotros', {

13 templateUrl: 'nosotros.html'

14 })

15 }

16 AppCtrl.$inject = ['$scope', '$templateRequest'];17 function AppCtrl($scope, $templateRequest) {

18 $templateRequest('nosotros.html');

19 }

Como habrás podido observar, estamos solicitando la página nosotros.html dentro delcontrolador de la página de inicio. Con las herramientas de desarrollo del navegadorpuedes observar como cuando se abre la página principal se carga la plantilla inicio paramostrar como ruta por defecto, pero además se carga la página de nosotros para cuandoel usuario necesite visualizar su contenido se muestre de forma instantánea.

Haciendo un buen uso de este servicio evitaremos que el usuario espera por la aplicación.Esto hará que la estancia en la aplicación sea más agradable para el usuario, además deque mejorará visualmente el rendimiento de la aplicación.

El servicio $route

El servicio $route esta siempre observando a $location.url() para cuando haga algúncambio este responder si alguna de las rutas definidas coincide con la nueva URL. Esteservicio solo tiene un método reload() el cual hace que se recargue la ruta actual inclusoaunque no se hayan cambiado los valores en el servicio $location. Debido a la recarga elcontrolador es reinstanciado creando un nuevo $scope.

Este servicio además tiene dos propiedades. Una es routes la cual es un objeto que tienecomo propiedades todas las rutas declaradas en la aplicación desde la cual podremos

Page 158: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 137

acceder a las propiedades de cada una. La otra propiedad es current que es un objetoy tendremos disponible varios datos interesantes que podremos usar en la aplicación.Veremos ahora las propiedades del objeto current del servicio $route.

$route.current.controller: Devuelve el nombre del controlador para la ruta actual.

$route.current.locals: Es un objeto que tiene la referencia al $scope actual y el $tem-plate que se usa en la ruta actual.

$route.current.originalPath: Devuelve el camino de la ruta actual.

$route.current.template: Devuelve la plantilla para la ruta actual.

$route.current.params: Devuelve un objeto con todos los parámetros que le han sidopasados a la ruta actual.

En caso de que necesites especificar valores en la ruta para luego usarlos dentro de lavista o el controlador puedes hacerlo dentro del objeto de configuración de la ruta comouna propiedad más del objeto y esta estará disponible en $route.current.tuPropiedadveamos un ejemplo.

Archivo: App/js/Config/Routes.js

1 angular.module('miApp')

2 .config(['$routeProvider', function ($routeProvider) {

3 $routeProvider

4 .when('/ruta', {

5 template: '{{propiedad}}',

6 controller: 'RutaCtrl',

7 miPropiedad: 'Esta propiedad está definida en la ruta'

8 })

9 }])

En el archivo de configuración de la ruta definimos la ruta, dentro del objeto deconfiguración que le pasamos como segundoparámetro awhen()definimos la propiedadmiPropiedad. Esto lo hacemos como mismo definimos las propiedades controller ytemplate, en esta última mostraremos el texto de la propiedad que expondremos en elcontrolador.

Archivo: App/js/Controllers/RutaCtrl

1 angular.module('miApp')

2 .controller('RutaCtrl', ['$scope', '$route', function ($scope, $route) {

3 $scope.propiedad = $route.current.miPropiedad;

4 }])

Page 159: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 138

Al visitar esta ruta en la aplicación podrás comprobar que el texto que definimos en lapropiedad del objeto de configuración de la ruta ahora se muestra en la vista. Esto puedeser utilizado para varias configuraciones como por ejemplo definir el título de la páginaen la misma configuración de la ruta y después mostrarlo de forma automática en todaslas páginas. Sobre este tema hablaremos después de los eventos.

Cambio de parámetros en la ruta

A partir de la versión 1.3 de Angular, el servicio $route tiene otro método además delexplicado anteriormente reload. Este nuevo método nos permite desde el código de laaplicación cambiar parámetros de la ruta. El nuevométodo esupdateParams, este aceptaun objeto de tipo llave:valor donde la llave es el nombre del parámetro a actualizar y elvalor será el nuevo valor que obtendrá el parámetro.

Cuando estamos creando una aplicación, queremos que se pueda regresar a esta a travésde un bookmark para facilitar un acceso directo a lugares específicos. Esta funcionalidadya la tenemos actualmente sin hacer algún cambio a la aplicación. Pero si los parámetrosde la ruta son utilizados para organizar el comportamiento de la aplicación no teníamosla posibilidad de cambiarlos. Ahora con el nuevo método del servicio $route podremosactualizar los parámetros de forma muy sencilla.

Supongamos una aplicación que es una lista de productos con nombre, valor, votos y ventas.En esta tendremos un elemento de tipo select para tener la posibilidad de organizar lalista por los diferentes parámetros. Además, haremos que el parámetro de organizaciónse pueda especificar en la url. De esta forma cuando se navegue directamente a estadirección, con un parámetro que indique la organización, la aplicación organizará deforma automática dependiendo del parámetro. Haciendo uso del nuevo método delservicio $route, cambiaremos el parámetro de organización de la ruta para que cuando sehaga un marcador esta se guarde con la nueva organización.

Vamos a ver una imagen de la aplicación terminada y luego iremos describiendo porpartes el proceso completo.

Page 160: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 139

Listado de productos

Para comenzar creamos el modulo y especificamos ngRoute como dependencia. Despuéscreamos un bloque de configuración donde crearemos la ruta.

1 angular.module('app', ['ngRoute'])2 .config(function ($routeProvider) {

3 $routeProvider

4 .when('/', {

5 template: 'Inicio'

6 })

7 .when('/productos/:orden?', {

8 templateUrl: 'views/productos.html',

9 controller: 'ProductosCtrl',

10 controllerAs: 'vm'

11 });

12 });

Como habrás podido observar, la ruta productos tiene un parámetro orden y además unsímbolo ?, este signo es utilizado para hacer que el parámetro sea opcional. Ahora queya tenemos la ruta, necesitamos crear el controlador. Inyectamos los servicios $route-Params y $route para poder utilizarlos posteriormente. Lo primero que necesitamoshacer es especificar un orden por defecto para la lista. Después necesitamos un arreglocon los objetos que se mostraran en la lista, usualmente estos datos se obtendrían desdeun servidor remoto.

Page 161: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 140

1 .controller('ProductosCtrl', function ($routeParams, $route) {

2 var vm = this;3

4 vm.orden = $routeParams.orden || '-precio';

5

6 vm.productos = [7 { nombre: 'Samsung Galaxy S4', precio: 198.99, puntos: 175, ventas: 4718 },

8 { nombre: 'Samsung Galaxy S3', precio: 105.99, puntos: 196, ventas: 1820 },

9 { nombre: 'Asus Zenfone 2', precio: 179.99, puntos: 127, ventas: 716 },

10 { nombre: 'HTC Desire 620', precio: 199.99, puntos: 166, ventas: 914 },

11 { nombre: 'HTC One M7', precio: 175.95, puntos: 1694, ventas: 1589 },

12 { nombre: 'LG L Bello', precio: 149.99, puntos: 1211, ventas: 891 },

13 { nombre: 'Motorola Moto X 2', precio: 219.99, puntos: 1865, ventas: 6174 }

14 ];15 })

Ahora vamos a crear la vista para mostrar la lista de los productos.

1 <div class="row">2 <table class="table">3 <tr>4 <th>Nombre</th>5 <th>Precio</th>6 <th>Puntos</th>7 <th>Ventas</th>8 </tr>9 <tr ng-repeat="producto in vm.productos | orderBy: vm.orden">

10 <td>{{producto.nombre}}</td>11 <td>{{producto.precio | currency}}</td>12 <td>{{producto.puntos}}</td>13 <td>{{producto.ventas}}</td>14 </tr>15 </table>16 </div>

Hasta el momento la aplicación ya es funcional. Podremos navegar hacia la ruta produc-tos y se mostrará la lista de los productos, e incluso si queremos organizar por algunode las columnas podríamos navegar hacia los productos especificando un orden. Comoejemplo podremos visitar la ruta /#/productos/ventas la cual mostrará la lista ordenadapor ventas de menor cantidad a mayor. Ahora solo nos queda crear el elemento selectcon las opciones para ordenar los elementos. Primero definiremos las ordenes en elcontrolador. También crearemos un método para ejecutarlo con la directiva onChange

Page 162: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 141

que pondremos en el elemento select. En estemétodo utilizaremos el servicio route con sunueva funcionalidad updateParams. Pasaremos como parámetro un objeto con el nuevoorden para que, al ser cambiado, este actualice la ruta con el nuevo parámetro de orden.

1 vm.organizar = [2 { val: '-puntos', texto: 'Mayor Puntuado' },

3 { val: 'puntos', texto: 'Menor Puntuado' },

4 { val: '-ventas', texto: 'Más Vendido' },

5 { val: 'ventas', texto: 'Menos Vendido' },

6 { val: 'precio', texto: 'Menor Precio' },

7 { val: '-precio', texto: 'Mayor Precio' }

8 ];9 vm.cambiarOrden = function () {

10 $route.updateParams({ orden: vm.orden });

11 }

Ahora solo nos queda actualizar la vista con el elemento select.

1 <div class="row">2 <div class="col-md-12 master">3 <label for="orden">Organizar por:</label>4 <select id="orden" name="orden" ng-model="vm.orden"

5 ng-change="vm.cambiarOrden()"

6 ng-options="orden.val as orden.texto for orden in vm.organizar">7 </select>8 </div>9 </div>

Gracias a la directiva ng-options se agregarán las opciones que especificamos en elcontrolador al elemento select. Como puedes observar al hacer algún cambio en el orden,este actualiza la lista y a la vez la url con el nuevo parámetro de organización.

Eventos

El modulo ngRoute nos provee de 4 eventos que son disparados en determinadosmomentos en que se realiza el proceso de cambio de rutas. Estos eventos son lanzadossobre el servicio $rootScope que mediante el método $on. No detallaré los eventos ysu propagación en este capítulo, solo hablaré de los eventos del módulo ngRoute. Si noentiendes algo relacionado con los eventos en general no te preocupes, más adelante loentenderás perfectamente cuando se trate el tema de eventos.

Page 163: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 142

El primer evento que trataremos es el $routeChangeStart. Este evento se disparará en elmomento antes de que la ruta se cambie. En estemomento el servicio comienza a resolverlas dependencias que se hayan definido en la propiedad resolve de la configuración dela ruta, así como la plantilla que se mostrará al usuario. Cuando se resuelvan todas lasdependencias se disparará el evento $routeChangeSuccess.

Al escuchar este evento cuando es disparado recibiremos tres parámetros, el primero esun objeto evento con alguna información relacionada con el evento en sí. El segundoparámetro es un objeto con la ruta que se comenzará a cargar. En este objeto tendremosdisponible las mismas propiedades que el objeto current del servicio $route pero de laruta que se cargara. El tercer parámetro es otro objeto de igual forma al anterior, perocon la información de la ruta actual antes de comenzar a cambiar.

Conociendo sobre el evento $routeChangeStart vamos a utilizarlo para definir el títulode las páginas en el objeto de definición de cada ruta y mediante el evento actualizarloen la etiqueta <title> del <head>. Veamos un ejemplo del archivo de rutas.

Archivo: App/js/Config/rutas.js

1 angular.module('miApp')

2 .config(['$routeProvider', function ($routeProvider) {

3 $routeProvider

4 .when('/', {

5 template: '<h1>Página de inicio</h1><br><a href="#/contacto">Contacto</a\

6 >',

7 titulo: 'Página de inicio'

8 })

9 .when('/contacto', {

10 template: '<h1>Contacto</h1><br><a href="#/">Volver</a>',

11 titulo: 'Página de contacto'

12 })

13 }])

Es muy simple, solo dos rutas y las propiedades título de cada una para hacerlasdisponibles en la vista. Veamos el archivo index.html.

Page 164: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 143

Archivo: App/index.html

1 <!DOCTYPE html>2 <html lang="en" data-ng-app="miApp">3 <head>4 <meta charset="UTF-8">5 <title>{{titulo}}</title>6 </head>7 <body>8 <div data-ng-view></div>9 <script src="lib/angular/angular.js"></script>

10 <script src="lib/angular-route/angular-route.js"></script>11 <script src="js/app.js"></script>12 <script src="js/Config/Bootstrap.js"></script>13 <script src="js/Config/rutas.js"></script>14 </body>15 </html>

En la etiqueta <title> haremos uso del servicio $rootScope para obtener la propiedadtítulo ya que en este servicio es donde se escuchará el evento. El archivo Bootstrap.js esel que contendrá la configuración del evento, vamos a verlo.

Archivo: App/js/Config/Bootstrap.js

1 angular.module('miApp')

2 .run(['$rootScope', function ($rootScope) {

3 $rootScope.$on('$routeChangeStart', function(evento, siguiente, actual){

4 $rootScope.titulo = siguiente.titulo || 'Titulo por defecto';

5 });

6 }])

En este archivo se ha utilizado el método run del módulo. Este método es instanciadoen el momento en que angular ha terminado de cargar toda las dependencias y módulosde la aplicación. Es un buen lugar para escuchar los eventos y configurar el $rootScopepara que tome acciones en cada uno de ellos.

He inyectado el $rootScope en el método run y mediante el método $on escucharemosel evento $routeChangeStart que es el primer parámetro que le pasamos a este métodocomo cadena de texto. El segundo parámetro es una función que será ejecutada en elmomento en que se dispare el evento. Como mencionamos anteriormente el eventoinyecta tres parámetros. Para este ejemplo solo nos interesa el segundo parámetro quees el que tendrá el objeto de configuración de la ruta que se va a cargar. Asignamos una

Page 165: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 144

propiedad título en el $rootScope con el título que viene en la configuración de la rutao en caso de que no venga ningún título pasaremos un título por defecto.

Es suficiente, con el código anterior hemos escuchado al evento y hemos tomado lasacciones correspondientes. Siempre que se dispare el evento $routeChangeStart elservicio $rootScope estará escuchando y hará el cambio en la propiedad título. De estaforma siempre tendremos el título de la página actualizado. En caso de que necesitesgenerar un título dependiendo de datos en el controlador, puedes inyectar el servicio$rootScope y cambiar la propiedad título. El titulo definido dentro del controladortopara precedencia ya que el evento es lanzado antes de que sea instanciado el controla-dor. Cuando el controlador pueda cambiar el titulo remplazará el que habrá puesto pordefecto el evento.

Otro de los eventos es $routeChangeSuccess, este es disparado después de que se hayanresuelto todo lo dispuesto en la propiedad resolve del objeto de configuración de la ruta.Este evento recibe los mismos parámetros que el $routeChangeStart.

En caso de que alguno de los elementos de la propiedad resolve del objeto de configu-ración de la ruta no se resuelva, se disparará el evento $routeChangeError. Este eventode igual forma recibe los tres parámetros que reciben los demás eventos anteriores.Además, recibe un cuarto parámetro que es el mensaje de error devuelto por la promesarechazada.

El último de los eventos que nos provee el módulo ngRoute es $routeUpdate. Este esdisparado solo si la propiedad reloadOnSearch se ha definido con un valor false y secambian los valores de $location.search() o $location.hash() pero aún se usa la mismainstancia del controlador.

Los anteriormente mencionados son los eventos que añade el módulo ngRoute alframework. Estos no son los únicos, existen otros que veremos más adelante en elCapítulo de Eventos.

Una de los usos que podemos darles a los eventos de las rutas, por ejemplo, si estamosen una vista editando mediante un formulario y los cambios no han sido guardados aún.Mediante el evento $routeChangeStart podríamos cancelar el cambio de ruta ymostrar unmensaje de alerta al usuario para que no pierda los cambios. Vamos a ver cómo quedaríael código para este ejemplo.

Primero crearemos dos rutas para navegar desde una hacia la otra. A estas le agregaremosuna propiedad id para poder comprobar en qué ruta estamos cuando escuchemos elevento.

Page 166: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 145

1 .config(function ($routeProvider) {

2 $routeProvider

3 .when('/editar', {

4 id: 'editar',

5 template: 'Editar <a href="#lista">Volver a la lista</a>',6 controller: 'EditarCtrl'

7 })

8 .when('/lista', {

9 id: 'lista',

10 template: 'Lista'

11 })

12 })

Ahora en el controlador inyectaremos $rootScope para escuchar el evento * $route-ChangeStart. También he definido una variable *editando que servirá como condición parasi esta tiene valor verdadero prevenga que naveguemos hacia otra ruta.

1 .controller('EditarCtrl', function ($rootScope, $scope) {

2 $scope.editando = true;3 $rootScope.$on('$routeChangeStart', function (evento, siguiente, actual) {

4 if (!!actual && actual.id === 'editar' && $scope.editando) {

5 evento.preventDefault();

6 alert('Debes guardar los cambios antes de salir.')

7 }

8 });

9 });

Mediante el método $on del rootScope escuchamos el evento $routeChangeStart. Primerocomprobamos que exista un estado actual después que su id es la que estamos actual-mente que es la de editar y por ultimo si estamos editando. En caso de que la condición secumpla evitaremos que se navegue hacia otra ruta mediante el objeto evento ejecutandoel método preventDefault() y luego alertaremos al usuario que debe guardar los cambiosantes de salir.

El servicio $location

Hasta elmomento hemos hecho uso del servicio $location aunque aún no se ha detalladosus usos, métodos y propiedades. El servicio $location es una interface para tratar conel objetowindow.location de javascript. Este tiene algunas diferencias que lo hacenmásútil tratándose de que está completamente relacionado con el ciclo de vida y las fases de laaplicación. En él se exponen las propiedades con getters y setters al estilo jQuery. Tiene

Page 167: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 146

integración con el API de HTML5 con soporte para navegadores viejos. Esto entre otrasson las ventajas de utilizar el servicio $location en vez de utilizar el nativo de javascriptwindow.location.

Este servicio tiene una desventaja en cuanto al objeto windows.location y es que nopuede recargar la página por completo cuando la URL del navegador cambia ya quesolo recarga porciones de la aplicación. Para hacer una recarga completa de la páginase deberá utilizar el servicio $window.location.href.

A diferencia del nativo de javascript, este está totalmente integrado con el framework.Cambios en la URL del navegador son reflejados directamente en el servicio $locationy viceversa. Ahora veremos los métodos y propiedades que tiene el servicio $location.

path(): Este método si es ejecutado sin parámetros devuelve el camino en el que estamosactualmente. Si se le pasa un parámetro con una cadena de texto este servicio hará que senavegue hacia esa dirección. Este es el método que hemos estado usando en los ejemplosanteriores para cambiar de una ruta a otra dentro de la aplicación. Este método noproduce una recarga total de la página, solo se recargan las partes necesarias. Además,este método interactúa directamente con el API de Historial de HTML5 de forma que,si el usuario presiona el botón Atrás del navegador, este podrá navegar a la ruta anteriorsin recargar la página.

replace(): En algunas ocasiones no que remos que el comportamiento producido porla función path() unido al Api historial de HTML5 guarde una referencia a la páginaanterior. El método replace hará que se remplaza el historial y no que se cree un nuevoregistro de la página por la que se navega. Esto hace que al presionar el botón Atrás delnavegador, no se navegue a la ruta anterior. Este método es muy útil para casos comocuando se redirige al usuario después de hacer login y no queremos que regrese a laredirección. Un ejemplo de su uso seria.

1 $location.path('/dashboard');

2 $location.replace();

Estos métodos se pueden ejecutar en cadena como se realiza en jQuery.

1 $location.path('/dashboard').replace();

absUrl(): Este método devuelve la dirección absoluta con todos los segmentos codifica-dos. Será exactamente lo que podemos observar en la barra de dirección del navegador.

hash(): Devuelve el fragmento de los hash que existan en laURL. En caso de que se le paseun parámetro cadena de texto se cambiará el hash hacia el nuevo que se ha introducido.

Page 168: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 8: Rutas 147

1 $location.hash(); // #procesos-sistema2 $location.hash('procesos-usuario'); // se cambiará a #procesos-usuario

search(): Devuelve un objeto con los fragmento search que existan en la URL. En casode que se le pase un parámetro cadena de texto previamente codificada o un arreglo dellaves y valores, se cambiará hacia la nueva dirección de búsqueda.

1 $location.search(); // persistir=true devilverá Object {persistir: "true"}2 $location.search('persistir=true'); // se cambiará a ?persistir=true3 $location.search({persistir:true}); // se cambiará a ?persistir=true

host(): Devolverá el host donde se está ejecutando la aplicación sin el método por la quese accede ni los demás segmentos de la ruta.

port(): Devolverá el puerto por el cual se accede a la aplicación.

protocol(): Devolverá el protocolo por el cual se accede a la aplicación ya sea http o https.

url(): Devolverá la url del navegador sin prefijo, host o método. Esta incluye los segmen-tos de búsqueda y el hash. Este método puede recibir un parámetro de tipo cadena detexto. Si le pasamos ese parámetro se cambiará la url con los segmentos de búsqueda yhash a la nueva url.

1 $location.url(); // devuelve /12/editar?persistir=true#credito2 $location.url('/12#credito'); // cambiará a la nueva URL

Este servicio proporciona dos eventos que pueden ser utilizados para tomar acciones deacuerdo a los cambios en la URL. El primero es $locationChangeStart que es disparadoexactamente antes de que se produzca el cambio en la URL. Este evento puede serdetenido en caso de que sea necesario llamando al método preventDefault en el evento.Este evento recibe tres parámetros, el primero es el objeto evento con el cual podremosprevenir que se produzca el cambio de la url, el segundo es la url absoluta hacia donde seva y el tercero es la url absoluta actual.

El otro evento que proporciona este servicio es $locationChangeSuccess. Este es dispa-rado cuando la ruta se ha terminado de cambiar. Este evento también recibe los mismostres parámetros que el evento $locationChangeStart.

Lo anteriormente mencionado es lo relacionado con el servicio $location. Este es el queestaremos utilizando para movernos de un lugar a otro dentro de la aplicación.

Page 169: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 9: EventosHasta ahora hemos visto varios eventos y su funcionamiento, por ejemplo, los eventos delservicio $route. Angular no nos limita a solo esos eventos, permite que crees tus propioseventos y que tomes acciones en dependencia de lo que suceda en tu aplicación. Estaserá una de las vías que tendrás para intercambiar información dentro de la aplicaciónen tiempo real de acuerdo a las interacciones del usuario.

Como se ha explicado anteriormente los scopes de la aplicación pertenecen a un árboljerárquico donde el padre de todos los scopes es $rootScope y tendrá los scopes dela aplicación como hijos o nietos sucesivamente. Existen dos formas de propagar loseventos en Angular, estos se propagan en dos direcciones, uno hacia arriba o sea lospadres del scope actual y la segunda es hacia abajo a los scopes hijos. Hay que teneren cuenta que propagar eventos en la dirección equivocada o de manera global puedeocasionar mal funcionamiento en la aplicación.

Propagando eventos hacia los scopes padres

Una de las dos formas de propagar eventos es haciéndolo hacia los scopes padres,lo podemos realizar mediante el método $emit del scope. Este método recibe dosparámetros, el primero es el nombre del evento de tipo cadena de texto por el cualserá escuchado y el segundo es un objeto con los parámetros que recibirá el disparador.Cuando el método $emit es llamado se alertará a los scopes padres para que tomenacciones al escuchar el evento. Veamos un ejemplo.

Archivo: index.html

1 <body>2 <div data-ng-controller="PadreCtrl">3 <h3>Scope Padre</h3>4 <div data-ng-controller="HijoCtrl">5 <h4>Scope Hijo</h4>6 <button data-ng-click="click()">Click</button>7 </div>8 </div>9 <script src="lib/angular/angular.js"></script>

10 <script src="js/app.js"></script>11 <script src="js/Controllers/PadreCtrl.js"></script>12 <script src="js/Controllers/HijoCtrl.js"></script>13 </body>

148

Page 170: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 9: Eventos 149

Archivo: App/js/Controllers/PadreCtrl.js

1 angular.module('miApp')

2 .controller('PadreCtrl', ['$scope', function ($scope) {

3 $scope.$on('eventoHijo', function(evt,arg){4 console.log(arg.msg);

5 })

6 }])

Archivo: App/js/Controllers/HijoCtrl.js

1 angular.module('miApp')

2 .controller('HijoCtrl', ['$scope', function ($scope) {

3 $scope.click = function(){4 $scope.$emit('eventoHijo', {msg:'Se ha hecho clic en el scope Hijo.'});

5 };

6 }])

En el ejemplo anterior propagaremos un evento eventoHijo al hacer clic en el botón delscope hijo que enviará un objeto con unmensaje. En el controlador padre escucharemosel evento eventoHijo y enviaremos a la consola el mensaje que recibimos con el evento.

Propagando eventos hacia los scopes hijos

La segunda forma de propagar eventos es haciéndolo hacia los hijos del scope actual yesto los realizaremos mediante el método $broadcast del scope. Este método recibe losmismos parámetros que el método $emit. Veamos un ejemplo.

Archivo: index.html

1 <body>2 <div data-ng-controller="PadreCtrl">3 <h3>Scope Padre</h3>4 <button data-ng-click="click()">Click</button>5 <div data-ng-controller="HijoCtrl">6 <h4>Scope Hijo</h4>7 </div>8 </div>9 <script src="lib/angular/angular.js"></script>

10 <script src="js/app.js"></script>11 <script src="js/Controllers/PadreCtrl.js"></script>12 <script src="js/Controllers/HijoCtrl.js"></script>13 </body>

Page 171: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 9: Eventos 150

Archivo: App/js/Controllers/PadreCtrl.js

1 angular.module('miApp')

2 .controller('PadreCtrl', ['$scope', function ($scope) {

3 $scope.click = function(){4 $scope.$broadcast('eventoPadre', {

5 msg:'Se ha hecho clic en el scope Padre.'

6 });

7 };

8 }])

Archivo: App/js/Controllers/HijoCtrl.js

1 angular.module('miApp')

2 .controller('HijoCtrl', ['$scope', function ($scope) {

3 $scope.$on('eventoPadre', function(evt,arg){4 console.log(arg.msg);

5 })

6 }])

En el ejemplo anterior se envía el evento eventoPadre desde el scope padre hacia el scopehijo y se escribe el mensaje en la consola cuantas veces sea disparado.

Escuchando eventos

Ya hemos visto como disparar los eventos en ambas direcciones, desde los padres hacialos hijos y desde los hijos a los padres. Ahora solo queda escuchar estos eventos yrealizar acciones cuando cada uno ocurra. En los ejemplos anteriores puedes observarque escuchar el evento lo hacemosmediante elmétodo$on()del scope donde tomaremoslas acciones. Este recibe dos parámetros, el primero es el nombre del evento que se estáescuchando y el segundo es una función que será llamada en elmomento en que el eventosea disparado.

Esta función siempre recibirá como primer parámetro el objeto evento de angular, yasea un evento creado por nosotros o uno nativo de angular como $viewContentLoaded.Además, esta función recibirá todos los parámetros que se le envíen desde el disparadoro sea $emit o $broadcast.

Page 172: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 9: Eventos 151

Objeto Evento de Angular

El objeto evento que es entregado en cada ocasión que escuchamos un disparador nosbrinda información sobre el evento en sí.

targetScope: (objeto) Es el scope de donde se ha emitido el evento ya sea con $emito con $broadcast. currentScope: (objeto) Es el scope que se encargará de manejar elevento. name: (string) Es el nombre del evento que fue emitido y estamos manejandoen estos momentos. stopPropagation: (función) Esta función cancela la propagación delevento. preventDefault: (función) Esta función cambia la propiedad defaultPreventeda true. Aunque no cancela la propagación del evento informa a los scopes hijos que nose deberá tomar ninguna acción con respecto a este evento. defaultPrevented: (boolean)Esta propiedad es cambiada a true por la función preventDefault.

Los eventos en una aplicación nos brindan una gran funcionalidad en la aplicación yaque nos permite tomar acciones específicas en los momentos necesarios mediante lasinteracciones del usuario.

Page 173: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: RecursosEn el Capítulo 5 tratamos el tema sobre las peticiones al servidor utilizando el servicio$http. Aunque solo hicimos peticiones de tipo get el servicio puede realizar peticionesa todos los métodos estándar de HTTP. En Este capítulo comenzaremos a utilizar unservicio llamado ngResource que está basado completamente en el servicio $http peroestá más enfocado al trabajo con APIs RESTful. Aunque el servicio $http por si solopuede hacer uso de un Api Rest por sí solo, tendríamos que escribir mucho código paracompletar las tareas.

Obteniendo ngResource

El servicio ngResource no forma parte del núcleo de angular se debe incluir en laaplicación después del framework. Para obtenerlo lo podremos descargar desde la páginaoficial de angular, usando elCDN o instalándolo como dependencia de la aplicación conbower. Veamos un ejemplo utilizando bower.

1 bower install angular-resource --save

Especificando–save añadiremos angular-resource como una dependencia en el archivobower.json de la aplicación. Después de haber obtenido el servicio lo incluimos en laaplicación.

1 <body>2 <div class="container">3 <h1>Hello</h1>4 </div>5

6 <script src="lib/bootstrap/dist/js/bootstrap.min.js"></script>7 <script src="lib/angular/angular.js"></script>8 <script src="lib/angular-resource/angular-resource.js"></script>9 <script src="js/app.js"></script>

10 </body>

Solo queda un paso para que podamos comenzar a utilizar este servicio, y es que necesi-tamos incluirlo como una de las dependencias del módulo que estamos desarrollando.

152

Page 174: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 153

1 angular.module('miApp', ['ngResource']);

De esta forma ya podremos comenzar a Hacer uso del servicio $resource. Este serviciolo utilizaremos mayormente para crear nuestros propios servicios que se encarguen detratar con el servidor de una forma RESTful. Amedida que vallamos viendo los ejemplosdescribiré el servicio y las facilidades que nos brinda con respecto a $http.

Primera petición al servidor REST

Enel siguiente ejemplohare unapetición a un recursoRESTdel servidor dondeobtendréuna lista de mensajes y la mostraré mediante la directiva ng-repeat al usuario. Loprimero que necesitamos es un factory que devuelva el servicio REST para comenzara ejecutar las peticiones.

1 var app = angular.module('miApp', ['ngResource'])

2 .factory('Mensajes', ['$resource', function ($resource) {

3 return $resource('/api/mensajes/:id');

4 }]);

El servicio $resource recibe varios parámetros, pero por ahora solo utilizaremos elprimer parámetro que será una cadena de texto con la url a la que haremos la petición.Como has podido observar en la url de la petición tenemos un parámetro :id, si leíste elCapítulo 8 te parecerá familiar ya que es de la misma forma que se pasan los parámetrosen las rutas. Internamente el servicio $resource hará uso de ese parámetro para hacerpeticiones a recursos individuales mediante el id.

Ahora creare un controlador para hacer uso del servicioMensajes que acabo de crear. Enel controlador ejecutaremos la petición y en caso de ser satisfactoria haremos disponibleslos mensajes en la vista asignándolos al $scope.mensajes. En caso de que la respuesta seaun error imprimiremos en la consola el código del error y el mensaje proporcionado porel servidor.

1 app.controller('MensajesCtrl',['$scope', 'Mensajes',

2 function ($scope, Mensajes) {

3 Mensajes.query(function (datos) {

4 $scope.mensajes = datos;

5 }, function (err) {

6 console.error('Error ' + err.status + ': ' + err.data.mensaje);

7 });

8 }]);

Page 175: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 154

El servicio devuelto por $resource nos brinda cinco métodos para interactuar conrecursos en el servidor que facilita mucho las tareas con respecto a hacer peticionescon el servicio $http. Como has podido observar se ha utilizado el método query elcual explicare al detalle más adelante. Por ahora solo mencionar que lo utilizamos parahacer una petición de tipo get al servidor donde obtendremos un arreglo de recursos enforma de colección. Esta petición espera una respuesta de tipo json. Ahora que ya se hanasignado los datos del servidor al $scope podremos mostrarlo en la vista con ng-repeat.

1 <div class="container" ng-controller="MensajesCtrl">2 <div class="row">3 <div class="col-md-12">4 <h2>Mensajes</h2>5 <table class="table table-hover">6 <thead>7 <tr>8 <th>Usuario</th>9 <th>Mensaje</th>

10 </tr>11 </thead>12 <tbody >13 <tr ng-repeat="mensaje in mensajes">14 <td>{{mensaje.usuario}}</td>15 <td>{{mensaje.mensaje}}</td>16 </tr>17 </tbody>18 </table>19 </div>20 </div>21 </div>

En la vista mostraremos cada mensaje en una fila de la tabla mediante ng-repeat, Elresultado podemos observarlo en la imagen a continuación.

Page 176: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 155

Lista de los mensajes usando Bootstrap3

Parámetros del servicio $resource

En los ejemplos anteriores he creado un servicio mediante $resource utilizando un soloparámetro. Este primer parámetro es la ruta a la cual se realizarán las peticiones. Como secomentó anteriormente en esta se pueden especificar parámetros utilizando la notaciónde rutas de angular, :nombre donde nombre será el nombre del parámetro por el queposteriormente se le hará referencia.

El Segundo parámetro es opcional, un objeto de configuración con valores por defectode la configuración de la ruta. Estos pueden ser remplazados por losmétodos del serviciocuando son ejecutados. Cada uno de las llaves: valor del objeto de configuración seránremplazados en los parámetros de la ruta. En caso que el objeto posea más parámetrosque la ruta los restantes serán añadidos como parte de la cadena query de la url. En casode que la url sea ‘/categoria/:slug’ y el objeto de parámetros sea {slug:’internet’, pagina:2}el resultado de la url sería el siguiente /categoria/internet?pagina=2.

Otra manera de especificar los parámetros de la ruta es utilizando un@ como prefijo delvalor. De esta forma el valor será extraído del cuerpo de la petición cuando es llamado.Por ejemplo, si la ruta es ‘/usuario/:id’ el objeto de configuración es {id: ‘@uid’} el id seráextraído del cuerpo de la petición data.uid.

El tercer parámetro que recibe el servicio $resource es opcional y es un objeto conla declaración de métodos personalizadas para extender las acciones por defecto delservicio. Este objeto se definirá de la forma {nombreMetodo:{objetoConfiguracion}}

Page 177: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 156

donde cada key será el nombre de cada método que añadiremos al servicio y el valor unobjeto de configuración del método. El objeto de configuración de cada método puedetener varios elementos que describiré a continuación.

• action: Cadena de texto con el nombre del nuevo método.• method: Cadena de texto con el tipo de petición que deberá hacer este método.(GET, POST, PUT, DELETE, JSONP, etc.)

• params: Objeto de parámetros para ser remplazados en la url, como el segundoparámetro que recibe el servicio $resource.

• url: Cadena de texto especificando una url a la que hacer la petición, si es especifi-cada se utilizará esta y no la especificada en el primer parámetro de $resource.

• isArray: Boolean, de ser verdadero se esperará una respuesta de tipo arreglo dejson, de lo contrario se esperará un objeto json. Es utilizado cuando se espera unalista de objetos.

• transformRequest: Función o arreglo de funciones que devolverán la respuestatransformada. Esta función recibe como parámetros el cuerpo de la petición y losheaders. Por lo general es utilizado para serializar el cuerpo de la petición.

• transformResponse: Función o arreglo de funciones como transformRequestpero para transformar la respuesta. Usualmente utilizado para de serializar elcuerpo de la respuesta.

• cache: Boolean o instancia de $cacheFactory, si es verdadero se utilizará el com-portamiento del servicio $http para hacer cache cuando la petición es de tipo GET.Si se utiliza una instancia de $cacheFactory será utilizada para hacer la cache. Encaso que sea falso no se hará cache de la petición.

• timeout: Número u objeto de tipo $promise. Si se utiliza un número será lacantidad de milisegundos, si se utiliza una promesa esta deberá abortar la peticióncuando esta sea resuelta.

• withCredentials: Boolean que definirá si se utiliza withCredentials en el objetoXHR.

• responseType: Cadena de texto que se utiliza para definir el XMLHttpRequestRes-ponseType en la petición con los diferentes tipos de respuesta que esperaremos.

• interceptor: Objeto como los interceptores del servicio $http detallado en elCapitulo 5.

Como has podido observar el servicio $resource brinda una gran flexibilidad y extensi-bilidad para interactuar con los recursos del servidor. Ahora vamos a ver un ejemplo decómo quedaría un objeto configurado.

Page 178: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 157

1 .factory('Mensajes', ['$resource', function ($resource) {

2 return $resource('/api/mensajes/:id', {id: '@mid'}, {

3 actualizar: {

4 method: 'PUT',

5 isArray: false,6 transformRequest: function (datos, headerFn) {

7 return JSON.stringify(datos);

8 },

9 transformResponse: function (datos, headerFn) {

10 return JSON.parse(datos);

11 },

12 cache: false,13 timeout: 2000,

14 withCredentials: true,15 responseType: 'json'

16 }

17 })

18 }])

El objeto de respuesta

El factory del ejemplo anterior devuelve una clase de tipo resource con varios métodosque permiten interactuar con el servidor de una manera muy sencilla. Los métodos quenos brinda esta clase son los siguientes, get, save, query, remove y delete. De estos cincoparámetros dos son de tipo GET, los demás de tipo POST yDELETE.

Métodos de tipo GET

Los dos métodos GET son get() y query(), estos esperan tres parámetros.

1. El primer parámetro es un objeto con los parámetros que se enviarán en la petición,estos pueden ser parámetros de la url o parámetros de query que serán codificadosen la url.

2. El segundo parámetro es una función que será llamada cuando la respuesta seasatisfactoria. Recuerden que se considera respuestas satisfactorias las que su códigode respuesta este entre los valores de 200 y 299.

3. El tercer parámetro es otra función, la cual será llamada en caso de que la respuestano sea satisfactoria.

La función que se ejecuta cuando la respuesta es satisfactoria recibe dos parámetros,el primero son los datos que solicitamos al servidor, y el segundo es una función para

Page 179: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 158

obtener los headers. La función para cuando no se obtiene una respuesta satisfactoriarecibe un único parámetro que es un objeto de error. Veamos un ejemplo usando elmétodo get().

1 app.controller('MensajesCtrl',['$scope', 'Mensajes',

2 function ($scope, Mensajes) {

3 $scope.seleccion = function (mid) {

4 Mensajes.get({id: mid}, function (data, headersFn) {

5 $scope.seleccionado = data;

6 })

7 }

8 }]);

Con este método que hemos añadido al $scope podemos llamarlo en la vista pasándoleel id del mensaje que queremos obtener y mostrarlo de forma independiente en otrasección.

1 <tbody >2 <tr ng-repeat="mensaje in mensajes" ng-click="seleccion(mensaje.mid)">3 <td>{{mensaje.usuario}}</td><td>{{mensaje.mensaje}}</td>4 </tr>5 </tbody>6 //... fin de la tabla

7 <div class="row" ng-show="seleccionado">8 <div class="col-md-12">9 <h2>Seleccionado:</h2>

10 <p>Usuario: {{seleccionado.usuario}}</p>11 <p>Mensaje: {{seleccionado.mensaje}}</p>12 </div>13 </div>

Elmétodo get() espera como respuesta un objeto json. En cambio, si necesitamos obteneruna lista de objeto deberemos usar el método query que espera como respuesta unarreglo de objetos. Este comportamiento esta pre definido en angular utilizando lapropiedad isArray con valor verdadero en la configuración del método.

Otros métodos

Los tres restantes métodos que proporciona el servicio $resource son save(), remove()y delete(). Estos esperan cuatro parámetros.

Page 180: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 159

1. El primer parámetro es un objeto con los parámetros que se enviarán en la petición,estos pueden ser parámetros de la url o parámetros de query que serán codificadosen la url.

2. El segundo parámetro es un objeto que será enviado como cuerpo de la petición.3. El tercer parámetro es una función que será llamada cuando la respuesta sea

satisfactoria. Recuerden que se considera respuestas satisfactorias las que su códigode respuesta este entre los valores de 200 y 299.

4. El cuarto parámetro es otra función, la cual será llamada en caso de que la respuestano sea satisfactoria.

El método save() es una petición de tipo POST. Es utilizado para crear nuevos recursosen el servidor y utiliza el segundo parámetro como cuerpo de la petición. Veamos unejemplo continuando con el servicio deMensajes anterior. Creare un formulario en lavista para nuevosmensajes, un nuevométodo en el $scope para hacer una peticiónPOSTcon el método save(). Si obtenemos una respuesta satisfactoria añadiremos la respuestaal arreglo de mensajes $scope.mensajes y limpiaremos el formulario.

1 $scope.nuevo = function () {

2 Mensajes.save({}, $scope.msg, function (res) {

3 $scope.mensajes.push(res);

4 $scope.msg = {};

5 });

6 }

Como primer parámetro enviaremos un objeto vacío ya que no necesitamos enviarningún parámetro en la url para realizar la petición. También podríamos obviar el primerparámetro y funcionaría de igual manera. Como segundo parámetro el cuerpo de lapetición que será el formulario con los datos. El tercer parámetro es la función que seejecutará si la respuesta es satisfactoria. De ser así agregaremos la respuesta como unnuevomensaje en la lista demensajes y limpiaremos el formulario dejándolo en unobjetovacío.

1 <div class="col-md-4">2 <h2>Nuevo mensaje</h2>3 <form class="form-horizontal" name="nuevoMensaje"

4 role="form" ng-submit="nuevo()">5 <div class="form-group">6 <input type="text" ng-model="msg.usuario"

7 class="form-control" placeholder="Usuario">8 </div>9 <div class="form-group">

Page 181: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 160

10 <textarea ng-model="msg.mensaje" class="form-control"

11 rows="3" placeholder="Mensaje"></textarea>12 </div>13 <div class="form-group">14 <button type="submit" class="btn btn-success">Enviar</button>15 </div>16 </form>17 </div>

Con lo anterior será suficiente para crear nuevos recursos en el servidor haciendopeticiones POST con el servicioMensajesmediante el método save()

Nos quedan dos métodos por detallar, delete() y remove(). Estos dos hacen la mismafunción y es enviar una petición al servidor de tipo DELETE. La única diferencia entreellos es que en el lenguaje JavaScript la palabra delete es una palabra reservada y enInternet Explorer puede ocasionar problemas de incompatibilidad. Para ver un ejemplodel uso de remove() vamos a agregar un enlace a cada mensaje para hacer una petición yeliminar el mensaje. Si la respuesta es satisfactoria utilizamos el índice del mensaje paraeliminarlo del arreglo y no tener que volver a pedir los mensajes al servidor.

1 $scope.eliminar = function (index) {

2 Mensajes.remove({id: $scope.mensajes[index].mid}, function () {

3 $scope.mensajes.splice(index,1);

4 });

5 }

De esta forma hacemos la petición de tipoDELETE en la cual especificamos en el primerparámetro un objeto de configuración especificando el id del mensaje que queremoseliminar.

1 <tbody>2 <tr ng-repeat="mensaje in mensajes">3 <td>{{mensaje.usuario}}</td><td>{{mensaje.mensaje}}</td>4 <td>5 <a ng-click="eliminar($index)">6 <i class="glyphicon glyphicon-minus"></i>7 </a>8 </td>9 </tr>

10 </tbody>

En la vista ejecutamos el método eliminar() pasando como parámetro el $index propor-cionado por la directiva ng-repeat.

Page 182: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 161

Ahora solo nos queda poder editarmensajes para cumplir con las funcionalidades básicasde un CRUD (Create-Retrieve-Update-Delete)(Crear-Obtener-Actualizar-Eliminar)

Creando el método update

Como estamos tratando con APIs RESTful necesitamos hacer peticiones de tipo PUTpara poder actualizar un elemento ya que las peticiones de tipo POST se utilizan paracrear nuevos recursos. El servidor estará esperando una petición con método PUT pararealizar la actualización de un elemento. Para lograrlo necesitaremos crear una nuevaacción en la declaración del servicio.

1 .factory('Mensajes', ['$resource', function ($resource) {

2 return $resource('/api/mensajes/:id',

3 {id: '@mid'},

4 {update: { method: 'PUT'}}

5 );

6 }])

Ahora disponemos del método update que realizará peticiones PUT. Actualizaremos elmétodo $scope.seleccion para obtener una referencia directa desde la lista demensajes ypoder editarlo en un formulario. Ya que el mensaje está disponible en la lista evitaremoshacer una petición innecesaria al servidor.

1 $scope.seleccion = function (index) {

2 $scope.actualizando = true;3 $scope.act = $scope.mensajes[index];4 }

He creado una nueva variable $scope.actualizando que definirá si se muestra o no elformulario de actualización, y otra variable $scope.act que contendrá el mensaje quequeremos actualizar. Este método lo ejecutamos en la vista con la directiva ng-clickpasándole como parámetro el $index del ng-repeat.

1 <td>{{mensaje.usuario}}</td>2 <td style="cursor: pointer" ng-click="seleccion($index)">3 {{mensaje.mensaje}}4 </td>

De esta forma ya tenemos disponible el mensaje listo para editar, creare un formularioque use como modelo el objeto $scope.act de la selección.

Page 183: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 162

1 <h2>Actualizar mensaje</h2>2 <form class="form-horizontal" role="form" ng-submit="actualizar()">3 <div class="form-group">4 <input type="text" ng-model="act.usuario"

5 class="form-control" placeholder="Usuario">6 </div>7 <div class="form-group">8 <textarea class="form-control" ng-model="act.mensaje"

9 rows="5" placeholder="Mensaje"></textarea>10 </div>11 <div class="form-group">12 <button type="submit" class="btn btn-success">Actualizar</button>13 </div>14 </form>

Definimos la directiva ng-submit del formulario a un método actualizar() que crearé acontinuación.

1 $scope.actualizar = function () {

2 Mensajes.update($scope.act, function (res) {

3 $scope.actualizando = false;4 })

5 }

Al hacer submit en el formulario ejecutará el método actualizar y mediante el servicioMensajes se ejecutará el método update() que creamos anteriormente en la confi-guración del servicio para poder hacer peticiones PUT. Si obtenemos una respuestasatisfactoria ocultamos el formulario de actualización estableciendo un valor falso en$scope.actualizando. Como detalle podemos agregar que cuando se ejecute el métodoeliminar y el formulario de actualizar este visible editando el mensaje que se quiereeliminar, desaparezca el formulario ya que se eliminará el mensaje y aun estaría en elformulario para editarlo.

1 $scope.eliminar = function (index) {

2 if ( $scope.actualizando && $scope.mensajes[index].mid == $scope.act.mid )

3 $scope.actualizando = false;4 Mensajes.remove({id: $scope.mensajes[index].mid}, function () {

5 $scope.mensajes.splice(index,1);

6 });

7 }

Page 184: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 163

Con los ejemplos anteriores completamos una pequeña aplicación capaz de realizar lastareas básicas que son Leer, crear, actualizar y eliminar datos en un servidor remoto.Como te habrás percatado, todas las operaciones anteriores se logran sin hacer recargasde la página. Todas las acciones del servicio ngResource al ser basadas en $http soncompletamente mediante AJAX. Aunque la aplicación es funcional y cumple el objetivopara lo que fue creada se pueden hacer algunas optimizaciones. Hasta el momentohemos utilizado los métodos que nos brinda los servicios creados con $resource. Ahoramejoraremos la aplicación utilizando los métodos que poseen las instancias del servicio.

Instancia de un recurso

Cuando ejecutamos una petición con un servicio de $resource, si la petición es satisfac-toria este nos devuelve unobjeto o una coleccióndependiendode la petición.Cada objetodevuelto es una instancia de la clase resource por lo que tendremos acceso a losmétodos$save, $remove y $delete así como los demás que hayamos creado en la definición delrecurso.

Para optimizar la aplicación lo primero que podemos hacer es utilizar la instancia de laclase resource que tenemos de cada uno de los mensajes, pare realizar las operaciones.Comenzaremos por el método eliminar, dejando de utilizar el servicio Mensajes yutilizando la instancia ejecutando el método $remove de la misma.

1 $scope.eliminar = function (index) {

2 if ( $scope.actualizando && $scope.mensajes[index].mid == $scope.act.mid )

3 $scope.actualizando = false;4 $scope.mensajes[index].$remove(function () {

5 $scope.mensajes.splice(index,1);

6 })

7 }

A continuación dejaremos de utilizar el método $scope.actualizar ya que podemosejecutar directamente update dentro del objeto. Cuando ejecutamos las acciones comométodos de la instancia de resource tendremos que utilizar un el símbolo $ comoprefijo. Así que en la vista en la directiva ng-submit del formulario de actualización locambiamos por act.$update()

1 <form class="form-horizontal" name="nuevoMensaje"

2 role="form" ng-submit="act.$update()">

De esta forma la aplicación está terminada. En resumen, la vista de quedaría como laimagen que se muestra a continuación.

Page 185: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 164

Aplicación de mensajes terminada

Ahora que ya dominamos el uso del servicio $resource es importantemencionar un últi-mo concepto. Las peticiones que se hacen a través del servicio son totalmenteAsíncronas,lo que quiere decir que en el momento en que se ejecuta la acción el servicio devolveráuna referencia vacía al recurso. Si tratamos de ejecutar acciones inmediatamente que seejecuta una petición los resultados no serán los esperados. Cuando el servicio obtienelos datos desde el servidor Angular rellenara la respuesta automáticamente. Teniendoen cuenta esto, en la aplicación que estábamos desarrollando anteriormente podríamosobtener los mensajes de la siguiente forma.

1 $scope.mensajes = Mensajes.query();

Ejecutándolo de esta forma no tendríamos problemas porque por el momento losmensajes solo semuestran en la vista yAngular refrescará la vista cuando la respuesta estélista. Pero si tratamos de acceder al primer mensaje inmediatamente después de hacer lapetición, obtendríamos un error.

1 $scope.mensajes = Mensajes.query();

2 console.log($scope.mensajes[0].mensaje);

Este código detendrá la carga de la aplicación con un TypeError: Cannot read property‘mensaje’ of undefined ya que en ese momento en que estamos accediendo a la propiedadmensaje aún no tenemos la respuesta lista.

Page 186: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 165

Aún nos quedan dos propiedades más que tratar con respecto a la instancia de un$resource. La primera es la propiedad $resolved, está siempre tendrá el valor falsemientras se ejecuta la petición. Cuando la petición es resuelta esta toma el valor true.Siempre que se resuelva la petición esta obtendrá valor verdadero, independientementede que la respuesta sea satisfactoria o no.

1 $scope.mensajes = Mensajes.query();

2 console.log($scope.mensajes.$resolved);

El código anterior imprimirá en la consola un valor falso ya que aún la petición se estáejecutando en el momento que se ha consultado el valor de $resolved.

1 Mensajes.query(function (datos) {

2 console.log(datos.$resolved);

3 });

El ejemplo anterior imprimirá en la consola un valor verdadero ya que la función seejecuta cuando se obtiene una respuesta satisfactoria.

La última propiedad que queda por describir es $promise. Esta es la promesa que seha utilizado para crear el $resource. Si la petición que se ejecuta tiene una respuestasatisfactoria la promesa es resuelta con la colección o la instancia del recurso. De no sersatisfactoria, la promesa es resuelta con un objeto de respuesta HTTP sin la propiedadresource. Volviendo a los ejemplos anteriores donde accedíamos al primermensaje antesde estar listo, veámoslo utilizando la promesa.

1 $scope.mensajes.$promise.then(function (data) {

2 console.log(data[0].mensaje);3 });

Esta propiedad $promise es especialmente utilizada en la propiedad resolve del métodowhen() del servicio $resourceProvider cuando estamos definiendo las rutas.

Trailing Slash

Por defecto en el servicio $resource cuando se hace una petición a un servidor remoto,angular elimina los slash al final de la dirección. Vamos a ver un ejemplo.

Page 187: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 10: Recursos 166

1 angular.module('app', ['ngResource'])2 .factory('res', function ($resource) {

3 return $resource('/resource/:id/');

4 })

5 .controller('AppCtrl', function ($scope, res) {

6 res.get({ id: 3 }, function (data) {

7 console.log('done');

8 })

9 });

En el ejemplo anterior se ha definido un nuevo factory con un resource apuntando ala dirección ‘/resource/:id/’ incluyendo el slash del final. Después en el controladorhacemos una petición al recurso con una id con valor 3. Si vemos este ejemplo en elnavegador, abrimos la consola del mismo y podemos observar en el error 404 que lapetición se ha hecho a la dirección ‘/resource/3’ sin el slash del final.

En la mayoría de los casos este comportamiento no nos será un problema, pero hayocasiones que los servicios RESTFul que estamos consumiendo, requieren que se hagala petición especificando este slash del final de la dirección.

A partir de la versión 1.3 de Angular tendremos la posibilidad de configurar estecomportamiento mediante el provider del servicio resource en el bloque de configuraciónde la aplicación. Para ello debemos especificar la propiedad stripTrailingSlashes delobjeto default a un valor falso.

1 angular.module('app', ['ngResource'])2 .config(function ($resourceProvider) {

3 $resourceProvider.defaults.stripTrailingSlashes = false;4 });

Ahora cuando hagamos alguna petición con el servicio $resource, este incluirá el slash delfinal de la dirección para que no existan problemas con los servidores que lo requieren.

Como has podido observar es muy fácil de utilizar el servicio ngResource de Angular ynos brinda una forma sencilla, pero a la vez muy potente para realizar aplicaciones quedependan de un backend remoto.

Page 188: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y ValidaciónCon la llegada de HTML5 los formularios en la web se vieron mejorados grande-mente con respecto al estándar anterior. En la actualidad los formularios son unaparte imprescindible de cualquier aplicación y la vía principal por la que el usuariointercambia información con el servidor. Dada la necesidad de la veracidad de los datosintercambiados con el servidor, la validación es la parte más importante si queremosobtener una información correcta y útil. Entre otras ventajas, una correcta validaciónde los datos antes de ser procesados ayuda al usuario a rectificar la información y evitahacer peticiones innecesarias al servidor.

AngularJS nos provee de una infraestructura completa para la validación de formularios.En este caso los enriquece permitiéndole declarar estado valido, invalido o modificadopara cada elemento. De esta forma podremos comprobar si los datos que ha introducidoel usuario son válidos antes de procesar la información o enviarlos al servidor. Élframework se basa en las reglas de validación de los elementos HTML5, así comodirectivas para validaciones queno existen aún en el estándarHTML, pero sonnecesariasen mucho de los casos que trabajamos con formularios.

Además de la validaciónHTML5AngularJSnos brinda directivas para validar elementosdel formulario incluso sin el uso de código extra. A lo largo de este capituló detallaré eluso de la validación con las reglas de HTML5 así como las directivas proporcionadaspor AngularJS y también la creación validación personalizada. Para comenzar veamosalgunas de las reglas de validación y su utilización.

Reglas de Validación

El elemento HTML <input> es el que recibe las reglas de validación en dos formas.Primero con la propiedad type donde especificando un tipo de elemento como email,number ourlAngularJS validará que sean correctos por su definición. La segunda formade validación esmediante propiedades como es required que hace que el elemento tengaal menos un valor. Esta última es una de las reglas más utilizadas y podemos utilizarlade dos formas. Una de ellas es especificándolo como el estándar de HTML5 como en elejemplo siguiente.

167

Page 189: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 168

1 ...

2 <input type="text" name="nombre" required>

3 <input type="password" name="password" ng-required="true">

4 ...

La otra vía para su uso es mediante la directiva ng-required la cual toma un valorverdadero. En cualquiera de los dos casos AngularJS validará que el elemento HTMLtenga algún contenido como valor antes de ser procesado.

Además de las reglas básicas deHTML5angular provee otras tres directivas de validaciónaplicable a la mayoría de los elementos. Estas reglas son las siguientes.

Valor mínimo

Para validar un elemento <input> y hacer que posea un valor mínimo de caracteresutilizaremos la directiva ng-minlength que recibe como valor un número.

1 ..2 <input type="text" name="nombre" ng-minlength=3>3 ..

Valor máximo

Al contrario de la directiva anterior la directiva ng-maxlength es definida para validarun máximo de caracteres.

1 ...

2 <input type="text" name="nombre" ng-maxlength=15>

3 ...

Expresión regular

La directiva ng-pattern asegura que el valor cumpla con una expresión regular deJavaScript para que este sea válido.

1 ...

2 <input type="text" name="nombre" ng-pattern="/a-zA-Z/">

3 ...

Creando una regla de validación

Además de estas reglas de validaciónAngularJS nos permite crear nuestras propias reglaspara alguna especificidad de nuestra aplicación. A continuación, crearemos una reglapara validar que el email introducido por el usuario es único en un arreglo de correos.

Page 190: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 169

Archivo: index.html

1 ...

2 <body ng-controller="mainController">3 <h1>Formularios y Validación</h1>4 Nombre <input type="email" unique="isUnique" ng-model='email'><br>5 </body>6 ...

En esta vista estamos definiendo un elemento <input> de tipo email lo cual tiene supropia validación. Además, estamos haciendo uso de la directiva unique que crearemosa continuación y le estamos pasando un valor isUnique que será un método del $scopeque definiremos en el controlador, este comprobará si el email es único o ya está en elarreglo de email. También estamos utilizando la directivang-modelpara poder utilizarloen la directiva.

Archivo: app.js

1 ...

2 app.directive('unique', [

3 function() {

4 return {

5 require: 'ngModel',

6 link: function(scope, elem, attrs, ctrl){

7 var original;

8 ctrl.$formatters.unshift( function(modelValue) {

9 original = modelValue;

10 return modelValue;

11 });

12 ctrl.$parsers.push(function(val){13 if (val && val !== original) {

14 ctrl.$setValidity( 'unique' , scope[attrs.unique](val));

15 }

16 return val;

17 })

18 }

19 };

20 }

21 ]);

22 ...

En la directiva unique hacemos que la directiva ng-model sea requerida para poderacceder al controlador de esemodelo. En la función de la directiva primero obtenemos el

Page 191: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 170

valor del modelo y lo guardamos en la variable original luego añadimos una función enel arreglo $parsers que será la encargada de definir si el modelo es válido o no medianteel método $setValidity del controlador del modelo. Esta función recibe dos parámetrosel primero es la llave por la que llamaremos a esta regla de validación a la hora de ver loserrores y el segundo es el valor verdadero o falso de la regla, en este caso ejecutamos lafunción del controlador con el valor como parámetro para analizar si es único o no. Estafunción es definida en el controlador.

Archivo: app.js

1 ...

2 app.controller('mainController', ['$scope', function ($scope) {

3 var mailList = [

4 '[email protected]',

5 '[email protected]',

6 '[email protected]'

7 ];

8 $scope.isUnique = function(val){9 var res;

10 for (var i = 0; i < mailList.length; i++) {

11 if (mailList[i] == val) {

12 res = false;13 break;14 } else res = true;15 }

16 return res;

17 };

18 }]);

19 ...

En el controlador tenemos definido un arreglo mailList con las direcciones de correoque no deben ser utilizadas por el usuario. Exponemos al $scope el método isUniqueque es el que se ejecutará para comprobar si el email introducido por el usuario es único.Estemétodo recibe como parámetro el valor del elemento <input> proporcionado por ladirectiva, itera sobre el arreglo de correos y devuelve un valor verdadero o falso si existeo no el correo.

Mejoras creando reglas de validación

Apartir de la versión 1.3 deAngular, se definió una nueva propiedad llamada$validatorspara crear reglas de validación personalizadas. Anteriormente necesitábamos hacer uso

Page 192: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 171

de las propiedades $parsers y $formatters para crear una regla. La nueva vía para crearreglas de validación hace que el proceso sea mucho más sencillo y fácil de implementar.

A continuación, vamos a crear un ejemplo donde tendremos dos campos de fecha paraun evento, uno fecha de inicio y otra fecha fin. Necesitaremos una directiva para validarque la fecha de fin no sea posterior a la fecha de inicio del evento. Además, pondremosun mensaje de validación para cuando falle la validación.

1 ...

2 <form name="formulario">3 Inicio:

4 <input type="date" ng-model="inicio" name="inicio"><br><br>5 Fin:

6 <input type="date" ng-model="fin" name="fin" validador-rango="inicio">7 <span ng-if="formulario.fin.$dirty" ng-messages="formulario.fin.$error">8 <span ng-message="fueraRango">9 La fecha de fin de evento no puede ser anterior a la de inicio

10 </span>11 </span>12 </form>13 ...

Para continuar debemos definir en el controlador los valores por defecto de los elemen-tos del formulario y pasamos a crear la directiva. Recuerden al nombrar la directiva, debetener un nombre en notación de camello (Camel Case) para poder utilizarla en la vistaseparado por guiones (Snake Case). Restringimos la directiva para que solo pueda serutilizada como atributo de un elemento. Requerimos el modelo para crear la regla devalidación. Definimos el scope con la propiedad fechaInicio igual a el valor de la directivay pasamos a crear la función link.

1 angular.module('app',['ngMessages'])2 .controller('ctrl', ['$scope', function($scope){

3 $scope.inicio = new Date();4 $scope.fin = new Date();5 }])6 .directive('validadorRango', function(){7 return {

8 restrict: 'A',

9 require: 'ngModel',

10 scope: { fechaInicio: '=validadorRango'},

11 link: function(scope, element, attrs, ngModel) {

12 ngModel.$validators.fueraRango = function(val){13 return Date.parse(val) >= Date.parse(scope.fechaInicio);

Page 193: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 172

14 };

15 scope.$watch('fechaInicio', function(){16 ngModel.$validate();

17 });

18 }

19 }

20 });

En la función link de la directiva inyectamos el modelo para poder acceder a la nuevapropiedad de los validadores. En esta definimos un nuevo validador con el nombrefueraRango, este es el nombre que será utilizado en el objeto $error para saber si lavalidación ha sido correcta o no. Esta función acepta el primer parámetro que es el valorintroducido en el elemento. Para comprobar si la fecha es posterior o no simplementeutilizamos el método parse del objeto Date de Javascript y lo comprobamos con el de lafecha de inicio. Este valor lo devolvemos ya que será un verdadero o false dependiendode los valores de los elementos. De esta forma ya está listo el validador para las fechas.

Como puede pasar que en vez de cambiar la fecha de fin puede que se cambie la fechade inicio, es necesario observar los cambios en el elemento de la fecha de inicio y encada cambio volver a validar la fecha de fin. Para esto implementamos un $watch en elelemento fechaInicio del scope y volvemos a validar el modelo en caso de cambios.

Como habrás podido notar utilizando la nueva propiedad $validators del modelo paradefinir nuevas reglas de validación, hace que el proceso sea mucho más simple y fácil deimplementar.

Ejecutando validación asíncrona

En versiones de Angular anteriores a 1.3 realizar la validación de un elemento medianteun servidor remoto era un poco trabajoso. En esta nueva versión se ha definido unanueva propiedad para los validadores asíncronos. La propiedad $asyncValidators es unarreglo de validadores que se ejecutaran demanera asíncrona y siempre después de haberejecutado los validadores síncronos.

Ahora para detallar bien el proceso vamos a crear una directiva para evitar que unusuario se registre dos veces en la aplicación con la misma dirección de correo. Paraello necesitaremos un servidor que permita comprobar si la dirección de correo existey devolvernos una respuesta para la validación. La directiva que crearemos será laencargada de hacer la petición y validar dependiendo de la respuesta del servidor. Paraesta directiva necesitamos el servicio de promesas $q y el servicio para comunicarnoscon el servidor $http. Es importante mencionar que todos los validadores registradoscomo asíncronos deben devolver una promesa. Si la promesa es rechazada el validadorfallará y si se resuelve la validación será verdadera.

Page 194: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 173

1 ...

2 <form name="formulario">3 Correo:

4 <input type="email" ng-model="email" name="email" validar-email-duplicado>5 <span ng-if="formulario.email.$dirty" ng-messages="formulario.email.$error">6 <span ng-message="emailDuplicado">7 Esta direccion de correo está siendo utilizada por otro usuario

8 </span>9 </span>

10 </form>11 ...

En el formulario anterior se ha definido un elemento de tipo email y este utiliza ladirectiva validar-email-duplicado. Además, se ha incluido un mensaje de error con elmodulo ngMessages para el error emailDuplicado. Ahora vamos a crear la directiva quevalide el email de forma asíncrona con el servidor.

1 .directive('validarEmailDuplicado', [ "$q", "$http",

2 function($q, $http){

3 return {

4 restrict: 'A',

5 require: 'ngModel',

6 link: function(scope, element, attrs, ngModel) {

7 ngModel.$asyncValidators.emailDuplicado = function (val) {

8 var def = $q.defer();

9 $http.get('/emails', {params:{email: val}}).then(function(res){

10 def.reject("Existe ese email en la base de datos.");

11 }).catch(function(err){

12 def.resolve();

13 });

14 return def.promise;

15 }

16 }

17 }

18 }]);

Primero que todo necesitamos inyectar los servicios $q y $http para crear el funcio-namiento. Restringimos la directiva a que solo pueda ser utilizada como atributo einyectamos el modelo para acceder al objeto ** $asyncValidators*. En la función linkde la directiva utilizamos el modelo para crear el nuevo validador *emailDuplicado. Primerocreamos un objeto defer y pasamos a hacer la petición al servidor enviando por métodoGET la dirección email que ha introducido el usuario. Si esta petición devuelve una

Page 195: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 174

respuesta satisfactoria el email ya existe en el servidor, por lo que tendremos que rechazarla promesa para que falle el validador. Si la respuesta es un error 404 resolveremos lapromesa. Para finalizar devolvemos la promesa.

Las formas de comprobar las respuestas para rechazar o resolver la promesa puedenvariar dependiendo del servidor, pero en esencia el comportamiento de la directiva estáclaro. Debes considerar el uso de la directiva ng-model-options para actualizar el modelosolo cuando el usuario deja el campo. De lo contrario en cada vez que se cambie el valorse hará una comprobación, lo que quiere decir que se realizaran muchas peticiones alservidor que son innecesarias.

El formulario

Hasta el momento hemos aprendido a validar los elementos <input> pero aún no hemoshablado del formulario. En el momento en que declaramos un formulario, AngularJSenriquece este con varios estados que podremos utilizar para dar información al usuariode la veracidad de sus datos. Para poder referirnos al formulario este debe tener definidala propiedad name la cual será automáticamente definida en el $scope para referirnos aeste.

Estados del formulario

AngularJS definirá automáticamente cuatro estados en el formulario dependiendo de losdatos introducidos por el usuario. Los estados son los siguientes.

• $valid: Devolverá verdadero o falso dependiendo de si el contenido del formularioes válido. Cada uno de sus elementos <input> debe ser válido.

• $invalid: Devolverá verdadero o falso dependiendo de si el contenido del formu-lario es erróneo. Si alguno de los elementos <input> es erróneo el resultado de esteserá falso.

• $dirty: Devolverá verdadero si el usuario ha interactuado con el formulario intro-duciendo algún dato o modificando alguno de los que están de lo contrario tendrávalor falso.

• $pristine: Devolverá verdadero si el usuario no ha interactuado con el formularioaún. Desde que el usuario introduzca algún dato devolverá falso.

• $submitted: En la versión 1.3 de Angular se añadió un nuevo estado al formulario.Este estado nos devolverá verdadero o falso dependiendo si el formulario ha sidoprocesado o no.

Estos estados serán propiedades del formulario desde el momento en que lo definamoscon un nombre para referirnos a él. Veamos un ejemplo donde mostramos los estados.

Page 196: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 175

1 ...

2 <form name="formulario">3 Nombre:

4 <input type="text"

5 ng-minlength=3

6 ng-maxlength=10

7 ng-model="nombre"

8 name="nombre">9 </form>

10 Válido: <strong>{{formulario.$valid}}</strong><br>11 Errores: <strong>{{formulario.$invalid}}</strong><br>12 Modificado: <strong>{{formulario.$dirty}}</strong><br>13 No Modificado: <strong>{{formulario.$pristine}}</strong>14 </div>15 ...

Estilos en el formulario

Mediante estos estados podremos comprobar si el formulario es válido o si ha sidomodificado. Ahora que tenemos control sobre estos estados podremos informar alusuario en caso de que haya introducido datos incorrectos. AngularJS define en cadaelemento del formulario unas clases CSS que permiten que muestres al usuario algúntipo de información respecto a los datos que está introduciendo. Un ejemplo clásico deesto es cambiar el borde del <input> a rojo cuando el dato introducido es incorrecto ousar un color verde cuando lo está.

Las clases que define Angular en cada elemento son las siguientes:

• ng-valid: Cuando todas las reglas aplicadas al elemento son válidas.• ng-invalid: Cuando alguna de las reglas aplicadas al elemento es inválida.• ng-pristine: Cuando el elemento no ha sido modificado.• ng-dirty: Cuando el elemento ha sido modificado de alguna forma.

A continuación, vamos a ver un ejemplo del uso de estas clases para mostrar al usuariosi sus datos son válidos.

Page 197: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 176

Archivo: index.html

1 ...

2 <form name="formulario">3 Nombre:

4 <input type="text"

5 ng-minlength=3

6 ng-maxlength=10

7 ng-model="nombre"

8 name="nombre">9 Email: <input type="email" ng-model="email" name="email">

10 </form>11 ...

En el formulario anterior definimos dos elementos, uno para el nombre y otro para elcorreo. El primero tiene dos tipos de validación donde especificamos que el mínimo decaracteres es de tres y un máximo de 10. Para el segundo solo especificamos que es detipo email y el framework lo validará como una dirección de correo.

Haciendo uso de las reglas CSS que le añade AngularJS a cada elemento dependiendo desu validación, podemos especificar en el archivo de estilos algunas reglas para mostrar alusuario un feedback.

Archivo: app.css

1 ...

2 input.ng-invalid.ng-dirty {

3 border: 2px solid #e51c23;

4 background-color: #ff5177;

5 }

6

7 input.ng-valid.ng-dirty {

8 border: 2px solid #259b24;

9 background-color: #5af158;

10 }

11 ...

En el código anterior declaramos dos reglas CSS para los elementos <input> una paralos que tengan las clases ng-invalid donde le ponemos un borde y fondo rojo. Otra paralos que tienen la clase ng-valid con un borde y fondo verde. Ambas tienen que tener a suvez la clase ng-dirty por qué no tendría sentido quemostráramos colores antes de habertocado los controles.

El ejemplo anterior nos devolverá algo como la siguiente imagen.

Page 198: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 177

Uso de clases CSS en la validación de formularios

En la imagen tenemos seis controles, tres para nombre y tres para correo. La primera filade nombre y correo han sido introducidos correctamente por lo que reciben un colorverde. La segunda fila es todo lo contrario, se han introducido datos erróneos ya que elnombre debe tener al menos 3 caracteres y la dirección de correo es incorrecta. En latercera fila el nombre ha sido modificado, y aunque se elimine su contenido y quede enblanco continuará con la clase ng-dirty por lo que toma el color verde al ser válido, notoma color rojo ya que no es requerido, si hubiésemos puesto una regla de validaciónrequired en ese elemento tomaría color rojo. El último elemento correo está en colorpor defecto ya que no se ha tocado aun y tiene la clase ng-pristine.

Además de las cuatro clases antesmencionadasAngularJS también añade clases para cadaunade las validaciones.Un ejemplo de lo antesmencionado lo podemos ver en el segundocampo nombre de la imagen. Este campo además de tener las clases que hemos discutidoantes también tiene una clase ng-invalid-minlength, a su vez en campo de correo desu derecha tiene la clase ng-invalid-email. AngularJS declara clases para cada una delas validaciones que hayamos especificado en cada elemento. Estas son declaradas con elsiguiente patrón: ng-invalid-regla o ng-valid-regla. Este comportamiento está para lasreglas que trae el framework como para las que creemos nosotros para la aplicación quedesarrollamos. Un ejemplo de esto es cuando creamos la regla unique anteriormente,el elemento obtenía una clase ng-invalid-unique o ng-valid-unique dependiendo deldato introducido.

Mostrando errores de validación

Aunque mostrar este tipo de aviso al usuario es un buen comienzo, no es lo suficientebueno. En elementos que tengamos varias reglas de validación mostraríamos solo colorpara válido o inválido. Aun así, el usuario no puede saber qué es lo que está incorrectoen el dato introducido. Para una correcta comunicación con el usuario debemosmostrarun mensaje de error por cada una de las validaciones en cado de error.

Para lograr lo antes mencionado AngularJS define propiedades para cada uno de loselementos del formulario en el siguiente formato.

1 formulario.input.propiedad

Page 199: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 178

El primer objeto es el nombre del formulario por el que se creó en el $scope recuerdenque a través de este objeto podemos tener acceso a las propiedades $dirty, $pristine,$valid e $invalid. El segundo nivel es el nombre del elemento <input> al que queremosacceder. El tercero es el nombre de la propiedad definida por AngularJS, por cadaelemento también tenemos acceso a las propiedades antes mencionadas del formulario.En otras palabras, podemos comprobar si es válido o ha sido modificado un elementoespecífico del formulario.

Ahora necesitamosmostrarmensajes de error para que el usuario conozca cual es el erroren la información introducida. Para lograrlo AngularJS define una propiedad $error encada elemento del formulario. Esta propiedad es un objeto que tendrá una propiedadcon el nombre de cada regla que se haya validado de forma incorrecta. Utilizando esteobjeto podemos mostrar mensajes de validación individuales para cada elemento y paracada regla. En el siguiente ejemplo veremos cómo mostrar errores dependiendo de cadauno de los errores de validación. Para lograrlo solo haremos uso de HTML y CSS, paraobtener una vista más atractiva puedes utilizar el framework CSS: Twitter Bootstrap.

Para comenzar definimos el formulario, es importante asignarle un nombre ya quemediante este nos referiremos a él para poder mostrar los errores de validación.

1 ...

2 <form name="form" novalidate>3 <input type="submit"

4 class="btn btn-info"

5 ng-disabled="form.$invalid">6 Crear

7 </input>8 </form>9 ...

En el botón hemos hecho uso de la directivang-disabled para deshabilitar el botón y queel formulario no sea enviado hasta que sea completamente válido utilizando la propiedad$invalid del formulario. A continuación, definiremos el <input> para introducir elnombre y le asignaremos algunas reglas de validación. También mediante la directivang-class aplicaremos los estilos CSS de Bootstrap para los elementos de formularios.Cuando el valor es válido se le aplicará la clase has-success y has-error cuando tengaerrores.

Page 200: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 179

1 ...

2 <div class="form-group" ng-class="{

3 'has-error': (form.nombre.$invalid && form.nombre.$dirty),

4 'has-success': form.nombre.$valid && form.nombre.$dirty}">5 <label>Nombre</label>6 <input type="text" class="form-control" name="nombre"

7 ng-minlength=3 ng-maxlength=15 required ng-model="nombre">8 </div>9 ...

Al <input> nombre se le ha aplicado varias reglas de validación como son un mínimode tres caracteres y un máximo de quince. Además, se ha especificado que este esrequerido. Ahora especificaremos un mensaje de error para cada una de las validacionesanteriores. Utilizaremos la directiva ng-show para mostrar cada error independiente siestá definido en la propiedad $error.

1 ...

2 <p class="text-danger" ng-show="form.nombre.$error.minlength">3 El campo <strong>Nombre</strong> debe tener al menos 3 caracteres

4 </p>5 <p class="text-danger" ng-show="form.nombre.$error.maxlength">6 El campo <strong>Nombre</strong> excede el máximo de 15 caracteres

7 </p>8 <p class="text-danger"

9 ng-show="form.nombre.$error.required && form.nombre.$dirty">10 El campo <strong>Nombre</strong> es requerido

11 </p>12 ...

Como pueden observar se ha utilizado cada una de las propiedades de $error paramostrar los mensajes de cada una de las validaciones de forma independiente. Ahorapara campo de correo lo realizaremos de la misma forma.

Page 201: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 180

1 ...

2 <div class="form-group"ng-class="{

3 'has-error': form.email.$invalid && form.email.$dirty,

4 'has-success': form.email.$valid && form.email.$dirty}">5 <label>Correo</label>6 <input type="email" class="form-control" name="email"

7 required ng-model="email">8 </div>9 <p class="text-danger" ng-show="form.email.$error.email">

10 La dirección de <strong>Correo</strong> es incorrecta

11 </p>12 <p class="text-danger"

13 ng-show="form.email.$error.required && form.email.$dirty">14 El campo <strong>Correo</strong> es requerido

15 </p>16 ...

Con el ejemplo anterior obtendremos un resultado como el de la siguiente imagen.Debajo de cada uno de los <input> se muestra el error que definimos para cada una delas reglas de validación.

Uso de la propiedad $error de cada elemento input

Estado de los elementos de formulario

Además de los estados que posee el formulario, los elementos del formulario poseendos estados añadidos en la versión 1.3 del framework. Estos estados son $touched y$untouched, Es importante mencionar que, aunque su nombre tenga que ver con lapalabra touch esto no significa que sea para pantallas táctiles.

Estas dos nuevas propiedades tendrán un valor verdadero o falso los cuales serándefinidas en elmomento que el usuario entre a un elemento y salga de él. Específicamente

Page 202: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 181

el elemento obtendrá el valor verdadero en la propiedad $touched en el momento en queel elemento pierda el foco. A su vez la propiedad $untouched recibirá el valor falso.

Hasta el momento habías utilizado la propiedad $dirty para mostrar los errores devalidación. Pero para los elementos requeridos la propiedad $dirty no es la más idealya que el usuario puede entrar y salir del elemento sin modificarlo y no se mostraría elerror de requerido. Si en ese caso utilizamos la propiedad $touched, aunque el usuario noescriba nada en el elemento, al salir estemostrará los errores de validación ya que ha sidotocado.

Mostrando errores con ngMessages

Como habrás podido comprobar, mostrar errores de validación es una tarea un pocoengorrosa por la gran cantidad de repetición de código que se necesita. Para mostrarcorrectamente los errores hasta el momento hemos necesitado una serie de ng-showpara cada uno de los errores. Además, hemos estado repitiendo constantemente elnombre del formulario, el nombre del elemento, el objeto $error en cada una de lasvalidaciones. Existe una vía para solucionar estos problemas y es la que describiré acontinuación.

Con la llegada de Angular 1.3 fue creado un módulo que soluciona el problema a la horademostrar errores de validación. Este módulo no forma parte del núcleo de Angularlo que quiere decir que tendremos que instalarlo de manera independiente, e incluirlocomo un script en la aplicación, así como en las dependencias del módulo que estasdesarrollando. Veamos los pasos uno a uno.

Para comenzar lo primero es obtener elmódulo. Este lo podemos obtener por lasmismasvías que obtenemos Angular. Para este ejemplo utilizaremos bower* para simplificar elproceso. En la consola vamos hasta el directorio donde se encuentra nuestra aplicacióny ejecutamos el siguiente comando.

1 bower install angular-messages

Bower se encargará de obtener el módulo por nosotros y lo pondrá en directoriode los componentes de bower. Ahora necesitamos incluirlo en nuestra aplicación. Esimportante que se incluya después de haber incluido el framework.

1 ...

2 <script src="bower_components/angular/angular.js"></script>3 <script src="bower_components/angular-messages/angular-messages.js"></script>4 ...

Ahora que ya lo tenemos disponible en la aplicación, es necesario añadirlo como de-pendencia del módulo que estamos desarrollado. Para ellos vamos a la declaración delmódulo y especificamos como dependencia ngMessages.

Page 203: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 182

1 <script>2 angular.module('app',['ngMessages'])

3 .controller('ctrl', ['$scope', function($scope){

4

5 }]);

6 </script>

Con este nuevo módulo en la aplicación, ganamos 3 nuevas directivas que explicaremosa continuación. Anteriormente para mostrar los mensajes de validación teníamos queimplementar mensajes basados en la directiva ng-show y las propiedades del objeto $errorde cada elemento. Ahora Con la nueva directiva ng-messages podemos especificar elobjeto $error de un elemento. Después anidado dentro podremos especificarmensajes devalidación para cada uno de los errores de validación utilizando la directivang-message.

Vamos a ver un ejemplo donde utilizamos el nuevo soporte para los inputs de tipo date.En este especificaremos una fecha mínima y una fecha máxima, además lo haremosrequerido. Para este elemento mostraremos mensajes de validación para cada uno delos errores.

1 <body ng-controller="ctrl">2 <form name="formulario">3 Fecha:

4 <input type="date"

5 min="2015-09-22"

6 max="2015-10-22"

7 ng-model="fecha" name="fecha" required>8 <span ng-if="formulario.fecha.$dirty" ng-messages="formulario.fecha.$error">9 <span ng-message="required">

10 La fecha es requerida</span>11 <span ng-message="min">12 La fecha debe ser posterior a 2015-09-22</span>13 <span ng-message="max">14 La fecha debe ser antes de 2015-10-22</span>15 </span><br><br>16 Correo:

17 <input type="email"

18 minlength="3"

19 required

20 ng-model="email" name="email" >21 <span ng-if="formulario.email.$dirty" ng-messages="formulario.email.$error">22 <span ng-message="required">23 El correo es requerido</span>24 <span ng-message="minlength">

Page 204: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 183

25 El correo debe terner al menos 3 caracteres</span>26 <span ng-message="email">27 El correo es inválido</span>28 </span>29 </form>30 <script src="bower_components/angular/angular.js"></script>31 <script src="bower_components/angular-messages/angular-messages.js"></script>32 <script>33 angular.module('app',['ngMessages'])

34 .controller('ctrl', ['$scope', function($scope){

35 $scope.fecha = new Date();

36 }]);

37 </script>38 </body>

En el ejemplo anterior tenemos dos inputs dentro del formulario, uno de tipo date yotro de tipo email. Debajo de cada elemento tenemos una etiqueta span donde se definela directiva ng-messages especificando el objeto error del elemento. Anidados dentrotenemos una serie de span definiendo la directiva ng-message con solo el nombre delerror dentro del objeto $error. Estos mensajes aparecerán en el orden en que se hayandefinido. Es importante mencionar que para que funcione, los elementos de formularionecesitan un modelo definido con la directiva ng-model.

En algunas ocasiones necesitamos que se muestren varios errores de validación a lavez. Por ejemplo, en el elemento del correo posee un límite mínimo de 3 caracteres,si escribimos solo dos letras en elemento, la validación para mínimo y para correo sedispararán. Pero por el comportamiento por defecto que tiene la directiva ng-messagessolo mostrara el primer error de validación. Para solucionar este problema podremosutilizar una tercera directiva que proporciona el módulo ngMessages y es ng-messages-multiple. Esta directiva debe estar en el mismo elemento donde se ha utilizado ng-messages.

1 ...

2 <span

3 ng-if="formulario.email.$dirty"

4 ng-messages="formulario.email.$error"

5 ng-messages-multiple>

6 ...

En resumen, la directiva ng-messages** observa los cambios en el objeto error del elementode formulario especificado. Esta va activando y desactivando los mensajes de error dependiendodel orden definido en los mensajes. La directiva *ng-message debe ser definida como hijo delelemento que posee la directiva ng-messages y es la encargada de mostrar y ocultar un

Page 205: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 184

mensaje de validación específico. La directiva ng-messages-multiple debe ser definida en elelemento que posee la directiva ng-messages, esta añadirá el comportamiento de mostrarvarios mensajes de error a la vez.

Reusando mensajes de validación

Con el uso del módulo ngMessages es posible reutilizar mensajes de validación genéricos.En casos que en tu aplicación no sea tan importante utilizar mensajes genéricos en lavalidación, como son los mensajes para un elemento que es requerido o que el elementodebe tener un mínimo de caracteres específico. Para estos casos tendremos una nuevadirectiva que permitirá incluir un archivo HTML con la definición de los mensajesgenéricos. De esta forma podríamos ahorrarnos gran cantidad de código repetido.

Para hacer uso de esta funcionalidad necesitamos crear un archivo HTML con losmensajes definidos. Este archivo es incluidomediante la directiva ng-messages-includeque recibe como valor la dirección del archivo a incluir. Veamos un ejemplo de su uso.

Primero creamos un archivo con el nombre errores.html con los siguientes errores comocontenido.

1 <span ng-message="required">2 Este elemento es requerido.

3 </span>4 <span ng-message="minlength">5 No ha completado el mínimo de caracteres para este elemento.

6 </span>7 <span ng-message="maxlength">8 Ha sobrepasado el máximo de caracteres para este elemento.

9 </span>

A continuación, incluimos el archivo errores.html con la directiva ng-messages-include.

1 <body ng-controller="ctrl">2 <form name="formulario">3 Correo:

4 <input type="email"

5 minlength="3"

6 required

7 ng-model="email" name="email" >8 <span9 ng-if="formulario.email.$dirty"

10 ng-messages="formulario.email.$error"

Page 206: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 185

11 ng-messages-include="errores.html"

12 ng-messages-multiple>13 <span ng-message="email">El correo es inválido</span>14 </span>15 </form>16 <script src="bower_components/angular/angular.js"></script>17 <script src="bower_components/angular-messages/angular-messages.js"></script>18 <script>19 angular.module('app',['ngMessages'])

20 .controller('ctrl', ['$scope', function($scope){}]);

21 </script>22 </body>

Como habrás podido observar he dejado un mensaje de error para la comprobacióndel correo ya que este no funcionaría para otros elementos. Es importante mencionarque además de los mensajes incluidos, la directiva ng-messages mostrará con prioridadlos mensajes definidos como hijos. Esto nos da la posibilidad de poder reemplazarmensajes específicos para un elemento. Estemismo archivo lo podemos reutilizar en otroelemento que requiera mensajes de validación para un mínimo y máximo de caracteresy sea requerido.

Soporte para nuevos elementos de HTML5

Con la llegada de HTML5 obtuvimos nuevos tipos de input de los cuales hemos esta-do sirviéndonos. En la nueva versión de Angular se ha ampliado el soporte para loselementos de formularios. Concretamente en la versión 1.3 se añadió soporte para loselementos de tipodate, time,datetime-local,month yweek. En versiones anteriores delframework teníamos que utilizar un elemento de tipo texto para las fechas, no habíamospodido utilizar los controles que brinda el navegador ya queAngular no brindaba soportepara estos elementos.

Elementos date, month y datetime-local en Google Chrome

Page 207: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 186

En muchas ocasiones cuando trabajamos con fechas, estas son obtenidas desde un APIremoto donde se reciben en un formato JSON. En esta nueva versión de Angular parapoder utilizar los elementos de HTML5 es necesario convertir estas fechas desde elformato String hacia un objeto Date de Javascript. De lo contrario Angular tirara unerror y los campos serán mostrados como una caja de texto.

Supongamos que estas creando un API para el control de tareas de donde estas sonalmacenadas en formato JSON. El Estándar JSON no tiene un formato definido para lasfechas lo que quiere decir que estas serán almacenadas como cadenas de texto. Veamosun ejemplo de JSON el cual utilizaremos más adelante.

1 {

2 "tareaId": 171,

3 "nombre": "Evento Circle",

4 "descripcion": "Participar en el evento Circle 2015",

5 "fechaInicio": "09-23-2015",

6 "fechaFin": "09-25-2015"

7 }

Como podrás observar en el JSON anterior tenemos fechaInicio y fechaFin los cualesson dos fechas. Para poder utilizar elementos date en la aplicación para mostrar estasfechas, primero necesitamos convertirlos en objetos Date de Javascript. Para ello utiliza-remos el constructor de la clase Date.

1 <body ng-app="app">2 <div style="margin: 0 auto; width: 50%" ng-controller="ctrl">3 <h1>Tarea</h1>4 <p>Nombre: {{tarea.nombre}}</p>5 <p>Descripción: {{tarea.descripcion}}</p>6 <p>Inicio: <input type="date" ng-model="tarea.fechaInicio"> </p>7 <p>Fin: <input type="date" ng-model="tarea.fechaFin"> </p>8 </div>9

10 <script src="bower_components/angular/angular.js"></script>11 <script>12 angular.module('app', [])

13 .controller('ctrl', function($scope){

14 $scope.tarea = {

15 "tareaId": 171,

16 "nombre": "Evento Circle",

17 "descripcion": "Participar en el evento Circle 2015",

18 "fechaInicio": "09-23-2015",

19 "fechaFin": "09-25-2015"

Page 208: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 187

20 };

21 $scope.tarea.fechaInicio = new Date($scope.tarea.fechaInicio);

22 $scope.tarea.fechaFin = new Date($scope.tarea.fechaFin);

23 });

24 </script>25 </body>

En el ejemplo anterior se ha asignado una tarea al $scope una tarea y posteriormente sehan convertido las fechas a objetos Date de Javascript con el constructor. De esta formapodemos hacer uso de los elementos input de tipo date que aparecen en la vista.

Validación de HTML5

En versiones 1.2.x de AngularJS existe soporte para alguno de las validaciones deHTML5. En la nueva versión del framework la validación deHTML5 está completamen-te soportada. Ya no sería necesario utilizar las directivas ng-minlength y ng-maxlength paradefinir la cantidadmínima ymáxima de caracteres. Ahora todos los errores de validaciónestán correctamente unidos a la propiedad $error de cada elemento del formulario.

En esta nueva versión podremos utilizar la validación de HTML5 como min, max,minlength, maxlength y pattern. Ahora que disponemos de soporte para elementos deformulario tipo date la validación min y max serán de gran ayuda para definir errorespara un mínimo y un máximo de fechas. Para lograrlo, ahora disponemos de la nuevapropiedad date en el objeto **$error$ del elemento de formulario.

1 <span class="error" ng-show="formulario.fecha.$error.date">

Otras formas de validación

Ya hemos visto como validar el formulario, pero en la versión 1.3 de Angular se introdujouna nueva directiva que va a hacer aún más sencillo la validación. Esta nueva directivaes ng-model-options y nos permite definir algunas opciones para la forma en quequeremos que se actualice el modelo. Como parámetro recibe un objeto con varioselementos de configuración. Vamos a detallarlos y después los veremos con ejemplos.

Esta directiva es de tipo atributo y en el objeto que recibe como configuración, figuranlos siguientes elementos.

• updateOn: Recibirá una cadena de texto con el nombre de un evento el cual será elencargado de actualizar el modelo. Como ejemplo podremos utilizar el evento blur,entonces elmodelo se actualizará cuando el elemento pierda el foco. Varios eventos

Page 209: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 188

pueden ser utilizados, especificando cada uno separados por un espacio. Además,existe un evento con el nombre default, el cual realizará el evento por defecto delcontrol donde se utilice.

• debounce: Recibirá un número especificando la cantidad de milisegundos que seesperará antes de actualizar el modelo después de la última actualización. Estaopción funciona esencialmente de la siguiente forma. Cuando una modificaciónse haya realizado se disparará un temporizador con la cantidad de milisegundosespecificados y al finalizar este se ejecutará la acción de actualizar el modelo. Encaso de que alguna modificación se realice antes de que termine el temporizador,este es reiniciado y comenzará nuevamente. Si esta propiedad en vez de recibir unnúmero, recibe un objeto, en él se podría especificar un tiempo en milisegundospara cada uno de los eventos. De esta forma se escribiría evento:tiempo por ejemplo{‘default’: 1000, ‘blur’: 0} lo que haría que normalmente espere 1 segundo paraactualizar, pero si pierde el foco actualiza de forma instantánea.

• getterSetter: Esta opción recibe como parámetro un valor booleano que definirási este modelo estará unido a una función getter/setter.

• allowInvalid: Recibirá un valor de tipo booleano que define si permitiremos o noque elmodelo sea actualizado con valores inválidos. Este comportamiento previeneque el modelo sea undefined cuando el valor introducido en el campo es invalido.

Ahora veamos varios ejemplos de su uso.

1 <body ng-app="app">2 <div ng-controller="ctrl">3 <form action="#" name="form">4 <input type="email" name="email" id="email"

5 ng-model="email" ng-model-options="{updateOn: 'blur' }">6 <span ng-show="form.email.$invalid">El correo es incorrecto.</span>7 <p>{{email}}</p>8 </form>9 </div>

10

11 <script src="bower_components/angular/angular.js"></script>12 <script>13 angular.module('app', [])

14 .controller('ctrl', ['$scope', function($scope) {}]);

15 </script>16 </body>

En ejemplo anterior podemos comprobar como solo se actualiza el modelo cuando lacaja de texto pierde el foco. Con esto adicionalmente obtenemos que la validación nose ejecutará en cada una de las ocasiones donde el usuario presione una tecla. Por este

Page 210: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 189

motivo mostrar los mensajes de validación se hace un poco más sencillo y se necesitaescribir menos código.

Si en la directiva especificamos varios tipos de configuración, aun así, esta funcionariade forma esperada. En el caso de que especifiquemos los eventos en que queremos que seactualice, pero además especificamos el tiempo que queremos esperar antes de actualizarelmodelo, podríamos utilizar las dos propiedades de configuración sin ningúnproblema.

1 <body ng-app="app">2 <div ng-controller="ctrl">3 <form action="#" name="form">4 <input type="email" name="email" id="email"

5 ng-model="email"

6 ng-model-options="{

7 updateOn: 'default blur',

8 allowInvalid: true,

9 debounce: { 'default': 2000, 'blur': 0}

10 }">11 <span ng-show="form.email.$invalid">El correo es incorrecto.</span>12 <p>{{email}}</p>13 </form>14 </div>15

16 <script src="bower_components/angular/angular.js"></script>17 <script>18 angular.module('app', [])

19 .controller('ctrl', ['$scope', function($scope) {}]);

20 </script>21 </body>

De esta forma cuando dejemos de escribir se disparará el temporizador y dos segundosdespués se actualizará el modelo. O de lo contrario podemos salir de la caja de texto yesta se actualizará de forma instantánea.

En el siguiente ejemplo utilizaremos la opción getterSetter para convertir una fecha queobtenemos en formato string a una instancia del objeto Date. Con esta funcionalidadpodremos hacer uso del input de tipo date de HTML.

Page 211: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 190

1 <body ng-app="app">2 <div style="margin: 0 auto; width: 50%" ng-controller="ctrl">3 <h1>Tarea</h1>4 <p>Nombre: {{tarea.nombre}}</p>5 <p>Descripción: {{tarea.descripcion}}</p>6 <p>Inicio: <input type="date" ng-model="tarea.fecha.inicio"

7 ng-model-options="{getterSetter: true}"></p>8 <p>Fin: <input type="date" ng-model="tarea.fecha.fin"

9 ng-model-options="{getterSetter: true}"> </p>10 </div>11

12 <script src="bower_components/angular/angular.js"></script>13 <script>14 angular.module('app', [])

15 .controller('ctrl', function($scope){

16 $scope.tarea = {

17 "tareaId": 171,

18 "nombre": "Evento Circle",

19 "descripcion": "Participar en el evento Circle 2015",

20 "fechaInicio": "09-23-2015",

21 "fechaFin": "09-25-2015"

22 };

23 var _inicio = new Date($scope.tarea.fechaInicio);

24 var _fin = new Date($scope.tarea.fechaFin);

25 $scope.tarea.fecha = {

26 inicio: function(val) {

27 if (angular.isDefined(val)){

28 _inicio = val;

29 $scope.tarea.fechaInicio = _inicio;

30 }

31 return _inicio;

32 },

33 fin: function(val){

34 if (angular.isDefined(val)){

35 _fin = val;

36 $scope.tarea.fechaInicio = _fin;

37 }

38 return _fin;

39 }

40 }

41 });

42 </script>

Page 212: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 191

43 </body>

Lo primero que necesitamos hacer es establecer la directiva ng-model-options con lapropiedad getterSertter a un valor verdadero en cada uno de los elementos de tipo datedel formulario. Lo siguiente es crear las funciones que estarán ejecutándose cuando estosmodelos sean requeridos. Creare dos variables una para cada una de las fechas. Cómotrabajan las funciones getters y setters en Javascript es de la siguiente forma. Cuandoes invocada sin parámetros, esta devuelve el valor. Si es invocada con un valor comoparámetro esta cambia el valor.

Estas funciones las pondremos dentro de un objeto con el nombre fecha dentro de latarea. Además, necesitamos definir dos variables para convertir la fecha inicialmente,las cuáles serán las devueltas por las funciones. Para terminar, necesitamos cambiar laspropiedades en las directivas ng-model que apunten a las nuevas fechas.

En casos muy específicos podremos especificar la configuración allowInvalid para per-mitir que el modelo sea actualizado con valores inválidos. En la mayoría de las ocasionesesto no es lo que quisiéramos para una aplicación.

Si te has dado cuenta que si utilizas un botón con la directiva ng-click donde utilizamosalguno de los elementos del formulario, y estos aún no han actualizado el modelo,podríamos obtener comportamientos indeseados. Por este motivo cuando hagas uso dela directiva ng-model-options asegúrate de no utilizar la directiva ng-click para el evento deacción, sino la directiva ng-submit. Esta directiva ejecutará de manera instantánea todoslos eventos en espera y se actualizará el modelo antes de ejecutar las acciones.

Resetear elementos de formulario

En versiones anteriores a Angular 1.3, si necesitáramos implementar una funcionalidaddonde pudiéramos resetear los elementos del formulario, tendrías que hacerla de formamanual. En esta nueva versión se incluye esta funcionalidad a nivel de formulario o deun elemento en específico. Es importante mencionar que esta funcionalidad solo estarádisponible para los elementos que tengan la directiva ng-model-options con una de laspropiedades updateOn o debounce.

Para implementarlo a nivel de formulario, podemos utilizar la directiva ng-model-options con la propiedad updateOn en el evento submit. Veámoslo en el ejemplo que semuestra a continuación.

Page 213: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 192

1 <body ng-app="app">2 <div ng-controller="ctrl">3 <form name="form" ng-model-options="{updateOn: 'submit'}">4 <div><label for="nombre">5 Nombre: <input type="text" name="nombre" id="nombre" ng-model="nombre">6 </label></div><br>7 <div><label for="email">8 Email: <input type="email" name="email" id="email" ng-model="email">9 </label></div>

10 <div><br>11 <button ng-click="form.$rollbackViewValue()">Resetear</button>12 </div>13 <hr>14 <div><p>{{nombre}}</p><p>{{email}}</p></div>15 </form>16 </div>17 <script src="bower_components/angular/angular.js"></script>18 <script>19 angular.module('app', [])

20 .controller('ctrl', ['$scope', function($scope) {

21 $scope.nombre = 'John Doe';

22 $scope.email = '[email protected]';

23 }]);

24 </script>25 </body>

En el ejemplo anterior se ha creado un botón que resetea el formulario con sus valores asu estado inicial. Esta funcionalidad la podemos implementar a nivel de elemento. En elsiguiente ejemplo implementaremos esta funcionalidad para los elementos. Queremosque cuando el usuario presione la tecla escape sin haber salido del elemento, este vuelvaa su estado inicial.

1 <body ng-app="app">2 <div ng-controller="ctrl">3 <form name="form">4 <div><label for="nombre">Nombre:5 <input type="text" id="nombre" name="nombre"

6 ng-model="nombre" ng-model-options="{updateOn: 'blur'}"

7 ng-keyup="cancelar(form.nombre, $event)">8 </label></div><br>9 <div><label for="email">Email:

10 <input type="email" id="email" name="email"

Page 214: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 193

11 ng-model="email" ng-model-options="{updateOn: 'blur'}"

12 ng-keyup="cancelar(form.email, $event)">13 </label></div>14 <hr>15 <div><p>{{nombre}}</p><p>{{email}}</p></div>16 </form>17 </div>18 <script src="bower_components/angular/angular.js"></script>19 <script>20 angular.module('app', [])

21 .controller('ctrl', ['$scope', function($scope) {

22 $scope.nombre = 'John Doe';

23 $scope.email = '[email protected]';

24 $scope.cancelar = function(control, evt) {

25 if ( evt.keyCode == 27 ) {

26 control.$rollbackViewValue();

27 }

28 }

29 }]);

30 </script>31 </body>

En este ejemplo he creado un método que recibe dos parámetros. El primero es elelemento del formulario que necesitamos resetear y el segundo es el evento. Compruebosi la tecla presionada es la 27 la cual es escape y restauro el modelo a su estado inicial.Es importante recordar que esta funcionalidad solo estará disponible para los elementosque tengan definido la directiva ng-model-options en sí.

Para la última propiedad no necesitamos un ejemplo, solo mencionar que si agregamosallowInvalid como true a la configuración de ng-model-options; el modelo será actuali-zado, aunque la validación falle.

Con todos los elementos anteriormente mencionados tienes la posibilidad de crear for-mularios realmente amigables y cómodos para el usuario. Esto ayuda a que tu aplicaciónsea más fácil de utilizar y que el usuario se sienta más confiado utilizándola.

Nombre de elementos interpolables

En versiones anteriores a la 1.3 de AngularJS los nombres de los elementos no podíanser interpolables, estos debían ser escritos directamente en la vista. Si los nombres delos elementos del formulario eran obtenidos mediante una llamada al servidor remotoo especificados dentro del controlador, estos no podían ser expuestos a la vista e

Page 215: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Capítulo 11: Formularios y Validación 194

interpolados con la sintaxis {{ }}. A partir de la versión 1.3 la propiedad name de loselementos de formulario puede ser interpolada.

En el siguiente ejemplo veremos cómo definimos el nombre del elemento dentro delcontrolador. En la vista el nombre del elemento es interpolado. Para comprobar que hafuncionado vamos a mostrar el formulario mediante el filtro json.

Vista

1 <body ng-controller="AppCtrl as vm">2 <form name="miForm">3 <input type="email" name="{{vm.elemCorreo}}" ng-model="vm.email">4 <pre>{{miForm | json}}</pre>5 </form>6 </body>

Controlador

1 angular.module('app', [])2 .controller('AppCtrl', AppCtrl);

3

4 function AppCtrl(){

5 var vm = this;6 vm.elemCorreo = 'correo';

7 vm.email = '';

8 }

Como habrás podido observar el elemento ha tomado el nombre correo ya que en elcontrolador lo definimos con ese nombre. Esta nueva funcionalidad nos permitirá crearelementos de formularios para modelos directamente desde el controlador. Además,para crear formularios en los que el usuario pueda añadir campos, por ejemplo, en unformulario de contacto donde se puedan añadir campos que no hayan sido definidos pordefecto.

Page 216: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Servidor API RESTfulPara propósitos de este libro como contenido extra he desarrollado un servidor utili-zando NodeJS, Express.js yMongoDB. En este servidor podrás hacer prueba de todoslos ejemplos expuestos en este libro. También dispone de un API RESTful para realizarpeticiones y es el que he utilizado en el Capítulo 10 para demostrar el uso del servicio$resource de Angular. A continuación, explicaré el uso de este servidor paso a paso.

Requerimientos

Al estar desarrollado con NodeJS es necesario que node esté disponible en tu sistemapara ejecutar la aplicación. Si aún no tienes node instalado puedes obtenerlo desde el sitiooficial http://nodejs.org¹⁵ y seguir los pasos de la instalación hasta tenerlo disponible.

Para asegurarte que node está listo para ser usado puedes ir a la consola en Mac y Linuxo el intérprete de comandos en Windows y ejecutar.

1 node --version

Deberás obtener una respuesta similar a esta v0.10.35 de lo contrario necesitarás volver arealizar los pasos de la instalación. Además de node necesitasnpm que es el encargado degestionar las dependencias en node, esta utilidad viene en la misma instalación de nodey es la que utilizaremos para instalar las dependencias del servidor.

Ahora que ya tenemos listo node y npm necesitamos instalar bower. Bower es un gestorde paquetes para el frontend con el cual obtendremos las librerías de angular y angular-resource. Para instalar bower en el sistema lo hacemos mediante npm.

1 npm install -g bower

Después de haber instalado bower podemos ejecutar en la consola el siguiente comandopara asegurarnos de que se ha instalado correctamente.

1 node --version

¹⁵http://nodejs.org

195

Page 217: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Servidor API RESTful 196

Si obtenemos una respuesta similar a 1.3.12 estamos listos para comenzar a obtener lasdependencias del servidor.

Otro de los requisitos que necesitamos para poder correr el servidor es un servidordeMongoDB, podemos tener un propio servidor local obteniendo MongoDB desde susitio oficial http://www.mongodb.org¹⁶. O podremos utilizar uno de los servidores eninternet comoMongolab.com¹⁷ que incluso se puede utilizar gratis.

Estos son todos los requisitos para ejecutar el servidor, ahora necesitaremos instalar lasdependencias y configurar el servidor.

Instalando dependencias

Para instalar las dependencias abrimos la consola y vamos hasta la carpeta dondetenemos el servidor. Ejecutamos el comando npm install y esperamos a que termine deinstalar. Cuando npm finalice ejecutará bower para gestionar las librerías.

Bower instalará las dependencias en la carpeta public/lib para que estén disponiblescomo archivos estáticos desde el servidor. Generalmente bower instala las dependenciasen una carpeta llamada bower_components, este comportamiento ha sido cambiandomediante el archivo .bowerrc que está en la carpeta del servidor.

Configurando el servidor

El servidor en si es solo el archivo llamado server.js que está en la raíz. Para configurarloabre el archivo en tu editor de texto favorito y ve hasta la línea 18. Aquí se definen 4variables.

1. appPort: El puerto por el que el servidor estará esperando conexiones.2. dbServer: Dirección del servidor de base de datos MongoDB3. dbPort: Puerto del servidor de base de datos MongoDB4. dbName: Nombre de la base de datos que utilizará este servidor.

Configurando cada una de estas variables con los datos reales que necesites utilizar,quedará configurado el servidor listo para usarse.

Iniciando el servidor

Para iniciar el servidor dirígete en la consola hasta la carpeta del servidor y ejecuta nodeserver y el servidor comenzará a esperar conexiones por el puerto que has definido en laconfiguración.

¹⁶http://www.mongodb.org¹⁷https://mongolab.com

Page 218: AngularJs Paso a Paso - coreditec.com.cocoreditec.com.co/libros/angularjs-paso-a-paso-pdf.pdf · Estadirectivalograsufunción,peronoporartedemagia,esmuysencillo,AngularJS tieneunampliomanejodeclasesCSSlascualesvienenincluidasconelframework.Un

Servidor API RESTful 197

Uso del servidor

La primera vez que el servidor inicie intentará introducir mensajes de prueba en labase de datos para posteriormente utilizarlos en los ejemplos. En caso de que no puedaimprimirá el error en la consola.

El servidor posee una API RESTful para mensajes. Solo tiene definido dos rutas confor-mando así un recurso REST.

• Para acceder a la lista de los mensajes puedes hacerlo mediante una petición GETa /api/mensajes.

• Para acceder a unmensaje especifico ejecuta una peticiónGET a /api/mensajes/:middonde :mid sea la id del mensaje. Este se puede obtener en la propiedadmid queposee cada mensaje.

• Para crear un nuevomensaje ejecuta una peticiónPOST /api/mensajes con un objetojson como cuerpo de la petición. El objeto debe contener dos propiedades. 1:usuario y 2: mensaje.

• Para actualizar un mensaje debes hacer una petición PUT a /api/mensajes/:mid conun objeto json en el cuerpo de la petición con las propiedades usuario ymensaje.

• Para eliminar un mensaje ejecuta una peticiónDELETE a /api/mensajes/:mid.

En caso de que exista algún error en alguna de las peticiones el servidor devolverá unobjeto json con la propiedad mensaje explicando el error ocurrido.

Las peticiones POST y PUT devuelven el nuevo objeto para que pueda ser utilizado enel cliente.

Para todas las demás peticionesGET que no cumplan con ninguna de las rutas anterior-mente mencionadas se devolverá el archivo public/main.html como respuesta. En estearchivo reside la aplicación angular detallada en el Capítulo 10. Para cualquier pruebaque necesites realizar puedes utilizar este archivo, así como el de la aplicación que resideen public/js/app.js.