En esta segunda parte del tutorial básico con AngularJS vamos a definir un archivo HTML5 donde transcurrirá toda la acción de nuestra aplicación, además instalaremos las librerías necesarias para el desarrollo y empezaremos a definir los módulos.
Instalando dependencias con Bower
Vamos a instalar las librerías necesarias con el administrador de paquetes Bower.
Para instalar Bower necesitas Node, doy por hecho que tienes instalado Node, si no es así lo explico en este artículo.
Ahora escribe este comando en la consola y se instalará bower globalmente:
1 |
npm install -g bower |
Antes de empezar a instalar dependencias créate un archivo llamado .bowerrc en la raíz del proyecto con este contenido:
1 2 3 |
{ "directory": "app/libs" } |
Este fichero en JSON configura Bower para que cada librería que instalemos se guarde en el directorio app/libs (librerías). Sin este archivo todo se instalaría en la carpeta autogenerada llamada bower_components.
Y ahora crea con este comando en consola: bower init, el fichero bower.json, aquí se irán añadiendo las dependencias que instalemos.
Vamos a empezar con las dependencias de angular js, sitúate en la raíz de tu proyecto desde la consola y escribe:
1 |
bower install --save angular |
Y ahora añadimos Bootstrap que lo usaremos para la interfaz, escribe el siguiente comando:
1 |
bower install --save bootstrap |
Una vez instalado todo, añadimos los estilos y los scripts, y nos quedaría un index.html así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!doctype html> <html lang="es-ES"> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>Blog en AngularJS</title> <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Bootstrap --> <link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.css" /> <!-- Custom styles --> <link rel="stylesheet" href="css/main.css"> </head> <body> <p>¡Hola Blog!</p> <!-- Scripts --> <script src="lib/jquery/dist/jquery.js"></script> <script src="lib/angular/angular.js"></script> <script src="lib/bootstrap/dist/js/bootstrap.js"></script> <!-- Custom scripts --> <script src="/scripts/main.js"></script> </body> </html> |
Copia y pega este código y crea el index.html dentro de la carpeta app.
Este archivo index.html será la única página HTML del proyecto, el resto serán plantillas que las cargaremos de forma dinámica según la interacción del usuario o los datos que muestre la app.
Como ya dijimos en la primera entrada, este es el estilo de las Single Page Applications (SPA) y así será nuestro blog.
Creando los módulos
Dentro de app vamos a crear la carpeta app/scripts, donde meteremos todos los ficheros que contienen la funcionalidad de la app.
¿Qué es un módulo en AngularJS?
En angular js un módulo sería lo equivalente a los paquetes en Java, es una forma de agrupar funcionalidades de este framework.
Un ejemplo de creación de módulo sería:
1 |
angular.module("blog",[]); |
Al ejecutar el código de arriba se crea el módulo «blog», aunque ahora no tiene ninguna funcionalidad será necesario añadirla posteriormente.
Debemos incluir los [] ya que no existe un método para obtener una referencia a un módulo y otro diferente para crearlo, en los dos casos se usa module(). Al meter los corchetes indicamos que lo estamos creando, pero si no incluimos un segundo parámetro lo que hacemos es llamar a un módulo que ya existe.
Recuerda pasar como segundo parámetro los corchetes[] para crear el módulo
Los módulos de nuestro blog
La forma de trabajo ideal para Angular es crear un módulo por cada funcionalidad de la app. Pero como nuestra aplicación no es grande podemos juntar estas funcionalidades dentro de un mismo fichero (app.js), y solo separaremos controladores, directivas, etc.
Nuestro blog tendrá entradas y comentarios, lo ideal es que la app reciba los datos de una API RESTful y que se devuelvan estos datos en formato JSON, sin embargo, esto pertenece al backend del blog y eso escapa del aprendizaje que queremos dar sobre Angularjs.
Así que usaremos como API el proyecto JSONPlaceholder, que nos ofrece una API de testeo y protitipado que devuelve JSONs de entradas y comentarios.
Los módulos lo meteremos en la carpeta app/scripts, y las urls de la API que vamos a usar serían estas:
URL | Method |
---|---|
/posts | POST |
/posts | GET |
/posts/:postId | GET |
/comments | GET |
/comments/:commentId | GET |
/users | GET |
/users/:userId | GET |
Ejemplos con los datos que recibiríamos en JSON según cada URL:
GET /posts/7
Obtengo por ejemplo el post 7
1 2 3 4 5 6 |
{ userId: 1, id: 7, title: "magnam facilis autem", body: "dolore placeat quibusdam ea quo vitae magni quis enim qui quis quo nemo aut saepe quidem repellat excepturi ut quia sunt ut sequi eos ea sed quas" } |
GET /comments/5
Obtengo el comentario 5 del post 1
1 2 3 4 5 6 7 |
{ postId: 1, id: 5, name: "vero eaque aliquid doloribus et culpa", email: "Hayden@althea.biz", body: "harum non quasi et ratione tempore iure ex voluptates in ratione harum architecto fugit inventore cupiditate voluptates magni quo et" } |
GET /users/2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
{ id: 2, name: "Ervin Howell", username: "Antonette", email: "Shanna@melissa.tv", address: { street: "Victor Plains", suite: "Suite 879", city: "Wisokyburgh", zipcode: "90566-7771", geo: { lat: "-43.9509", lng: "-34.4618" } }, phone: "010-692-6593 x09125", website: "anastasia.net", company: { name: "Deckow-Crist", catchPhrase: "Proactive didactic contingency", bs: "synergize scalable supply-chains" } } |
Estructura del contenido de la carpeta app
No hay una estructura concreta definida en AngularJS, esto a día de hoy sigue siendo un debate. Lo que sí es ideal para una app en angular es que sea modular, separar las funcionalidades por carpetas, y que cada funcionalidad tenga sus servicios, controladores, rutas, etc… separados del resto.
Pero la funcionalidad que vamos a realizar para el blog es muy sencilla por lo que la carpeta /app con los archivos que iremos añadiendo durante el tutorial quedaría así:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
app/ ├── lib/ ├── scripts/ | ├── services.js | ├── controllers.js | └── app.js ├── css/ ├── views/ | ├── post-detail.tpl.html | ├── post-list.tpl.html | └── post-create.tpl.html | └── index.html |
En /scripts tenemos los siguientes ficheros:
Servicios (services.js)
services.js contiene los servicios de la aplicación. Los servicios en angular js son un objeto en javascript que nos permite obtener información, un servicio nunca interacciona con la propia página, solo interacciona con otros servicios o con un servidor de datos que pueda estar alojado en otro host.
En nuestro caso lo usaremos para hacer llamadas AJAX a la API.
Controladores (controllers.js)
Una vez creados los servicios, en controllers.js implementamos los controladores de las vistas parciales y por tanto de la aplicación.
Los controladores nos permiten mediante programación implementar la lógica de la parte del cliente en AngularJS. En ellos podemos mantener el código necesario para inicializar una aplicación, gestionar los eventos, etc.
Nos sirven para separar ciertas partes del código de una aplicación y evitar que escribamos Javascript en la vista. Es decir para que el HTML utilizado para la parte del cliente no se mezcle con el Javascript que le da la vida, animaciones, etc.
Script principal (app.js)
app.js dará la funcionalidad principal a la aplicación y donde agruparemos los módulos que usaremos.
Otra ración extra de dependencias y directivas
Antes de nada vamos a instalar las siguientes librerías que necesitaremos, volvemos a usar Bower y con el «flag» –save-dev le indicamos que queden guardadas en el fichero bower.json.
Desde consola sitúate en la raíz del proyecto y escribe:
1 2 |
bower install --save angular-route bower install --save angular-resource |
Estas librerías nos permitirán usar ciertas directivas. ¿Qué son las directivas en AngularJS?
Las directivas son marcas en los elementos del árbol DOM, en los nodos del HTML, que indican al compilador de Angular que debe asignar cierto comportamiento a dichos elementos o transformarlos según corresponda.
Las directivas en AngularJS permite la manipulación y reutilización de código HTML.
Nos permiten añadir comportamiento dinámico al árbol DOM haciendo uso de las nativas del propio AngularJS o creando nuestras propias directivas. Es buena práctica que sea en las directivas el único sitio donde manipulemos el árbol DOM, para que entre dentro del ciclo de compilación, binding y renderización del HTML.
Las directivas que hemos instalado son:
- angular-route nos permite hacer uso de la directiva $routeProvider para poder manejar URLs desde el navegador y mostrar una página u otra al usuario.
- angular-resource por su parte, nos deja emplear la directiva $resource que nos permite manejar peticiones AJAX a recursos REST de una manera más sencilla y con una sintaxis más limpia, en lugar de usar las directivas $http.get o $http.post .
Empezamos a darle caña a Angular js
Para indicar al HTML que usaremos una app Angular, tenemos que poner la directiva ng-app
en una parte de nuestro index.html , normalmente se pone en la etiqueta body para que englobe a toda la página. A esta aplicación la llamaremos «blog», el fichero quedaría así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<!doctype html> <html lang="es-ES"> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>Blog en AngularJS</title> <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Bootstrap --> <link rel="stylesheet" href="lib/bootstrap/dist/css/bootstrap.css" /> <!-- Custom styles --> <link rel="stylesheet" href="css/main.css"> </head> <body ng-app="blog"> <div class="container"> <div class="row"> <aside class="col-sm-3"> <a ng-href="/new">Crear Entrada</a> </aside> <section class="col-sm-9" ng-view></section> </div> </div> <!-- Scripts --> <script src="lib/jquery/dist/jquery.js"></script> <script src="lib/angular/angular.js"></script> <script src="lib/bootstrap/dist/js/bootstrap.js"></script> <!-- Custom scripts --> <script src="/scripts/main.js"></script> </body> </html> |
Como puedes ver hemos añadido clases de Bootstrap para ir maquetando, como son .container
, .row
, .col-sm-3
y .col-sm-9
, de esta forma tenemos dos columnas en la página, una de tamaño 3 que sirve para la info sobre el autor del blog, y otra de tamaño 9 para el contenido y el listado de artículos.
Esta última sección en blanco tiene la directiva ng-view
, que le indica a la aplicación Angular blog que en ese espacio se cargarán las vistas de la app que manejaremos gracias al routing de $routeProvider más adelante.
Agrupando nuestros módulos en app.js
Crea el fichero llamado app.js dentro del directorio app/scripts/. Este es el primer fichero que dará la funcionalidad a nuestra app-blog, en él metemos los módulos que vamos a usar, ya que nuestra app es tan sencilla que no merece la pena tenerlos separados.
En nuestro fichero js usaremos la anotación «use strict», que es una característica de ECMAScript5 que te permite optar por una versión restringida de JavaScript que deshabilita algunas de las funcionalidades más problemáticas o propensas a errores de este lenguaje.
Además vamos a agruparlos por módulos de angular y a su vez como closures (funciones internas).
De esta forma la funcionalidad de la aplicación tiene su propio scope, teniendo así la tranquilidad de que ningún otro archivo javascript puede modificar o acceder a algún atributo o variable, evitando conflictos a la hora de usar plugins o librerías externas. Las variables que usemos dentro de esa función solo quedan definidas dentro de ella.
En principio app.js nos quedaría así:
1 2 3 4 |
(function () { 'use strict'; // Aquí va la funcionalidad })(); |
Configurando las rutas
Ahora creamos y configuramos el módulo blog con la dependencia ngRoute que obtenemos de añadir la librería angular-route (para poder manejar URLs desde el navegador y mostrar páginas al usuario):
1 2 3 4 5 |
(function () { 'use strict'; angular.module('blog', ['ngRoute']); })(); |
Después creamos una función de configuración para indicarle a la aplicación que rutas escuchar en el navegador y que vistas parciales cargar en cada caso. Para ello usamos la directiva $routeProvider:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
(function () { 'use strict'; angular.module('blog', ['ngRoute']); /* Rutas */ function config ($locationProvider, $routeProvider) { $locationProvider.html5Mode(true); $routeProvider .when('/', { templateUrl: 'views/post-list.tpl.html', controller: 'PostListController', controllerAs: 'postlist' }) .when('/post/:postId', { templateUrl: 'views/post-detail.tpl.html', controller: 'PostDetailController', controllerAs: 'postdetail' }) .when('/new', { templateUrl: 'views/post-create.tpl.html', controller: 'PostCreateController', controllerAs: 'postcreate' }); } })(); |
He nombrado los archivos html como tpl.html para que señalar que estos archivos los usaremos como plantillas (tpl = template)
La línea $locationProvider.html5Mode(true)
usa el módulo $locationProvider
para borrar el carácter # de las URLs de Angular, por defecto éste deja la «almohadilla». Para ello tira de la API History de HTML5.
Para que el modo HTML5 funcione correctamente, debes añadir la etiqueta
<base>
en tu documento index.html, dentro de las cabeceras<head>
:
1234567 <!doctype html><html lang="es-ES"><head><meta charset="utf-8"><base href="/"></head>Más información aquí.
En total tenemos tres rutas, la raíz o principal / , la de ver detalles de un post (+leer más, por ejemplo) post/:postId y la del formulario que usaremos para publicar una nueva entrada.
Cada una de ellas carga una vista parcial que crearemos en las siguientes partes del tutorial y estarán almacenada en la carpeta app/views .
A su vez cada vista tiene un controlador asociado que serán los que manejen la funcionalidad asociada: PostListController , para manejar la lista de posts del blog, PostDetailController para manejar un post concreto y PostCreateController .
Estas funcionalidades las declararemos más tarde en un fichero y módulo aparte, blog.controllers , por lo que para poder hacer uso de ellas en este archivo(app.js), debemos incluirlo como dependencia al declarar el módulo, al igual que hicimos con ngRoute:
1 2 3 4 5 6 |
(function () { 'use strict'; angular.module('blog', ['ngRoute', 'blog.controllers']); [...] }) |
Por otro lado atributo controllerAs nos permite usar variables del controlador dentro de la plantilla HTML sin necesidad de emplear la directiva $scope
.
Para terminar solo nos queda asociar la función config que hemos creado al módulo:
1 2 3 |
angular .module('blog') .config(config); |
El fichero app/scripts/app.js final sería este:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
(function () { 'use strict'; angular.module('blog', ['ngRoute', 'blog.controllers']); /* Rutas */ function config ($locationProvider, $routeProvider) { $locationProvider.html5Mode(true); $routeProvider .when('/', { templateUrl: 'views/post-list.tpl.html', controller: 'PostListController', controllerAs: 'postlist' }) .when('/post/:postId', { templateUrl: 'views/post-detail.tpl.html', controller: 'PostDetailController', controllerAs: 'postdetail' }) .when('/new', { templateUrl: 'views/post-create.tpl.html', controller: 'PostCreateController', controllerAs: 'postcreate' }); } angular .module('blog') .config(config); })(); |
¡Y hasta aquí esta segunda parte! En la siguiente parte del tutorial angularjs crearemos los Servicios y los Controladores asociados a cada vista del blog.
muy chulo el tuto, pero una duda ¿por qué no instalar directamente angular con npm en vez de bower? ambos son gestores de paquetes y npm ya viene con node no? ¿tiene alguna ventaja bower importante respecto a npm?
https://www.npmjs.com/package/angular
https://www.npmjs.com/package/bootstrap
NPM, como gestor de módulos, es muy eficiente en la gestión de dependencias, el problema es que usa dependencias anidadas y este enfoque no es óptimo para las librerías utilizadas en el desarrollo front-end, que deben evitar las inconsistencias de versiones (usa dependencia plana) y realizar las instalaciones más pequeñas posibles.
Debido a necesidades internas la gente de twiter desarrolló Bower, que es justamente un gestor de paquetes especialmente diseñado y optimizado para el front-end.
Aquí se detalla mucho más el tema y se detalla el «problema» de npm con la anidación de dependencias:
http://stackoverflow.com/questions/18641899/what-is-the-difference-between-bower-and-npm
http://blog.eperedo.com/diferencia-entre-npm-y-bower/
ahhh ok interesante, de todas formas creo que bootstrap y angular no tienen otras dependencias, osea que también se podría usar con npm de forma «plana»
La 3er parte? xD estoy ansioso de que la subas
Si por favor la tercera parte, gracias
Hola chicos, un poco de paciencia, tengo que hacer una entrada y luego retomaré esta de Angular JS para acabarlo por fin. Si os suscribís al newsletter os avisaré por correo!, un saludo.
Sería estupendo que subieras la tercera parte para ver como queda el blog, ya me hice seguidor de tu página, la leo semanalmente, tiene cosas interesantes, y lo mejor, en español, hay pocas así.
Muchas gracias por compartir, un fuerte abrazo.
Holaaa, me encantaron los tuto, cuando sera que publicas la parte 3 de crear un blog en angular, me es super necesario y como lo explicas es perfecto, gracias por la atencion me subscribo
Hola, iba muy emocionado haciendo el tutorial, pero hasta aquí llegué, espero no se demoren en la tercera parte que es muy importante
Y la tecera parteeeee
Chale… se ve muy bueno, pero si no está completo no tiene caso.