Desarrollo Angular

62
1. Introduction 2. Conceptos básicos i. Estructura de una aplicación web ii. Tecnologías 3. Configuración del entorno de trabajo i. Instalar Google Chrome ii. Instalar y configurar Sublime Text2 o Atom iii. Instalar y configurar iTerm2 iv. Instalar Git i. Registro en GitHub ii. Registro en GitLab v. Estructura de directorios y ficheros vi. Automatizando nuestro flujo de trabajo 4. Anatomía de una aplicación AngularJS i. HTML5Boilerplate ii. Instalando dependencias iii. Modulos de la aplicación i. Arquitectura ii. Principal iii. Servicios iv. Controladores v. Vistas parciales 5. Diseño con preprocesadores CSS i. Fontawesome ii. Fuentes Tipográficas iii. Estilos de la aplicación 6. Optimización y despliegue en producción i. Cacheado de plantillas ii. Concatenación de ficheros JS y CSS iii. Servidor de archivos de producción iv. Reducción de código CSS Table of Contents

description

asdads

Transcript of Desarrollo Angular

  • 1. Introduction2. Conceptos bsicos

    i. Estructura de una aplicacin webii. Tecnologas

    3. Configuracin del entorno de trabajoi. Instalar Google Chromeii. Instalar y configurar Sublime Text2 o Atomiii. Instalar y configurar iTerm2iv. Instalar Git

    i. Registro en GitHubii. Registro en GitLab

    v. Estructura de directorios y ficherosvi. Automatizando nuestro flujo de trabajo

    4. Anatoma de una aplicacin AngularJSi. HTML5Boilerplateii. Instalando dependenciasiii. Modulos de la aplicacin

    i. Arquitecturaii. Principaliii. Serviciosiv. Controladoresv. Vistas parciales

    5. Diseo con preprocesadores CSSi. Fontawesomeii. Fuentes Tipogrficasiii. Estilos de la aplicacin

    6. Optimizacin y despliegue en produccini. Cacheado de plantillasii. Concatenacin de ficheros JS y CSSiii. Servidor de archivos de producciniv. Reduccin de cdigo CSS

    Table of Contents

  • Copyright 2015 Carlos Azaustre por la obra y la edicin.

    1 Edicin: Agosto de 20142 Edicin: Enero de 2015

    Publicado por carlosazaustre.es Books

    Carlos Azaustre (Madrid, 1984) Desarrollador Web, enfocado en la parteFrontend, Amante de JavaScript. Con varios aos de experiencia tanto enempresa privada, Startups y como Freelance. Actualmente trabaja como CTO enla Startup Chefly

    Ingeniero en Telemtica por la Universidad Carlos III de Madrid y con estudios deMster en Tecnologas Web por la Universidad de Castilla-La Mancha (Espaa).Fuera de la educacin formal, es un amante del autoaprendizaje a travs deinternet. Puedes seguir sus artculos y tutoriales en su blog carlosazaustre.es

    Desarrollo web gil con ngularJS

    Sobre el Autor

    Otros libros publicados recientemente

    Instant Zurb Foundation 4

  • Get up and running in an instant with Zurb Foundation 4 Framework

    ISBN: 9781782164029 (Septiembre 2013)Pginas: 56Lenguaje:: InglsAutores: Jorge Arvalo y Carlos AzaustreComprar en Amazon

  • NotaEste libro va dirigido para personas que tengan conocimientos bsicos enprogramacin, desarrollo web, JavaScript y conozcan el framework Angular.jsaunque sea a un nivel muy bsico.

    NotaA lo largo del libro se desarrollar un ejemplo de una aplicacin web sencilla(Blog), que consume datos de una API externa que no desarrollaremos, ya quese escapa del mbito de este libro. Angular.js es mucho ms, este libroproporciona las bases para implementar aplicaciones web escalables ymantenibles y el uso de gestores de tareas como Gulp.js pata ser ms agiles.

    Una aplicacin web, actual, est compuesta habitualmente de tres partes principales:

    La parte pblica o cliente, tambin conocida como FrontendLa parte del servidor, conocida como BackendEl almacenamiento de datos, o Base de Datos

    La base de datos se encarga de almacenar toda la informacin de nuestra aplicacin.Usuarios, datos relativos a nuestra aplicacin, etc... Esta base de datos se comunica

    2. Conceptos bsicos

    2.1 Estructura de una aplicacin web moderna

  • con el Backend, el cual se encarga de controlar la seguridad, el procesamiento dedatos, la autorizacin, etc... Y por ltimo el Frontend es la parte que se ejecuta en elnavegador del usuario final, se encarga de mostrar la informacin de una maneraatractiva y comunicarse con el Backend para la creacin de datos y visualizarlos. Enuna aplicacin web moderna, la comunicacin se realiza de manera asncrona conJavaScript (AJAX) utilizando el formato de documentos JSON, para enviar y recibirdatos desde el Backend por medio de una API REST.

    En resumen, para crear una aplicacin web completa necesitamos:

    Una base de datos para que la informacin quede almacenada de manerapersistente.Un Backend que se encargue de la seguridad, las autorizaciones y elprocesamiento de datos mediante una API REST.Y un Frontend que maquete estos datos y los presente y se comunique con la APImediante AJAX y JSON. En este ejemplo vamos a tratar la parte Frontend.

    A lo largo de este ejemplo programaremos lo que se conoce como Simple PageApplication, una aplicacin web de una sla pgina que no se recargue con cadallamada y peticin que hagamos al servidor, si no que se comunicar de maneraasncrona gracias a JavaScript.

    JavaScript era el lenguaje que se utilizaba en la web para aadir efectos y animacionesa las pginas. Pero este lenguaje ha evolucionado mucho en nuestros das hasta llegaral punto de ser llevado al servidor con Node.js. Node.js es un entorno de programacindirigido por eventos, es decir es el usuario el que define el flujo del programa mediantela interfaz de usuario, en este caso la pgina web. Sigue un modelo no bloqueante deentrada y salida, esto nos permite hacer una programacin asncrona, parecido al AJAXen el JavaScript del cliente. En estos momentos existen numerosos frameworks parahacer ms sencilla la programacin en el lado del servidor. El ms conocido y extendidoes Express. aunque existen otros, dependiendo de las necesidades, como: Meteor,Restify, Sails, Koa, Hapi,...

    JavaScript ha evolucionado hasta tal punto que tambin podemos encontrarlo en basesde datos, como es el caso de MongoDB, una base de datos no relacional (NoSQL)cuyos registros son almacenados como documentos de tipo BSON: Binary JSON quepermite que la lectura y escritura de datos sea muy rpida y de manera tomica,siempre que organicemos nuestro modelo de datos siguiendo una estructura norelacional. Es una manera diferente de pensar al clsico SQL.

    2.2 Tecnologas

  • Y por supuesto JavaScript ha evolucionado en la parte Frontend, donde siempre hahabitado, pero ha mejorado su implementacin gracias la aparicin de frameworks MVCque nos permiten modularizar el cdigo y evitar el famoso Spaguetti Code que segenera cuando no nos preocupamos de ello. Dentro de esta familia de frameworkspodemos encontrar Backbone.js, Ember.js y el que utilizaremos nosotros que esAngular.js, un proyecto surgido dentro de Google en 2009 y que hoy en da estalcanzando gran popularidad.

    Uniendo estas tecnologas en todas las partes que componen una aplicacin web, seconsigue emplear el mismo lenguaje de programacin (JavaScript) en todo el stacktecnolgico de principio a fin, es lo que se conoce como JavaScript End-To-End otambin Stack MEAN, llamado as por utilizar (M)ongoDB, (E)xpressJS, (A)ngularJS y(N)odeJS.

  • Nuestro entorno de trabajo tiene que tener las herramientas adecuadas que nospermitan ser giles y comprobar errores sin perder tiempo en tareas repetitivas.

    Necesitamos un navegador web moderno, que de soporte a los nuevos estndares yque contenga una consola y herramientas de desarrollo. Hoy por hoy, uno de los quemejores resultados da, de cara al desarrollo, es el navegador de Google, Chrome.

    Tambin necesitamos un editor de texto. Hay desarrolladores que prefieren un IDEcomo puede ser Eclipse, WebStorm, Netbeans, etc.. Yo personalmente me encuentroms cmodo con los editores minimalistas tipo Sublime Text o el reciente Atom.io.

    Aunque seamos Frontends, y la terminal nos parezca que es algo del Backend o deadministracin de servidores, tambin tenemos que usarla. Necesitamos una sencilla deusar y que sea eficaz. Si dispones de un sistema MacOS X, te recomiendo iTerm2.

    Nota Los siguientes pasos son para instalar los programas en un sistema MacOSX. Para otros sistemas operativos consulta la documentacin disponible en suspginas web respectivas.

    3. Configuracin del entorno de trabajo

    3.1 Instalar Google Chrome

  • Dirgete a http://google.com/chrome. Descarga el fichero .dmg y brelo una vezdescargado. Arrastra el la aplicacin de Google Chrome a la carpeta de aplicaciones.

    Para acceder a las herramientas de desarrollador. Dirgete al menu Vista de Chrome,Desarrollo/Herramientasdedesarrollo. Ahi tienes a tu disposicin la consola deJavaScript, el acceso a los scripts, herramientas de red, etc...

    En la Chrome Store tenemos una extensin para Angular, llamada Batarang. Puedesdescargarla desde aqu. Esta extensin te permite visualizar los scope y otrasparticularidades que son tiles a la hora de depurar.

    3.2 Instalar y configurar Sublime Text o Atom

  • Puedes descargarte SublimeText desde su pgina web. Su uso es gratuito, peroadquirir su licencia (para evitar el popup que aparece de vez en cuando) cuestaalrededor de $70. Otro editor muy parecido es Atom creado por los desarrolladores deGitHub que es el que utilizo yo actualmente. Si quieres probar opciones an mshispters tienes la beta de Chrome Dev Editor.

    Atom posee una gran cantidad de plugins, desarrollados por el equipo de Atom y lacomunidad. Puedes instalarlos desde el menu Atom>Preferencias.

  • .Entre los ms curiosos y que no son muy conocidos se encuentan el plugin de terminal,para tener un terminal de comandos en una pestaa del editor.

    O un visor de ficheros Markdown para visualizar en tiempo real el resultado de lo queescribes:

  • Descrgate iTerm2 para Mac. Una vez instalado, inicia la aplicacin. Para tener unprompt como el de la imagen, en el que puedes ver tu nombre de usuario, tu equipo, eldirectorio actual, e incluso la rama del repositorio en la que te encuentres, puedes

    3.3 Instalar y configurar iTerm2

  • descargar los siguientes scripts que configurarn la terminal como la tengo yo,escribiendo en ella lo siguiente:

    $cd~$curl-Ohttps://raw.githubusercontent.com/carlosazaustre/mac-dev-setup/master/.bash_profile$curl-Ohttps://raw.githubusercontent.com/carlosazaustre/mac-dev-setup/master/.bash_prompt$curl-Ohttps://raw.githubusercontent.com/carlosazaustre/mac-dev-setup/master/.aliases$curl-Ohttps://raw.githubusercontent.com/carlosazaustre/mac-dev-setup/master/.gitconfig

    Reinicia la iTerm y ya lo tendrs configurado.

    Git es muy importante para un desarrollador, es una forma de guardar las versiones ycambios que hagamos en nuestros desarrollos, poder colaborar en otros proyectos desoftware libre con la comunidad y poco a poco labrarse un currculum.

    Para tener Git en tu sistema, lo puedes instalar con homebrew en Mac. Para instalar estegestor de paquetes en Mac, necesitas correr los siguientes scripts en tu terminal:

    #InstalahomebrewparaMac.Ungestordepaquetesalestilodeapt-getenUbuntu$ruby-e"$(curl-fsSLhttps://raw.github.com/Homebrew/homebrew/go/install)"$brewupdate$exportPATH="/usr/local/bin:$PATH"

    Despus ya podemos instalar Git con:

    $brewinstallgit

    Cuando lo hayas hecho, puedes comprobar la versin con:

    $git--versiongitversion1.8.4

    Procedemos a configurar git con nuestro usuario:

    $gitconfig--globaluser.name"CarlosAzaustre"$gitconfig--globaluser.email"[email protected]"

    3.4 Instalar Git

  • Cada vez que hagamos push a un repositorio remoto, nos pedir el nombre ycontrasea de nuestra cuenta que ahora crearemos.

    GitHub adems de ser un sistema de control de versiones (o repositorio) en la nube, estambin una red social de desarrolladores, donde se forman comunidades entorno aproyectos Open Source. Si eres desarrollador y no tienes una cuenta en GitHub,lamento decirte que no existes. Crea hoy mismo tu cuenta en Github y empieza apublicar tus proyectos libres. Su pgina web es github.com

    Si por el contrario, quieres usar la nube para tus repositorios privados, tienes la opcinde GitLab con la que tienes repositorios ilimitados y tambin, al ser OpenSource laopcin de instalarlo en un servidor propio y as tenerlo ms controlado.

    3.4.1 Registro en GitHub

    3.4.2 Registro en GitLab

  • Para comenzar a desarrollar nuestro proyecto Frontend, vamos a estructurar losarchivos de la siguiente manera:

    project/app/dist/package.jsonbower.jsonREADME.md.gitignore.editorconfig.jshintrc

    Posteriormente iremos aadiendo ms ficheros y subdirectorios, pero para comenzareste ser nuestro esqueleto.

    project es la carpeta raiz de nuestro proyecto web y tenemos 2 subdirectorios pordebajo de l.

    app contiene todo el cdigo fuente completo de nuestra aplicacin web, sin

    3.5 Estructura de directorios y ficheros

  • minificar ni optimizar, solamente el cdigo de desarrollo. Y en dist tendremos todoel proyecto minificado y optimizado para ser desplegado en produccin.

    package.json contiene la informacin de nuestro proyecto as como el nombre y lasversiones de las dependencias que utilizaremos para desarrollo.

    bower.json es similar a package.json para manejar las dependencias queusaremos en el Frontend, como pueden ser las libreras de Angular, frameworksCSS, etc...

    README.md es el fichero que usamos para dar informacin acerca de la aplicacinque estamos desarrollando y como documentacin del proyecto.

    .gitignore nos sirve para indicar a Git qu archivos no queremos que se suban alrepositorio (por ejemplo, claves, ficheros de configuraciones, etc.)

    .editorconfig nos permite configurar nuestro editor de texto para que todos losdesarrolladores del mismo proyecto, que lo utilicen, tengan en el cdigo el mismoespaciado, tabulacin, etc...

    .jshintrc es un fichero JSON que nos permite comprobar errores tanto de cdigocomo de sintaxis, uso de variables, o estilo en nuestro cdigo JavaScript segnguardamos los archivos. Combinado con un gestor de tareas, como Gulp, queusaremos ms adelante, nos permite un desarrollo gil de nuestros proyectos.

    Este es el contenido que utilizaremos, cada uno puede adaptarlo a sus preferencias:

    root=true

    [*]indent_style=spaceindent_size=2end_of_line=lfcharset=utf-8trim_trailing_whitespace=trueinsert_final_newline=true

    [*.md]trim_trailing_whitespace=falseindent_style=tab

    En este archivo estamos indicando que queremos que el indentado sea con espacios en

    .editorconfig

  • lugar de con tabulaciones. El tamao de la tabulacin sean 2 espacios. El final de lnealo marque como LF. La codificacin de caracteres a usar sea UTF-8. Y para archivos.md como es el caso del README.md la identacin sea con tabulaciones.

    Puedes consultar el resto de propiedades a configurar en la documentacin de supgina web: http://editorconfig.org/ y tambin descargar el plugin para tu editor de textofavorito.

    Este es el contenido de nuestro fichero JSHint. Puedes ver que propiedades aadir enla pgina web de su documentacin: http://www.jshint.com/docs/options/:

    {"node":true,"browser":true,"esnext":true,"bitwise":true,"camelcase":true,"curly":true,"eqeqeq":true,"immed":true,"indent":4,"latedef":true,"newcap":true,"noarg":true,"quotmark":"single","undef":true,"unused":true,"strict":true,"trailing":true,"smarttabs":true,"jquery":true,"globals":{"angular":false}}

    Es un archivo JSON a modo de manifiesto donde incluimos toda la informacin relativa anuestro proyecto. Se pueden aadir numerosos campos. Puedes ver que camposaadir en la documentacin de la siguiente pgina web:https://www.npmjs.org/doc/files/package.json.html. Para el ejemplo de este libro, ste esel contenido que tendremos:

    .jshintrc

    package.json

  • {"name":"angularapp-gulp-boilerplate","version":"0.0.1","description":"EjemplodedesarrollodeaplicacinwebconAngularJS","bugs":{"url":"https://github.com/carlosazaustre/angularapp-gulp-boilerplate/issues","email":"[email protected]"},"license":"MIT","author":"CarlosAzaustre-http://carlosazaustre.es","repository":{"type":"git","url":"https://github.com/carlosazaustre/angularapp-gulp-boilerplate"},"dependencies":{},"devDependencies":{}}

    El objeto devDependencies lo iremos rellenando cada vez que instalemos un nuevopaquete va npm desde el terminal.

    Como dijimos anteriormente, es similar al package.json pero para las dependencias ylibreras que emplearemos para el Frontend. El contenido de este fichero ser elsiguiente:

    {"name":"angularapp-gulp-boilerplate","version":"0.0.1","description":"EjemplodedesarrollodeaplicacinwebconJavaScript","dependencies":{}}

    El objeto dependencies lo iremos rellenando cada vez que instalemos una nuevadependencia va bower desde el terminal.

    Antes de nada, debemos instalar Node.js. Aunque esto sea un proyecto de Frontend yla parte Backend no la vamos a desarrollar (usaremos un BackFake), necesitamosNode.js para poder instalar Bower, Gulp y ejecutar las tareas que especifiquemos en elGulpfile.js.

    bower.json

    3.6 Automatizando nuestro flujo de trabajo

  • Para instalar Node puedes dirigirte a su pgina web [http://nodejs.org/downloads] o sitienes Mac, desde la terminal puedes hacerlo con homebrew como hicimos con git:

    #InstalaNode.jsconhomebrew$brewinstallnode$node-vv0.10.26$npm-v1.4.3

    Con Node.js instalado, procedemos a instalar las siguientes dependencias, de modoglobal (usando el flag -g) que utilizaremos a lo largo de nuestro proyecto. Al serinstaladas globalmente, podremos ejecutar los comandos desde cualquier directorio yen cualquier otro proyecto:

    $npminstall-ggulp$npminstall-gbower

    Gulp es un lanzador de tareas que corre bajo Node.js, nos permite automatizar tareasque hagamos a menudo. Hasta ahora el gestor ms conocido era Grunt pero lasimplicidad y rapidez que ofrece Gulp hace que se est extendiendo en muchosproyectos. Gulp posee una gran comunidad y existen plugins para cualquier cosa quese nos ocurra. Procedemos pues a instalar las dependencias y plugins que vamos aemplear localmente en nuestro proyecto. En este caso s debemos situarnos en eldirectorio raiz de nuestra aplicacin y escribir lo siguiente:

    $npminstall--save-devgulp$npminstall--save-devgulp-connect$npminstall--save-devconnect-history-api-fallback$npminstall--save-devgulp-jshint$npminstall--save-devgulp-useref$npminstall--save-devgulp-if$npminstall--save-devgulp-uglify$npminstall--save-devgulp-minify-css$npminstall--save-devgulp-stylus$npminstall--save-devnib

    Estas dependencias nos servirn para automatizar la correcin de cdigo de JavaScript,el minificado del css, la creacin de un servidor web de desarrollo para poder ver loscambios que hagamos en el cdigo en tiempo real en el navegador, etc... Iremos viendoen detalle cada paquete a medida que avancemos.

  • Si nos fijamos, nuestro package.json ha cambiado, y el objeto devDependencies tieneahora esta pinta:

    "devDependencies":{"gulp":"^3.8.6","gulp-connect":"^2.0.6","connect-history-api-fallback":"0.0.4","gulp-jshint":"^1.8.0","gulp-useref":"^0.6.0","gulp-if":"^1.2.4","gulp-uglify":"^0.3.1","gulp-minify-css":"^0.3.7","gulp-stylus":"^1.3.0","nib":"^1.0.3"}

    Se han aadido las dependencias junto con su nmero de versin en el archivo demanera automtica, gracias al flag --save y con -dev en el objecto en cuestin.

    Vamos a crear un fichero Gulpfile.js en el directorio raz de nuestro proyecto, en elque vamos a detallar unas cuantas tareas para comenzar que nos agilizarn la vida:

    vargulp=require('gulp'),connect=require('gulp-connect'),historyApiFallback=require('connect-history-api-fallback');

    //Servidorwebdedesarrollogulp.task('server',function(){connect.server({root:'./app',hostname:'0.0.0.0',port:8080,livereload:true,middleware:function(connect,opt){return[historyApiFallback];}});});

    Esta tarea toma el contenido del directorio app y lo muestra como si fuera un servidorweb, en la direccin http://localhost:8080, http://127.0.0.1:8080 ohttp://0.0.0.0:8080. Al indicar el hostname 0.0.0.0 conseguimos que el livereload,que es lo que permite ver los cambios que hacemos en tiempo real, sea visible desdecualquier dispositivo conectado a la misma red.

    Gulpfile.js

  • Imaginemos que en nuestra red local, nuestro equipo tiene la IP privada 192.168.1.20.Si desde un mvil, un tablet u otro ordenador, conectado a la misma red que nuestroequipo de desarrollo, accedemos a la direccin http://192.168.1.20:8080 veremosnuestra aplicacin web y los cambios que hagamos se reflejarn de manera automtica.Maga negra.

    Para ello debemos seguir configurando el fichero Gulpfile.js, vamos a aadir que loscambios que hagamos en un fichero .styl de Stylus, se vean reflejados como CSS enel navegador. Stylus es un preprocesador de CSS, permite escribir el diseo en unlenguaje y despues transformarlo a CSS, E n el capitulo 4 entramos ms en detalle eneste tema. De momento, para ver un ejemplo rpido, aadimos las siguientes tareas:

    varstylus=require('gulp-stylus'),nib=require('nib');

    //Pre-procesaarchivosStylusaCSSyrecargaloscambiosgulp.task('css',function(){gulp.src('./app/stylesheets/main.styl').pipe(stylus({use:nib()})).pipe(gulp.dest('./app/stylesheets')).pipe(connect.reload());});

    //RecargaelnavegadorcuandohaycambiosenelHTMLgulp.task('html',function(){gulp.src('./app/**/*.html').pipe(connect.reload());});

    //Vigilacambiosqueseproduzcanenelcdigo//ylanzalastareasrelacionadasgulp.task('watch',function(){gulp.watch(['./app/**/*.html'],['html']);gulp.watch(['./app/stylesheets/**/*.styl'],['css']);});

    gulp.task('default',['server','watch']);

    La tarea css toma el archivo app/sytlesheets/main.styl y preprocesa su contenido acss en el fichero app/stylesheets/main.css, usando la librera nib que aade de formaautomtica las propiedades css para Firefox, Internet Explorer y Webkit. As nosahorramos tener que escribir los prefijos especficos de cada navegador.

    La tarea html simplemente reinicia el navegador a travs de connect.reload() cadavez que haya un cambio en un archivo HTML.

    La tarea watch vigila los cambios que se produzcan en ficheros HTML y Stylus y lanza

  • las tareas html y css respectivamente.

    Por ltimo, definimos una tarea por defecto default que lance las tareas server ywatch. Para ejecutar la tarea que engloba a todas, escribimos en el terminal

    $gulp

    Se lanzarn todas las tareas y podremos hacer cambios en nuestro cdigo que sereflejarn en el navegador sin necesidad de recargar manualmente.

    Veamos un rpido ejemplo para poner todo esto en marcha y ver como funciona Gulp ysus tareas automatizadas. Creamos un fichero de prueba index.html dentro de lacarpeta app. con el siguiente contenido:

    Ejemplo

    HolaMundo!

    Como se puede ver, enlazamos a un fichero de estilos main.css en la carpetaapp/stylesheets Este fichero ser generado por Gulp a partir de los ficheros .styl enlos que codificamos nuestro diseo.

    Por tanto creamos un fichero main.styl en app/stylesheets con el siguiente contenido:

    bodybackground-colorwhite;colorblack;

    Salvamos y ejecutamos Gulp. Si nos dirigimos a una ventana del navegador enhttp://localhost:8080 podremos ver lo siguiente:

  • Sin parar el proceso de Gulp y sin cerrar el navegador, vamos de nuevo a nuestrofichero main.styl y cambiamos lo siguiente:

    bodybackground-colorblue;colorwhite;

    Salvamos y automticamente nuestro navegador cambiar a esto:

  • De esta manera ya tenemos configurado Gulp para recargar el navegadorautomticamente segn los cambios que hagamos en el HTML y en el CSS,quedndonos as un flujo de trabajo mucho ms gil que nos permite ahorrar muchotiempo de desarrollo.

    Por ltimo vamos a aadir una ltima tarea (por ahora), cuya funcin sea revisar elcdigo JavaScript que escribimos, en busca de errores, concordancias de estilo, etc.Esto lo conseguimos con el plugin gulp-jshint en concordancia con el fichero.jshintrc que habamos escrito anteriormente. Aunque antes, aadiremos esta nuevadependencia, que nos permitir una mejor lectura de los errores por la terminal:

    $npminstall--save-devjshint-stylish

    Aadimos pues la siguiente tarea a nuestro fichero Gulpfile.js:

    varjshint=require('gulp-jshint'),stylish=require('jshint-stylish');

    //BuscaerroresenelJSynoslosmuestraporpantallagulp.task('jshint',function(){returngulp.src('./app/scripts/**/*.js')

  • .pipe(jshint('.jshintrc')).pipe(jshint.reporter('jshint-stylish')).pipe(jshint.reporter('fail'));});

    Tambien aadimos una nueva sub-tarea a la tarea watch para que vigile los cambiosque hagamos en el cdigo JavaScript y as lance la tarea que acabamos de crear:

    //Vigilacambiosqueseproduzcanenelcdigo//ylanzalastareasrelacionadasgulp.task('watch',function(){gulp.watch(['./app/**/*.html'],['html']);gulp.watch(['./app/stylesheets/**/*.styl'],['css']);gulp.watch(['./app/scripts/**/*.js'],['jshint']);});

    Nuestro fichero Gulpfile.js completo, por el momento, quedara de esta manera:

    //File:Gulpfile.js'usestrict';

    vargulp=require('gulp'),connect=require('gulp-connect'),stylus=require('gulp-stylus'),nib=require('nib'),jshint=require('gulp-jshint'),stylish=require('jshint-stylish'),historyApiFallback=require('connect-history-api-fallback');

    //Servidorwebdedesarrollogulp.task('server',function(){connect.server({root:'./app',hostname:'0.0.0.0',port:8080,livereload:true,middleware:function(connect,opt){return[historyApiFallback];}});});

    //BuscaerroresenelJSynoslosmuestraporpantallagulp.task('jshint',function(){returngulp.src('./app/scripts/**/*.js').pipe(jshint('.jshintrc')).pipe(jshint.reporter('jshint-stylish')).pipe(jshint.reporter('fail'));});

    //PreprocesaarchivosStylusaCSSyrecargaloscambiosgulp.task('css',function(){

  • gulp.src('./app/stylesheets/main.styl').pipe(stylus({use:nib()})).pipe(gulp.dest('./app/stylesheets')).pipe(connect.reload());});

    //RecargaelnavegadorcuandohaycambiosenelHTMLgulp.task('html',function(){gulp.src('./app/**/*.html').pipe(connect.reload());});

    //Vigilacambiosqueseproduzcanenelcdigo//ylanzalastareasrelacionadasgulp.task('watch',function(){gulp.watch(['./app/**/*.html'],['html']);gulp.watch(['./app/stylesheets/**/*.styl'],['css']);gulp.watch(['./app/scripts/**/*.js','./Gulpfile.js'],['jshint']);});

    gulp.task('default',['server','watch']);

    Para probar como funciona JSHint, vamos a escribir un pequeo archivo de ejemplo enJavaScript con errores, para que lance la tarea. Aadimos el fichero main.js en eldirectorio app/scripts

    (function(){console.log('holamundo!');})();

    Si ejecutamos Gulp, veremos lo siguiente en el terminal:

    $gulp[12:32:29]Usinggulpfile~/development/courses/ebook-frontend/project/Gulpfile.js[12:32:29]Starting'server'...[12:32:29]Serverstartedhttp://localhost:8080[12:32:29]LiveReloadstartedonport35729[12:32:29]Finished'server'after25ms[12:32:29]Starting'watch'...[12:32:29]Finished'watch'after13ms[12:32:29]Starting'default'...[12:32:29]Finished'default'after5.52s[12:32:38]Starting'jshint'...

    /Users/carlosazaustre/development/courses/ebook-frontend/project/app/scripts/main.jsline2col3Missing"usestrict"statement.

    1problem

    [12:32:38]'jshint'erroredafter69msJSHintfailedfor:/Users/carlosazaustre/development/courses/ebook-frontend/project/app/scripts/main.js

  • Nos encontramos con un error, y es que nos falta aadir la linea usestrict; alprincipio del fichero. Esto es debido a que en nuestro fichero .jshintrc le hemosindicado que verifique que en todos nuestros ficheros /js exista ese elemento o elparser nos mostrar error. As, en nuestro equipo, todos los desarrolladores seguirnlas mismas reglas de estilo.

    Aadimos esa lnea al archivo main.js:

    //File:app/scripts/main.js'usestrict';

    (function(){console.log('holamundo!');})();

    Seguidamente despus de salvar el archivo, vemos en la terminal que la tarea se haejecutado automticamente y ya no muestra errores:

    [12:37:08]Starting'jshint'...[12:37:08]Finished'jshint'after26ms

    Listo, ya tenemos automatizado nuestro flujo de trabajo cada vez que hagamos uncambio. En el siguiente capitulo pasaremos a ver como estructurar los archivosJavaScript y el uso de Bower para empezar a aadir funcionalidad a nuestra aplicacin.

  • En este captulo vamos a implementar nuestra aplicacin web de ejemplo conAngular.js. Para ello vamos a necesitar plantillas y libreras que nos permitan hacernuestro trabajo. Comencemos.

    Vamos a usar la plantilla HTML5Boilerplate con todos los elementos necesarios paraempezar a desarrollar. La puedes descargar desde la pgina web del proyecto o en surepositorio oficial, o simplemente copiar el siguiente archivo en tu app/index.html yaque le hemos hecho algunos cambios de acuerdo a nuestro proyecto:

    HolaMundo!EstoesHTML5Boilerplate.

    (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+newDate;e=o.createElement(i);r=o.getElementsByTagName(i)[0];e.src='//www.google-analytics.com/analytics.js';r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));ga('create','UA-XXXXX-X','auto');ga('send','pageview');

    4. Anatoma de una aplicacin AngularJS.

    4.1 HTML5Boilerplate

  • Esta ser nuestra nica pgina HTML completa que tendremos, ya que el resto sernplantillas que se cargarn dinmicamente segn el estado de la aplicacin o lainteraccin del usuario. Es lo que actualmente se conoce como Single PageApplications (SPA)

    Vamos a instalar las libreras de Frontend que necesitamos con la herramienta Bower.Antes de nada, necesitamos crear un archivo .bowerrc en el directorio raz del proyectocon el siguiente contenido:

    {"directory":"app/lib"}

    Con esto conseguimos que cada librera que instalemos, quede guardada en eldirectorio app/lib. Si no tenemos este archivo, las libreras quedan instaladas en eldirectorio por defecto bower_components en el raz.

    Vamos a preparar nuestro HTML e implementaremos una nueva tarea de Gulp que nosva a solucionar la vida. Se trata de la tarea gulp-inject la cual va a tomar los ficherosque tengamos en las carpetas de estilos y scripts y los va a inyectar como enlaces en elHTML.

    Aadimos al HTML los siguientes "tags" de comentarios donde irn los enlaces CSS ylos scripts de JavaScript:

    [...][...]

    4.2 Instalando dependencias

  • Las lineas como comentarios y sern las quelean la tarea de Gulp que vamos a escribir a continuacin que inyectar los archivos. Ylas lineas como comentarios y inyectarn laslibreras que instalemos con Bower. Dependiendo de si los archivos son de estilos (css)o scripts (js) los colocar en un sitio u otro dentro del HTML. Para implementar la tarea,primero debemos instalar los plugins requeridos:

    $npminstall--save-devgulp-inject$npminstall--save-devwiredep

    A continuacin ya podemos insertar las siguientes tareas en el Gulpfile.js y actualizarwatch y default:

    varinject=require('gulp-inject');varwiredep=require('wiredep').stream;

    //Buscaenlascarpetasdeestilosyjavascriptlosarchivosquehayamoscreado//parainyectarlosenelindex.htmlgulp.task('inject',function(){varsources=gulp.src(['./app/scripts/**/*.js','./app/stylesheets/**/*.css']);returngulp.src('index.html',{cwd:'./app'}).pipe(inject(sources,{read:false,ignorePath:'/app'})).pipe(gulp.dest('./app'));});//InyectalaslibreriasqueinstalemosvaBowergulp.task('wiredep',function(){gulp.src('./app/index.html').pipe(wiredep({directory:'./app/lib'})).pipe(gulp.dest('./app'));});

    gulp.task('watch',function(){[...]gulp.watch(['./app/stylesheets/**/*.styl'],['css','inject']);gulp.watch(['./app/scripts/**/*.js','./Gulpfile.js'],['jshint','inject']);gulp.watch(['./bower.json'],['wiredep']);});

    gulp.task('default',['server','inject','wiredep','watch']);

    Con Gulp ejecutndose en una ventana del terminal, ya podemos instalar desde otra las

  • dependencias con Bower, por ejemplo, empezamos con las de angular y el frameworkcss Bootstrap:

    $bowerinstall--saveangular$bowerinstall--savebootstrap

    Si abrimos index.html podemos ver que Gulp ha inyectado automticamente la librerade AngularJS y los ficheros main.css y main.js que tenamos como estilos y losscripts de Bootstrap tambin han sido inyectados:

    [...][...]

    Si por cualquier motivo, no necesitamos hacer uso de alguna librera de las que hemosinstalado, las podemos eliminar con uninstall como el siguiente ejemplo:

    $boweruninstall--saveangular

    Todos los ficheros que contienen la funcionalidad de nuestra aplicacin, estarn dentrodel directorio app/scripts.

    4.3 Mdulos de la aplicacin

  • Lo ideal en Angular es crear mdulos para cada funcionalidad que tengamos en laaplicacin. Si la aplicacin no es muy grande, como ser el caso del ejemplo de estelibro, podemos reunir las funcionalidades afines dentro de un mismo archivo y sloseparar controladores, de servicios, directivas, etc... De esta manera tendremos unaaplicacin escalable y mantenible.

    El proyecto que vamos a realizar para este ejemplo ser un blog, con entradas ycomentarios.

    Los datos es ideal que los recibamos de una API RESTful que devuelva los datos enformato JSON. El desarrollo del Backend se escapa del mbito de este libro, es por elloque vamos a utilizar el proyecto JSONPlaceholder que proporciona una API de pruebapara testear y prototipar que devuelve JSONs de blogposts y comentarios.

    las URLs de la API que vamos a emplear son:

    URL Method

    /posts POST

    /posts GET

    /posts/:postId GET

    /comments GET

    /comments/:commentId GET

    /users GET

    /users/:userId GET

    A continuacin veremos unos ejemplos de los datos en formato JSON que recibimos dela API proporcionada por JSONPlaceholder:

    GET /posts/1:

    {"userId":1,"id":1,"title":"suntautfacererepellatprovidentoccaecatiexcepturioptioreprehenderit""body":"quiaetsuscipit\nsuscipitrecusandaeconsequunturexpeditaetcum\nreprehenderitmolestiaeututquastotam\nnostrumrerumestautemsuntremevenietarchitecto"}

    GET /comments/1:

  • {"postId":1,"id":1,"name":"idlaboreexetquamlaborum","email":"[email protected]","body":"laudantiumenimquasiestquidemmagnamvoluptateipsameos\ntemporaquonecessitatibus\ndolorquamautemquasi\nreiciendisetnamsapienteaccusantium"}

    GET /users/1:

    {"id":1,"name":"LeanneGraham","username":"Bret","email":"[email protected]","address":{"street":"KulasLight","suite":"Apt.556","city":"Gwenborough","zipcode":"92998-3874","geo":{"lat":"-37.3159","lng":"81.1496"}},"phone":"1-770-736-8031x56442","website":"hildegard.org","company":{"name":"Romaguera-Crona","catchPhrase":"Multi-layeredclient-serverneural-net","bs":"harnessreal-timee-markets"}}

    La estructura de archivos que vamos a utilizar dentro del directorio app ser lasiguiente:

    app/lib/scripts/|services.js|controllers.js|app.jsstylesheets/views/|post-detail.tpl.html

    4.3.1 Arquitectura

  • |post-list.tpl.html|post-create.tpl.html|index.html

    Nota: lo ideal, para que una aplicacin Angular sea ms modular, es separar lasfuncionalidades por carpetas, y que cada funcionalidad tenga sus servicios,controladores, rutas, etc... separados del resto. Sera algo como esto:

    app/lib/modules/|users/||module.js||controllers.js||services.js|posts/||module.js||controllers.js||services.js||views/||post-detail.tpl.html||post-list.tpl.html||post-create.tpl.html|comments|module.js|controllers.js|services.jsstylesheets/|index.html

    Incluso, separar los estilos por funcionalidad. Pero esto escapa del mbito deeste libro, ya que la funcionalidad que vamos a realizar para el ejemplo es muysencilla y puede ser escrita con la arquitectura anterior.

    Vamos a instalar las siguientes libreras que vamos a necesitar, lo hacemos comosiempre con Bower, con el "flag" --save-dev para que queden guardadas en el archivobower.json y Gulp pueda ejecutar la tarea pertinente.

    $bowerinstall--saveangular-route$bowerinstall--saveangular-resource

    angular-route nos permite hacer uso de la directiva $routeProvider para podermanejar URLs desde el navegador y mostrar una pgina u otra al usuario enfuncin.

  • angular-resource por su parte, nos deja emplear la directiva $resource que nospermite manejar peticiones AJAX a recursos REST de una manera ms sencilla ycon una sintaxis ms limpia, en lugar de usar las directivas $http.get o$http.post.

    Para poder indicar al HTML que estamos usando una aplicacin Angular, tenemos queponer el atributo ng-app en una parte de nuestro index.html, en este caso lopondremos en la etiqueta body para que englobe toda la pgina. A esta aplicacin lallamaremos "blog":

    CrearEntrada

    En el ejemplo anterior adems del atributo ng-app="blog" hemos aadido algo demaquetacin usando clases de Bootstrap, como son col-sm-3 y col-sm-9 que nospermiten tener 2 columnas en la pgina, una de tamao 3/12 que puede servir para lainformacin sobre el autor del blog, y otra de tamao 9/12 para el contenido y el listadode blogs.

    Esta ltima columna a su vez lleva el atributo ng-view que le indica a la aplicacinAngular blog que en ese espacio se cargarn las vistas parciales que manejaremosgracias al routing de $routeProvider ms adelante.

    Empezamos con el primero de los scripts JS que darn funcionalidad a la aplicacin, eneste caso el principal app/scripts/app.js

    En todos los ficheros JS, adems de usar la anotacin "usestrict"; vamos aagruparlos por mdulos de Angular y a su vez como closures. Esto nos ayuda a que lasvariables que usemos dentro de esa funcin solo queden definidas dentro de ella, y a lahora de minificar no aparezcan errores. El fichero comenzara as:

    index.html

    4.3.2 Principal

  • (function(){'usestrict';//Aquirlafuncionalidad})();

    A continuacin configuramos el mdulo blog con la dependencia ngRoute queobtenemos de aadir la librera angular-route

    (function(){'usestrict';

    angular.module('blog',['ngRoute']);

    })();

    Vamos a crear una funcin de configuracin para indicarle a la aplicacin que rutasescuchar en el navegador y que vistas parciales cargar en cada caso. Esto lo hacemoscon la directiva $routeProvider.

    functionconfig($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'});}

    La lnea $locationProvider.html5Mode(true) es importante, ya que permite que las URLsno lleven el caracter # al inicio de ellas, como utiliza por defecto Angular. Quedandoas ms limpias.

    Importante Para que el modo HTML5 funcione correctamente, debes aadir eltag base en tu documento index.html, dentro de las cabeceras tal que

  • as:

    ...

    Puedes encontrar ms informacin en este enlace

    Hemos aadido tres rutas, la raiz o principal /, la de detalle de un blog postpost/:postId y la del formulario para publicar una nueva entrada. Cada una de ellascarga una vista parcial que vamos a crear en unos momentos y estar almacenada enla carpeta app/views. Tambin cada vista tiene un controlador asociado que sern losque manejen la funcionalidad asociada.

    Estos sern PostListController, para manejar la lista de post y PostDetailControllerpara manejar un post concreto y PostCreateController. Todas ellas las declararemosen un fichero y mdulo aparte, blog.controllers, por lo que para poder hacer uso deellas en este archivo, debemos incluirlo como dependencia al declarar el mdulo, aligual que hicimos con ngRoute.

    El atributo controllerAs nos permite usar variables del controlador dentro de la plantillaHTML sin necesidad de emplear la directiva $scope

    (function(){'usestrict';

    angular.module('blog',['ngRoute','blog.controllers']);[...]})

    Para finalizar con este archivo, tan solo debemos asociar la funcin config creada almodulo:

    angular.module('blog').config(config);

    El fichero app/scripts/app.js completo quedara de la siguiente manera:

  • (function(){'usestrict';

    angular.module('blog',['ngRoute','blog.controllers']);

    functionconfig($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);

    })();

    Antes de implementar las funciones de los controladores, vamos a crear unos servicioscon la directiva $resource que nos permitirn hacer llamadas AJAX a la API de unamanera ms sencilla. Vamos a crear tres factoras (as es como se llaman), una para losPosts, otra para los Comentarios y otra para los Usuarios. Cada una de ellas estarasociada a una URL de la API REST.

    Al igual que en el anterior fichero, empezamos creando un closure, y el nombre delmdulo (blog.services) al que le incluimos la dependencia ngResource contenida en lalibreria angular-resource que nos permite emplear la directiva $resource:

    (function(){'usestrict';

    angular.module('blog.services',['ngResource']);

    })();

    4.3.3 Servicios

  • Despus creamos 3 funciones, una para cada factory que apuntarn a una URL. laURL base del servidor se la pasaremos como constante.

    functionPost($resource,BaseUrl){return$resource(BaseUrl+'/posts/:postId',{postId:'@_id'});}

    functionComment($resource,BaseUrl){return$resource(BaseUrl+'/comments/:commentId',{commentId:'@_id'});}

    functionUser($resource,BaseUrl){return$resource(BaseUrl+'/users/:userId',{userId:'@_id'});}

    Asociamos estas factoras al mdulo creado, y creamos tambin una constanteBaseUrl que apunte a la URL de la API:

    angular.module('blog.services').constant('BaseUrl','http://jsonplaceholder.typicode.com').factory('Post',Post).factory('Comment',Comment).factory('User',User);

    El fichero app/scripts/services.js al completo sera:

    (function(){'usestrict';

    angular.module('blog.services',['ngResource']);

    functionPost($resource,BaseUrl){return$resource(BaseUrl+'/posts/:postId',{postId:'@_id'});}

    functionComment($resource,BaseUrl){return$resource(BaseUrl+'/comments/:commentId',{commentId:'@_id'});}

    functionUser($resource,BaseUrl){return$resource(BaseUrl+'/users/:userId',{userId:'@_id'});}

  • angular.module('blog.services').constant('BaseUrl','http://jsonplaceholder.typicode.com').factory('Post',Post).factory('Comment',Comment).factory('User',User);

    })();

    Con los servicios ya creados podemos dar paso a implementar los controladores de lasvistas parciales y por tanto de la aplicacin. Como siempre, creamos un mdulo deangular, en este caso blog.controllers, con la dependencia de blog.services y loincluimos dentro de un closure:

    (function(){'usestrict';

    angular.module('blog.controllers',['blog.services']);

    })();

    El primer controlador PostListController es el ms sencillo de todos, la funcin seraas:

    functionPostListController(Post){this.posts=Post.query();}

    Aqu lo que hacemos es una llamada AJAX a la URlhttp://jsonplaceholder.typicode.com/posts y que nos devuelva el resultado dentro de lavariable posts. Eso lo conseguimos con el servicio Post.

    Ahora pasamos al controlador de la vista detalle, PostDetailController. Si tan soloqueremos ver el contenido de un post en concreto y sus comentarios, le pasamos el iddel post que queremos mostrar que nos proporciona la ruta del navegador a travs de$routeParams la funcin sera as:

    functionPostDetailController($routeParams,Post,Comment){this.post=Post.query({id:$routeParams.postId});this.comments=Comment.query({postId:$routeParams.postId});}

    4.3.4 Controladores

  • Pero, si queremos ver la informacin del usuario que ha escrito la entrada, Cmo lohacemos?. lo primero en lo que piensas es algo as:

    functionPostDetailController($routeParams,Post,Comment,User){this.post=Post.query({id:$routeParams.postId});this.comments=Comment.query({postId:$routeParams.postId});this.user=User.query({id:this.post.userId});}

    Pero nuestro amigo JavaScript no es secuencial, es asncrono, y cuando ejecutamos la3a lnea, this.post an no tiene nada y nos da error, porque en ese momentothis.post.userId es undefined. Cmo lo solucionamos? A travs de la directiva$promise y en una funcin de callback:

    functionPostDetailController($routeParams,Post,Comment){this.post={};this.comments={};this.user={}

    varself=this;//Paraguardarlareferencia

    Post.query({id:$routeParams.postId}).$promise.then(//Successfunction(data){self.post=data[0];self.user=User.query({id:self.user.userId});},//Errorfunction(error){console.log(error);});

    this.comments=Comment.query({postId:$routeParams.postId});}

    De esta manera, slo cuando tenemos los datos relativos al post, podemos acceder aellos para hacer mas consultas, como en este caso, los datos de un usuario relacionadocon el post.

    El siguiente controlador es el que permite crear una nueva entrada:

    functionPostCreateController(Post){

  • varself=this;

    this.create=function(){Post.save(self.post);};}

    El fichero completo quedara as:

    (function(){

    angular.module('blog.controllers',['blog.services']).controller('PostListController',PostListController).controller('PostCreateController',PostCreateController).controller('PostDetailController',PostDetailController)

    functionPostListController(Post){this.posts=Post.query();};

    functionPostCreateController(Post){varself=this;

    this.create=function(){Post.save(self.post);};};

    functionPostDetailController($routeParams,Post,Comment){this.post={};this.comments={};this.user={}

    varself=this;//Paraguardarlareferencia

    Post.query({id:$routeParams.postId}).$promise.then(//Successfunction(data){self.post=data[0];self.user=User.query({id:self.user.userId});},//Errorfunction(error){console.log(error);});

    this.comments=Comment.query({postId:$routeParams.postId});};

    })();

  • Gracias al recurso Post podemos hacer uso del mtodo save() que se encarga derealizar una peticin POST a la API que estamos manejando. Como la API queempleamos es fake el POST no se llega a almacenar, pero en una API real si ocurrira.

    Aadimos las funciones creadas al mdulo blog.controllers y ya podemos usar lasvariables this.posts, this.post, this.comments y this.user en nuestras vistasparciales, as como recoger los datos que se envan en el formulario de creacin deentradas. Lo siguiente que haremos ser crear las vistas parciales o plantillas.

    sta ser la vista que muestre la lista de post que nos devuelve la API. Est manejadapor el controlador PostListController, que si recordamos, tena en su interior lavariable this.posts donde estn almacenados todos. Para acceder a esa variabledesde el HTML tan slo debemos indicar el "alias" que pusimos al controlador en lafuncin config de app/scripts/app.js que fue postlist y ah acceder con notacin"punto" al atributo posts con postlist.posts.

    Como es un array de elementos, podemos iterar sobre ellos y para ello usar la directivang-repeat de Angular de la siguiente manera:

    {{post.title}}

    El cdigo anterior recorre el array de posts, y para cada uno de ellos, pinta un elemento en el HTML, con un enlace que apunta al id del post con post.id y el ttulo delpost con post.title

    Si en un navegador accedemos a http://localhost:8080 veramos una lista de ttulos,todos ellos devueltos a travs de la API que nos proporcionahttp://jsonplaceholder.com/posts

    Le hemos aadido tambin a las etiquetas HTML clases para poder utilizarlas despusen los ficheros CSS para darles estilo.

    4.3.5 Vistas parciales

    views/post-list.tpl.html

    views/post-detail.tpl.html

  • Ahora vamos a crear la vista detalle de un post en concreto, donde emplearemos lasvariables post, comments y user que utiliza el controlador PostDetailController atravs del alias postdetail.

    Primero maquetamos el elemento donde colocaremos el contenido del posty el usuario que lo ha escrito, los cuales estn almacenados en las variablespostdetail.post y postdetail.user:

    {{postdetail.post.title}}{{postdetail.post.body}}Escritopor:{{postdetail.user[0].name}}{{postdetail.user[0].email}}

    A continuacin aadimos el elemento donde estarn la lista de comentariosasociados, almacenados en la variable postdetail.comments. Como es un array deelementos, podemos hacer uso de la directiva ng-repeat como en la lista de post, paramostrarlos en la vista:

    Commentarios{{comment.email}}{{comment.body}}

    Esta ser la vista que veamos cuando pulsemos en el enlace CrearEntrada,

    views/post-create.tpl.html

  • simplemente se trata de un formulario con los campos necesarios para introducir elttulo del post y su contenido:

    Publicar

    Las clases utilizadas son parte del framework de CSS Bootstrap y las usamos para darestilo al ejemplo.

    Si nos fijamos, tanto el input como el textarea tienen un atributo ng-model. Esto nossirve para que Angular, a travs del controlador que hemos definido anteriormente paraeste formulario, pueda recoger estos datos y nosotros envirselos a la API. Estarncomprendidos dentro del objeto this.post

    Otra cosa importante que hemos hecho, es inhabilitar el botn de "Publicar" hasta queel formulario est rellenado. Esto lo conseguimos con el atributo ng-disabled="!createPost.$valid" siendo createPost el name que le hemos puesto alformulario.

    El procesamiento de este formulario se hace a travs de la funcin this.create() delPostCreateController que llamamos desde el atributo ng-submit.

    Con esto finalizaramos la maquetacin y funcionalidad de esta aplicacin de ejemplocon AngularJS, usando factoras como modelo para obtener los datos de una APIexterna. Ahora es momento de aadir unas gotas de estilo CSS (usando Stylus) pararematar nuestra app.

  • Aunque puedes disear un sitio con CSS completamente desde cero, para este ejemplohemos partido de uno de los frameworks ms conocidos, Bootstrap que como sunombre indica, sirve para empezar rpido, tener un diseo de grilla (columnas) yalgunos elementos predefinidos con un estilo agradable.

    Aparte de usar Bootstrap, puedes darle estilo a elementos de tu web con CSS, o utilizarun pre-procesador como pueden ser Less, Sass o Stylus que te faciliten el diseo,usando variables, funciones, etc. En nuestro caso vamos a usar Stylus, que usa unasintaxis muy parecida a Python, basada en tabulaciones, no se utilizan llaves {...}, ni:, ni ;

    Stylus funciona con Node.js. Para hacerlo correr debemos instalarlo de manera globalen nuestro equipo con npm:

    $npminstall-gstylus

    Los archivos .styl los vamos a guardar en app/stylesheets y la tarea de Gulp queconfiguramos en el capitulo 2, los compilar directamente a CSS y veremos los cambiosreflejados en el navegador en tiempo real.

    Fontawesome es una librera CSS de iconos tipo fuente, ideal para usar en todo tipo deresoluciones, dado que los archivos de fuentes son vectoriales y no se pixelan. Parausarlo en nuestra aplicacin lo podemos instalar va bower

    $bowerinstall--savefontawesome

    Y a partir de ahora cuando queremos emplear un icono en nuestros HTML nicamentedebemos poner la siguiente clase a un elemento

    5. Diseo con preprocesadores CSS

    5.1 Fontawesome

  • Para este ejemplo hemos usado varios en la plantilla app/views/post-detail.tpl.htmlcomo:

    {{postdetail.user[0].email}}

    para mostrar un icono de un sobre que simbolice el correo electrnico, o:

    para mostrar unos globos de texto para simbolizar los comentarios, o por ltimo:

    para simbolizar un icono de usuario.

    Tienes una lista completa de todos los que se pueden usar en su documentacin

    la cul es muy completa y fcil de usar.

    5.2 Fuentes Tipogrficas

  • El navegador por defecto incluye una serie de fuentes, pero nosotros podemos usarotras con slo incluirlas en el CSS, como por ejemplo Google Webfonts. En mi caso heelegido la tipografa Raleway para emplearla en este ejemplo.

    Para ello creamos un archivo fonts.styl dnde aadimos el siguiente cdigo:

    @font-facefont-family'Raleway-Light'font-stylenormalfont-weight300srclocal('RalewayLight'),local('Raleway-Light'),url('//themes.googleusercontent.com/static/fonts/raleway/v7/-_Ctzj9b56b8RgXW8FArib3hpw3pgy2gAi-Ip7WPMi0.woff'

    @font-facefont-family'Raleway'font-stylenormalfont-weight400srclocal('Raleway'),url('//themes.googleusercontent.com/static/fonts/raleway/v7/cIFypx4yrWPDz3zOxk7hIQLUuEpTyoUstqEm5AMlJo4.woff'

    Para poder enlazar este archivo con el principal lo hacemos con @import. Lo aadimosen main.styl que es el que indicamos a Gulp que procese y genere los archivos css.Para poder emplearla, se lo asignamos al atributo font-family de body, de estamanera:

    @import'fonts'bodyfont-family'Raleway-Light'font-size16px

    Ahora nuestra aplicacin tendr un estilo de fuente diferente. Vamos a editar un poco elestilo de la lista de posts:

    .blog-post-listlist-style-typenone

    .blog-post-linkfont-family'Raleway'margin.5em01em0padding00.25em0border-bottom1pxsolidb_silver

    5.3 Estilos de la aplicacin

  • Y ahora el titulo del post en la vista detalle:

    .blog-post.blog-post-headerh1font-family'Raleway'

    Y los comentarios:

    .comments

    .comments-listlist-style-typenone

    .comment-itemborder1pxsolidb_silverborder-radius5pxpadding.5emmargin.5em

    .comment-authorfont-family'Raleway'font-weightbold

    .comment-bodyfont-styleitalic

    Si nos fijamos, hemos indicado que el color del borde de la caja del comentario seab_silver, y ese color no existe en el HTML/CSS estndar. Lo vamos a aadir comouna variable en Stylus y as poder reutilizarlo ms adelante si queremos:

    b_silver=#ddd

    el fichero main.styl completo quedara as:

    @import'fonts'

    b_silver=#ddd

    bodyfont-family'Raleway-Light'font-size16px

    .blog-post-listlist-style-typenone

  • .blog-post-linkfont-family'Raleway'margin.5em01em0padding00.25em0border-bottom1pxsolidb_silver

    .blog-post

    .blog-post-headerh1font-family'Raleway'

    .comments

    .comments-listlist-style-typenone

    .comment-itemborder1pxsolidb_silverborder-radius5pxpadding.5emmargin.5em

    .comment-authorfont-family'Raleway'font-weightbold

    .comment-bodyfont-styleitalic

    Y as es como quedara nuestra app, si ejecutamos Gulp en la terminal y abrimos elnavegador en la URL http://localhost:8080:

    Lista de Posts

  • Detalle de Post

  • Creacin de una nueva entrada

  • Ya tenemos nuestra aplicacin funcionando, pero tiene varios archivos CSS con partesde Bootstrap que quiz ni lleguemos a usar. Demasiados archivos JavaScript, quesuponen varias peticiones al servidor y hacen que nuestra pgina tarde ms en cargar:

    [...][...][...]

    Lo ideal es que nuestro HTML slo tenga un fichero CSS al que llamar, y tambin unsolo JS. Debemos concatenarlos en uno slo y si es posible minificarlos (eliminandoespacios en blanco y dejando todo el contenido en una lnea), para que el fichero pesemenos y su carga sea ms rpida.

    Hacerlo a mano es una tarea bastante tediosa y poco productiva, para eso tenemosautomatizadores de tareas como Gulp para que lo hagan por nosotros.

    Vamos a instalar unos plugins de Gulp que nos ayudarn en estas tareas, lo hacemosva npm:

    $npminstall--save-devgulp-minify-css$npminstall--save-devgulp-angular-templatecache$npminstall--save-devgulp-uncss$npminstall--save-devgulp-if

    A continuacin detallamos que hace cada plugin y en que tareas dentro del

    6. Optimizando para produccin

  • Gulpfile.js vamos a implementarlos.

    La primera tarea que vamos a implementar es el cacheado de plantillas HTML, comomdulo de AngularJS, gracias al plugin gulp-angular-templatecache, aadiendo losiguiente a nuestro Gulpfile.js:

    vartemplateCache=require('gulp-angular-templatecache');

    gulp.task('templates',function(){gulp.src('./app/views/**/*.tpl.html').pipe(templateCache({root:'views/',module:'blog.templates',standalone:true})).pipe(gulp.dest('./app/scripts'));});

    Esta tarea crea un archivo templates.js en el directorio app/scripts/ con el contenidode las plantillas HTML cacheado como String para usarlo como dependencia en laaplicacin. Para ello necesitamos incluir el nuevo mdulo creado en el ficheroprincipal,app/scripts/app.js, que es el que usa las vistas:

    angular.module('blog',['ngRoute','blog.controllers','blog.templates']);

    Con esto an no hemos minificado nada, tan solo hemos creado un nuevo ficheroJavaScript que contiene las plantillas HTML, as nos ahorramos ms llamadas.

    Ahora empezamos a concatenar y minificar. Lo vamos a hacer directamente en el HTMLy con una tarea de Gulp. Tomamos nuestro index.html y le vamos a aadir lossiguientes comentarios: entre los enlaces a losficheros CSS para concatenarlos y minificarlos, y y para hacer lo mismo pero con los JS:

    [...]

    6.1 Cacheado de plantillas

    6.2 Concatenacin de ficheros JS y CSS

  • [...][...]

    Esto lo acompaamos de la siguiente tarea en el Gulpfile.js:

    vargulpif=require('gulp-if');varminifyCss=require('gulp-minify-css');varuseref=require('gulp-useref');varuglify=require('gulp-uglify');

    gulp.task('compress',function(){gulp.src('./app/index.html').pipe(useref.assets()).pipe(gulpif('*.js',uglify({mangle:false}))).pipe(gulpif('*.css',minifyCss())).pipe(gulp.dest('./dist'));});

    Esta tarea archivos enlazados en el index.html los depositar en el nuevo directoriopara produccin que ser /dist, ya minificados.

    En este directorio tambin necesitamos el index.html pero sin los comentarios y conlos enlaces a los nuevos ficheros minificados. Eso lo conseguimos con la siguiente

  • tarea:

    gulp.task('copy',function(){gulp.src('./app/index.html').pipe(useref()).pipe(gulp.dest('./dist'));gulp.src('./app/lib/fontawesome/fonts/**').pipe(gulp.dest('./dist/fonts'));});

    Esta tarea adems copia los ficheros de fuentes que usamos en la libreria fontawesome.

    Ahora englobamos todas estas tareas dentro de una nueva que llamaremos build yque ejecutaremos cuando queramos tener nuestro cdigo listo para produccin:

    gulp.task('build',['templates','compress','copy']);

    En el directorio /dist tendremos los siguientes archivos con esta estructura:

    /dist/js|vendor.min.js|app.min.js/css|styles.min.cssindex.html

    Siendo los archivos dentro de las carpetas /js y /css, archivos minificados para queocupen lo mnimo posible.

    Para probar que tenemos todo bien, vamos a crear una nueva tarea en Gulp que nospermita servir los archivos del directorio /dist como hacemos con la versin dedesarrollo:

    gulp.task('server-dist',function(){connect.server({root:'./dist',hostname:'0.0.0.0',port:8080,livereload:true,

    6.3 Servidor de archivos de produccin

  • middleware:function(connect,opt){return[historyApiFallback];}});});

    Por tanto, para probar nuestra aplicacin en versin de produccin antes de subirla acualquier servidor, debemos ejecutar lo siguiente en nuestra terminal:

    $gulpbuild$gulpserver-dist

    Y dirigirnos a nuestro navegador a la URL http://localhost:8080. Aqu veremos lamisma aplicacin ejecutndose, pero en este caso el index.html solo tiene un enlace aun fichero .css y un par de enlaces a ficheros .js, que son el de las libreras y el dela aplicacin. Todos ellos minificados, reduciendo as el nmero de peticiones HTTP y altener menor peso, su tiempo de carga es menor.

    Si nos fijamos en las herramientas de desarrollo del navegador, en concreto la pestaade Red, podemos ver las peticiones que hace nuestra aplicacin a los archivos queenlaza. Nos damos cuenta de que el archivo styles.min.css ocupa 146 Kb.

    6.4 Reduccin de cdigo CSS

  • No es mucho, pero si tenemos en cuenta que en ese archivo estn incluidas todas lasclases de Bootstrap y tambin todas las clases de Fontawesome y no usamos todasellas. Hay alguna manera de prescindir de las clases que no utilizamos? Si que la hay,con un plugin de Gulp llamado gulp-uncss. Este nos permite indicar que fichero CSSqueremos editar, los ficheros HTML en los que debe fijarse para remover el CSS noutilizado y ya tenemos listo nuestro CSS ultrareducido. Instalamos el plugin:

    $npminstall--save-devgulp-uncss

    E implementamos la tarea:

    varuncss=require('gulp-uncss');

    gulp.task('uncss',function(){gulp.src('./dist/css/style.min.css').pipe(uncss({html:['./app/index.html','./app/views/post-detail.tpl.html','./app/views/post-list.tpl.html'})).pipe(gulp.dest('./dist/css'));});

    gulp.task('build',['templates','compress','copy','uncss']);

  • Volvemos a ejecutar gulp-build y gulpserver-dist, y al fijarnos en la pestaa de redtenemos lo siguiente:

    Ahora styles.min.css ocupa solamente 13.5 Kb lo que permite que sea ms rpida sucarga.

    Para terminar, este sera el cdigo completo del fichero Gulpfile.js:

    //File:Gulpfile.js'usestrict';

    vargulp=require('gulp'),connect=require('gulp-connect'),stylus=require('gulp-stylus'),nib=require('nib'),jshint=require('gulp-jshint'),stylish=require('jshint-stylish'),inject=require('gulp-inject'),wiredep=require('wiredep').stream,gulpif=require('gulp-if'),minifyCss=require('gulp-minify-css'),useref=require('gulp-useref'),uglify=require('gulp-uglify'),uncss=require('gulp-uncss'),angularFilesort=require('gulp-angular-filesort'),

    5.5 Gulpfile al completo

  • templateCache=require('gulp-angular-templatecache'),historyApiFallback=require('connect-history-api-fallback');

    //Servidorwebdedesarrollogulp.task('server',function(){connect.server({root:'./app',hostname:'0.0.0.0',port:8080,livereload:true,middleware:function(connect,opt){return[historyApiFallback];}});});

    //Servidorwebparaprobarelentornodeproduccingulp.task('server-dist',function(){connect.server({root:'./dist',hostname:'0.0.0.0',port:8080,livereload:true,middleware:function(connect,opt){return[historyApiFallback];}});});

    //BuscaerroresenelJSynoslosmuestraenelterminalgulp.task('jshint',function(){returngulp.src('./app/scripts/**/*.js').pipe(jshint('.jshintrc')).pipe(jshint.reporter('jshint-stylish')).pipe(jshint.reporter('fail'));});

    //PreprocesaarchivosStylusaCSSyrecargaloscambiosgulp.task('css',function(){gulp.src('./app/stylesheets/main.styl').pipe(stylus({use:nib()})).pipe(gulp.dest('./app/stylesheets')).pipe(connect.reload());});

    //RecargaelnavegadorcuandohaycambiosenelHTMLgulp.task('html',function(){gulp.src('./app/**/*.html').pipe().pipe(connect.reload());});

    //Buscaenlascarpetasdeestilosyjavascriptlosarchivos//parainyectarlosenelindex.htmlgulp.task('inject',function(){returngulp.src('index.html',{cwd:'./app'}).pipe(inject(gulp.src(['./app/scripts/**/*.js']).pipe(angularFilesort()),{read:false,

  • ignorePath:'/app'})).pipe(inject(gulp.src(['./app/stylesheets/**/*.css']),{read:false,ignorePath:'/app'})).pipe(gulp.dest('./app'));});

    //InyectalaslibreriasqueinstalemosvaBowergulp.task('wiredep',function(){gulp.src('./app/index.html').pipe(wiredep({directory:'./app/lib'})).pipe(gulp.dest('./app'));});

    //CompilalasplantillasHTMLparcialesaJavaScript//paraserinyectadasporAngularJSyminificarelcdigogulp.task('templates',function(){gulp.src('./app/views/**/*.tpl.html').pipe(templateCache({root:'views/',module:'blog.templates',standalone:true})).pipe(gulp.dest('./app/scripts'));});

    //ComprimelosarchivosCSSyJSenlazadosenelindex.html//ylosminifica.gulp.task('compress',function(){gulp.src('./app/index.html').pipe(useref.assets()).pipe(gulpif('*.js',uglify({mangle:false}))).pipe(gulpif('*.css',minifyCss())).pipe(gulp.dest('./dist'));});

    //EliminaelCSSquenoesutilizadoparareducirelpesodelarchivogulp.task('uncss',function(){gulp.src('./dist/css/style.min.css').pipe(uncss({html:['./app/index.html','./app/views/post-list.tpl.html','./app/views/post-detail.tpl.html'})).pipe(gulp.dest('./dist/css'));});

    //Copiaelcontenidodelosestticoseindex.htmlaldirectorio//deproduccinsintagsdecomentariosgulp.task('copy',function(){gulp.src('./app/index.html').pipe(useref()).pipe(gulp.dest('./dist'));gulp.src('./app/lib/fontawesome/fonts/**').pipe(gulp.dest('./dist/fonts'));

  • });

    //Vigilacambiosqueseproduzcanenelcdigo//ylanzalastareasrelacionadasgulp.task('watch',function(){gulp.watch(['./app/**/*.html'],['html','templates']);gulp.watch(['./app/stylesheets/**/*.styl'],['css','inject']);gulp.watch(['./app/scripts/**/*.js','./Gulpfile.js'],['jshint','inject']);gulp.watch(['./bower.json'],['wiredep']);});

    gulp.task('default',['server','templates','inject','wiredep','watch']);gulp.task('build',['templates','compress','copy','uncss']);

    Cmo habrs podido vera lo largo del desarrollo de esta aplicacin web de ejemplo,hemos programado de una manera gil sin tener que recargar constantemente elnavegador para ver los cambios y olvidndonos de hacer tareas repetitivas dejndoleesa labor a Gulp. Tambin hemos usado un preprocesador de CSS que en caso de quenuestro proyecto web fuese ms extenso, nos ayudara a mantener el diseo de unamanera modular, mantenible y escalable. Al igual que Angular.js, el framework Modelo-Vista-Controlador para la parte Frontend que hemos utilizado. Angular es mucho ms ysu curva de aprendizaje tiene muchos altibajos, pero creo que lo visto en este ejemplonos sirve para empezar a desarrollar aplicaciones ms complejas de una maneraescalable.

    Te invito a que sigas investigando y usando Angular en tus proyectos. Descubrirs cadada una nueva forma de usarlo. Eso es lo que lo hace tan potente.

    Espero que este libro te haya servido y hayas aprendido algo nuevo. Puedes contactarconmigo en las redes sociales y comentarme que te ha parecido, cualquier feedback esbien recibido y se agradece:

    twitter.com/carlosazaustregithub.com/carlosazaustregoogle.com/+CarlosAzaustrefacebook.com/carlosazaustre.weblinkedin.com/in/carlosazaustre

    Hasta el prximo libro!

    IntroductionConceptos bsicosConfiguracin del entorno de trabajoAnatoma de una aplicacin AngularJSDiseo con preprocesadores CSSOptimizacin y despliegue en produccin