Escolar Documentos
Profissional Documentos
Cultura Documentos
Tabla de contenido
1. Introduction
2. Tutorial
3. 0 - Iniciando la aplicacion
4. 1 - Template Estatico
5. 2 - Template Angular
6. 3 - Filtrando Repetidores
7. 4 - Enlace de Datos Bidireccional
8. 5 - XHRs e Inyeccion de Dependencias
9. 6 - Modelando Imagenes y Enlaces
10. 7 - Ruteo y Vistas Multiples
11. 8 - Ms y ms Templates
12. 9 - Filtros
13. 10 - Controladores de Eventos
14. 11 - REST y Servicios Personalizados
15. 12 - Animaciones
AngularJS
Qu es AngularJS?
AngularJS es un framework JavaScript de desarrollo de aplicaciones web en el lado cliente, desarrollado por Google y
utiliza el patrn MVC (Modelo-Vista-Controlador)
A travs de los siguientes capitulos aprenders a construir una aplicacin web con AngularJS.
Introduction
AngularJS
Tutorial
Una muy buena forma de aprender Angular JS es trabajar a travs de este tutorial, en el cual aprenderemos a construir
una aplicacin web. La app que construirs es un catlogo que muestra una lista de dispositivos del Android, te dejer
filtrar una lista para ver los telfonos que te interesen, y luego podrs ver en detalle de cada dispositivo:
Sigue este tutorial para ver como angular hace a tu navegador ms inteligente sin necesidad de utilizar extensiones
nativas o plugins.
Observa ejemplos sobre cmo utilizar data binding (enlace de datos) del lado del cliente para crear vistas dinmicas de
datos que cambian de acuerdo a las acciones de tus usuarios. -Angular guarda las vistas en sincronizacin con tus datos
sin necesidad de manipulacin DOM. Aprende una mejor manera y ms fcil de probar tus aplicaciones web, con Karma y
Protractor. Aprende a utilizar inyeccin de dependencias y servicios para hacer una las tareas ms comunes de la web,
como es obtener datos de tu aplicacin, mucho ms fcil.
Cuando termines este tutorial sers capaz de: Crear una aplicacin dinmica que funciona en todos los navegadores web
modernos. Utilizar enlace de datos (data binding) para conectar el modelo de datos para tus vistas. Crear y ejecutar
prueba unitarias con Karma. Crear y ejecutar pruebas E2E (end-to-end), con Protractor.
Mover la lgica de la aplicacin del template y colocarla en los controladores. Obtener datos de un servidor mediante los
servicios de Angular. Aplicar animaciones a tu aplicacin, mediante ngAnimate. Conocer ms recursos para aprender ms
sobre AngularJS.
Este tutorial te guiar por todo el proceso de creacin de una single page app y aprenders a escribir y ejecutar pruebas
unitarias end-to-end. Pruebas al final de cada paso y sugerencias para que aprendas ms acerca de AngularJS y sobre la
aplicacin que vas a crear.
Puedes ir a travs de todo el tutorial en un par de horas o puedes profundizar ms en cada paso. Si ests buscando una
introduccin ms corta para AngularJS, revisa el documento Getting Started
Tutorial
AngularJS
Primeros Pasos
El resto de esta pgina explica cmo puedes configurar el entorno de desarrollo . Si quieres puedes ir directamente a leer
el resto del tutorial, Encendiendo motores.
Instalando Git
Descarga e instala git en tu computadora http://git-scm.com/download una vez instalado, tendrs acceso a la linea de
comandos git. Estos son los principales comandos git que debes conocer:
git clone : clona un repositorio remoto en tu computadora.
git checkout : Recupera un archivo desde la rama o revisin actual.
Descarga angular-phonecat
Clona el repositorio angular-phonecat localizado en GitHub corriendo el siguiente comando:
git clone --depth=14 https://github.com/angular/angular-phonecat.git
AngularJS
Los mdulos Bower, Http-Server, Karma y Protractor son tambin ejecutables, los cuales pueden ser instalados
globalmente y ejecutados directamente desde la terminal de comandos.
No es necesario hacer esto para seguir con este tutorial, pero si decides hacerlo, puedes instalar estos mdulos
globalmente utilizando: sudo npm install -g
Por ejemplo, para instalar la lnea de comandos de Bower ejectuta: sudo npm install -g bower omite la palabra sudo si
ests en windows.
A continuacin, puedes ejecutar la utlidad bower directamente, mediante el comando: bower install
Tests end-to-end
Utilizamos test de extremo a extremo para asegurarnos que nuestra aplicacin funcione correctamente como esperamos.
estn diseadas para probar la aplicacin del lado cliente enteramente, simulan la accin de un usuario y se ejecuta en el
navegador.
Las pruebas de extremo a extremo se guardan en el directorio test/e2e. El proyecto Angular phonecat est configurado
para utilizar Protractor para ejecutar las pruebas de extremo a extremo. Protractor se basa en un conjunto de
controladores que le permiten interactuar con el navegador. Puedes instalar estos controladores ejecutando: npm run
Tutorial
AngularJS
Protractor trabaja interactuando con la aplicacin en ejecucin, tenemos que iniciar nuestro servidor web: npm start
Luego ejecuta en una nueva ventana esta linea de comandos: npm run protractor
Protractor leer la configuracin del archivo en el test/protractor-conf.js. Esta configuracin dice lo siguiente: Abre el
navegador chrome y corre tu aplicacin Ejecuta las pruebas de extremo a extremo en el navegador. Reporta los resultados
de estas pruebas en la ventana de la lnea de comandos del terminal. Cierra el navegador.
Es bueno ejecutar las pruebas end-to-end cada vez que hagas cambios a la vistas HTML o desees comprobar que la
aplicacin como un todo, se ejecuta correctamente.
Tutorial
AngularJS
Iniciando la aplicacin
En este paso, te familiarizaras con los archivos ms importantes del cdigo fuente, aprenders cmo comunicarte con
servidores de desarrollo como angular-seed que es bsicamente un esqueleto de una tpica aplicacin AngularJs, y
ejecutars la aplicacin en el navegador.
En el directorio del proyecto angular phonecat, corre este comando:
git checkout -f step-0 Esto nos deja un espacio de trabajo base en el paso 0.
Tienes que repetir este paso a medida que avances en el tutorial, e ir cambiando el numero por el paso en el que estes (ej.
step-0 si ests en el paso 0, step-1 si ests en el paso 1). Esto va a ser que cada cambio en tu directorio se pierda.
Si no lo haz hecho an es necesario instalar las dependencias ejecutando:
NPM install
Para ver como la aplicacin se ejecuta en un navegador, dirigete a tu navegador y a la direccin:
(http://localhost:8000/app/index.html) deberas ver la aplicacin por defecto funcionando.
Ahora puedes ver la pgina en tu navegador. An no es muy emocionante, pero est bien.
La pgina HTML slo muestra el "Aqu no hay nada aqu todava!" pero el cdigo contiene algunos de los elementos ms
importantes, que vamos a necesitar a medida que avancemos en el desarrollo de la aplicacin:
app / index.html:
0 - Iniciando la aplicacion
AngularJS
Ng-app es la directiva encargada de iniciar una aplicacin Angular, indica el elemento raz y se debe colocar como atributo
en la etiqueta que quieres que sea la raz de la aplicacin.
La directiva ng-app define nuestra aplicacin.AngularJS se ejecutar en el mbito que le indiquemos, es decir abarcar
todo el entorno donde usemos el atributo ng-app. Si lo usamos en la declaracin de HTML entonces se extender por todo
el documento, en caso de ser usado en alguna etiqueta como por ejemplo en el body su alcance se ver reducido al cierre
de la misma.
El tag del script de Angular
<script src="bower_components/angular/angular.js">
Este cdigo descarga el script angular.js que registra una devolucin de llamada que se ejecutar por el navegador
cuando la pgina HTML que contiene este completamente descargada.
Cuando la devolucin de llamada se ejecuta, Angular busca la Directiva ngApp. Si la Directiva encuentra Angular, se inicia
la aplicacin con la raz de la aplicacin DOM siendo el elemento sobre el que se defini la Directiva ngApp.
Expresin Doble llave {{}} Con estas expresiones tambin enriquecemos el HTML, ya que nos permiten colocar
cualquier variable dentro de ella y AngularJS se encargar de evaluarla, interpretarla y resolverla.
Nada por aqu {{'an' + '!'}} Esta lnea muestra las caractersticas fundamentales de las capacidades de angular:
Binding, es enlace de datos entre la vista y el controlador expresada por dobles llaves
La expresiones en AngularJS son escritas dentro de dobles llaves: {{ expresin}} Las expresiones en Angulars enlazan
datos al HTML de la misma forma que la directiva ng-bind. AngularJS mostrar los datos de salida exactamente donde la
expresin fue escrita. Las expresiones de AngularJS son mucho ms que las expresiones de JavaScript: Estas pueden
contener literales, operadores y variables. Por ejemplo:
0 - Iniciando la aplicacion
AngularJS
Experimentos
Agrega esta nueva expresin al archivo index.html que resuelva esta operacin matemtica:
<p>1 + 2 = {{ 1 + 2 }}</p>
0 - Iniciando la aplicacion
10
AngularJS
Template Estatico
Para ver cmo Angular mejora el estndar HTML, puedes crear una pgina HTML puramente esttica y luego examinar
cmo podemos convertir este cdigo HTML en una plantilla que Angular use para mostrar dinmicamente el mismo
resultado con cualquier conjunto de datos.
En este paso agregaremos informacin bsica sobre dos telfonos celulares a una pgina HTML. Ahora, la pgina
contiene una lista con informacin acerca de los dos telfonos.
Para ver como se modifica el documento html, debemos detener el servidor y ejecutar en la consola el comando: git
checkout -f step-1
Los cambios ms importantes se enumeran a continuacin. Y los puedes ver completos en GitHub app/index.html:
<ul>
<li>
<span>Nexus S</span>
<p>
Fast just got faster with Nexus S.
</p>
</li>
<li>
<span>Motorola XOOM with Wi-Fi</span>
<p>
The Next, Next Generation tablet.
</p>
</li>
</ul>
Experimentos
Intenta aadir ms contenido estatico HTML al documento index.html. como por ejemplo: <p>Total number of phones:
2</p>
Resumen
Agregamos a la aplicacin una lista de contenido estatico, en el segundo paso de este tutorial, aprenderemos a usar
Angular para crear dinamicamente la misma lista de datos.
1 - Template Estatico
11
AngularJS
Template de Angular
Este es el momento de hacer la pgina web dinmica con AngularJS.
Aadiremos una prueba que verifica el cdigo para el controlador que vamos a aadir.Hay muchas formas de estructurar
el cdigo de una aplicacin web.
Para aplicaciones Angular, recomendamos el uso del patrn de diseo Modelo-Vista-Controlador (MVC) para desacoplar el
cdigo y evitar problemas futuros.
La idea de la estructura MVC no es otra que presentar una organizacin en el cdigo, donde el manejo de los datos
(Modelo) estar separado de la lgica (Controlador) de la aplicacin, y a su vez la informacin presentada al usuario
(Vistas) se encontrar totalmente independiente.
Es un proceso bastante sencillo donde el usuario interacta con las vistas de la aplicacin, stas se comunican con los
controladores notificando las acciones del usuario, los controladores realizan peticiones a los modelos y estos gestionan la
solicitud segn la informacin brindada.
Esta estructura provee una organizacin esencial a la hora de desarrollar aplicaciones de gran escala, de lo contrario sera
muy difcil mantenerlas o extenderlas.
Con esto en mente, vamos a usar un poco de Angular y JavaScript para agregar los componentes modelo vista
controlador a nuestra aplicacin.
Ejecutemos este comando en la terminal: git checkout -f step-2
Actualiza tu navegador o consulta en lnea este paso: Step 2 Live Demo. Los cambios ms importantes se enumeran a
continuacin. Los puedes ver completos en GitHub.
En Angular, la vista es una proyeccin del modelo a travs del documento HTML. Cuando el modelo cambia la vista refleja
los cambios y viceversa.
La vista se construye mediante Angular atravs de esta plantilla: app/index.html:
<html ng-app="phonecatApp">
<head>
<script src="bower_components/angular/angular.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
<ul>
<li ng-repeat="phone in phones">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
</body>
</html>
Ahora sustituiremos la lista de telfonos mediante la Directiva ngRepeat y dos expresiones de angular:
ng-repeat="phone in phones" attribute in the
Esta directiva te sirve para implementar una repeticin (un bucle). Es usada para repetir un grupo de etiquetas una
2 - Template Angular
12
AngularJS
serie de veces.. Esta repeticin le dice a Angular que cree un elemento <li> por cada telefono.
Las expresiones cerradas entre llaves ( y) sern reemplazadas por el valor de cada expresin.
Hemos agregado una nueva Directiva, llamada ng-controller ng-controller conecta el controlador con el objeto
PhoneListCtrl en la etiqueta body. Las expresiones entre llaves ( { {phone.name}} y { {phone.snippet}} indica enlaces, que
se refieren a nuestro modelo de la aplicacin, que est configurado en nuestro controlador. PhoneListCtr
Modelo y Controlador
El modelo (el array de phones) est ahora instanciado dentro del controlador PhoneListCtrl. El controlador es simplemente
un constructor que toma $scope como parametro. App/JS/Controllers.js:
13
AngularJS
Scope
El concepto de Scope en Angular es crucial. El scope conecta la vista con el modelo. Angular usa el scope junto con la
informacin contenida en la vista, el modelo y el controlador, para mantener el modelo y la vista separada pero
sincronizada, cualquier cambo en el modelo es reflejada en la vista y viceversa.
Para aprender ms sobre Scopes puedes leer la documentacin de scope.
Test
La forma en que Angular separa el controlador de la vista, facilita el desarrollo del codigo de prueba. Si el controlador est
disponible en el espacio de nombres global entonces nosotros podramos simplemente instanciarlo con un objeto scope
simulado:
describe('PhoneListCtrl', function(){
it('should create "phones" model with 3 phones', function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);
expect(scope.phones.length).toBe(3);
});
});
La prueba crea una instancia de PhoneListCtrl y verifica que la propiedad array phones en el contenga tres registros.
Este ejemplo muestra lo fcil que es crear una prueba unitaria en Angular. Puesto que la prueba es una parte crtica del
desarrollo de software, hacemos que sea fcil crear pruebas en Angular para que los desarrolladores se animen a codear.
2 - Template Angular
14
AngularJS
Karma server started at http://localhost:9876/ info (launcher): Starting browser "Chrome" info (Chrome 22.0):
Connected on socket id tPUm9DXcLHtZTKbAEO-n Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
Experimentos
Aade esta linea de cdigo: <p>Total number of phones: {{phones.length}}</p> Crear una nueva propiedad de modelo del
controlador y unelo a la vista. Por ejemplo: $scope.name = "World"; Ahora agrega esta linea al archivo index.html:
<p>Hello, {{name}}!</p> Actualiza el explorador y verifica que diga "Hola, mundo!". La actualizacin de la prueba unitaria
para el controlador ./test/unit/controllersSpec.js reflejar el cambio anterior. Ahora aade por ejemplo:
expect(scope.name).toBe('World');
Luego crea un repetidor en index.html que construya una tabla sencilla como esta:
<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
</table>
<table>
<tr> <th>row number</th> </tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
Punto extra: probar hacer una tabla de 8 x 8 utilizando una directiva ng-repeat adicional.
2 - Template Angular
15
AngularJS
Filtrando Repetidores
Hasta aqui hicimos un gran trabajo estableciendo la base para nuestra aplicacin, en el ltimo paso. Ahora vamos a hacer
algo mas simple; vamos a aadir una caja bsqueda de texto (s, ser sencillo!).
Tambin vamos a hacer escribir un test e2e porque es una buena practica y rapidamente detectar errores. Ejecuta esta
setencia en la terminal: git checkout -f step-3
Ahora la app tiene una caja de bsqueda. Observa que la lista de telfonos en la pgina cambia dependiendo de la
bsqueda que realice el usuario.
Controlador
No haremos cambios en el controlador.
Template
app/index.html:
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in phones | filter:query">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
Hemos aadido una etiqueta <input> y usado la funcin filtro para procesar la directiva de ngRepeat, en la etiqueta input.
Esto le permite al usuario ingresar criterios de bsqueda y ver inmediatamente los efectos de su bsqueda en la lista de
telfonos.
Este nuevo cdigo se muestra a continuacin:
Data-Binding: Es una de las caractersticas ms importantes de AngularJS. Cuando la pgina carga, Angular une el
nombre que coloca el usuario en la caja de bsqueda a la variable del mismo nombre en el modelo de datos y los
mantiene a los dos en sincrona.
En este cdigo, la data que un usuario escribe en el input de busqueda query est inmediatamente disponibles en el filtro
del listado .
Cuando el modelo query cambi, causa que el string por el que esta filtrando el repetidor de la lista ng-repeat , actualice
el DOM de manera eficientemente para reflejar el estado actual del modelo.
3 - Filtrando Repetidores
16
AngularJS
Uso del filtro filter: la funcin del filtro es utilizar el valor de la consulta para crear un nuevo array que contenga slo los
registros que coincidan con la query .
ng-Repeat actualiza automticamente la vista en respuesta a la cantidad de telfonos devueltos por el filtro. El proceso es
Test
En el segundo paso hemos aprendido a cmo escribir y ejecutar pruebas unitarias. Las pruebas unitarias son perfectas
para probar controladores y otros componentes de nuestra aplicacin escrita en JavaScript, pero no pueden fcilmente
probar el cableado de nuestra aplicacin o la manipulacin del DOM. Para stos fines, una prueba de extremo a extremo
es una mucho mejor opcin.
La funcin de bsqueda fue totalmente implementada a travs de vistas y binding, por lo que vamos a escribir nuestra
primera prueba end-to-end, para comprobar que esta caracterstica funciona .
test/e2e/scenarios.js:
it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
expect(phoneList.count()).toBe(3);
3 - Filtrando Repetidores
17
AngularJS
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
});
});
`
Esta prueba comprueba que la caja de busqueda y el repetidor estn conectados correctamente. Observe lo fcil que es
escribir pruebas end-to-end en Angular. Aunque este ejemplo es para una prueba simple, es realmente fcil crear
cualquier prueba funcional end-to-end.
Experimentos
Al igual que usamos Karma para correr las pruebas unitarias, utilizamos Protractor para ejecutar pruebas end-to-end.
18
AngularJS
3 - Filtrando Repetidores
19
AngularJS
Template
app/index.html:
<select ng-model="orderProp">
<option value="name">Alphabetical</option>
<option value="age">Newest</option>
</select>
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
20
AngularJS
Angular crea doble binding entre el elemento select y el modelo de orderProp. OrderProp es utilizada como entrada
para el filtro de orderBy.
Como ya mencionamos en la seccin de enlace de datos (data binding) y el repetidor en el paso 3, cuando el modelo
cambia (por ejemplo porque un usuario cambia el orden cuando selecciona el men desplegable), el enlace de datos de
Angular har que la vista se actualice automticamente No es necesario manipular el DOM.
Controlador
app/js/controllers.js:
Hemos modificado el modelo phones - la gama de telfonos - y aadimos una propiedad edad a cada registro del telfono.
Esta propiedad se utiliza para clasificar los telefonos por edad. Hemos aadido una lnea al controlador que establece el
valor predeterminado de orderProp a la edad. Si no hubiramos establecido este valor por defecto aqu, el filtro de orderBy
no se inicializaria hasta que el usuario seleccione una opcin en el men desplegable.
21
AngularJS
Este es un buen momento para hablar del enlace de datos bidireccional (two-way data biding). Observe que cuando la
aplicacin se carga en el navegador, el valor de menor edad queda seleccionado. Esto es porque hemos creado orderProp
para la 'edad' en el controlador. As que el enlace funciona en la direccin de nuestro modelo para la interfaz de usuario.
Ahora si seleccionas "Orden alfabtico" en el men desplegable, tambin se actualizar el modelo y los telfonos se
reordenaran. El enlace de datos est haciendo su trabajo en la direccin opuesta, desde la interfaz de usuario al modelo.
Test
Las modificaciones deben ser verificadas con una prueba unitaria y un test de end-to-end. Ejecuta el test:
test/unit/controllersSpec.js:
Ahora debera ver la siguiente salida en la pestaa de Karma: Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001
secs)
it('should be possible to control phone order via the drop down select box', function() {
var phoneNameColumn = element.all(by.repeater('phone in phones').column('phone.name'));
var query = element(by.model('query'));
function getNames() {
return phoneNameColumn.map(function(elm) {
return elm.getText();
});
}
query.sendKeys('tablet'); //let's narrow the dataset to make the test assertions shorter
expect(getNames()).toEqual([
"Motorola XOOM\u2122 with Wi-Fi",
"MOTOROLA XOOM\u2122"
]);
element(by.model('orderProp')).element(by.css('option[value="name"]')).click();
expect(getNames()).toEqual([
"MOTOROLA XOOM\u2122",
"Motorola XOOM\u2122 with Wi-Fi"
]);
});...
22
AngularJS
La prueba end-to-end verifica que el orden de la caja de seleccin est trabajando correctamente.
Ahora puede volver a ejecutar npm run protractor para ver que las pruebas que se ejecutan.
Experimentos
En el controlador PhoneListCtrl , quita la instruccin que establece el valor de orderProp y vers que Angular
temporalmente agregar una nueva opcin en blanco ("desconocida") a la lista desplegable y el orden por defecto
cambiar.
Agregar un enlace de en la plantilla index.html para ver su valor actual como texto. Invierte el orden de clasificacin
mediante la adicin de un - smbolo antes del valor de: <option value="-age">Oldest</option>
23
AngularJS
Datos
El archivo app/phones/phones.json de tu proyecto es un conjunto de datos que contiene una lista ms grande de telfonos
almacenada en formato JSON. Lo siguiente es una muestra del archivo:
[
{
"age": 13,
"id": "motorola-defy-with-motoblur",
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
"snippet": "Are you ready for everything life throws your way?"
...
},
...
]
Controlador
Vamos a usar el servicio de $http de Angular en nuestro controlador para realizar una peticin HTTP al servidor web para
recuperar los datos que estn en el archivo app/phones/phones.json .
Los servicios son gestionados por el subsistema de DI (Inyeccin de Dependencias) de Angular. La Inyeccin de
dependencia ayuda a que tus aplicaciones web estn bien estructuradas y acopladas. app/js/controllers.js :
El servicio $http hace una peticin GET a nuestro servidor web, pidiendo el archivo phones/phones.json (la url es relativa
a nuestro archivo index.html). El servidor responde con los datos en un archivo json. La respuesta tambin puede haber
sido generada dinmicamente por un servidor de back-end. El navegador y nuestra aplicacin tienen el mismo aspecto.
Por una cuestin de simplicidad utilizaremos un archivo json en este tutorial.
El servicio de $http devuelve un objeto promise con el mtodo success. Llamamos a este mtodo para controlar la
respuesta asincrnica y asignar los datos del telfono al mbito controlado por este controlador, como un modelo llamado
telfonos. Angular detecta la respuesta json y la analiza por nosotros!
Para utilizar este servicio de Angular, simplemente declaras los nombres de las dependencias que necesita como
argumentos para la funcin constructora del controlador:
24
AngularJS
25
AngularJS
Usa una anotacin en linea donde, en vez de simplemente proveer una funcin vas a proveer un array. Este array
contiene una lista de nombres de servicio seguidas por la funcin.
Ambos mtodos funcionan si la funcin puede ser inyectada por Angular, est en vos usar el estilo que quieras en tus
proyecto.
Cuando estamos usando el segundo mtodo, es comn proveer el constructor en lnea cmo una funcin annima cuando
creamos el controlador:
De aqu en adelante, vamos a usar el mtodo en linea . Con esto en mente, pasemos las anotaciones a nuestro
PhoneListCtrl :
PhoneListCtrl: app/js/controllers.js:
Test
test/unit/controllersSpec.js :
Ya que hemos utilizado inyeccin de dependencias y nuestro controlador tiene dependencias, construir el controlador en
nuestro test es un poco ms complicado.
Podramos utilizar el operador new y proveer una implementacin falsa $htttp como sea Angular provee de un servicio
mock $http que podemos usar en nuestros test unitarios.
Configuramos las respuestas "falsas" a las peticiones del servidor llamando mtodos en un servicio llamado $httpBackend :
26
AngularJS
Ahora vamos a realizar aseveraciones para verificar que los modelo telfonos no existe en el scope antes de que la
respuesta sea recibida:
it('should create "phones" model with 2 phones fetched from xhr', function() {
expect(scope.phones).toBeUndefined();
$httpBackend.flush();
expect(scope.phones).toEqual([{name: 'Nexus S'},
{name: 'Motorola DROID'}]);
});
Vaciamos la cola de peticiones en el navegador llamando el metodo $httpBackend.flush(). Esto hace que la promise
retornada por el servicio http sea resuelta con la respuesta esperada. Finalmente verificamos el valor por defecto de
orderProp este seteado correctamente:
Experimentos:
Al final de index.html, agreg: <pre>{{phones | filter:query | orderBy:orderProp | json}}</pre>
Y en el PhoneListCtrl procesa la respuesta http limitando el numero de telefonos a los 5 primeros en la lista. Para hacer
eso usa el siguiente codigo en el $http callback: $scope.phones = data.splice(0, 5);
27
AngularJS
Datos
Notaras que el archivo phones.json contiene un ID y una imagen para cada telfono. El URL apunta al directorio
app/img/phones/.
[
{
...
"id": "motorola-defy-with-motoblur",
"imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg",
"name": "Motorola DEFY\u2122 with MOTOBLUR\u2122",
...
},
...
]
Template
En los atributos href utilizamos, las ya conocidas, {{ }} (doble-llaves) . En la segunda parte, agregamos el nombre del
telefono , y hacemos lo mismo con el ID .
Utilizaremos la directiva ng-src que previene al navegador de que estamos utilizando Angular para que no nos lleve a una
direccion invalida.
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
<p>{{phone.snippet}}</p>
</li>
</ul>
Testing
Aplicamos una verificacin End-To-End para ver que los enlaces e imagenes se esten cargando de la manera correcta
28
AngularJS
Dependencias
La funcionabilidad del ruteo se provee via ngRoute, un mdulo que viene por separado de Angular.
Utilizaremos Bower para instalar las dependendencias. En este paso, el archivo bower.json ser actualizado, incluyendo
nuestra nueva dependencia.
{
"name": "angular-phonecat",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-phonecat",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.4.x",
"angular-mocks": "1.4.x",
"jquery": "~2.1.1",
"bootstrap": "~3.1.1",
"angular-route": "~1.4.0"
}
}
La dependencia "angular-route": "~1.4.0" le dice a Bower, que instale el componente ng-Route, de tal manera que sea
compatible con la version 1.4.x.
29
AngularJS
Providers son objetos que proveen instacias de serivicios y exponen configuraciones de APIs que pueden ser usadas
para controlar la creacion y problemas de un servicio. En el caso del $route, el $routeProvider expone APIs que te
permiten definir rutas para tu aplicacion.
Los modulos de Angular resuelven problemas al remover estados globales de la aplicacion y proveer una manera de
configurar tu injector.
Para entender un poco mas acerca de el DI de Angular, vistit Entendiendo la Injeccion de Dependencia (en ingles)
Plantillas
El servicio de $route es usualmente usado en conjunto con la direccitiva ngView. El rol de ngView es incluir la vista actual
para la ruta dentro del diseo. Esto hace que encaje perfecto con nuestro template index.html.
A partir de la version 1.2 de Angular, el modulo ngRoute viene por separado y debe ser cargado como archivo angularroute.js.
...
Incluimos dos nuevos elementos script que hacen referencia a dos nuevos archivos:
angular-route.js --> Define el modulo ngRoute para Angular
app.js --> El archivo que contiene el modulo principal de nuestra app
Ahora, debemos remover casi todo lo que tiene nuestro archivo index.html(de manera que quede como el codigo de
arriba) y todo lo que sacamos, lo enviaremos a un nuevo archivo, llamado phone-list.html.
Debe estar almacenado en app/partials/...
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="query">
Sort by:
<select ng-model="orderProp">
<option value="name">Alphabetical</option>
<option value="age">Newest</option>
</select>
</div>
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
30
AngularJS
Tambien aadimos un template para la vista con los detalles llamada phone-detail.html y lo guardamos en
app/partials/...
El mdulo de app.js
Para mejorar la organizacin de la aplicacin vamos a utilizar el mdulo de Angular, ngRoute y vamos a mover los
controladores a nuestro propio mdulo phonecatControllers(como vern ahora).
Agregamos el archivo angular-route.js a nuestro index.html y creamos un nuevo modulo phocatControllers en
controllers.js. Sin embargo, eso no es todo lo que necesitamos para ser poder utilizar su cdigo. Necesitamos aadir los
mdulos como una dependencia a nuestra app. Listando estos dos mdulos como dependencias de phonecatApp,
podemos usar las directivas y servicios que proveen.
app/js/app.js
En el segundo argumento, creamos un array con los mdulos de los cuales phonecatApp depende.
...
phonecatApp.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/phones/:phoneId', {
templateUrl: 'partials/phone-detail.html',
controller: 'PhoneDetailCtrl'
}).
otherwise({
redirectTo: '/phones'
});
}]);
31
AngularJS
when('/phones'): se muestra la lista de dispositivos cuando la URL esta posicionada en /phones. Para crear esta
vista, Angular mostrar el diseo de phone-list.html y el controlador PhoneListCtrl.
when('/phones/:phonesId'): se muestran los detalles del dispostivo cuando la URL este posicionada en
/phones/:phoneId, donde :phoneId es una variable de la URL. Para crear y mostrar esta vista, Angular usar el diseo
de phone-detail.html y el controlador PhoneDetailCtrl.
otherwise({redirectTo:'/phones'}): redirecciona hacia /phones cuando no encuentra lo que busca.
Otra vez, not que creamos un nuevo mdulo llamado phonecatControllers. Para aplicaciones pequeas de Angular, es
comn crear solo un mdulo para todos tus controladores, si son pocos. A medida que nuestra aplicacin crezca, es mejor
separar el cdigo en diferentes mdulos. Para aplicaciones ms y ms grandes, debers crear mdulos separados para
mejorar el rendimiento de tu app.
Debido a que nuestra app es pequea, solo aadiremos nuestros controladores a phonecatControllers.
Testing
Para saber que todo funciona bien, ejecuta el siguiente codigo Ent-To-End.
...
it('should redirect index.html to index.html#/phones', function() {
browser.get('app/index.html');
browser.getLocationAbsUrl().then(function(url) {
expect(url).toEqual('/phones');
});
});
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html#/phones');
});
...
describe('Phone detail view', function() {
beforeEach(function() {
browser.get('app/index.html#/phones/nexus-s');
});
32
AngularJS
Ms y ms templates
En este paso implementaremos la vista de detalles del dispositivo cuando el usuario aprete en el telefono deseado.
Datos
Ademas del phones.json, dentro de app/phones, encontraremos un archivo para cada telefono con las especificaciones
del mismo.
app/phones/nexus-s.json (ejemplo)
{
"additionalFeatures": "Contour Display, Near Field Communications (NFC), ...",
"android": {
"os": "Android 2.3",
"ui": "Android"
},
...
"images": [
"img/phones/nexus-s.0.jpg",
"img/phones/nexus-s.1.jpg",
"img/phones/nexus-s.2.jpg",
"img/phones/nexus-s.3.jpg"
],
"storage": {
"flash": "16384MB",
"ram": "512MB"
}
}
Controladores
Expandiremos PhoneDetailCtrl usando el servicio $http para obtener archivos del tipo JSON. Esto funciona de la misma
manera que el controlador de la lista de telefonos.
app/js/controllers.js
Para constuir la URL para la peticin HTTP, usamos $routeParams.phoneId que extrameos de la ruta actual brindada
por $route.
Plantilla
La linea con el elemento span que hicimos en el phone-detail.html la cambiaremos por las expresiones {{}} .
app/partials/phone-detail.html
8 - Ms y ms Templates
33
AngularJS
Testing
Ejecutaremos este cdigo, el cual es parecido al que hicimos en el captulo 5.
test/unit/controllersSpec.js
...
describe('PhoneDetailCtrl', function(){
var scope, $httpBackend, ctrl;
beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/xyz.json').respond({name:'phone xyz'});
$routeParams.phoneId = 'xyz';
scope = $rootScope.$new();
ctrl = $controller('PhoneDetailCtrl', {$scope: scope});
}));
Tambin aadimos un cdigo End-To-End que navega por los detalles del Nexus S y verifica que el encabezado (heading)
de la pgina es "Nexus S".
8 - Ms y ms Templates
34
AngularJS
test/e2e/scenarios.js
...
describe('Phone detail view', function() {
beforeEach(function() {
browser.get('app/index.html#/phones/nexus-s');
});
8 - Ms y ms Templates
35
AngularJS
Filtros
En este captulo, veremos como crear nuestro propio filtro para el template.
Filtros personalizados
Para crear un nuevo filtro, crearermos un mdulo phonecatFilters y registra tu filtro personalizado con este cdigo:
app/js/filters.js
El nombre de nuestro filtro es "checkmarck". El input evalua si es verdadero o falso y devuelve uno de los dos
caracretres:
true -> '\u2713'
false -> '\u2718'
Ahora que nuestro filtro esta listo, necesitamos registrar el mdulo phonecatFiltrers como una dependencia de nuestro
mdulo principal phonecatApp.
app/js/app.js
...
angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']);
...
Template
Desde que creamos el archivo app/js/filter.js, necesitamos incliuirlo a nuestro diseo para que funcione.
app/index.html
...
<script src="js/controllers.js"></script>
<script src="js/filters.js"></script>
...
...
<dl>
9 - Filtros
36
AngularJS
<dt>Infrared</dt>
<dd>{{phone.connectivity.infrared | checkmark}}</dd>
<dt>GPS</dt>
<dd>{{phone.connectivity.gps | checkmark}}</dd>
</dl>
...
Prueba
Los filtros, como todo elemento, deben ser probados.
test/unit/filtersSpec.js
describe('filter', function() {
beforeEach(module('phonecatFilters'));
describe('checkmark', function() {
it('should convert boolean values to unicode checkmark or cross',
inject(function(checkmarkFilter) {
expect(checkmarkFilter(true)).toBe('\u2713');
expect(checkmarkFilter(false)).toBe('\u2718');
}));
});
});
Siempre debemos llamar a la funcin beforeEach(module('phonecatFilters')) antes que cualquier ejecucin de filtros. Esto
carga el mdulo phoneCatFilters dentro del injector para esta prueba.
La funcin ineject(function(checkmarckFileter){ ... }) ayuda a acceder a los filtros que queremos probar.
Ahora, deberias de ver el siguiente mensaje en la tabla de Karma
9 - Filtros
37
AngularJS
Controladores de eventos
En este paso, veremos como aadir una imagen que al apretarla, nos mostrar la pagina de detalles.
Controlador
app/js/controllers.js
...
var phonecatControllers = angular.module('phonecatControllers',[]);
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', '$http',
function($scope, $routeParams, $http) {
$http.get('phones/' + $routeParams.phoneId + '.json').success(function(data) {
$scope.phone = data;
$scope.mainImageUrl = data.images[0];
});
$scope.setImage = function(imageUrl) {
$scope.mainImageUrl = imageUrl;
};
}]);
En PhoneDetailCtrl creamos un propiedad modelo mainImageUrl y la seteamos al valor default de la URL de la primera
imagen del dispositivo.
Tambin creamos el envento setImage que cambiar el valor de mainImageUrl.
Template
app/partials/phone-detail.html
Prueba
Para verificar estos dos nuevos elementos, aadimos dos codigos End-To-End. El primero verifica que la imagen principal
este establecida a la imagen principal por default. La segunda prueba realiza clicks en las imagenes miniaturas y verifica
que cambie la imagen principal.
10 - Controladores de Eventos
38
AngularJS
test/e2e/scenarios.js
...
describe('Phone detail view', function() {
...
it('should display the first phone image as the main phone image', function() {
expect(element(by.css('img.phone')).getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
});
test/unit/controllersSpec.js
...
beforeEach(module('phonecatApp'));
...
describe('PhoneDetailCtrl', function(){
var scope, $httpBackend, ctrl,
xyzPhoneData = function() {
return {
name: 'phone xyz',
images: ['image/url1.png', 'image/url2.png']
}
};
10 - Controladores de Eventos
39
AngularJS
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "~1.3.0",
"angular-mocks": "~1.3.0",
"bootstrap": "~3.1.1",
"angular-route": "~1.3.0",
"angular-resource": "~1.3.0"
}
}
La dependencia "angular-resource": "~1.3.0" le dice a Bower, que instale el componente ngResource, de tal manera que
sea compatible con la version 1.3.x.
Template
Nuestro servicio personalizado sera definido en el archivo app/js/services.js asi que necesitamos incluir el archivo en
nuestra estructura. Adicionalmente, tambien necesitamos cargar el archivo angular-resouce.js, que contiene el modulo
ngResource:
app/index.html
...
<script src="bower_components/angular-resource/angular-resource.js"></script>
<script src="js/services.js"></script>
...
- Servicio
Creamos nuestro propio servico para proveer acceso a los datos del dispositivo desde un serviodr
- app/js/services.js var phonecatServices = angular.module('phonecatServices', ['ngResource']);
phonecatServices.factory('Phone', ['$resource',
function($resource){
return $resource('phones/:phoneId.json', {}, {
query: {method:'GET', params:{phoneId:'phones'}, isArray:true}
});
}]);
Creamos un mdulo API para registrar nuestros servicios personalizados usando un metodo de "fabricacin". Utilizamos el
nombre del servicio ('Phone') y la funcin factory. Esta, es similar al constructor de un controlador en el caso de que
ambas dependencias deben ser injectadas mediante argumentos. El servicio Phone se declara una dependencia del
40
AngularJS
servicio $resource.
El servicio $resource hace fcil el crear un cliente RESTful con tan solo pocas lineas de cdigo. Esto puede despues ser
usado por aplicaciones, en vez del servicio $http.
app/js/app.js
...
angular.module('phonecatApp', ['ngRoute', 'phonecatControllers','phonecatFilters', 'phonecatServices']).
...
Controlador
Simplificamos nuestros sub-controladores (PhoneListCtrl y PhoneDetailCtrl) al factorizar el servicio $http, remplazandolo
con un nuevo servicio llamado Phone. El servicio $resource de Angular es mas fcil que usar $http para interactuar con
los datos RESTful. Tambin es ms fcil enterender el cdigo que nuestros controladores estan haciendo.
app/js/contoladores.js
$http.get('phones/phones.json').success(function(data) {
$scope.phones = data;
});
con:
$scope.phones = Phone.query();
Esta, es una simple declaracin que usaremos para todos los telfonos de nuestra lista.
Testing
11 - REST y Servicios Personalizados
41
AngularJS
Ya que estamos utilzando el mdulo ngResource, es necesario actualizar nuestro archivo de configuracion de Karma con
nuestro recurso de Angular.
test/karma.conf.js
iles : [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-route/angular-route.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/js/**/*.js',
'test/unit/**/*.js'
],
Hemos modificado nuestra prueba para verificar que nuestro nuevo servicio HTTP pida y procece de manera correcta.
Tambien controla que los controladores estan interactuando de manera correcta con el servicio.
test/unit/controllersSpec.js
describe('PhoneListCtrl', function(){
var scope, ctrl, $httpBackend;
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/phones.json').
respond([{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
scope = $rootScope.$new();
ctrl = $controller('PhoneListCtrl', {$scope: scope});
}));
it('should create "phones" model with 2 phones fetched from xhr', function() {
expect(scope.phones).toEqualData([]);
$httpBackend.flush();
expect(scope.phones).toEqualData(
[{name: 'Nexus S'}, {name: 'Motorola DROID'}]);
});
describe('PhoneDetailCtrl', function(){
var scope, $httpBackend, ctrl,
xyzPhoneData = function() {
return {
name: 'phone xyz',
images: ['image/url1.png', 'image/url2.png']
}
};
42
AngularJS
43
AngularJS
Animaciones
En este ltimo captulo aplicaremos animaciones css y javascript al cdigo que creamos anteriormente.
Dependencias
La funcionabilidad de la animacion es provista por el mdulo ngAnimate de Angular, que viene separado del cdigo de
Angular original. Tambien aadiremos jQuery para las animaciones.
Utilizamos Bower para instalar las dependendencias. En este paso el archivo bower.json ser actualizado, incluyendo
nuestra nueva dependencia.
{
"name": "angular-seed",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "~1.3.0",
"angular-mocks": "~1.3.0",
"bootstrap": "~3.1.1",
"angular-route": "~1.3.0",
"angular-resource": "~1.3.0",
"jquery": "~2.1.1",
"angular-animate": "~1.3.0"
}
}
...
<!-- for CSS Transitions and/or Keyframe Animations -->
<link rel="stylesheet" href="css/animations.css">
...
<!-- jQuery is used for JavaScript animations (include this before angular.js) -->
<script src="bower_components/jquery/dist/jquery.js"></script>
...
<!-- required module to enable animation support in AngularJS -->
<script src="bower_components/angular-animate/angular-animate.js"></script>
<!-- for JavaScript Animations -->
<script src="js/animations.js"></script>
...
12 - Animaciones
44
AngularJS
Mdulo y Animaciones
app/js/animations.js
angular.module('phonecatAnimations', ['ngAnimate']);
// ...
// this module will later be used to define animations
// ...
// ...
angular.module('phonecatApp', [
'ngRoute',
'phonecatAnimations',
'phonecatControllers',
'phonecatFilters',
'phonecatServices',
]);
// ...
<!- Let's change the repeater HTML to include a new CSS class
which we will later use for animations:
-->
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp"
class="thumbnail phone-listing">
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
<p>{{phone.snippet}}</p>
</li>
</ul>
Notste que aadimos la clase CSS phone-listing? Esto es todo lo que necesitamos para que funcione.
Para nuestra transicion CSS:
app/css/animations.css
.phone-listing.ng-enter,
.phone-listing.ng-leave,
.phone-listing.ng-move {
-webkit-transition: 0.5s linear all;
-moz-transition: 0.5s linear all;
12 - Animaciones
45
AngularJS
Como veras, la clase phone-listing esta combinada con la animacin al agregar o remover items de la lista:
ng-enter aplica cuando un nuevo elemento es aadido
ng-move aplica cuando se mueven items por la lista
ng-leave aplica cuando se borran items de la lista
12 - Animaciones
46