Vous êtes sur la page 1sur 153

Desarrollo de Apps con IONIC

Aprenda a crear aplicaciones para mviles desde cero con


el framework ms poderoso y verstil

Victor Hugo Garcia


Este libro est a la venta en http://leanpub.com/desarrollodeappsconionic

Esta versin se public en 2017-02-07

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

2017 Victor Hugo Garcia


Para Laura, mi familia, amigos, y todos aquellos que me han enseado algo
ndice general

Captulo 1: Qu es IONIC? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Qu significa eso de aplicaciones hbridas? . . . . . . . . . . . . . . . . . . . . . . . . . 2
Configurando el entorno de desarrollo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Sobre el formato del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Primera aplicacin con ionic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Estructura de una aplicacin en IONIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Captulo 2: Primera Aplicacin Completa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31


Creacin del Proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Creando una nueva pgina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Seteando la pgina raz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Creando un modelo de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Listas y estructuras repetitivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Crear una ventana modal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

Captulo 3: Servidor de desarrollo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86


Api Rest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Servicios Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

Captulo 4: Bsquedas y filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123


Pipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Gravatar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

Captulo 5: Corriendo la aplicacin desde el emulador . . . . . . . . . . . . . . . . . . . . 135


APK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Publicando una aplicacin para Android . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Palabras de despedida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

Captulo 6: Apndices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145


Captulo 1: Qu es IONIC?
Si estn leyendo este libro, probablemente ya sepan que INONIC es un framework para el desarrollo
de aplicaciones mviles hbridas, que permite desplegar dichas apps en dispositivos con Android,
IOs y Windows a partir de una nica base de cdigo.
Sin embargo, hay algunos conceptos que conviene tratar con mayor detalle para sacar luego mayor
provecho a esta herramienta.
Desde hace un tiempo, el desarrollo de aplicaciones ha estado limitado a un grupo de personas que
se especializan en entornos y lenguajes tales como Java, Objective C, Swift y otros.
Esta variedad, se desprende de la existencia de distintos sistemas operativos en multitud de
dispositivos, por lo que desarrollar una app para diferentes plataformas implica escribir cdigo que
pueda ser compilado para el entorno destino, con el consecuente incremento en los esfuerzos de
desarrollo.
Ante esta situacin, algunas personas pensaron que sera interesante poder escribir una nica base
de cdigo que pudiera luego ser compilado o traducido de alguna manera para obtener versiones de
la misma aplicacin que funcionen en mviles, tabletas, y otros; prcticamente sin modificaciones.
Estos esfuerzos no son nuevos y en ese sentido IONIC no es el pionero. Sin embargo, existen ciertas
caractersticas que hacen de IONIC un framework nico con el que desarrollar aplicaciones mviles
se convierte en una experiencia placentera y para nada traumtica.
Estas son:

IONIC permite utilizar las mismas tecnologas que se emplean para el desarrollo web (HTML5,
SASS, JavaScript, TypeScript, Angular) para la construccin de aplicaciones mviles. Esto
significa que se abre el juego y muchos desarrolladores web pueden pasar a participar del
mundo del desarrollo mvil, aplicando los conocimientos que ya tienen. Esto es algo fantstico
para todos aquellos que desean comenzar a desarrollar apps pronto, sin tener que meterse de
narices a aprender Java, Objective C, u otro lenguaje que puede resultar crptico.
Existe una nica base de cdigo, que permite desplegar las aplicaciones construidas con IONIC
a las principales app store de la actualidad.
Es un framework completamente gratuito y open source, con licencia MIT, lo que significa
que nunca se deber pagar algn tipo de comisin por su uso.

Existen, por supuesto, otras caractersticas que iremos explorando a lo largo de este libro, pero las
arriba mencionadas son suficientes para percibir que se trata de una herramienta fantstica a la que
podemos sacarle mucho provecho.
Captulo 1: Qu es IONIC? 2

Qu significa eso de aplicaciones hbridas?


Al comienzo de este captulo, se mencion que IONIC es un framework para la construccin de
aplicacin mviles hbridas. Veamos ahora qu significa eso y qu implicaciones tiene para nosotros.
Una aplicacin hbrida, bsicamente es una aplicacin web que se ejecuta en un dispositivo mvil
dentro de un wrapper o envoltorio, que es el que en definitiva tiene acceso a la plataforma del
dispositivo. Es decir, podemos pensar en una app desarrollada con IONIC como un conjunto de
pginas web que son encapsuladas en un envoltorio, que es el que permite que este conjunto de
pginas se comporte como una aplicacin nativa. El wrapper que utiliza IONIC es Cordova.
Esto significa que las apps construidas con IONIC son ejecutadas en un navegador web? Bueno, s
y no. Las apps construidas con IONIC no se ejecutan a travs de una aplicacin de un navegador
mvil como Safari y Chrome, sino a travs del navegador de bajo nivel del dispositivo (UIWebView
en IOS y WebView en Android) que a su vez tiene como wrapper a Cordova.
Esta es una consideracin importante, ya que muchos de ustedes habrn notado, esta capa extra
tiene efectos sobre el rendimiento de las aplicaciones construidas con IONIC, e incluso algunos de
ustedes habrn odo crticas de que las aplicaciones hbridas son lentas y proporcionan una mala
experiencia al usuario.
Sobre este punto hay que sealar que en efecto, la capa extra necesaria para ejecutar las aplicaciones
hbridas provoca una sobrecarga que puede ir en detrimento de la performance. Es algo a tener en
cuenta sin dudas, pero esto por s solo no determina que las aplicaciones hbridas sean lentas o que
el usuario tenga una mala experiencia con ellas.
Veremos cules son las prcticas a seguir para que la diferencia en performance entre nuestras
aplicaciones hbridas y las aplicaciones nativas, sea prcticamente inexistente.
En definitiva, IONIC es un framework maravilloso que nos permite introducirnos de lleno en el
mundo del desarrollo de aplicaciones para mviles, que se vern y comportarn estupendamente.
Vamos a por ello.

Configurando el entorno de desarrollo


En todo libro como ste, la configuracin del entorno de desarrollo es una de esas partes que
desearamos obviar, pero no podemos hacerlo.
Vamos a ver cmo instalar las herramientas necesarias para comenzar a construir aplicaciones de
inmediato.
Una cosa que debe tenerse en cuenta, sin embargo, es que existen lgicamente multitud de variantes
posibles en cuanto a sistemas operativos, plataformas, etc.; y por lo tanto no es posible contemplar
aqu todos los casos.
Lo bueno es que las herramientas que necesitamos no son demasiadas y para cada problema que
se pueda presentar con su instalacin la solucin est a una bsqueda en Google de distancia.
Comencemos pues.
Captulo 1: Qu es IONIC? 3

Navegador web
Aunque en principio podra utilizarse cualquiera, recomiendo utilizar Google Chrome. Cranme, me
lo van a agradecer cuando echemos mano a sus herramientas de depuracin.
Editor de texto
Aunque en principio podramos trabajar con cualquier editor de texto, es conveniente contar con
algn editor de texto inteligente, o incluso an mejor una IDE para que el trabajo sea ms provechoso.
Particularmente, a m me gusta mucho Sublime Text. Es un muy buen editor, con funcionalidades
tales como el resaltado de texto, y multitud de plugins disponibles para agregar an ms poder si
hiciera falta. Si trabajan con la versin gratuita, de vez en cuando tendrn que lidiar con mensajes que
les sugieren comprar el producto, pero esta molestia se ve compensada con creces por la versatilidad
del producto.
Otra herramienta muy buena es Visual Studio Code, un editor de cdigo muy potente que incluye:
opciones de depuracin, integracin con Git, entre muchas otras.
Cualquiera de estas dos opciones servirn muy bien a nuestros propsitos.
Apache y MySQL
Si bien nos ocuparemos del desarrollo mvil, lgicamente nuestras aplicaciones consumirn datos.
Estos datos habitualmente provendrn de algn servicio web.
Para que podamos ver cmo se efectan las peticiones a servicios web, construiremos los nuestros.
No se preocupen, no nos enfrascaremos en demasiados detalles. Lo importante es que tengamos una
fuente a partir de la cual podamos consumir datos.
Una manera sencilla de configurar Apache y MySQL sin liarnos demasiado, es utilizar por ejemplo
Xampp, una aplicacin disponible para las principales plataformas y que nos permitir tener todo
funcionando de manera sencilla.
En los apndices correspondientes, se muestra como instalar Xampp en Windows y Linux.
Shell
Este paso no es estrictamente necesario, pero lo incluyo porque puede ser de utilidad. Si estn
desarrollando con Windows, puede ser til contar con una terminal de comandos poderosa que
puede aumentar nuestra productividad.
En dicho caso, recomiendo Cygwin, para darle un sabor Linux al entorno Windows.
Android SKD
Para poder desplegar nuestras apps en Android (lo que implica generar el apk, firmarla y alinearla
antes de poder publicarla), debemos contar con el Android SDK. Para nuestros propsitos, no es
necesario instalar todo el Android Studio (el IDE de desarrollo) y podemos simplemente instalar el
SKD. Podemos encontrar ambas opciones aqu.
https://www.sublimetext.com/3
https://code.visualstudio.com/
https://developer.android.com/studio/index.html
Captulo 1: Qu es IONIC? 4

Node JS
Para crear proyectos en IONIC, necesita contar con una versin actualizada de CLI (command line
interface) y Cordova, para lo cual previamente debern tener instalado Node.js. Es conveniente que
instalen la ltima versin, la cual pueden encontrar aqu.
Para comprobar qu versin de node tienen instalada, pueden ejecutar el comando node v desde
la lnea de comandos:

Versin de NodeJS

Esas son todas las herramientas que necesitamos para trabajar. Todas ellas vienen en la forma de un
sencillo instalador, por lo que no deberamos tener problemas para ponerlas en marcha. He incluido
apndices para aquellos casos que pudieran resultar ms complicados. En caso de que encuentren
alguna dificultad, no desesperen. Configurar el entorno de desarrollo es la parte ms tediosa de todo
el proceso.
Lo bueno es que las dificultades con las que puedan encontrarse, seguramente ya se le han presentado
a alguien ms, y una bsqueda en Google puede resultar de suma utilidad. Tambin existen sitios
como Stackoverflow donde mucha gente est dispuesta a echar una mano. Recuerden siempre ser
concisos y amables en sus preguntas, y les ir bien. Asimismo, si alguna respuesta les ha sido
de utilidad, no olviden agradecer y reconocer la respuesta como vlida. La persona que les haya
respondido lo valorar mucho y estar ms dispuesta a echar una mano en la prxima oportunidad.

Sobre el formato del libro


En este libro encontrarn todo el cdigo necesario para construir las aplicaciones que desarrolla-
remos. El cdigo est copiado directamente desde mi IDE para asegurarme de que es totalmente
funcional. Sin embargo y como podrn notar, debido a las restricciones de formato, puede que no
resulte muy legible tal y como aparece aqu. Puede utilizar algn formateador de cdigo en lnea
para embellecerlo.
https://nodejs.org/es/
Captulo 1: Qu es IONIC? 5

En cualquier caso, para cada fragmento de cdigo relevante que aparece en libro, incluyo el
correspondiente Gist. Para los que no estn al tanto, un Gist es un fragmento de cdigo compartido
a travs de la plataforma github. Dicho cdigo est correctamente formateado y resaltado, por lo
que ser mucho ms agradable de ver.
Esto es todo como introduccin. Gracias por su paciencia, y ahora s vamos a ensuciarnos las manos.

Primera aplicacin con ionic


Una vez que tenemos todo el entorno de desarrollo configurado, debemos comenzar por instalar la
CLI de ionic y cordova. Para ello empleamos el comando:
npm install -g ionic cordova
Ntese que estamos empleando la bandera g. Esto indica que deseamos instalar la utilidad de forma
global, con lo que automticamente estar disponible para todos nuestros proyectos.
Hecho esto, podemos dirigirnos al directorio en el que deseamos crear nuestra aplicacin. Por
ejemplo, podemos tener un directorio MisApps, dentro del cual pondremos todas las aplicaciones de
prueba que desarrollemos.
Ubicados dentro de este directorio, ejecutamos el comando:
ionic start ionicTestOne v2
Con esto, habremos creado una aplicacin denominada ionicTestOne. El modificador v2 se utiliza
para indicar que deseamos trabajar con la versin 2 de ionic.

Primer Proyecto

Una vez que el proceso ha finalizado, ingresamos al directorio recientemente creado, y desde all
ejecutamos:
ionic serve
Captulo 1: Qu es IONIC? 6

Lo que estamos haciendo con esto, es ejecutar el servidor embebido que tiene ionic para probar
nuestra aplicacin en el navegador. Para detener el servidor debemos presionar la letra q.
NOTA: puede ser que al momento de ejecutar ionic serve obtengamos una salida similar a esta en
el navegador:

Error el ejecutar ionic serve

Por qu ocurre esto? Para comprenderlo abramos nuestro proyecto en nuestro editor de texto. La
estructura es la siguiente:

Estructura del proyecto

En la raz, vemos un archivo denominado package.json, que define cosas tales como los scripts a
ejecutar cuando lanzamos un comando, dependencias, etc.
El contenido de este paquete variar de acuerdo a la forma en que nuestra aplicacin fue creada
originalmente.
Ionic ofrece un nmero de plantillas que sirven como punto de partida para nuestros proyectos.
Cuando nosotros ejecutamos al comienzo ionic start ionicTestOne v2, se cre un proyecto con una
plantilla por defecto que nos proporciona una aplicacin con 3 tabs o pestaas.
Al momento de escribir este libro, el contenido del archivo package.json es el siguiente:
Captulo 1: Qu es IONIC? 7

1 {
2 "name": "ionic-hello-world",
3 "author": "Ionic Framework",
4 "homepage": "http://ionicframework.com/",
5 "private": true,
6 "scripts": {
7 "clean": "ionic-app-scripts clean",
8 "build": "ionic-app-scripts build",
9 "ionic:build": "ionic-app-scripts build",
10 "ionic:serve": "ionic-app-scripts serve"
11 },
12 "dependencies": {
13 "@angular/common": "2.2.1",
14 "@angular/compiler": "2.2.1",
15 "@angular/compiler-cli": "2.2.1",
16 "@angular/core": "2.2.1",
17 "@angular/forms": "2.2.1",
18 "@angular/http": "2.2.1",
19 "@angular/platform-browser": "2.2.1",
20 "@angular/platform-browser-dynamic": "2.2.1",
21 "@angular/platform-server": "2.2.1",
22 "@ionic/storage": "1.1.7",
23 "ionic-angular": "2.0.0-rc.4",
24 "ionic-native": "2.2.11",
25 "ionicons": "3.0.0",
26 "rxjs": "5.0.0-beta.12",
27 "zone.js": "0.6.26"
28 },
29 "devDependencies": {
30 "@ionic/app-scripts": "0.0.47",
31 "typescript": "2.0.9"
32 },
33 "description": "ionicTestOne: An Ionic project",
34 "cordovaPlugins": [
35 "cordova-plugin-device",
36 "cordova-plugin-console",
37 "cordova-plugin-whitelist",
38 "cordova-plugin-splashscreen",
39 "cordova-plugin-statusbar",
40 "ionic-plugin-keyboard"
41 ],
42 "cordovaPlatforms": []
Captulo 1: Qu es IONIC? 8

43 }

El problema se encuentra en la seccin scripts. Debemos reemplazar

1 "scripts": {
2 "clean": "ionic-app-scripts clean",
3 "build": "ionic-app-scripts build",
4 "ionic:build": "ionic-app-scripts build",
5 "ionic:serve": "ionic-app-scripts serve"
6 },

Por
Del libro:

1 "scripts": {
2 "build": "ionic-app-scripts build",
3 "watch": "ionic-app-scripts watch",
4 "serve:before": "watch",
5 "emulate:before": "build",
6 "deploy:before": "build",
7 "build:before": "build",
8 "run:before": "build"
9 },

Gist
Por ahora no se preocupen por la razn y simplemente hagan el cambio.
Ahora, si ejecutan ionic serve, podrn finalmente ver la aplicacin funcionando en el navegador:
https://gist.github.com/vihugarcia/3cbcd246475b2ba03289b7403ed02ecd
Captulo 1: Qu es IONIC? 9

Primera Aplicacin

Vemos que se trata de una muy sencilla aplicacin con tres pestaas visibles en la parte inferior.
La apariencia que muestra se debe a que estamos ejecutndola en el navegador. Sin embargo, si
estamos utilizando Chrome podemos utilizar las herramientas que nos proporciona para obtener
una vista similar a la de un mvil.

Consola de desarrollador
Captulo 1: Qu es IONIC? 10

Desde esta vista, tenemos disponibles herramientas de depuracin que nos sern extremadamente
tiles.
Analicemos brevemente algunas de las pestaas de la consola de desarrollador.
En la pestaa Elements podemos visualizar los elementos del DOM (Document Object Model) que
conforman la pgina que estamos visualizando en ese momento.
En la porcin inferior, podemos observar adems que tenemos acceso a los estilos que estn aplicados
a los elementos que tenemos seleccionados.

Pestaa Elements

Por ejemplo, si seleccionamos en la pestaa el tag html ion-app, podemos ver como en la parte
inferior se muestran los estilos aplicados a esa seccin del documento.
La pestaa Console ser probablemente la que ms visitemos. All podremos ver los errores y/o
Captulo 1: Qu es IONIC? 11

advertencias relacionadas a los scripts que se estn ejecutando. Ser nuestra principal fuente de
informacin para solucionar errores.
Si la seleccionamos en este momento, veremos que nos muestra dos advertencias.

Pestaa Console

Estas advertencias simplemente se producen porque estamos corriendo la aplicacin en un navega-


dor en lugar de un dispositivo o simulador, por lo que no tenemos acceso a ciertas propiedades y
funciones nativas. No es algo que deba preocuparnos por ahora.
En la pestaa Network se muestran todas las peticiones que se realizan a recursos tales como
imgenes, datos de servicios web y otros. Tambin la veremos frecuentemente ya que toda aplicacin
tiene la necesidad de consumir datos. La comunicacin es en ambos sentidos. Por un lado por
ejemplo podemos obtener una serie de posts a travs de un servicio web, y por otro podemos enviar
comentarios sobre dicho post.

Pestaa Network

Por ahora es suficiente. Ahora comencemos a analizar la estructura de nuestra aplicacin.


Captulo 1: Qu es IONIC? 12

Estructura de una aplicacin en IONIC


Nuestro trabajo, lo haremos principalmente en el directorio src. Este contiene el cdigo que da vida
a nuestra aplicacin.

Directorio src

Dentro de este directorio, encontramos los siguientes:


App: en este directorio encontramos los archivos necesarios para inicializar nuestra aplicacin.
Iremos viendo cada uno de ellos a medida que avancemos.
Assets: aqu se guardan recursos estticos tales como imgenes.
Pages: en este directorio se encuentran cada una de las pginas que componen nuestra aplicacin.
Recordemos que una aplicacin de IONIC est compuesta por un conjunto de pginas. Cada una de
estas pginas vive en su propio directorio, lo cual contribuye a mantener la estructura ordenada y
manejable.
Theme: en este directorio, se encuentra por defecto un nico archivo denominado variables.scss.
Vale la pena analizarlo con mayor detenimiento.
Qu es un archivo scss?
Seguramente, en sus proyectos habrn trabajado con hojas de estilo, archivos con extensin css que
Captulo 1: Qu es IONIC? 13

permiten definir la apariencia de secciones de una pgina o incluso del sitio completo. Por ejemplo,
una entrada tpica de css puede lucir as:

1 #header {
2 background-color: #387ef5;
3 }

Ahora bien, qu ocurre si el color #387ef5 es usado en distintos lugares de nuestra aplicacin?
Entonces tenemos que repetirlo tantas veces como sea necesario. Y qu ocurre si en el futuro
deseamos reemplazarlo por otro? Entonces deberemos buscar cada ocurrencia en nuestro proyecto
y reemplazarlo.
Una solucin sera poder guardar dicho valor en una variable, algo as como:

1 $primary = #387ef5;

Lamentablemente, css no permite esto.


La solucin es emplear una extensin de css denominada Sass (Syntactically Awesome Style Sheet),
a la que podemos definir como un lenguaje de hojas de estilo.
Lo que debemos saber, es que un archivo sass (identificado por su extensin scss), es traducido a css.
Sass nos proporciona una serie de ventajas sobre css que lo convierten en una herramienta
extremadamente til. Entre ellas, se encuentra la posibilidad de poder definir variables. Por ejemplo,
si observamos el archivo variables.scss, podemos ver la siguiente seccin:

1 $colors: (
2 primary: #387ef5,
3 secondary: #32db64,
4 danger: #f53d3d,
5 light: #f4f4f4,
6 dark: #222
7 );

Lo que se est haciendo aqu, es definir un mapa de colores denominado $colors, dentro del cual
se encuentran una serie de colores definidos por nombre de manera que podamos reutilizarlos a lo
largo de nuestra aplicacin.
Podemos cambiar los cdigos de color, y agregar o remover colores. El nico color que es obligatorio
definir en el mapa $colors es primary.
Pronto veremos todo esto en accin.
Ahora vamos a centrarnos en las pginas que componen nuestra aplicacin. Si observamos el
directorio pages, veremos que dentro de l se encuentran 4 directorios, que se corresponden a las
Captulo 1: Qu es IONIC? 14

pginas de nuestra aplicacin: home, about y contact; el otro directorio, tabs, contiene la pgina que
se encarga de mostrar las 3 pestaas anteriores.
Veamos dentro del directorio home. Podemos observar que contiene 3 archivos: home.html,
home.scss y home.ts.
El archivo home.html, es el que contiene el cdigo html que conforma la pgina. Si lo abrimos,
podemos ver que el cdigo es el siguiente:

1 <ion-header>
2 <ion-navbar>
3 <ion-title>Home</ion-title>
4 </ion-navbar>
5 </ion-header>
6
7 <ion-content padding>
8 <h2>Welcome to Ionic!</h2>
9 <p>
10 This starter project comes with simple tabs-based layout for apps
11 that are going to primarily use a Tabbed UI.
12 </p>
13 <p>
14 Take a look at the <code>src/pages/</code> directory to add or change tabs,
15 update any existing page or create new pages.
16 </p>
17 </ion-content>

En l podemos reconocer etiquetas html ordinarias, tales como h2 y p, y otras que son especficas
de ionic y que comienzan con el prefijo ion.
Por ejemplo, en la parte superior tenemos:

1 <ion-header>
2 <ion-navbar>
3 <ion-title>Home</ion-title>
4 </ion-navbar>
5 </ion-header>

Se define un encabezado, dentro de l una barra de navegacin (aqu pondramos tpicamente


botones o un men), y dentro de la barra de navegacin un ttulo.
Las etiquetas que comienzan con el prefijo ion nos permiten definir elementos que son especficos
de ionic, pero lo bueno es que son semnticamente compatibles con html5. Anteriormente vimos
como en el archivo variables.scss se definen variables globales que determinan la presentacin de
Captulo 1: Qu es IONIC? 15

nuestra aplicacin en su conjunto. Veamos cmo usarlas. Reemplacemos la seccin anterior por la
siguiente:
Del libro:

1 <ion-header>
2 <ion-navbar color="primary">
3 <ion-title>Home</ion-title>
4 </ion-navbar>
5 </ion-header>

Gist
Podemos ver ahora cmo ha cambiado la apariencia de la aplicacin

Barra de Navegacin Home

Lo que hemos hecho es agregar la propiedad color=primary para la etiqueta navbar. Realicemos
el mismo cambio en las pginas about.html y contact.html.
La barra de navegacin de todas las pginas mostrar ahora el mismo color.
Vamos a aprovechar lo que hemos visto hasta ahora para personalizar un poco el estilo de nuestra
aplicacin.
Regresamos al archivo variables.scss dentro del directorio theme y reemplacemos el mapa $colors
por lo siguiente:
Del libro:

https://gist.github.com/vihugarcia/650c8411e8103da98b2ec0fdfaff707f
Captulo 1: Qu es IONIC? 16

1 $colors: (
2 primary: #D93240,
3 secondary: #638CA6,
4 danger: #F19143,
5 light: #9E99A7,
6 dark: #313628,
7 favorite: #7FB069
8 );

Gist
Podemos ver ahora que la barra de navegacin de todas las pginas presenta un lindo color
personalizado.

Nuevo Color de la Barra

Es importante que el estilo de nuestra aplicacin se adapte al tipo de producto que estamos
desarrollando y que ste difiera del diseo genrico que proporciona IONIC. Una buena imagen
es de fundamental importancia para el xito de nuestra aplicacin. Sigamos modificando la pgina
home. De regreso al archivo home.html Reemplacemos el contenido, delimitado por las etiquetas
ion-content, por lo siguiente:
Del libro:

1 <ion-content padding>
2 <h2>Hola Ionic!</h2>
3 <p>
4 Esta es la pgina inicial de nuestro proyecto.
5 </p>
6 </ion-content>

https://gist.github.com/vihugarcia/c509daff7fa7224d85632ad011e869e0
Captulo 1: Qu es IONIC? 17

Gist
Si estamos ejecutando el servidor embebido mediante ionic serve, habremos notado que los cambios
se reflejan automticamente. Nuestra pgina de inicio ahora se ver as:

Nueva Pgina de Inicio

Fantstico. Vamos progresando. Ahora centremos nuestra atencin en el archivo home.ts. Los
archivos con extensin ts, son archivos typescript. Typescript es un lenguaje de scripting basado
en el estndar ECMA6. El estndar ECMA es el mismo en el que se basa el ultra popular lenguaje
Javascript, pero a diferencia de este, Typescript agrega nuevas caractersticas tales como tipado y
verdaderas clases, cosas que en Javascript slo pueden simularse.
Prcticamente toda pgina en un proyecto de IONIC tiene asociado un archivo ts, que posteriormente
ser traducido a un archivo js.
El contenido del archivo home.ts es el siguiente:

1 import { Component } from '@angular/core';


2
3 import { NavController } from 'ionic-angular';
4
5 @Component({
6 selector: 'page-home',
7 templateUrl: 'home.html'
8 })
9 export class HomePage {
10
11 constructor(public navCtrl: NavController) {
12
13 }
14
15 }

Las primeras dos lneas, importan clases necesarias para el funcionamiento de nuestra pgina.
https://gist.github.com/vihugarcia/3684f20d971ba3ea8f0d63d5d39d279d
Captulo 1: Qu es IONIC? 18

Es importante aqu entender que IONIC es un framework orientado a componentes, y por lo tanto,
cada elemento que conforma nuestra aplicacin (desde una pgina hasta una seccin de la misma)
es un componente. De hecho, determinar qu componentes conformarn nuestra aplicacin es una
de las tareas fundamentales del diseo.
Con esto en mente, analicemos las lneas siguientes:

1 @Component({
2 selector: 'page-home',
3 templateUrl: 'home.html'
4 })

Con esto estamos declarando un componente, en este caso una pgina. Vemos que la declaracin
tiene un par de propiedades.
selector, hace referencia la etiqueta html que identificar al componente. En este caso, estamos
diciendo que cuando en nuestro cdigo html se encuentre un par de etiquetas <page-home></page-
home>, dichas etiquetas contendrn el cdigo html de nuestro componente.
templateUrl contiene el nombre del archivo donde est definida la estructura de nuestro componente.
Se trata del archivo home.html que ya hemos analizado y modificado.
Alternativamente, el cdigo html se podra definir sin recurrir a un archivo html, utilizando la
propiedad template y poniendo el cdigo html entre comillas simples o dobles. Esto, sin embargo,
no es recomendable a menos que se trate de un contenido super sencillo.
Veamos ahora las ltimas lneas:

1 export class HomePage {


2
3 constructor(public navCtrl: NavController) {
4
5 }
6
7 }

Lo que hacemos aqu es exportar la clase que contiene nuestro componente, asignndole en este
caso el nombre HomePage. Esto permitir que podamos usar el componente desde cualquier otro
componente donde sea necesario, utilizando previamente una sentencia import. Veremos esto
cuando analicemos el componente Tabs.
Los componentes que corresponden a las pginas about y contact son prcticamente iguales.
Tenemos las sentencias import, la definicin del componente, y luego la exportacin de la clase
para que el componente pueda ser utilizado.
Analicemos ahora el componente Tabs.
Captulo 1: Qu es IONIC? 19

Cuando creamos el proyecto, IONIC construy por defecto una aplicacin que tiene un layout
gobernado por pestaas, es decir, la navegacin entre pginas se realiza mediante pestaas. Esto
quiere decir que adems de los componentes correspondientes a cada una de las pginas que se
muestran en pestaas, necesitamos un componente que contenga la navegacin. Dicho componente
es Tabs.
Examinemos el archivo tabs.ts. Su contenido es el siguiente:

1 import { Component } from '@angular/core';


2
3 import { HomePage } from '../home/home';
4 import { AboutPage } from '../about/about';
5 import { ContactPage } from '../contact/contact';
6
7 @Component({
8 templateUrl: 'tabs.html'
9 })
10 export class TabsPage {
11 // this tells the tabs component which Pages
12 // should be each tab's root Page
13 tab1Root: any = HomePage;
14 tab2Root: any = AboutPage;
15 tab3Root: any = ContactPage;
16
17 constructor() {
18
19 }
20 }

Es ligeramente ms complejo que lo visto hasta ahora.


La primera lnea, es un import de los componentes del ncleo de angular, necesario para poder
trabajar con componentes:

1 import { Component } from '@angular/core';

Algo que podamos notar, es que a diferencia de las pginas vistas anteriormente, no se encuentra
un import del componente NavController.
Esto es as porque en esta pgina no se hace uso de una barra de navegacin.
Las tres lneas siguientes son imports de las pginas home, about y contact, que son las que pueden
visualizarse en las pestaas.
Captulo 1: Qu es IONIC? 20

1 import { HomePage } from '../home/home';


2 import { AboutPage } from '../about/about';
3 import { ContactPage } from '../contact/contact';

Si estos imports no estuvieran, no podramos usar los componentes asociados a cada una de dichas
pginas.
Luego viene la declaracin del componente:

1 @Component({
2 templateUrl: 'tabs.html'
3 })

En este caso, a diferencia de los casos anteriores, no estamos definiendo un selector, es decir una
etiqueta html asociada al componente. Esto es as porque se trata del componente raz, a partir del
cual se agregarn todos los siguientes.
A continuacin, como siempre, se debe exportar la clase para que pueda ser utilizada:

1 export class TabsPage {


2 // this tells the tabs component which Pages
3 // should be each tab's root Page
4 tab1Root: any = HomePage;
5 tab2Root: any = AboutPage;
6 tab3Root: any = ContactPage;
7
8 constructor() {
9
10 }
11 }

Obsrvese que se definen tres variables, y a cada una de ellas se le asigna cada una de las clases que
hacen referencia a las pginas que se mostrarn en las pestaas.
Ntese tambin que despus de cada variable, se tienen dos puntos y una declaracin de tipo antes
de la asignacin.
Como habamos mencionado anteriormente, typescript es un lenguaje tipado, es decir, permite
declarar los tipos asociados a cada variable de la forma nombreVariable: tipo. Esta declaracin del
tipo puede estar seguida o no de una asignacin.
Tambin podemos observar que al igual que en las clases vistas anteriormente, se tiene un mtodo
constructor sin implementar.
Captulo 1: Qu es IONIC? 21

Tpicamente all realizaramos todas las tareas de inicializacin requeridas. Por ejemplo, dentro del
constructor podramos llamar a un mtodo encargado de iniciar una peticin a un servicio web para
obtener datos necesarios.
El archivo tabs.html es muy simple.

1 <ion-tabs>
2 <ion-tab [root]="tab1Root" tabTitle="Home" tabIcon="home"></ion-tab>
3 <ion-tab [root]="tab2Root" tabTitle="About" tabIcon="information-circle">
4 </ion-tab>
5 <ion-tab [root]="tab3Root" tabTitle="Contact" tabIcon="contacts"></ion-tab>
6 </ion-tabs>

Tenemos un componente tabs donde se definen cada una de las pestaas que estarn disponibles.
Vayamos ahora un poco ms lejos y veamos cmo podemos agregar una nueva pgina y mostrarla
en su correspondiente pestaa.
Una de las mejores caractersticas de IONIC es su CLI (Command Line Interface), que nos permite
utilizar una serie de comandos que simplifican enormemente el trabajo. Siempre desde la raz de
nuestro proyecto, ejecutemos el comando:
ionic g page privacy
Si todo ha ido bien, deberamos ver un nuevo directorio denominado privacy, dentro del directorio
pages.

Pgina privacy

Estupendo.
Podemos observar que el directorio privacy contiene los tres mismo tipos de archivo con los que ya
estamos familiarizados.
El contenido del archivo privacy.html es el siguiente:
Captulo 1: Qu es IONIC? 22

1 <!--
2 Generated template for the Privacy page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar>
10 <ion-title>privacy</ion-title>
11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17
18 </ion-content>

Tenemos un encabezado, dentro de l una barra de navegacin, y dentro de esta ltima un ttulo.
En primer lugar, asignemos a la barra de navegacin el mismo color que el de las otras pginas.
La etiqueta debera quedarnos como:
<ion-navbar color=primary>
Luego, reemplacemos el ttulo por el siguiente: Poltica de Privacidad
Podemos ver que el contenido de la pgina est vaco:

1 <ion-content padding>
2
3 </ion-content>

En primer lugar, agreguemos un ttulo utilizando la etiqueta <h2>


Luego, agreguemos un texto de ejemplo que podramos utilizar para sealar la poltica de privacidad
de nuestra aplicacin.
El texto fue generado a partir de una plantilla en https://politicadeprivacidadplantilla.com/
Es un recurso que puede serles de utilidad para un proyecto web o mvil.
En definitiva, el contenido debera quedarnos de la siguiente manera:
Del libro:
https://politicadeprivacidadplantilla.com/
Captulo 1: Qu es IONIC? 23

1 <ion-content padding>
2 <h2>Poltica de Privacidad</h2>
3
4 <p>El presente Poltica de Privacidad establece los trminos en que TestOne
5 usa y protege la informacin que es proporcionada por sus usuarios al momento
6 de utilizar su sitio web. Esta compaa est comprometida con la seguridad de
7 los datos de sus usuarios. Cuando le pedimos llenar los campos de informacin
8 personal con la cual usted pueda ser identificado, lo hacemos asegurando
9 que slo se emplear de acuerdo con los trminos de este documento. Sin
10 embargo esta Poltica de Privacidad puede cambiar con el tiempo o ser
11 actualizada por lo que le recomendamos y enfatizamos revisar continuamente
12 esta pgina para asegurarse que est de acuerdo con dichos cambios.</p>
13
14 <h3>Informacin que es recogida</h3>
15
16 <p>Nuestro sitio web podr recoger informacin personal por ejemplo: Nombre,
17 informacin de contacto como su direccin de correo electrnica e informacin
18 demogrfica. As mismo cuando sea necesario podr ser requerida informacin
19 especfica para procesar algn pedido o realizar una entrega o facturacin.</p>
20
21 <h3>Uso de la informacin recogida</h3>
22
23 <p>Nuestro sitio web emplea la informacin con el fin de proporcionar el
24 mejor servicio posible, particularmente para mantener un registro de usuarios,
25 de pedidos en caso que aplique, y mejorar nuestros productos y servicios.
26 Es posible que sean enviados correos electrnicos peridicamente a
27 travs de nuestro sitio con ofertas especiales, nuevos productos y otra
28 informacin publicitaria que consideremos relevante para usted o que
29 pueda brindarle algn beneficio, estos correos electrnicos sern enviados
30 a la direccin que usted proporcione y podrn ser cancelados en
31 cualquier momento.</p>
32
33 <p>TestOne est altamente comprometido para cumplir con el compromiso de
34 mantener su informacin segura. Usamos los sistemas ms avanzados y los
35 actualizamos constantemente para asegurarnos que no exista ningn
36 acceso no autorizado.</p>
37
38 <h3>Enlaces a Terceros</h3>
39
40 <p>Este sitio web pudiera contener enlaces a otros sitios que pudieran ser de
41 su inters. Una vez que usted d clic en estos enlaces y abandone nuestra
42 pgina, ya no tenemos control sobre al sitio al que es redirigido y por lo tanto
Captulo 1: Qu es IONIC? 24

43 no somos responsables de los trminos o privacidad ni de la proteccin de sus


44 datos en esos otros sitios terceros. Dichos sitios estn sujetos a sus propias
45 polticas de privacidad por lo cual es recomendable que los consulte para
46 confirmar que usted est de acuerdo con estas.</p>
47
48 <h3>Control de su informacin personal</h3>
49
50 <p>En cualquier momento usted puede restringir la recopilacin o el uso de
51 la informacin personal que es proporcionada a nuestro sitio web. Cada vez que
52 se le solicite rellenar un formulario, como el de alta de usuario, puede
53 marcar o desmarcar la opcin de recibir informacin por correo electrnico.
54 En caso de que haya marcado la opcin de recibir nuestro boletn o publicidad
55 usted puede cancelarla en cualquier momento.</p>
56
57 <p>Esta compaa no vender, ceder ni distribuir la informacin personal que
58 es recopilada sin su consentimiento, salvo que sea requerido por un juez con
59 un orden judicial.</p>
60
61 <p>TestOne Se reserva el derecho de cambiar los trminos de la presente
62 Poltica de Privacidad en cualquier momento.</p>
63 </ion-content>

Gist
Fantstico. Ya tenemos la plantilla correspondiente a nuestra pgina. Sin embargo, no la hemos
ubicado an en una pestaa.
Regresemos al archivo tabs.ts
Al igual que lo que ocurre con las otras pginas, debemos agregar la sentencia import para la nueva
pgina que hemos creado:
import { PrivacyPage } from ../privacy/privacy;
Debemos tambin agregar una nueva variable que har referencia a la clase:
tab4Root: any = PrivacyPage;
Ahora, vayamos al archivo tabs.html y agreguemos la pestaa correspondiente.
<ion-tab [root]=tab4Root tabTitle=Privacy tabIcon=chatbubbles></ion-tab>
Si ahora ejecutamos ionic serve, podremos ver la nueva pestaa:
https://gist.github.com/vihugarcia/8ae00162e2501f081b94a79192e21364
Captulo 1: Qu es IONIC? 25

Nueva Pestaa

Sin embargo, si intentamos seleccionarla, veremos que se producen errores:


Captulo 1: Qu es IONIC? 26

Errores

Aqu es donde la informacin que obtenemos de la consola de depuracin comienza o mostrar su


valor.
Podemos ver que la primera entrada en la lista de errores nos seala:
error_handler.js:47 EXCEPTION: Error in ./Tabs class Tabs - inline template:0:43 caused by: No
component factory found for PrivacyPage
Esto ocurre, porque si bien hemos creado nuestro componente de manera correcta, y hemos utilizado
el import correspondiente en el archivo tabs.ts, nuestra aplicacin no sabe an cmo construir un
componente PrivacyPage.
Vayamos al directorio app, y dentro de l busquemos y abramos el archivo app.module.ts. Su
contenido actualmente es el siguiente:

1 import { NgModule, ErrorHandler } from '@angular/core';


2 import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
3 import { MyApp } from './app.component';
4 import { AboutPage } from '../pages/about/about';
5 import { ContactPage } from '../pages/contact/contact';
6 import { HomePage } from '../pages/home/home';
7 import { TabsPage } from '../pages/tabs/tabs';
8
9 @NgModule({
10 declarations: [
11 MyApp,
12 AboutPage,
Captulo 1: Qu es IONIC? 27

13 ContactPage,
14 HomePage,
15 TabsPage
16 ],
17 imports: [
18 IonicModule.forRoot(MyApp)
19 ],
20 bootstrap: [IonicApp],
21 entryComponents: [
22 MyApp,
23 AboutPage,
24 ContactPage,
25 HomePage,
26 TabsPage
27 ],
28 providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
29 })
30 export class AppModule {}

Este archivo, representa a toda nuestra aplicacin como un mdulo. Cada componente que es
utilizado en nuestro proyecto, debe ser declarado aqu de lo contrario nuestra aplicacin no se dar
por enterada de que existe y obtendremos un error como el que se ha presentado.
Como podemos ver, existen cuatro sentencias import para cada una de nuestras pginas:

1 import { AboutPage } from '../pages/about/about';


2 import { ContactPage } from '../pages/contact/contact';
3 import { HomePage } from '../pages/home/home';
4 import { TabsPage } from '../pages/tabs/tabs';

Debemos agregar la sentencia correspondiente a nuestra nueva pgina. Tendremos ahora:

1 import { AboutPage } from '../pages/about/about';


2 import { ContactPage } from '../pages/contact/contact';
3 import { HomePage } from '../pages/home/home';
4 import { PrivacyPage } from '../pages/privacy/privacy';
5 import { TabsPage } from '../pages/tabs/tabs';

Adems, debemos incluirla en las secciones declarations y entryComponents. Para estar completa-
mente seguros, aqu est todo el cdigo del archivo con las modificaciones realizadas:
Captulo 1: Qu es IONIC? 28

1 import { NgModule, ErrorHandler } from '@angular/core';


2 import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
3 import { MyApp } from './app.component';
4 import { AboutPage } from '../pages/about/about';
5 import { ContactPage } from '../pages/contact/contact';
6 import { HomePage } from '../pages/home/home';
7 import { PrivacyPage } from '../pages/privacy/privacy';
8 import { TabsPage } from '../pages/tabs/tabs';
9
10 @NgModule({
11 declarations: [
12 MyApp,
13 AboutPage,
14 ContactPage,
15 HomePage,
16 PrivacyPage,
17 TabsPage
18 ],
19 imports: [
20 IonicModule.forRoot(MyApp)
21 ],
22 bootstrap: [IonicApp],
23 entryComponents: [
24 MyApp,
25 AboutPage,
26 ContactPage,
27 HomePage,
28 PrivacyPage,
29 TabsPage
30 ],
31 providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
32 })
33 export class AppModule {}

Como habamos mencionado anteriormente, si el servidor se est ejecutando an, podremos ver que
los cambios que realicemos se reflejarn en el navegador.
Si ahora nos dirigimos a la nueva pestaa, podremos visualizarla correctamente:
Captulo 1: Qu es IONIC? 29

Pgina Privacidad

Fantstico!
Realicemos algunos cambios ms a nuestra pgina recientemente creada.
Si analizamos el cdigo, veremos que tenemos dos ocurrencias del mismo texto: Poltica de
Privacidad.
Vamos a quitar esa redundancia.
En el archivo privacy.ts, agreguemos una variable de tipo String de la siguiente manera:

1 titulo: String = 'Poltica de Privacidad';

Ahora, en el archivo privacy.html, reemplacemos el ion-title por lo siguiente:

1 <ion-title>{{titulo}}</ion-title>

Y en el contenido, reemplacemos el h2:

1 <h2>{{titulo}}</h2>

Las llaves dobles indican que todo lo que se encuentre entre ellas debe ser evaluado como una
expresin.
Captulo 1: Qu es IONIC? 30

En este caso, la variable titulo ser reemplazada por su valor por lo que la salida es exactamente la
misma.
En muy poco tiempo, hemos aprendido mucho. Hemos creado un proyecto, hemos visto cmo se
estructuran las pginas de una aplicacin, como puede crearse una nueva pgina y cmo debe
agregarse su declaracin para que sea reconocida por la aplicacin.
En el prximo captulo comenzaremos con un nuevo proyecto que desarrollaremos hasta obtener
una aplicacin totalmente funcional.
Estamos ya en la senda para crear aplicaciones mviles espectaculares.
Captulo 2: Primera Aplicacin
Completa
Vamos a una construir una aplicacin para gestionar contactos. Podremos agregar, editar y eliminar
contactos.
En un primer momento nuestros datos estarn en memoria, pero luego los obtendremos a partir de
servicios web. Adquiriremos as los fundamentos necesarios para realizar aplicaciones complejas.
Cuando comenzamos con el desarrollo de una aplicacin (sea mvil o no) es muy til contar con
bosquejos de las pantallas que el usuario encontrar. Esto es muy til en la comunicacin con
el cliente ya que nos permite capturar requisitos de una manera rpida y eliminar potenciales
problemas antes de que se presenten.
Existen muchas herramientas para realizar sketchs de pantallas. Algunas incluyen funcionalidad
avanzada como por ejemplo la simulacin del funcionamiento de una aplicacin proporcionando la
posibilidad de interactuar con las pantallas.
No es necesario que incurramos en grandes costos para adquirir un software tan avanzado.
Particularmente yo uso una aplicacin denominada Pencil. Es una herramienta de prototipado
de cdigo abierto, totalmente gratuita, y que proporciona muy buenos resultados.
Por supuesto, no es obligatorio que utilicemos un programa para realizar el diseo de pantallas,
simplemente es algo recomendable. El tiempo que invirtamos en esta actividad ser ms que
compensado por la comprensin de las necesidades del cliente que obtendremos.
El programa tiene distintas colecciones de elementos de interface que podemos utilizar para disear
las pantallas. Por ejemplo en la figura siguiente se muestra la seccin correspondiente a Android:
http://pencil.evolus.vn/
Captulo 2: Primera Aplicacin Completa 32

Android

Tambin existe una seccin denominada Desktop Sketchy UI, cuyos tienen una apariencia de
dibujo a mano alzada, lo que resalta el concepto de que se trata de un diseo preliminar y esto til
en el trato con los clientes.
Sea cual sea el grupo de componentes que se utilice, siempre debemos sealar a nuestros clientes
que el diseo presentado es simplemente un bosquejo a fin de poder plasmar y discutir ideas.
El primer diseo de pantalla es el siguiente:
Captulo 2: Primera Aplicacin Completa 33

Pantalla Inicial

Muy bien. Con este sencillo diseo como gua, podemos comenzar a trabajar en nuestra aplicacin.

Creacin del Proyecto


Vamos a crear un nuevo proyecto, pero esta vez el comando que utilizaremos ser:
ionic start contactMgrApp blank v2
La diferencia con el proyecto anterior es que al utilizar el modificador blank, estamos indicando
que en este caso deseamos una aplicacin basada en una plantilla bsica, que contendr una nica
pgina.
A partir de este lienzo en blanco desarrollaremos la aplicacin.
Como en el proyecto anterior, debemos reemplazar la seccin scripts del archivo package.json por
lo siguiente:
Captulo 2: Primera Aplicacin Completa 34

1 "scripts": {
2 "build": "ionic-app-scripts build",
3 "watch": "ionic-app-scripts watch",
4 "serve:before": "watch",
5 "emulate:before": "build",
6 "deploy:before": "build",
7 "build:before": "build",
8 "run:before": "build"
9 },

Al ejecutar ionic serve debemos ver la siguiente pantalla:

Primera pantalla

Tenemos una nica pgina con un poco de texto.


Abramos el proyecto con nuestro editor para ver la estructura.
Podemos comprobar que dentro del directorio pages existe una nica carpeta denominada home.
Dejmosla tal como est.

Creando una nueva pgina


Vamos a crear una nueva pgina ejecutando:
ionic g page contactos
Obtendremos un nuevo directorio denominado contactos.
El contenido del archivo contactos.ts es el siguiente:
Captulo 2: Primera Aplicacin Completa 35

1 import { Component } from '@angular/core';


2 import { NavController, NavParams } from 'ionic-angular';
3
4 /*
5 Generated class for the Contactos page.
6
7 See http://ionicframework.com/docs/v2/components/#navigation for more info on
8 Ionic pages and navigation.
9 */
10 @Component({
11 selector: 'page-contactos',
12 templateUrl: 'contactos.html'
13 })
14 export class ContactosPage {
15
16 constructor(public navCtrl: NavController, public navParams: NavParams) {}
17
18 ionViewDidLoad() {
19 console.log('ionViewDidLoad ContactosPage');
20 }
21
22 }

Como primera medida, agregaremos una variable para contener el ttulo que mostraremos en la
pgina:
Luego de export class ContactosPage { agregamos:

1 titulo: String = 'Administrador de Contactos';

El contenido de contactos.html es el siguiente:

1 <!--
2 Generated template for the Contactos page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar>
10 <ion-title>contactos</ion-title>
Captulo 2: Primera Aplicacin Completa 36

11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17
18 </ion-content>

Comenzaremos por utilizar la variable que hemos creado en el ion-title y agregar una etiqueta h2
en el contenido de la pgina.
Ahora tendremos: Del libro:

1 <!--
2 Generated template for the Contactos page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar>
10 <ion-title>{{titulo}}</ion-title>
11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17 <h2>{{titulo}}</h2>
18 </ion-content>

Gist
Perfecto. Sin embargo, tal como est ahora la aplicacin, no tenemos modo de ver la pgina creada.
Vamos a modificar el proyecto de modo que la pgina que se cargue al comienzo sea la nueva pgina
en lugar de la pgina que viene por defecto.
https://gist.github.com/vihugarcia/2882bb440b1bbd6cccaad24cffee4751
Captulo 2: Primera Aplicacin Completa 37

Seteando la pgina raz


Si examinamos el archivo app.component.ts dentro del directorio app, encontraremos la siguiente
lnea:
rootPage = HomePage;
Esta es la lnea que indica que la pgina Home es la que debe tomarse como raz. Antes de modificar
esta lnea, sin embargo, debemos agregar la sentencia import para poder usar la nueva pgina:
import { ContactosPage } from ../pages/contactos/contactos;
Ahora podemos indicar la nueva raz:
rootPage = ContactosPage;
Sin embargo, si ahora ejecutamos ionic serve obtendremos un error. Si recordamos del captulo
anterior, esto sucede porque debemos informarle a la aplicacin sobre la nueva pgina creada. Esto
lo hacemos editando el archivo app.module.ts

1 import { ContactosPage } from '../pages/contactos/contactos';

Adems, debemos agregar la pgina en las secciones declarations y entryComponents.


El contenido completo del archivo quedar de la siguiente manera.
Del libro:

1 import { NgModule, ErrorHandler } from '@angular/core';


2 import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
3 import { MyApp } from './app.component';
4 import { HomePage } from '../pages/home/home';
5 import { ContactosPage } from '../pages/contactos/contactos';
6
7 @NgModule({
8 declarations: [
9 MyApp,
10 HomePage,
11 ContactosPage
12 ],
13 imports: [
14 IonicModule.forRoot(MyApp)
15 ],
16 bootstrap: [IonicApp],
17 entryComponents: [
18 MyApp,
Captulo 2: Primera Aplicacin Completa 38

19 HomePage,
20 ContactosPage
21 ],
22 providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
23 })
24 export class AppModule {}

Gist
Ahora lancemos ionic serve.

Nueva pantalla inicio

Nada extraordinario, pero seguimos avanzando.

Creando un modelo de datos


Sabemos que nuestra aplicacin mostrar un listado de contactos. Es decir, tenemos una entidad
fundamental denominada contacto.
Esta entidad tendr atributos tales como nombre y direccin, y algn otro que sirva como
identificador.
En nuestra aplicacin, tendremos que modelar todas las entidades que la conformen. Es decir, por
cada entidad que componga nuestra aplicacin ser conveniente construir un modelo de datos. No
se preocupen, suena ms complicado de lo que en realidad es.
Creemos al mismo nivel que el directorio pages una carpeta denominada model. No es obligatorio
que agrupemos todos nuestros modelos en un nico directorio, pero es una prctica recomendable
ya que nos permite mantener nuestra aplicacin ordenada y mantenible. Dentro de la carpeta model
creemos un archivo denominado contacto.ts.
Agreguemos a dicho archivo el siguiente contenido.
Del libro:
https://gist.github.com/vihugarcia/e1d7aad010c37e09ec0be903891e7d7a
Captulo 2: Primera Aplicacin Completa 39

1 export class Contacto{


2 constructor(
3 public id: number,
4 public nombre: string,
5 public direccion: string
6 ){}
7 }

Gist
Estamos definiendo una clase denominada Contacto con tres propiedades o atributos: id, nombre,
y direccin. Ntese que cada una de estas propiedades est precedida del modificador public. Esto
seala que los valores de dichas propiedades pueden ser accedidos desde afuera de la clase.
Si no se indica el nivel de visibilidad de una propiedad, se considera que es public. Es decir:
public id: number es equivalente a id: number.
Habiendo definido la clase Contacto, podemos hacer ya uso de ella.
En el archivo contactos.ts agreguemos el siguiente import:

1 import { Contacto } from '../../model/contacto';

Ahora definamos una variable que contenga algunas instancias de la clase definida:

1 public contactos = [
2 new Contacto(1, "Andrea Gmez", "Calle Uno 123"),
3 new Contacto(2, "Juan Perez", "Calle Dos 567"),
4 new Contacto(3, "Martn lvarez", "Calle del Pueblo 628")
5 ];

Lo que estamos haciendo, es definir un arreglo que contiene objetos del tipo Contacto. En el momento
de crear los objetos (con la sentencia new) le pasamos los parmetros que espera el constructor de
la clase Contacto.
Debemos ahora modificar el archivo contactos.html para mostrar los datos de los contactos que
hemos creado.

Listas y estructuras repetitivas


Podemos hacerlo utilizando una lista de tems. Para ello ionic nos brinda un componente denomi-
nado ion-list. Dentro de cada ion-list existirn tems. Cada tem corresponder a un contacto.
Luego de la etiqueta h2 agreguemos lo siguiente.
Del libro:
https://gist.github.com/vihugarcia/af8bf72b86aaa1dbd1d03261bdd096c4
Captulo 2: Primera Aplicacin Completa 40

1 <ion-list>
2 <ion-item *ngFor="let contacto of contactos">
3 <ion-label>{{contacto.nombre}}</ion-label>
4 <ion-label>{{contacto.direccion}}</ion-label>
5 </ion-item>
6 </ion-list>

Gist
La lista est comprendida entre las etiquetas <ion-list> y </ion-list>
Cada tem queda determinado por el par de etiquetas <ion-item> y </ion-item>
Dentro de ion-item, tenemos la siguiente sentencia:
*ngFor=let contacto of contactos
La directiva *ngFor nos permite utilizar un ciclo for para repetir un elemento. En ese caso, como
lo que queremos es repetir tantos tems como contactos, agregamos la sentencia en la etiqueta de
apertura <ion-item>
Dentro del *ngFor, estamos definiendo una variable ** contacto **. La variable contacto tomar un
valor distinto en cada iteracin del ciclo. Es decir, por cada elemento del arreglo contactos, la variable
** contacto ** tomar dicho valor. Cada uno de dichos elementos es en este caso un objeto.
Ejecutemos ahora ionic serve. La salida que obtenemos es la siguiente:

Lista de Contactos

Estamos ms cerca. Tenemos nuestra lista de contactos. Lgicamente, una aplicacin de contactos
real necesitar manejar ms datos que el nombre y la direccin. Podemos agregar tantos como sea
necesario en la definicin de nuestra clase Contacto.
Por ejemplo, agreguemos telfono y direccin, ambos como tipo String.
La nueva definicin de la clase quedar de la siguiente manera.
Del libro:
https://gist.github.com/vihugarcia/f847ea364b2eba7d56097fcdeedbc6f3
Captulo 2: Primera Aplicacin Completa 41

1 export class Contacto{


2 constructor(
3 public id: number,
4 public nombre: string,
5 public direccion: string,
6 public telefono: string,
7 public email: string
8 ){}
9 }

Gist
Sin embargo, ahora nos encontraremos con que la lnea de comandos nos muestra mensajes de error:

Error de nmero de parmetros

Esto ocurre porque ahora el constructor de la clase espera cinco parmetros, pero en el momento de
crear los objetos en el archivo contactos.ts, estamos proporcionando tres. Corrijamos.

https://gist.github.com/vihugarcia/f572f4db6387a8d9f4b6d3cdc71ea1a8
Captulo 2: Primera Aplicacin Completa 42

1 public contactos = [
2 new Contacto(1, "Andrea Gmez", "Calle Uno 123", "12345", "gomez@gmail.com"),
3 new Contacto(2, "Juan Perez", "Calle Dos 567", "23456", "perez@gmail.com"),
4 new Contacto(3, "Martn lvarez", "Calle del Pueblo 628", "34567",
5 "alvarez@gmail.com")
6 ];

Los errores han desaparecido y ahora nuestra aplicacin funciona nuevamente.


En la pantalla principal, se siguen mostrando como nicos datos el nombre y direccin. Lo que
haremos ahora es agregar la opcin para permitir ver los detalles de un contacto seleccionado.
Una forma elegante de agregar opciones para trabajar con cada uno de los tems, muy utilizada en
las apps, es la de presentar una serie de botones al deslizar el tem ya sea a izquierda o derecha.
Para ello, modifiquemos la ion-list de la siguiente manera.
Del libro:

1 <ion-list>
2 <ion-item-sliding *ngFor="let contacto of contactos">
3 <ion-item>
4 <ion-label>{{contacto.nombre}}</ion-label>
5 <ion-label>{{contacto.direccion}}</ion-label>
6 </ion-item>
7
8 <ion-item-options side="right">
9 <button ion-button color="favorite" (click)="verContacto(contacto)">
10 <ion-icon name="eye"></ion-icon>
11 Ver
12 </button>
13 </ion-item-options>
14 </ion-item-sliding>
15 </ion-list>

Gist
Las diferencias son las siguientes:
Se utiliza un ion-item-sliding como grupo repetitivo en lugar de ion-item. Este es el elemento que
nos permitir agregar un conjunto de botones para cada tem.
Luego tenemos el ion-list donde se muestran los datos del contacto.

https://gist.github.com/vihugarcia/5ceeee3868c76b4cf6513689095e9f1e
Captulo 2: Primera Aplicacin Completa 43

1 <ion-item>
2 <ion-label>{{contacto.nombre}}</ion-label>
3 <ion-label>{{contacto.direccion}}</ion-label>
4 </ion-item>

A continuacin, aadimos un elemento ion-item-options. La propiedad side determina la alineacin


de los botones. En este caso los botones aparecern a la derecha cuando se realice un deslizamiento
de derecha a izquierda.

1 <ion-item-options side="right">
2 <button ion-button color="favorite" (click)="verContacto(contacto)">
3 <ion-icon name="eye"></ion-icon>
4 Ver
5 </button>
6 </ion-item-options>

En este caso se mostrar un solo botn. Al hacer clic en este botn se convocar a un mtodo
verContacto que recibe como parmetro un objeto del tipo Contacto.
Escribamos dicho mtodo. Por ahora estar vaco.

1 verContacto(contacto: Contacto) {
2
3 }

El resultado es el siguiente:

Botn ver

Vamos a crear ahora la pgina que mostrar los detalles del contacto seleccionado. El comando para
crear una nueva pgina ya debe resultarnos familiar.
Captulo 2: Primera Aplicacin Completa 44

ionic g page contacto


Concentrmonos ahora en el archivo contacto.html. Modifiqumoslo de la siguiente manera. Del
libro:

1 <!--
2 Generated template for the Contacto page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar>
10 <ion-title>Contacto</ion-title>
11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17 <div *ngIf="contacto">
18 <ion-card>
19 <ion-card-content>
20 <ion-card-title>
21 {{contacto.nombre}}
22 </ion-card-title>
23 <p>{{contacto.direccion}}</p>
24 <p>{{contacto.telefono}}</p>
25 <p>{{contacto.email}}</p>
26 </ion-card-content>
27 </ion-card>
28 </div>
29 </ion-content>

Gist
Muy bien. Hay unas cuantas cosas interesantes en el fragmento de cdigo anterior.
Por una lado, tenemos la siguiente etiqueta: <div *ngIf=contacto>
Estamos usando aqu una estructura de control. Un if, que en IONIC representamos con *ngIf.
Esta estructura de control, evala una condicin y devuelve verdadero o falso. Al agregarla al
https://gist.github.com/vihugarcia/d0a4ab385ca285a9d8cea16ee19a7ddf
Captulo 2: Primera Aplicacin Completa 45

elemento div, estamos indicando que deseamos visualizar dicho elemento slo cuando la condicin
sea verdadera. En este caso, cuando exista un objeto contacto a mostrar.
Luego introducimos un nuevo elemento de IONICS. Una ion-card.
Este elemento, permite presentar contenido en un bloque que tiene una bonita presentacin y cuya
apariencia podemos configurar.
Una ion-card se compone de un ion-card-header y de un ion-card-content. Un encabezado y un
cuerpo respectivamente.
Cada vez que tengamos dudas sobre el uso de un componente, o querramos ampliar nues-
tros conocimientos, podemos recurrir a la muy buena documentacin de IONIC disponible en
https://ionicframework.com/docs/v2/components/
Existen multitud de ejemplos y adems tenemos se nos presentan simulaciones de aplicaciones donde
podemos ver los ejemplos ejecutndose en vivo. No podra ser mejor.
Ahora que tenemos la pgina de un contacto individual creada, regresemos a contactos.ts y
completemos el mtodo verContacto.

1 verContacto(contacto: Contacto) {
2 this.navCtrl.push(ContactoPage, {contacto});
3 }

Analicemos lo que ocurre aqu. En toda aplicacin mvil, las pginas se muestran en un stack (pila).
Una nueva pgina puede apilarse encima de las pginas existentes, utilizando una operacin push.
Para ello, invocamos al mtodo push de un objeto del tipo NavController. Como habremos notado,
cuando creamos una pgina mediante la lnea de comandos, ionic se encarga de pasar como
parmetro al mtodo constructor de la pgina un objeto navCtrl del tipo NavController.
El mtodo push tiene la forma: push(pgina, {parmetros})
En este caso, estamos indicando que debe apilarse la pgina ContactoPage, y que esta recibir como
parmetro un objeto de tipo Contacto.
Para que esto funcione, debemos agregar el import de la pgina contacto en la parte superior.
Para estar seguros, aqu est el cdigo completo de contactos.ts
Del libro:

https://ionicframework.com/docs/v2/components/
Captulo 2: Primera Aplicacin Completa 46

1 import { Component } from '@angular/core';


2 import { NavController, NavParams } from 'ionic-angular';
3 import { Contacto } from '../../model/contacto';
4 import { ContactoPage } from '../contacto/contacto';
5
6 /*
7 Generated class for the Contactos page.
8
9 See http://ionicframework.com/docs/v2/components/#navigation for more info on
10 Ionic pages and navigation.
11 */
12 @Component({
13 selector: 'page-contactos',
14 templateUrl: 'contactos.html'
15 })
16 export class ContactosPage {
17 titulo: String = 'Administrador de Contactos';
18 public contactos = [
19 new Contacto(1, "Andrea Gmez", "Calle Uno 123", "12345", "gomez@gmail.com"),
20 new Contacto(2, "Juan Perez", "Calle Dos 567", "23456", "perez@gmail.com"),
21 new Contacto(3, "Martn lvarez", "Calle del Pueblo 628", "34567",
22 "alvarez@gmail.com")
23 ];
24
25 constructor(public navCtrl: NavController, public navParams: NavParams) {}
26
27 ionViewDidLoad() {
28 console.log('ionViewDidLoad ContactosPage');
29 }
30
31 verContacto(contacto: Contacto) {
32 this.navCtrl.push(ContactoPage, {contacto});
33 }
34
35 }

Gist
El parmetro pasado a la pgina contacto, tiene el nombre contacto, y recibe el objeto contacto que
est en la declaracin del mtodo verContacto.
Cuando el parmetro tiene el mismo nombre que el objeto que recibe, se puede utilizar una notacin
abreviada como la de arriba. Equivalente a lo anterior sera:
https://gist.github.com/vihugarcia/2aa608d26ad04d931039daf21ee58e9c
Captulo 2: Primera Aplicacin Completa 47

this.navCtrl.push(ContactoPage, {contacto: contacto});


Ahora, el parmetro enviado debe recibirse. Aqu est el cdigo completo de contacto.ts.
Del libro:

1 import { Component } from '@angular/core';


2 import { NavController, NavParams } from 'ionic-angular';
3
4 import { Contacto } from '../../model/contacto';
5
6 /*
7 Generated class for the Contacto page.
8
9 See http://ionicframework.com/docs/v2/components/#navigation for more info on
10 Ionic pages and navigation.
11 */
12 @Component({
13 selector: 'page-contacto',
14 templateUrl: 'contacto.html'
15 })
16 export class ContactoPage {
17 public contacto: Contacto;
18
19 constructor(public navCtrl: NavController, public navParams: NavParams) {
20 this.contacto = this.navParams.get('contacto');
21 }
22
23 ionViewDidLoad() {
24 console.log('ionViewDidLoad ContactoPage');
25 }
26
27 }

Gist
Estupendo. Ejecutemos la aplicacin para ver los resultados:
https://gist.github.com/vihugarcia/fb76df64154d601804af89c24f20bce9
Captulo 2: Primera Aplicacin Completa 48

Detalle de Contacto

Nada mal.
Vamos a agregar ahora botones con opciones para editar y eliminar un contacto.
Para ello, deberemos modificar el archivo contactos.html de la siguiente manera.
Del libro:

1 <button ion-button color="favorite" (click)="verContacto(contacto)">


2 <ion-icon name="eye"></ion-icon>
3 Ver
4 </button>
5 <button ion-button color="dark" (click)="editarContacto(contacto)">
6 <ion-icon name="create"></ion-icon>
7 Editar
8 </button>
9 <button ion-button color="danger" (click)="eliminarContacto(contacto)">
10 <ion-icon name="remove-circle"></ion-icon>
11 Eliminar
12 </button>

Gist
Se han resaltado en negrita las partes a agregar.
Debemos agregar los dos mtodos que se invocan en contactos.ts. Por ahora dejaremos en suspenso
su implementacin. Simplemente escribiremos:

https://gist.github.com/vihugarcia/239061523f3b3f2d3c12a84aa97ef8e5
Captulo 2: Primera Aplicacin Completa 49

1 editarContacto(contacto: Contacto) {
2
3 }
4
5 eliminarContacto(contacto: Contacto) {
6
7 }

Con los cambios realizados, podremos ver que ahora al deslizar la pantalla hacia le derecha sobre
un contacto, visualizamos tres botones.

Nuevos botones

En este punto, y antes de avanzar con el resto de la funcionalidad de la aplicacin, vamos a tomarnos
un breve tiempo para mejorar la apariencia de la misma.
Vayamos al archivo variables.scss dentro del directorio theme y reemplacemos el mapa $colors por
lo siguiente.
Del libro:

1 $colors: (
2 primary: #638CA6,
3 secondary: #32db64,
4 favorite: #17A697,
5 danger: #D93240,
6 light: #BFD4D9,
7 dark: #F2671F
8 );

Gist
https://gist.github.com/vihugarcia/a3be82bd10e372c3da12eae4934761ff
Captulo 2: Primera Aplicacin Completa 50

Ahora indiquemos tanto en contactos.html como en contacto.html que nuestra barra de navegacin
debe usar el color primary.
<ion-navbar color=primary>
La apariencia de nuestra aplicacin debera ser ahora la siguiente:

Nuevos colores

Creo que se ve mejor. Sintanse en libertad de usar cualquier combinacin de colores que sea de su
agrado.
Algo que estamos echando en falta, es la posibilidad de agregar un nuevo contacto. Vamos a
comenzar a resolver el problema.
Para ello, aadamos en contactos.html luego de la etiqueta de cierre </ion-content> lo siguiente:

1 <ion-fab right bottom>


2 <button ion-fab (click)="mostrarAgregarContacto()">
3 <ion-icon name="add"></ion-icon>
4 </button>
5 </ion-fab>

Debemos aadir tambin el mtodo correspondiente en contactos.ts:

1 mostrarAgregarContacto() {
2
3 }

Podemos ver que ahora nuestra pantalla principal presenta un botn en la esquina inferior derecha.
Captulo 2: Primera Aplicacin Completa 51

Botn agregar contacto

Desde luego, el botn no hace nada an. Mejor dicho, convoca a un mtodo que todava no realiza
ninguna funcin.
Vamos a resolver eso.

Crear una ventana modal


Vamos a crear ahora una nueva pgina
ionic g page add-contacto-modal
Esta pgina, que mostraremos de forma modal, contendr un formulario que nos permitir agregar
un nuevo contacto.
Captulo 2: Primera Aplicacin Completa 52

Editemos el archivo add-contacto-modal.html y agreguemos lo siguiente dentro del elemento ion-


content.
Del libro:

1 <form #formContacto="ngForm" class="container" (ngSubmit)="onSubmit()">


2 <ion-item>
3 <ion-input name="nombre" id="nombre" #nombre="ngModel"
4 [(ngModel)]="contacto.nombre" required="required" placeholder="Nombre">
5 </ion-input>
6 </ion-item>
7 <ion-item danger [hidden]="nombre.valid || nombre.untouched">
8 El nombre es obligatorio
9 </ion-item>
10
11 <ion-item>
12 <ion-input name="direccion" id="direccion"
13 #direccion="ngModel" [(ngModel)]="contacto.direccion"
14 required="required" placeholder="Direccin"></ion-input>
15 </ion-item>
16 <ion-item danger [hidden]="direccion.valid || direccion.untouched">
17 La direccin es obligatoria
18 </ion-item>
19
20 <ion-item>
21 <ion-input name="telefono" id="telefono"
22 #telefono="ngModel" [(ngModel)]="contacto.telefono"
23 required="required" placeholder="Telfono"></ion-input>
24 </ion-item>
25 <ion-item danger [hidden]="telefono.valid || telefono.untouched">
26 El telfono es obligatorio
27 </ion-item>
28
29 <ion-item>
30 <ion-input name="email" id="email" #email="ngModel"
31 [(ngModel)]="contacto.email" placeholder="E-Mail"></ion-input>
32 </ion-item>
33
34 <div class="submit-button">
35 <button ion-button block
36 type="submit" [disabled]="!formContacto.form.valid">Enviar</button>
37 </div>
38
Captulo 2: Primera Aplicacin Completa 53

39 </form>

Gist
Varias cosas suceden aqu.
En primer lugar, estamos aadiendo un formulario con las etiquetas <form> y </form>. Esto
es exactamente igual a cuando creamos un formulario en una pgina web comn y corriente.
#formContacto=ngForm agrega un identificador a nuestro formulario, y adems le indica a IONIC
que se trata de un ngForm, lo que le agrega al formulario una serie de capacidades muy interesantes.
(ngSubmit)=onSubmit() intercepta el evento submit del formulario (representado por ngSubmit) y
le asigna un mtodo a ser ejecutado, en este caso onSubmit.
Luego tenemos varios inputs que estn contenidos dentro de respectivos ion-item. Como ven, un
ion-item no tiene que estar forzosamente asociado a un ion-list.
Un cuadro de texto en IONIC se corresponde con el elemento ion-input. Este tiene las mismas
caractersticas que un input ordinario, pero est especficamente pensado para aplicaciones mviles
por lo que debemos utilizarlo.
Si analizamos el primer ion-input podemos ver las propiedades required=required placehol-
der=Nombre
required es un atributo html5 que indica que el ingreso de datos es obligatorio. Un placeholder es un
texto que se muestra dentro del cuadro de texto para indicar al usuario qu dato debe ingresar. Este
texto desaparece en cuanto el usuario comienza a escribir y reaparece si el usuario borra el texto.
Analicemos ahora lo siguiente: #nombre=ngModel [(ngModel)]=contacto.nombre

1 #nombre="ngModel"

Asigna un identificador al cuadro de texto, y le indica a IONIC que se trata de un campo


correspondiente a un modelo de datos.
[(ngModel)]=contacto.nombre es la forma de indicar un databinding.
Qu es eso de data binding?
Vamos a ver un ejemplo ahora mismo antes de continuar avanzando.
En el archivo contactos.html, justo debajo de la etiqueta h2, agregue lo siguiente:

https://gist.github.com/vihugarcia/2081d84a3c97f3862024e43f3a4a49aa
Captulo 2: Primera Aplicacin Completa 54

1 <p>{{texto}}</p>
2
3 <ion-item>
4 <ion-input type="text" placeholder="escriba algo..." [(ngModel)]="texto">
5 </ion-input>
6 </ion-item>

Vern la siguiente pantalla:

Data binding

Si ahora comienzan a escribir en el cuadro de texto que ha agregado, vern que el texto aparece
arriba del mismo.
Captulo 2: Primera Aplicacin Completa 55

Data binding

Esto se debe a que estamos utilizando databinding.


Cuando escribimos [(ngModel)]=texto enlazamos el texto que contiene el ion-input con el valor
de una variable texto.
Justo arriba del cuadro de texto tenemos <p>{{texto}}</p>
Las llaves doblen indican que lo que est entre ellas debe evaluarse como una expresin. Cuando
escribimos un texto, el valor de la variable texto se modifica y este cambio automticamente se ve
reflejado. Angular (que est detrs de IONIC) se encarga de modificar el DOM (Document Object
Model) de forma transparente para nosotros.
No se preocupen si no ha quedado claro del todo. Lo iremos comprendiendo cada vez mejor.
Ahora continuemos.
El contenido del archivo add-contacto-modal.ts actualmente es el siguiente:

1 import { Component } from '@angular/core';


2 import { NavController, NavParams } from 'ionic-angular';
3
4 /*
5 Generated class for the AddContactoModal page.
6
7 See http://ionicframework.com/docs/v2/components/#navigation for more info on
8 Ionic pages and navigation.
9 */
10 @Component({
11 selector: 'page-add-contacto-modal',
12 templateUrl: 'add-contacto-modal.html'
Captulo 2: Primera Aplicacin Completa 56

13 })
14 export class AddContactoModalPage {
15
16 constructor(public navCtrl: NavController, public navParams: NavParams) {}
17
18 ionViewDidLoad() {
19 console.log('ionViewDidLoad AddContactoModalPage');
20 }
21
22 }

Como primera medida, vamos a cambiar el nombre de la clase de AddContactoModalPage a


AddContactoModal, ya que no pretendemos que funcione como una pgina ordinaria sino como
una ventana modal. No es obligatorio cambiarle el nombre, pero me parece prudente.
Ya que nuestro propsito es agregar un nuevo contacto, debemos importar nuestro modelo Contacto
a fin de poder usarlo.

1 import { Contacto } from '../../model/contacto';

Ahora declaremos una variable para contener una instancia vaca de nuestro modelo. public contacto
= new Contacto(0, , , , );
Ntese que debemos pasar los parmetros requeridos al constructor, aun cuando se trate de cero o
cadenas vacas, de lo contrarios obtendremos un error.
Perfecto. De regreso ahora a contactos.ts, debemos importar nuestra ventana modal para poder
usarla.
Luego cambiemos el import:

1 import { NavController, NavParams } from 'ionic-angular';

Por:

1 import { NavController, NavParams, ModalController } from 'ionic-angular';

Estamos incluyendo el componente necesario para mostrar una pantalla como modal.
Los argumentos del constructor ahora deben cambiar de:
constructor(public navCtrl: NavController, public navParams: NavParams)
a:
Captulo 2: Primera Aplicacin Completa 57

constructor(public navCtrl: NavController, public navParams: NavParams, public modalCtrl: Mo-


dalController)
Estamos declarando una nueva variable de tipo ModalController y la estamos haciendo pblica, de
manera de poder utilizarla en mtodos de la clase.
Ahora podemos reescribir el mtodo que mostrar la pantalla modal.

1 mostrarAgregarContacto() {
2 let modal = this.modalCtrl.create(AddContactoModal);
3 modal.present();
4
5 modal.onDidDismiss(data => {});
6 }

No estamos en condiciones de ver todo en funcionamiento an. Debemos agregar el import


correspondiente en app.module.ts.

1 import { AddContactoModal }
2 from '../pages/add-contacto-modal/add-contacto-modal';

Y agregar la clase en las secciones declarations y entryComponents.


Ahora s, al presionar el botn agregar, veremos nuestra pantalla modal.

Ventana modal

Podemos descartar la pantalla presionando la tecla Escape.


Realicemos algunas modificaciones. En primer lugar, asignmosle el estilo a la barra de navegacin.
Luego, creemos en el archivo add-contacto-modal.ts una variable titulo.
Captulo 2: Primera Aplicacin Completa 58

1 public titulo : String = 'Agregar Contacto';

Y utilicemos las llaves dobles para mostrar el valor de la variable en la plantilla. La barra de
navegacin tendr ahora el siguiente contenido:

1 <ion-navbar color="primary">
2 <ion-title>{{titulo}}</ion-title>
3 </ion-navbar>

Muy bien. Ahora tenemos que ocuparnos de otro detalle.


En estos momentos, estamos descartando la ventana modal mediante la tecla Escape. Vamos a
agregar un botn que se ocupe de esto.
Justo despus del ion-title aadamos lo siguiente:

1 <ion-buttons start>
2 <button ion-button (click)="dismiss()">
3 <span secondary showWhen="ios">Cancelar</span>
4 <ion-icon name="md-close" showWhen="android, windows">
5 </ion-icon>
6 </button>
7 </ion-buttons>

ion-buttons es un componente que nos permite agrupar botones. Luego viene un componente button.
Ntese que tenemos dos formas alternativas de presentar el contenido del botn. Por un lado, cuando
el sistema operativo sea ios, se mostrar el texto Cancelar. Por otro, para android y Windows
mostraremos un cono.
Podemos ver las diferencias a continuacin:
Captulo 2: Primera Aplicacin Completa 59

Botn cerrar en Android

Botn cerrar en IOS

Muy bien. Hasta ahora tenemos lo siguiente:

Una pantalla principal que muestra una lista de contactos. Al deslizar la pantalla de derecha
Captulo 2: Primera Aplicacin Completa 60

a izquierda estando sobre un contacto, se muestran tres botones con opciones para ver, editar
y eliminar el contacto.
Al presionar el botn ver podemos acceder a una pantalla con los detalles del contacto
seleccionado.
En la pantalla inicial, esquina inferior derecha, tenemos un botn que al ser presionado,
muestra una ventana modal con un formulario que permite agregar un nuevo contacto.

Sin embargo, la funcionalidad para agregar, editar y eliminar contactos an no se encuentra


implementada.
Vamos a resolver esto.
Comenzaremos por permitir agregar un nuevo contacto.
En el archivo add-contacto-modal.ts cambiemos el import:

1 import { NavController, NavParams } from 'ionic-angular';

Por:

1 import { NavController, NavParams, ViewController } from 'ionic-angular';

Luego, agreguemos un nuevo parmetro al constructor. Debe quedar de la siguiente manera:


constructor(public navCtrl: NavController, public navParams: NavParams, public viewCtrl: View-
Controller) {}
En la plantilla, es decir en el archivo add-contacto-modal.html, interceptamos el envo del formulario
de la siguiente manera:
(ngSubmit)=onSubmit()
Ntese que la sintaxis para asociar una funcin a un evento es:
(evento)=funcion()
An no hemos implementado el evento onSubmit, vamos a hacerlo ahora. Nuevamente en add-
contacto-modal.ts.
Antes de generar la implementacin real del mtodo, vamos a realizar una prueba para asegurarnos
de que el formulario est funcionando de la manera deseada.
Agreguemos lo siguiente:

1 onSubmit() {
2 console.log(this.contacto);
3 }
Captulo 2: Primera Aplicacin Completa 61

Aqu mostraremos por consola el contacto con los valores recibidos para sus propiedades de parte
del formulario.
Ejecutemos la aplicacin y presionemos el botn agregar. Veremos nuestra ventana modal.

Ventana modal

Ntese que el botn Enviar est desactivado. Slo se activar cuando el formulario sea vlido. Esto
lo logramos con las siguiente lnea presente en el archivo add-contacto-modal.html:

1 <button ion-button block type="submit" [disabled]="!formContacto.form.valid">


2 Enviar</button>

Lo que hace el fragmento [disabled]=!formContacto.form.valid es enlazar el valor de la propiedad


disabled con el valor al que evala la expresin que se encuentra entre comillas.
Dicha expresin es !formContacto.form.valid. El signo de admiracin que se encuentra delante
es el operador lgico de negacin. formContacto.form.valid tomar un valor verdadero cuando el
formulario sea vlido. Por lo tanto, al poner el operador de negacin delante estamos haciendo que
la expresin evale a verdadero cuando el formulario NO sea vlido.
Esto est bien, porque nosotros queremos deshabilitar el botn de envo exactamente cuando ocurre
eso.
El poder validar el formulario en tiempo real, es gracias a la magia de IONIC.
Recordemos que al crear el formulario, en su etiqueta de apertura, pusimos:

1 #formContacto="ngForm"

Habamos mencionado que esto le indica a IONIC que debe tratar a este formulario como un
formulario especial al que le agrega funcionalidad extra.
Captulo 2: Primera Aplicacin Completa 62

Esta funcionalidad es la que estamos utilizando para habilitar o deshabilitar el botn de envo.
Continuemos.
Cuando se han completado todos los campos que hemos definido como obligatorios, el botn
cambiar a su estado habilitado como podemos ver a continuacin:

Formulario completo

Si presionamos el botn ENVIAR, veremos el siguiente mensaje en la consola:

Contacto en consola

Podemos hacer clic en la flecha de la izquierda y desplegar los detalles:

Detalles de contacto

Como vemos nuestro formulario funciona correctamente. Una vez probado, podemos cambiar la
implementacin del mtodo onSubmit por la siguiente:
Captulo 2: Primera Aplicacin Completa 63

1 onSubmit() {
2 this.viewCtrl.dismiss(this.contacto);
3 }

Lo que estamos haciendo ahora es cerrar la vista o pantalla modal, utilizando el componente
ViewController, pero adems estamos pasando como parmetro al mtodo dismiss el contacto que
queremos incorporar.
Eso es todo lo que tenemos que hacer en add-contacto-modal.ts.
De regreso a contactos.ts
La implementacin que tenemos del mtodo que muestra la pantalla modal hasta ahora es la
siguiente:

1 mostrarAgregarContacto() {
2 let modal = this.modalCtrl.create(AddContactoModal);
3 modal.present();
4
5 modal.onDidDismiss(data => {});
6 }

Analicemos la lnea:
modal.onDidDismiss(data {})
Tenemos una variable (denominada modal) que referencia nuestra ventana normal. Cuando esta
ventana ejecuta el evento dismiss, nosotros lo capturamos para nuestros propsitos.
La variable data, representa un dato devuelto por la ventana modal. Recordemos que en el archivo
add-contacto-modal.ts nosotros pasbamos como parmetro al mtodo dismiss el objeto conteniendo
el nuevo contacto a agregar.
Por lo tanto, data viene a representar ese contacto devuelto por la venta modal y que ahora podemos
manipular. El operador indica que sobre ese dato devuelto operar una funcin. En este caso se
trata de una funcin vaca, representada por llaves sin contenido.
Para comprenderlo mejor: data {} es conceptualmente equivalente function(data) {}. Es decir, data
es el parmetro de la funcin cuyo cuerpo est entre llaves.
Ahora cambiemos la implementacin de todo el mtodo por la siguiente.
Del libro:
Captulo 2: Primera Aplicacin Completa 64

1 mostrarAgregarContacto() {
2 let modal = this.modalCtrl.create(AddContactoModal);
3 modal.present();
4
5 modal.onDidDismiss(data => {
6 if (data) {
7 this.contactos.push(data);
8 }
9 });
10 }

Gist
La parte nueva, es que ahora la funcin que opera sobre los datos devueltos deja de estar vaca.
Primero verificamos que se hayan recibido datos. Recordemos que data representa un objeto de tipo
contacto que ha sido enviado por la ventana modal.
Si tenemos un objeto contacto, procedemos a agregar el nuevo contacto a los contactos ya existentes,
los cuales se encuentran en nuestra variable contactos que es de tipo array, por lo que utilizamos
para ello el mtodo push.
Si ahora completamos el formulario y presionamos ENVIAR veremos como el nuevo contacto
aparece en nuestra lista de contactos.

Contacto agregado

Podemos ver incluso los detalles del nuevo contacto agregado.


https://gist.github.com/vihugarcia/df71a58254437d812de6af49bcbd5a2b
Captulo 2: Primera Aplicacin Completa 65

Detalles del contacto agregado

Por supuesto, los datos existen en memoria, y si refrescamos el navegador volveremos a los tres
contactos que tenamos originalmente.
Vamos a ocuparnos ahora de la funcionalidad para editar un contacto.
Como primer paso, vamos a tener que editar nuestro modelo Contacto.
Debajo del constructos, aadamos el siguiente mtodo esttico:

1 static clone(contacto: Contacto) {


2 return new Contacto(contacto.id, contacto.nombre, contacto.direccion,
3 contacto.telefono, contacto.email);
4 }

Lo que buscamos aqu, es obtener una nueva instancia de un objeto contacto, que es una copia de
un contacto determinado. Estamos clonando un contacto. Ya veremos cul es el propsito de ello.
Al hacer el mtodo esttico, vamos a poder convocarlo sin tener necesidad de crear una instancia
de un objeto Contacto.
Ahora si nos dirigimos a la plantilla contactos.html, podemos observar las siguientes lneas de cdigo:

1 <button ion-button color="dark" (click)="editarContacto(contacto)">


2 <ion-icon name="create"></ion-icon>
3 Editar
4 </button>

Cuando hacemos clic sobre el botn editar, se invoca un mtodo editarContacto al que se le pasa
como dato el contacto seleccionado.
Vamos a aprovechar la misma ventana modal que utilizamos para crear un nuevo contacto, y
utilizarla para editar los datos del mismo. Por ello, me parece buena idea cambiar el nombre del
mtodo editarContacto por mostrarEditarContacto.
En el archivo contactos.ts, definamos una variable pblica de tipo Contacto:
Captulo 2: Primera Aplicacin Completa 66

1 public contactoOriginal: Contacto;

Esta variable contendr nuestro contacto antes de las modificaciones. Debemos cambiar el nombre
del mtodo editarContacto por el de mostrarEditarContacto.
La implementacin completa del mtodo es la siguiente.
Del libro:

1 mostrarEditarContacto(contacto: Contacto) {
2 let modal = this.modalCtrl.create(AddContactoModal, {contacto});
3 this.contactoOriginal = contacto;
4 modal.present();
5
6 modal.onDidDismiss(data => {
7 if (data) {
8 console.log(this.contactoOriginal);
9 console.log(data);
10 }
11 });
12 }

Gist
Podemos ver que guarda muchas similitudes con el mtodo mostrarAgregarContacto que vimos
anteriormente. Una diferencia, es que antes de presentar la ventana normal, guardamos en nuestra
variable contactoOriginal una referencia al contacto sin modificar. Tambin, la ventana modal recibe
como parmetro el contacto seleccionado. Cuando la ventana modal se cierre (porque se presion
el botn ENVIAR) por ahora simplemente mostraremos por la consola el contacto original y el
modificado.
Modifiquemos ahora el archivo add-contacto-modal.ts
Lo que haremos ahora, es cambiar la implementacin del constructor. Reemplacmoslo por lo
siguiente:

1 constructor(public navCtrl: NavController, public navParams: NavParams,


2 public viewCtrl: ViewController) {
3 if (this.navParams.get('contacto')) {
4 this.contacto = Contacto.clone(this.navParams.get('contacto'));
5 }
6 }

https://gist.github.com/vihugarcia/5a9906121767122a12eb3304b1c785ec
Captulo 2: Primera Aplicacin Completa 67

Si la ventana modal recibe como parmetro un contacto, entonces la variable contacto, contendr
ahora una copia del contacto recibido. Aqu vemos la utilidad del mtodo clone. Queremos una
copia, y no una referencia al propio objeto. Por ello no podemos usar simplemente this.contacto =
contacto.
Veamos el funcionamiento hasta ahora.
Partamos de nuestra pantalla inicial:

Pantalla Inicial

Y seleccionemos Editar para el primer contacto.

Editar contacto

Vemos como ahora la pantalla modal se carga con los datos del contacto seleccionado. Muy bien.
Sin embargo, hay un pequeo detalle. El ttulo muestra Agregar Contacto, lo cual no es correcto.
Captulo 2: Primera Aplicacin Completa 68

Vamos a agregar una nueva lnea al constructor de add-contacto-modal.ts

1 constructor(public navCtrl: NavController, public navParams: NavParams,


2 public viewCtrl: ViewController) {
3 if (this.navParams.get('contacto')) {
4 this.contacto = Contacto.clone(this.navParams.get('contacto'));
5 this.titulo = 'Editar Contacto';
6 }
7 }

Se ha resaltado en negrita la nueva lnea agregada.


Repitamos ahora el proceso.

Editar contacto

Ahora est mejor.


Modifiquemos algunos datos del contacto.
Si presionamos ENVIAR, veremos lo siguiente en la consola:

Consola

Gracias a ello, sabemos que el formulario est trabajando perfectamente.


Captulo 2: Primera Aplicacin Completa 69

Estupendo. Vamos a quitar ahora los mensajes de la consola, y modificar el mtodo mostrarEditar-
Contacto para que los cambios se vean reflejados en nuestra lista.
Del libro:

1 mostrarEditarContacto(contacto: Contacto) {
2 let modal = this.modalCtrl.create(AddContactoModal, {contacto});
3 this.contactoOriginal = contacto;
4 modal.present();
5
6 modal.onDidDismiss(data => {
7 if (data) {
8 let index = this.contactos.indexOf(this.contactoOriginal);
9 this.contactos = [
10 ...this.contactos.slice(0,index),
11 data,
12 ...this.contactos.slice(index+1)
13 ];
14 }
15 });
16 }

Gist
Como vern, hemos quitado la impresin por consola. Analicemos los cambios: let index =
this.contactos.indexOf(this.contactoOriginal);
Aqu estamos definiendo una variable local, llamada index, y estamos guardando en ella la posicin
que ocupa en nuestro arreglo de contactos el contacto original.
Recordemos que la variable contactos no es otra cosa que un arreglo de objetos de tipo Contacto.
El mtodo indexOf, es un mtodo de bsqueda, que devuelve la posicin en el arreglo donde se
encuentra un elemento que pasamos como parmetro. Por ello era importante guardar una referencia
a nuestro contacto original.
Luego tenemos:

1 this.contactos = [
2 ...this.contactos.slice(0,index),
3 data,
4 ...this.contactos.slice(index+1)
5 ];

https://gist.github.com/vihugarcia/a015ac35a06f2414f6826905b70ebe45
Captulo 2: Primera Aplicacin Completa 70

Qu estamos haciendo aqu?


Pues bien, lo queremos hacer, es guardar en la lista de contactos, todos los elementos del arreglo que
se encuentran antes del elemento a modificar.
En la posicin donde se encuentra el elemento original, colocamos el elemento modificado que est
disponible en el parmetro data.
Luego, tomamos todos los elementos posteriores al elemento modificado.
El mtodo slice en un arreglo, nos devuelve una porcin del arreglo. Realizar un slice de 0 a index,
devuelve una porcin del arreglo compuesta por las posiciones 0, 1, etc. Hasta la posicin index.
Los puntos suspensivos () colocados antes de la porcin del arreglo, indican que esta porcin debe
expandirse en cada uno de los elementos que la componen.
Es decir, supongamos que index tiene el valor 2.
this.contactos.slice(0, 2) es equivalente a:
this.contactos(0), this.contactos(1)
Si omitimos el segundo parmetro del mtodo slice, entonces la porcin del arreglo que se tomar
es desde la posicin indicada hasta el final del arreglo.
Si ahora editamos un contacto, veremos que los cambios se ven reflejados en nuestra lista.

Contacto editado

Podemos modificar todos los contactos si lo deseamos, y funcionar perfectamente.


Genial! Nuestra aplicacin va tomando forma ya.
Nos resta ocuparnos de la eliminacin de un contacto.
En contactos.html tenemos las siguientes lneas:
Captulo 2: Primera Aplicacin Completa 71

1 <button ion-button color="danger" (click)="eliminarContacto(contacto)">


2 <ion-icon name="remove-circle"></ion-icon>
3 Eliminar
4 </button>

Podemos escribir la implementacin del mtodo eliminarContacto como sigue.


Del libro:

1 eliminarContacto(contacto: Contacto) {
2 let index = this.contactos.indexOf(contacto);
3 this.contactos = [
4 ...this.contactos.slice(0,index),
5 ...this.contactos.slice(index+1)
6 ];
7 }

Gist
Estamos usando nuevamente indexOf para obtener la posicin del elemento, y slice para obtener
todos los elementos del arreglo con excepcin del elemento que deseamos eliminar.
Podemos ver esto en funcionamiento:

Eliminar contacto

https://gist.github.com/vihugarcia/f8af86a087f0d9909ba2d176dba09723
Captulo 2: Primera Aplicacin Completa 72

Contacto eliminado

Funciona, pero en pos de reducir la posibilidad de errores involuntarios, sera interesante mostrarle
al usuario un mensaje de advertencia y darle la oportunidad de decidir si realmente quiere eliminar
el contacto o no.
Vamos a ver cmo realizar esto, y de paso introduciremos un nuevo componente que seguramente
nos ser de utilidad en nuestros proyectos.
Lo que vamos a hacer, es que en el momento en que el usuario presione el botn Eliminar, se muestre
un cuadro de dilogo preguntndole si realmente desea eliminar el contacto, dndole la posibilidad
de aceptar o cancelar la eliminacin.
Para ello, tenemos disponible un componente denominado AlertController. Este nos permite
presentar distintos mensajes, tales como cuadros de dilogo y confirmacin.
En contactos.ts, debemos modificar la lnea:

1 import { NavController, NavParams, ModalController } from 'ionic-angular';

Por:

1 import { NavController, NavParams, ModalController, AlertController }


2 from 'ionic-angular';

El constructor tambin debe ser modificado para recibir ahora como parmetro una nueva variable
de tipo AlertController para poder utilizarlo.
Cambiemos el constructor a:
Captulo 2: Primera Aplicacin Completa 73

1 constructor(
2 public navCtrl: NavController,
3 public navParams: NavParams,
4 public modalCtrl: ModalController,
5 public alertCtrl: AlertController
6 ) {}

Ahora vamos a crear un nuevo mtodo que ser el encargado de presentar al usuario el cuadro de
confirmacin, y en caso de que el usuario acepte, se convocar el mtodo que elimina el contacto
que no es otro que eliminarContacto.
Del libro:

1 confirmarEliminarContacto(contacto: Contacto) {
2 let confirm = this.alertCtrl.create({
3 title: 'Eliminar contacto',
4 message: 'Realmente desea eliminar el contacto?',
5 buttons: [
6 {
7 text: 'Cancelar'
8 },
9 {
10 text: 'Eliminar',
11 handler: () => {
12 this.eliminarContacto(contacto);
13 }
14 }
15 ]
16 });
17 confirm.present();
18 }

Gist
Finalmente debemos modificar contactos.html para que el presionar el botn Eliminar se convoque
a ese nuevo mtodo.

https://gist.github.com/vihugarcia/64f40a847e92d5457953d4f43865a75c
Captulo 2: Primera Aplicacin Completa 74

1 <button ion-button color="danger" (click)="confirmarEliminarContacto(contacto)">


2 <ion-icon name="remove-circle"></ion-icon>
3 Eliminar
4 </button>

Si ahora presionamos eliminar, veremos que se nos solicita confirmacin:

Dilogo de confirmacin

No es genial?
En muy poco tiempo, hemos podido construir una aplicacin completamente funcional con un
aspecto decente.
Existen por supuesto muchos detalles que podramos mejorar, pero en este punto espero que haya
podido ver el enorme potencial de esta herramienta.
Las cosas que podemos hacer, estn limitadas solamente por aquello que podamos concebir. Antes
de finalizar el captulo, y simplemente para asegurarnos de que todo funciona correctamente, voy a
proporcionar el listado completo de los archivos.
Pgina de Contactos
contactos.html
Del libro:
Captulo 2: Primera Aplicacin Completa 75

1 <!--
2 Generated template for the Contactos page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar color="primary">
10 <ion-title>{{titulo}}</ion-title>
11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17 <h2>Contactos</h2>
18
19 <p>{{texto}}</p>
20
21 <ion-item>
22 <ion-input type="text" placeholder="escriba algo..."
23 [(ngModel)]="texto"></ion-input>
24 </ion-item>
25
26 <ion-list>
27 <ion-item-sliding *ngFor="let contacto of contactos">
28 <ion-item>
29 <ion-label>{{contacto.nombre}}</ion-label>
30 <ion-label>{{contacto.direccion}}</ion-label>
31 </ion-item>
32
33 <ion-item-options side="right">
34 <button ion-button color="favorite" (click)="verContacto(contacto)">
35 <ion-icon name="eye"></ion-icon>
36 Ver
37 </button>
38 <button ion-button color="dark"
39 (click)="mostrarEditarContacto(contacto)">
40 <ion-icon name="create"></ion-icon>
41 Editar
42 </button>
Captulo 2: Primera Aplicacin Completa 76

43 <button ion-button color="danger"


44 (click)="confirmarEliminarContacto(contacto)">
45 <ion-icon name="remove-circle"></ion-icon>
46 Eliminar
47 </button>
48 </ion-item-options>
49 </ion-item-sliding>
50 </ion-list>
51 </ion-content>
52
53 <ion-fab right bottom>
54 <button ion-fab (click)="mostrarAgregarContacto()">
55 <ion-icon name="add"></ion-icon></button>
56 </ion-fab>

Gist
contactos.ts
Del libro:

1 import { Component } from '@angular/core';


2 import { NavController, NavParams, ModalController, AlertController }
3 from 'ionic-angular';
4 import { Contacto } from '../../model/contacto';
5 import { ContactoPage } from '../contacto/contacto';
6 import { AddContactoModal } from '../add-contacto-modal/add-contacto-modal';
7
8 /*
9 Generated class for the Contactos page.
10
11 See http://ionicframework.com/docs/v2/components/#navigation for more info on
12 Ionic pages and navigation.
13 */
14 @Component({
15 selector: 'page-contactos',
16 templateUrl: 'contactos.html'
17 })
18 export class ContactosPage {
19 titulo: String = 'Administrador de Contactos';
20 public contactos = [
21 new Contacto(1, "Andrea Gmez", "Calle Uno 123", "12345", "gomez@gmail.com"),

https://gist.github.com/vihugarcia/8331f50647bde1a3051ce59dbcbd14af
Captulo 2: Primera Aplicacin Completa 77

22 new Contacto(2, "Juan Perez", "Calle Dos 567", "23456", "perez@gmail.com"),


23 new Contacto(3, "Martn lvarez", "Calle del Pueblo 628", "34567",
24 "alvarez@gmail.com")
25 ];
26 public contactoOriginal: Contacto;
27
28 constructor(
29 public navCtrl: NavController,
30 public navParams: NavParams,
31 public modalCtrl: ModalController,
32 public alertCtrl: AlertController
33 ) {}
34
35 ionViewDidLoad() {
36 console.log('ionViewDidLoad ContactosPage');
37 }
38
39 verContacto(contacto: Contacto) {
40 this.navCtrl.push(ContactoPage, {contacto});
41 }
42
43 mostrarEditarContacto(contacto: Contacto) {
44 let modal = this.modalCtrl.create(AddContactoModal, {contacto});
45 this.contactoOriginal = contacto;
46 modal.present();
47
48 modal.onDidDismiss(data => {
49 if (data) {
50 let index = this.contactos.indexOf(this.contactoOriginal);
51 this.contactos = [
52 ...this.contactos.slice(0,index),
53 data,
54 ...this.contactos.slice(index+1)
55 ];
56 }
57 });
58 }
59
60 confirmarEliminarContacto(contacto: Contacto) {
61 let confirm = this.alertCtrl.create({
62 title: 'Eliminar contacto',
63 message: 'Realmente desea eliminar el contacto?',
Captulo 2: Primera Aplicacin Completa 78

64 buttons: [
65 {
66 text: 'Cancelar'
67 },
68 {
69 text: 'Eliminar',
70 handler: () => {
71 this.eliminarContacto(contacto);
72 }
73 }
74 ]
75 });
76 confirm.present();
77 }
78
79 eliminarContacto(contacto: Contacto) {
80 let index = this.contactos.indexOf(contacto);
81 this.contactos = [
82 ...this.contactos.slice(0,index),
83 ...this.contactos.slice(index+1)
84 ];
85 }
86
87 mostrarAgregarContacto() {
88 let modal = this.modalCtrl.create(AddContactoModal);
89 modal.present();
90
91 modal.onDidDismiss(data => {
92 if (data) {
93 this.contactos.push(data);
94 }
95 });
96 }
97
98 }

Gist
Pgina contacto (detalle de un contacto)
contacto.html
Del libro:
https://gist.github.com/vihugarcia/b1c5a8a22c6fc1645f81c89f3b5cbf2e
Captulo 2: Primera Aplicacin Completa 79

1 <!--
2 Generated template for the Contacto page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar color="primary">
10 <ion-title>Contacto</ion-title>
11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17 <div *ngIf="contacto">
18 <ion-card>
19 <ion-card-content>
20 <ion-card-title>
21 {{contacto.nombre}}
22 </ion-card-title>
23 <p>{{contacto.direccion}}</p>
24 <p>{{contacto.telefono}}</p>
25 <p>{{contacto.email}}</p>
26 </ion-card-content>
27 </ion-card>
28 </div>
29 </ion-content>

Gist
contacto.ts
Del libro:

https://gist.github.com/vihugarcia/0668d93c567cda8bd8e25766f8959588
Captulo 2: Primera Aplicacin Completa 80

1 import { Component } from '@angular/core';


2 import { NavController, NavParams } from 'ionic-angular';
3
4 import { Contacto } from '../../model/contacto';
5
6 /*
7 Generated class for the Contacto page.
8
9 See http://ionicframework.com/docs/v2/components/#navigation for more info on
10 Ionic pages and navigation.
11 */
12 @Component({
13 selector: 'page-contacto',
14 templateUrl: 'contacto.html'
15 })
16 export class ContactoPage {
17 public contacto: Contacto;
18
19 constructor(public navCtrl: NavController, public navParams: NavParams) {
20 this.contacto = this.navParams.get('contacto');
21 }
22
23 ionViewDidLoad() {
24 console.log('ionViewDidLoad ContactoPage');
25 }
26
27 }

Gist
Ventana modal Contacto
add-contacto-modal.html
Del libro:

https://gist.github.com/vihugarcia/e2c7812bd8a069fad8383e6ee3e90a30
Captulo 2: Primera Aplicacin Completa 81

1 <!--
2 Generated template for the AddContactoModal page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar color="primary">
10 <ion-title>{{titulo}}</ion-title>
11 <ion-buttons start>
12 <button ion-button (click)="dismiss()">
13 <span secondary showWhen="ios">Cancelar</span>
14 <ion-icon name="md-close" showWhen="android, windows">
15 </ion-icon>
16 </button>
17 </ion-buttons>
18 </ion-navbar>
19
20 </ion-header>
21
22
23 <ion-content padding>
24 <form #formContacto="ngForm" class="container" (ngSubmit)="onSubmit()">
25 <ion-item>
26 <ion-input name="nombre" id="nombre" #nombre="ngModel"
27 [(ngModel)]="contacto.nombre" required="required" placeholder="Nombre">
28 </ion-input>
29 </ion-item>
30 <ion-item danger [hidden]="nombre.valid || nombre.untouched">
31 El nombre es obligatorio
32 </ion-item>
33
34 <ion-item>
35 <ion-input name="direccion" id="direccion"
36 #direccion="ngModel" [(ngModel)]="contacto.direccion"
37 required="required" placeholder="Direccin"></ion-input>
38 </ion-item>
39 <ion-item danger [hidden]="direccion.valid || direccion.untouched">
40 La direccin es obligatoria
41 </ion-item>
42
Captulo 2: Primera Aplicacin Completa 82

43 <ion-item>
44 <ion-input name="telefono" id="telefono"
45 #telefono="ngModel" [(ngModel)]="contacto.telefono"
46 required="required" placeholder="Telfono"></ion-input>
47 </ion-item>
48 <ion-item danger [hidden]="telefono.valid || telefono.untouched">
49 El telfono es obligatorio
50 </ion-item>
51
52 <ion-item>
53 <ion-input name="email" id="email" #email="ngModel"
54 [(ngModel)]="contacto.email" placeholder="E-Mail"></ion-input>
55 </ion-item>
56
57 <div class="submit-button">
58 <button ion-button block
59 type="submit" [disabled]="!formContacto.form.valid">Enviar</button>
60 </div>
61
62 </form>
63 </ion-content>

Gist
add-contacto-modal.ts
Del libro:

1 import { Component } from '@angular/core';


2 import { NavController, NavParams, ViewController } from 'ionic-angular';
3 import { Contacto } from '../../model/contacto';
4
5 /*
6 Generated class for the AddContactoModal page.
7
8 See http://ionicframework.com/docs/v2/components/#navigation for more info on
9 Ionic pages and navigation.
10 */
11 @Component({
12 selector: 'page-add-contacto-modal',
13 templateUrl: 'add-contacto-modal.html'
14 })

https://gist.github.com/vihugarcia/6c8615e9bd2e0f9d9c12366a0221a47c
Captulo 2: Primera Aplicacin Completa 83

15 export class AddContactoModal {


16 public contacto = new Contacto(0, '', '', '', '');
17 public titulo : String = 'Agregar Contacto';
18
19 constructor(public navCtrl: NavController, public navParams: NavParams,
20 public viewCtrl: ViewController) {
21 if (this.navParams.get('contacto')) {
22 this.contacto = Contacto.clone(this.navParams.get('contacto'));
23 this.titulo = 'Editar Contacto';
24 }
25 }
26
27 ionViewDidLoad() {
28 console.log('ionViewDidLoad AddContactoModalPage');
29 }
30
31 onSubmit() {
32 this.viewCtrl.dismiss(this.contacto);
33 }
34
35 }

Gist
Mdulo principal
app.module.ts
Del libro:

1 import { NgModule, ErrorHandler } from '@angular/core';


2 import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
3 import { MyApp } from './app.component';
4 import { HomePage } from '../pages/home/home';
5 import { ContactosPage } from '../pages/contactos/contactos';
6 import { ContactoPage } from '../pages/contacto/contacto';
7 import { AddContactoModal }
8 from '../pages/add-contacto-modal/add-contacto-modal';
9
10 @NgModule({
11 declarations: [
12 MyApp,

https://gist.github.com/vihugarcia/1358d8e082dc07016b845a4e09c646aahttps:/gist.github.com/vihugarcia/1358d8e082dc07016b845a4e09c646aa
Captulo 2: Primera Aplicacin Completa 84

13 HomePage,
14 ContactosPage,
15 ContactoPage,
16 AddContactoModal
17 ],
18 imports: [
19 IonicModule.forRoot(MyApp)
20 ],
21 bootstrap: [IonicApp],
22 entryComponents: [
23 MyApp,
24 HomePage,
25 ContactosPage,
26 ContactoPage,
27 AddContactoModal
28 ],
29 providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
30 })
31 export class AppModule {}

Gist
Componente principal
app.component.ts
Del libro:

1 import { Component } from '@angular/core';


2 import { Platform } from 'ionic-angular';
3 import { StatusBar, Splashscreen } from 'ionic-native';
4
5 import { HomePage } from '../pages/home/home';
6 import { ContactosPage } from '../pages/contactos/contactos';
7
8
9 @Component({
10 templateUrl: 'app.html'
11 })
12 export class MyApp {
13 rootPage = ContactosPage;
14
https://gist.github.com/vihugarcia/bf8d837a12ca8e016ef88b0abd7bff47
Captulo 2: Primera Aplicacin Completa 85

15 constructor(platform: Platform) {
16 platform.ready().then(() => {
17 // Okay, so the platform is ready and our plugins are available.
18 // Here you can do any higher level native things you might need.
19 StatusBar.styleDefault();
20 Splashscreen.hide();
21 });
22 }
23 }

Gist
En el prximo captulo, comenzaremos a trabajar en la configuracin de nuestro servidor de
desarrollo, de manera que podamos obtener datos desde una fuente externa a nuestra aplicacin.
Veremos cmo comunicarnos de manera asncrona con un servidor, manejar errores, y muchas cosas
ms que llevarn nuestras capacidades a un nivel.
Espero que lo disfruten.
https://gist.github.com/vihugarcia/6e35a7eb61333329ac3900cdfccc44be
Captulo 3: Servidor de desarrollo
A partir de aqu vamos a comenzar a trabajar con datos provenientes de un servicio web. Para esto,
es necesario que configuremos completamente nuestro servidor de desarrollo.
Si no lo han hecho anteriormente, deben instalar ahora algn software que permita configurar un
servidor web.
En uno de los apndices de este libro, se muestra como instalar xampp. Con xampp, podr tener
configurado Apache y MariaDB (si no ha escuchado de MariaDB, se trata de un motor de base de
datos desarrollado por el mismo creador de MySQL y totalmente compatible con este mismo).
Si estn trabajando con Windows, otra alternativa es Wamp. Tiene una funcionalidad muy similar.
Por supuesto, no es obligatorio que utilicen alguna de esas herramientas. Si ya cuentan con un
entorno de desarrollo, entonces no hay razn para que utilicen otro.
Una de las cosas buenas que tiene Xampp, es una interface grfica para trabajar con bases de datos.
Vamos a crear una base de datos denominada contactmgr y seleccionar para ella como codificacin
utf8_unicode_ci.

Creacin BD

Vamos a crear ahora una tabla denominada contactos con cinco columnas:

Tabla de contactos

No olviden marcar la columna id como autoincremental.


Captulo 3: Servidor de desarrollo 87

Una vez creada la tabla, insertemos unos cuantos registros. Podemos utilizar los mismos datos de
nuestro arreglo contactos.

Insertar contactos

La tabla deber haber quedado similar a:

Registros insertados

Perfecto. Ya tenemos datos para trabajar.


Captulo 3: Servidor de desarrollo 88

Api Rest
Actualmente es muy comn que distintas compaas y organizaciones implementen software en la
forma de un conjunto de servicios.
Un servicio es bsicamente un conjunto de funciones que son encapsuladas y estn disponibles a
travs de una interface.
Los consumidores del servicio (personas u otro software) no necesitan estar al tanto de cmo es
implementado el servicio.
Simplemente necesitan conocer cul es el protocolo que deben utilizar para comunicarse con el
servicio.
Un tipo de servicios muy comn es el de servicio web.

Servicios Web
Un servicio web (en ingls, Web Service o Web services) es una tecnologa que utiliza un conjunto de
protocolos y estndares que sirven para intercambiar datos entre aplicaciones. Distintas aplicaciones
de software desarrolladas en lenguajes de programacin diferentes, y ejecutadas sobre cualquier
plataforma, pueden utilizar los servicios web para intercambiar datos en redes de ordenadores
como Internet. La interoperabilidad se consigue mediante la adopcin de estndares abiertos. Las
organizaciones OASIS y W3C son los comits responsables de la arquitectura y reglamentacin de
los servicios Web. Para mejorar la interoperabilidad entre distintas implementaciones de servicios
Web se ha creado el organismo WS-I, encargado de desarrollar diversos perfiles para definir de
manera ms exhaustiva estos estndares. Es una mquina que atiende las peticiones de los clientes
web y les enva los recursos solicitados.[1]
Entre los estndares utilizados por los servicios web estn los siguientes:

Web Services Protocol Stack: As se le denomina al conjunto de servicios y protocolos de los


servicios Web.
XML (Extensible Markup Language): Es el formato estndar para los datos que se vayan a
intercambiar.
SOAP (Simple Object Access Protocol) o XML-RPC (XML Remote Procedure Call): Protocolos
sobre los que se establece el intercambio.
Otros protocolos: los datos en XML tambin pueden enviarse de una aplicacin a otra mediante
protocolos normales como HTTP (Hypertext Transfer Protocol), FTP (File Transfer Protocol),
o SMTP (Simple Mail Transfer Protocol).
WSDL (Web Services Description Language): Es el lenguaje de la interfaz pblica para los
servicios Web. Es una descripcin basada en XML de los requisitos funcionales necesarios
para establecer una comunicacin con los servicios Web.
Captulo 3: Servidor de desarrollo 89

UDDI (Universal Description, Discovery and Integration): Protocolo para publicar la informa-
cin de los servicios Web. Permite comprobar qu servicios web estn disponibles.
WS-Security (Web Service Security): Protocolo de seguridad aceptado como estndar por
OASIS (Organization for the Advancement of Structured Information Standards). Garantiza
la autenticacin de los actores y la confidencialidad de los mensajes enviados.
REST (Representational State Transfer): arquitectura que, haciendo uso del protocolo HTTP,
proporciona una API que utiliza cada uno de sus mtodos (GET, POST, PUT, DELETE, etc)
para poder realizar diferentes operaciones entre la aplicacin que ofrece el servicio web y el
cliente.

De todas ellas, probablemente es la arquitectura REST aquella que goza de mayor popularidad, y es
la que vamos a utilizar para implementar nuestros servicios.
Ahora bien. Explicar la forma de desarrollar los servicios web escapa del alcance de este libro. Voy a
proporcionarles un enlace para descargar el cdigo fuente que deberan copiar dentro del directorio
raz de su servidor de desarrollo. Esta raz puede ser htdocs o www, dependiendo de qu entorno de
desarrollo est utilizando.
El enlace para descargar el cdigo es el siguiente:
https://drive.google.com/file/d/0B0zIX5vLhmRGTUI5b3l5eWRyLUE/view?usp=sharing
Otra cosa que recomiendo es instalar la extensin Postman de Google Chrome. Esta extensin
permite probar distintos tipos de peticiones y es excelente para probar apis. Podemos simular
peticiones con distintos parmetros, headers y otras muchas opciones.
No es algo obligatorio sin embargo.
Al descomprimir el archivo descargado, tendrn una carpeta llamada api-cmgr. Recuerden que esa
carpeta debe ir dentro de la raz de su servidor de desarrollo.
Dentro de la carpeta api-cmgr podrn encontrar un archivo .sql que pueden importar con phpM-
yAdmin para crear la base de datos y la tabla, si es que no lo han hecho anteriormente.
Finalmente, encontrarn un archivo denominado contactos-api.php. Este archivo contiene la api
para administrar recursos de tipo contacto.
No debemos preocuparnos por cmo est construido este archivo, basta con conocer la estructura
de los datos que enva y recibe.
Si les interesa explorar el archivo sin embargo, vern que es muy sencillo. Utiliza un micro
framework llamado Slim. Es un framework muy simple pero an as es poderoso y adecuado tanto
para el desarrollo de aplicaciones web como en este caso para APIs.
En el archivo contactos-api.php hay una lnea que debemos editar para poner los datos correspon-
dientes a nuestra configuracin del servidor.
https://drive.google.com/file/d/0B0zIX5vLhmRGTUI5b3l5eWRyLUE/view?usp=sharing
https://www.slimframework.com/
Captulo 3: Servidor de desarrollo 90

$db = new mysqli(localhost, root, , contactmgr);


Eso es todo. No deberamos tener que modificar nada ms.
Si abrimos ahora una nueva pestaa de nuestro navegador y vamos a la direccin: http://localhost/api-
cmgr/contactos-api.php/contactos
Veremos una salida como la siguiente:

Datos de contactos

Si estn usando Postman la salida ser similar a esta:

Postman

Nuevamente, no es obligatorio instalar esta extensin en el navegador. Es simplemente que se trata


de una herramienta tan buena que no puedo dejar de recomendarla.
Tenemos todo funcionando apropiadamente. Genial. Ahora podemos comenzar a modificar nuestra
aplicacin para que consuma recursos desde la fuente de datos que hemos configurado.
Como primera medida, creemos un directorio denominado shared a la misma altura que el directorio
pages, y dentro de este directorio aadamos un archivo llamado app.settings.ts con el siguiente
contenido:

http://localhost/api-cmgr/contactos-api.php/contactos
Captulo 3: Servidor de desarrollo 91

1 export class AppSettings {


2 public static get API_ENDPOINT() {
3 return 'http://localhost/api-cmgr/contactos-api.php';
4 }
5 }

En este archivo tenemos una funcin esttica que nos devolver la direccin de nuestro recurso. De
esta forma no tendremos que hard codear dicha ruta en las peticiones que realicemos.
Continuemos.
Ahora creemos un nuevo directorio, a la misma altura que el anterior, denominado services, y dentro
de l creemos un archivo denominado contacto.service.ts.
Comencemos agregando los siguientes imports al archivo:

1 import {Injectable} from "@angular/core";


2 import {Http, Response, Headers} from "@angular/http";
3 import "rxjs/add/operator/map";
4 import {Observable} from "rxjs/Observable";
5 import {Contacto} from "../model/contacto";
6 import {AppSettings} from "../shared/app.settings";

Muy bien. En IONIC, podemos considerar un servicio como un componente que nos permite realizar
peticiones asncronas a un recurso, y manejar las respuestas que recibimos como resultado de esa
peticin, incluyendo situaciones de error.
Los cuatro primeros import lidian con dichos aspectos, en tanto que los dos ltimos hacen referencia
a nuestro modelo de datos y a al archivo que contiene la direccin de la API.
A continuacin aadamos el siguiente contenido:

1 @Injectable()
2 export class ContactoService {
3 constructor(private _http:Http) {
4
5 }
6
7 getContactos() {
8 return this._http.get(`${AppSettings.API_ENDPOINT}/contactos`)
9 .map(res => res.json());
10 }
11 }
Captulo 3: Servidor de desarrollo 92

Vemos:
@Injectable() indica que la clase que estamos definiendo podr ser inyectada como dependencia
en otras clases. No nos preocupemos por ello ahora.
A continuacin realizamos el export de la clase, para que esta pueda ser utilizada, y finalmente
agregamos un mtodo que tendr como finalidad devolvernos nuestra lista de contactos.
Noten que en el mtodo getContactos tenemos la siguiente cadena de texto:
${AppSettings.API_ENDPOINT}/contactos

Estamos usando unas comillas especiales, las comillas invertidas (o backticks). Estas comillas son
muy tiles, porque nos permiten expandir variables dentro de ellas. En este caso. Para ello se utiliza
la notacin:
${variable}
Dentro de las comillas.
En este caso, entre llaves estamos llamando al mtodo esttico API_ENDPOINT de la clase
AppSettings, el cul como hemos visto devuelve una cadena con la direccin del recurso a consumir.
Eso es todo por ahora en este archivo.
Debemos ahora realizar varias modificaciones a nuestro archivo contactos.ts
En primer lugar, debemos cambiar el primer import por lo siguiente:

1 import { Component, OnInit } from '@angular/core';

Luego, necesitamos agregar el import del servicio que hemos creado.

1 import { ContactoService } from '../../services/contacto.service';

Cambiemos el export de la clase por:

1 export class ContactosPage implements OnInit

Definamos las siguientes dos variables:

1 public status: String;


2 public errorMessage: String;

Modifiquemos el constructor de la siguiente forma:


Captulo 3: Servidor de desarrollo 93

1 constructor(
2 public navCtrl: NavController,
3 public navParams: NavParams,
4 public modalCtrl: ModalController,
5 public alertCtrl: AlertController,
6 private _contactoService: ContactoService
7 ) {}

Agreguemos dos nuevos mtodos.


Del libro:

1 ngOnInit() {
2 this.getContactos();
3 // console.log("contactos-list component cargado");
4 // this.getContactos();
5 }
6
7 getContactos() {
8 this._contactoService.getContactos()
9 .subscribe(
10 result => {
11 this.contactos = result.data;
12 this.status = result.status;
13
14 if (this.status != "success") {
15 console.log("Error en el servidor");
16 }
17 },
18 error => {
19 this.errorMessage = <any>Error;
20 if (this.errorMessage != null) {
21 console.log(this.errorMessage);
22 }
23 }
24 );
25 }

Gist
Ahora centremos nuestra atencin en el archivo app.module.ts.
Debemos agregar all un nuevo import del servicio que hemos creado.
https://gist.github.com/vihugarcia/8039852af4da181178f884ad931bd582
Captulo 3: Servidor de desarrollo 94

1 import { ContactoService } from '../services/contacto.service';

Luego necesitamos reemplazar la seccin providers por la siguiente:

1 providers: [
2 {provide: ErrorHandler, useClass: IonicErrorHandler},
3 ContactoService
4 ]

Ntese que en este caso no hemos agregado la clase ContactoService a las secciones declarations y
entryComponents.
Esto es as porque ContactoService no es un componente de visualizacin, sino un servicio inyectable
que debe ser declarado como proveedor.
Si ahora ejecutamos ionic serve, veremos que en apariencia nuestra aplicacin no ha sufrido ningn
cambio. Incluso el agregado, edicin y eliminacin de contactos funciona.

Recursos del servidor

Sin embargo, si seleccionamos la pestaa Network, notaremos lo siguiente:


Captulo 3: Servidor de desarrollo 95

Peticin al servidor

Existe una peticin a un recurso externo, que no es otra cosa que nuestra api.
Vemos que esta devuelve una respuesta en formato JSON (json es un formato para intercambiar
datos entre un cliente y un servidor).
Esta respuesta en formato json es convertida en un conjunto de objetos, que son asignados a nuestro
arreglo contactos.
El mtodo encargado de enviar la peticin y manipular la respuesta es getContactos, por lo que
procederemos a analizarlo con cuidado.
La respuesta en formato json es la siguiente:
Captulo 3: Servidor de desarrollo 96

1 {"status":"success",
2 "data":[{"id":"3","nombre":"Mart\u00edn \u00c1lvarez",
3 "direccion":"Calle del Pueblo 628","telefono":"34567",
4 "email":"alvarez@gmail.com"},
5 {"id":"2","nombre":"Juan Perez",
6 "direccion":"Calle Dos 567","telefono":"23456","email":"perez@gmail.com"},
7 {"id":"1","nombre":"Andrea G\u00f3mez",
8 "direccion":"Calle Uno 123","telefono":"12345","email":"gomez@gmail.com"}]}

Tenemos por una lado un elemento denominado status. Este elemento contendr success o error
dependiendo de si la peticin ha sido exitosa o no.
Luego tenemos un elemento data, que est formado por un conjunto de elementos, que en este caso
son los contactos.
Resumiendo: status contiene el estado de la peticin, y data contiene la carga til de datos de la
respuesta.
Vamos ahora al mtodo en cuestin. Voy a mostrarlo primero omitiendo ciertas partes:

1 this._contactoService.getContactos()
2 .subscribe(
3 result => {
4
5 },
6 error => {
7
8 }
9 }
10 );

La variable privada _contactoService contiene un objeto de tipo ContactoService. A partir de ese


objeto, se invoca el mtodo getContactos que definimos anteriormente en el servicio.
Cuando se invoca a un servicio, uno puede suscribirse a dicho servicio. Es decir, podemos solicitar
que se nos enve una notificacin cuando el servicio haya devuelto una respuesta. Esto es necesario
porque las peticiones son asncronas y el tiempo de respuesta es variable.
El mtodo suscribe recibe como parmetros dos funciones. La primera corresponde a una respuesta
positiva (una respuesta con cdigo 200 por parte del servicio web) y la otra a una situacin de error.
Ntese que se est utilizando aqu la notacin de funcin abreviada en ambos casos.
La primera funcin recibe una respuesta en el parmetro result y la segunda en el parmetro error.
Luego cada funcin opera sobre dicho parmetro de manera apropiada.
La funcin de respuesta positiva es:
Captulo 3: Servidor de desarrollo 97

1 result => {
2 this.contactos = result.data;
3 this.status = result.status;
4
5 if (this.status != "success") {
6 console.log("Error en el servidor");
7 }
8 }

Lo que hacemos es tomar la carga til de datos que se encuentra en la respuesta y asignarla a nuestro
arreglo de contactos.
Asimismo, tomamos el estado de la peticin y preguntamos si la respuesta ha sido exitosa o no.
Aqu hay que prestar atencin y no confundir dos cosas diferentes.
Por un lado, habamos dicho que esta funcin se ejecuta en el caso de una respuesta positiva del
servidor. Pero eso se refiere simplemente a una respuesta con estado 200, que significa que el recurso
exista y fue alcanzado y que este respondi.
Por ejemplo, supongamos que realizamos una peticin a http://localhost/api-cmgr/contactos-api.php/recurso-
no-existente.
Esta direccin no existe. No hay ningn recurso disponible escuchando en ella. Por lo tanto, en este
caso la respuesta del servidor hubiera sido un cdigo 404 (u otro similar) en lugar de un cdigo 200
y la primera funcin no se hubiera ejecutado.
Ahora bien. Aunque el recurso se encuentre, es necesaria la segunda verificacin

1 if (this.status != "success") {
2 console.log("Error en el servidor");
3 }

Que en este caso se refiere especficamente a la respuesta de la aplicacin. Por ejemplo, la api podra
devolver un cdigo de error en status en el caso de que no hubiera contactos.
En el caso de que el servidor responda con un cdigo distinto a 200, se ejecutar la segunda funcin

1 error => {
2 this.errorMessage = <any>Error;
3 if (this.errorMessage != null) {
4 console.log(this.errorMessage);
5 }
6 }
Captulo 3: Servidor de desarrollo 98

Que simplemente realiza un log del error.


Vamos a agregar ahora a nuestro servicio un mtodo para aadir nuevos contactos. En contac-
to.service.ts.
Del libro:

1 addContacto(contacto: Contacto) {
2 let json = JSON.stringify(contacto);
3 let params = "json="+json;
4 let headers =
5 new Headers({"Content-Type":"application/x-www-form-urlencoded"});
6
7 return
8 this._http.post(`${AppSettings.API_ENDPOINT}/contactos`,
9 params, {headers: headers})
10 .map(res => res.json());
11 }

Gist
Agreguemos tambin un nuevo mtodo para editar contactos, por ahora vaco.

1 editContacto(contacto: Contacto) {
2
3 }

Vamos ahora a trabajar en nuestra ventana modal. Realizando las modificaciones necesarias para
agregar un nuevo contacto en la base de datos.
Aadamos el import de nuestro servicio:

1 import { ContactoService} from '../../services/contacto.service';

Agreguemos una variable pblica denominada accin, otras dos variables para el estado de la
peticin y mensajes de error, y una variable para contener el id del contacto:

https://gist.github.com/vihugarcia/be7c74711105e571eb54fc2065ac178a
Captulo 3: Servidor de desarrollo 99

1 public accion : String;


2 public status: String;
3 public errorMessage: String;
4 public id;

Reformulemos el constructor como sigue:

1 constructor(
2 public navCtrl: NavController,
3 public navParams: NavParams,
4 public viewCtrl: ViewController,
5 private _contactoService: ContactoService)
6 {
7 if (this.navParams.get('contacto')) {
8 this.contacto = Contacto.clone(this.navParams.get('contacto'));
9 this.titulo = 'Editar Contacto';
10 this.accion = 'editar';
11 }
12 }

Reemplacemos la anterior implementacin del mtodo onSubmit por esta.


Del libro:

1 onSubmit() {
2 let observable;
3 if (this.accion == "editar") {
4 observable = this._contactoService.editContacto(this.id, this.contacto);
5 } else {
6 observable = this._contactoService.addContacto(this.contacto);
7 }
8 observable.subscribe(
9 response => {
10 this.status = response.status;
11 if (this.status != "success") {
12 console.log("Error en el servidor");
13 }
14 },
15 error => {
16 this.errorMessage = <any>Error;
17 if (this.errorMessage != null) {
18 console.log(this.errorMessage);
Captulo 3: Servidor de desarrollo 100

19 }
20 }
21 );
22
23 this.viewCtrl.dismiss(this.contacto);
24 }

Gist
De regreso ahora a contactos.ts
En el mtodo mostrarAgregarContacto reemplacemos la lnea:

1 this.contactos.push(data);

Por:

1 this.getContactos();

Con esas modificaciones el agregado de un nuevo contacto debera funcionar. Verifiquemoslo.


Completamos el formulario y vemos aparecer en nuestra lista el nuevo contacto agregado:

Contacto agregado

La diferencia con lo que ocurra anteriormente es que ahora el contacto es agregado en la tabla de
la base de datos.
https://gist.github.com/vihugarcia/f8bde975ae6edd20c869e1926074cdd7
Captulo 3: Servidor de desarrollo 101

Registros insertados

Vamos a trabajar ahora en la edicin de un contacto.


Editemos el servicio contacto.service.ts para completar la implementacin del archivo editContacto.
Debe quedar as.
Del libro:

1 editContacto(id, contacto: Contacto) {


2 let json = JSON.stringify(contacto);
3 let params = "json="+json;
4 let headers = new Headers({"Content-Type":"application/x-www-form-urlencoded"});
5
6 return
7 this._http.put(`${AppSettings.API_ENDPOINT}/update-contacto/`+id,
8 params, {headers: headers})
9 .map(res => res.json());
10 }

Gist
Como pueden ver, el mtodo editContacto es muy similar al mtodo addContacto. Sin embargo hay
una diferencia importante.
En lugar de enviarse una peticin mediante un mtodo POST se utiliza una peticin de tipo PUT.
En internet hay muchas fuentes valiosas donde se habla del protocolo REST.
Ya que la API que se encuentra en el backend en este caso utiliza el framework Slim, me permite
indicarles un tutorial muy valioso sobre implementacin de una api rest con Slim que pueden
encontrar aqu, en caso de que quieran explorar el desarrollo backend.
Esta es una convencin del protocolo REST. POST se utiliza para la creacin de un nuevo recurso
(un nuevo contacto), y PUT para la modificacin de un recurso existente.
Ahora vayamos al archivo add-contacto-modal.ts. Tenemos que agregar una lnea en el constructor:
https://gist.github.com/vihugarcia/9b2f38d44be9a7960b06f4c2bbbe8e5e
https://manuais.iessanclemente.net/index.php/Introduccion_a_API_REST_y_framework_Slim_de_PHP#PUT_.28Actualizar.29
Captulo 3: Servidor de desarrollo 102

1 this.id = this.contacto.id;

A continuacin cambiemos la implementacin del mtodo mostrarEditarContacto de contactos.ts


de la siguiente manera.
Del libro:

1 mostrarEditarContacto(contacto: Contacto) {
2 let modal = this.modalCtrl.create(AddContactoModal, {contacto});
3 this.contactoOriginal = contacto;
4 modal.present();
5
6 modal.onDidDismiss(data => {
7 if (data) {
8 this.getContactos();
9 }
10 });
11 }

Gist
Con estos cambios ya tenemos funcionando la edicin de un contacto. Probemoslo.

Editar contacto

https://gist.github.com/vihugarcia/b4c0da414e482859f1789b395fba1f5d
Captulo 3: Servidor de desarrollo 103

Editar contacto

Genial!
Slo falta implementar la eliminacin de contactos en el servidor.
Vamos a agregar el siguiente mtodo a nuestro servicio.
Del libro:

1 deleteContacto(id) {
2 return this._http.delete(`${AppSettings.API_ENDPOINT}/delete-contacto/`+id)
3 .map(res => res.json());
4 }

Gist
Aqu tambin quiero sealar algo importante. Como vemos en este caso el tipo de peticin que se
enva no es ni POST ni PUT, sino DELETE. Nuevamente estamos siguiendo la convencin REST.
Ahora en contactos.ts modifiquemos la implementacin del mtodo eliminarContacto de la siguiente
manera.
Del libro:

https://gist.github.com/vihugarcia/9292304084ac03f1537ac84a2f7fe850
Captulo 3: Servidor de desarrollo 104

1 eliminarContacto(contacto: Contacto) {
2 this._contactoService.deleteContacto(contacto.id)
3 .subscribe(
4 result => {
5 this.status = result.status;
6
7 if (this.status != "success") {
8 console.log("Error en el servidor");
9 }
10 this.getContactos();
11 },
12 error => {
13 this.errorMessage = <any>Error;
14 if (this.errorMessage != null) {
15 console.log(this.errorMessage);
16 }
17 }
18 );
19 }

Gist
Con estos cambios ya debera funcionar la eliminacin. Verifiquemoslo.

Eliminar contacto

https://gist.github.com/vihugarcia/dac79aa477a88361046936ade8e4278e
Captulo 3: Servidor de desarrollo 105

Dilogo de confirmacin

Eliminar contacto

Fantstico! Nuestra aplicacin ya es totalmente funcional. Sin embargo, creo que an podemos
mejorar la experiencia del usuario.
Pensemos en la siguiente situacin.
Supongamos que una peticin que estamos realizando a un recurso, demora un tiempo en comple-
tarse.
Durante ese tiempo, el usuario se encuentra con una pantalla que est inactiva.
Captulo 3: Servidor de desarrollo 106

Puede ser que se impaciente o se confunda, no sabiendo si ocurri algo malo y si debe ejecutar la
accin nuevamente.
Es siempre conveniente darle alguna clase de feedback al usuario, an cuando se trate de algo tan
simple como mostrar un indicador de espera.
Afortunadamente, IONIC posee un componente que nos permite mostrar precisamente esto. Este
componente recibe el nombre de LoaderController.
Vamos a ver como implementarlo.
En el archivo contactos.ts modifiquemos el import:

1 import { NavController, NavParams, ModalController, AlertController }


2 from 'ionic-angular';

Por:

1 import { NavController, NavParams, ModalController, AlertController,


2 LoadingController } from 'ionic-angular';

Estamos agregando el componente mencionado. Como ya debemos saber en este punto, tenemos
que modificar el constructor de la clase para recibir un nuevo parmetro cuyo tipo corresponde al
componente importado.
La nueva implementacin de nuestro constructor es como sigue:

1 constructor(
2 public navCtrl: NavController,
3 public navParams: NavParams,
4 public modalCtrl: ModalController,
5 public alertCtrl: AlertController,
6 public loadingCtrl: LoadingController,
7 private _contactoService: ContactoService
8 ) {}

Perfecto. Vamos a poner ahora el loader en accin. Supongamos que queremos mostrar un indicador
de espera mientras esperamos que la eliminacin de un contacto concluya.
Modifiquemos el mtodo eliminarContacto de la siguiente manera.
Del libro:
Captulo 3: Servidor de desarrollo 107

1 eliminarContacto(contacto: Contacto) {
2 let loader = this.loadingCtrl.create();
3 loader.present();
4
5 this._contactoService.deleteContacto(contacto.id)
6 .subscribe(
7 result => {
8 this.status = result.status;
9
10 if (this.status != "success") {
11 console.log("Error en el servidor");
12 }
13 this.getContactos();
14 loader.dismiss();
15 },
16 error => {
17 this.errorMessage = <any>Error;
18 if (this.errorMessage != null) {
19 console.log(this.errorMessage);
20 }
21 loader.dismiss();
22 }
23 );
24 }

Gist
Para que podamos apreciar como funciona esto, voy a introducir un retraso intencional en la
respuesta del servidor.
Si ahora seleccionamos un contacto y hacemos clic en eliminar, veremos un loader mientras se
aguarda que la peticin concluya.
https://gist.github.com/vihugarcia/23f80ceb3afd550aaaa3c592daac4467
Captulo 3: Servidor de desarrollo 108

Loader

Este simple detalle mejora mucho la usabilidad de nuestro proyecto.


Con esto hemos concluido las operaciones bsicas sobre contactos y tenemos ya una aplicacin
funcional.
El prximo captulo girar en torno a agregar nueva funcionalidad (bsqueda de contacto, ordena-
miento, etc.) y al mismo tiempo mejorar la esttica general.
Antes de avanzar, y para asegurarnos de que todo funcione correctamente, voy a dejar el contenido
completo de cada archivo relevante.
Si est atascado con algo, puede comparar el contenido de su archivo con el listado correspondiente
y de esta manera resolver los errores que se puedan presentar.
Pgina contactos
contactos.html
Del libro:

1 <!--
2 Generated template for the Contactos page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar color="primary">
Captulo 3: Servidor de desarrollo 109

10 <ion-title>{{titulo}}</ion-title>
11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17 <h2>Contactos</h2>
18
19 <p>{{texto}}</p>
20
21 <ion-item>
22 <ion-input type="text" placeholder="escriba algo..."
23 [(ngModel)]="texto"></ion-input>
24 </ion-item>
25
26 <ion-list>
27 <ion-item-sliding *ngFor="let contacto of contactos">
28 <ion-item>
29 <ion-label>{{contacto.nombre}}</ion-label>
30 <ion-label>{{contacto.direccion}}</ion-label>
31 </ion-item>
32
33 <ion-item-options side="right">
34 <button ion-button color="favorite" (click)="verContacto(contacto)">
35 <ion-icon name="eye"></ion-icon>
36 Ver
37 </button>
38 <button ion-button color="dark" (click)="mostrarEditarContacto(contacto)">
39 <ion-icon name="create"></ion-icon>
40 Editar
41 </button>
42 <button ion-button color="danger"
43 (click)="confirmarEliminarContacto(contacto)">
44 <ion-icon name="remove-circle"></ion-icon>
45 Eliminar
46 </button>
47 </ion-item-options>
48 </ion-item-sliding>
49 </ion-list>
50 </ion-content>
51
Captulo 3: Servidor de desarrollo 110

52 <ion-fab right bottom>


53 <button ion-fab (click)="mostrarAgregarContacto()">
54 <ion-icon name="add"></ion-icon></button>
55 </ion-fab>

Gist
contactos.ts
Del libro:

1 import { Component, OnInit } from '@angular/core';


2 import { NavController, NavParams, ModalController, AlertController,
3 LoadingController } from 'ionic-angular';
4 import { Contacto } from '../../model/contacto';
5 import { ContactoPage } from '../contacto/contacto';
6 import { AddContactoModal } from '../add-contacto-modal/add-contacto-modal';
7 import { ContactoService } from '../../services/contacto.service';
8
9 /*
10 Generated class for the Contactos page.
11
12 See http://ionicframework.com/docs/v2/components/#navigation for more info on
13 Ionic pages and navigation.
14 */
15 @Component({
16 selector: 'page-contactos',
17 templateUrl: 'contactos.html'
18 })
19 export class ContactosPage implements OnInit {
20 titulo: String = 'Administrador de Contactos';
21 public contactos = [
22 new Contacto(1, "Andrea Gmez", "Calle Uno 123", "12345", "gomez@gmail.com"),
23 new Contacto(2, "Juan Perez", "Calle Dos 567", "23456", "perez@gmail.com"),
24 new Contacto(3, "Martn lvarez", "Calle del Pueblo 628", "34567",
25 "alvarez@gmail.com")
26 ];
27 public contactoOriginal: Contacto;
28 public status: String;
29 public errorMessage: String;
30
31 constructor(

https://gist.github.com/vihugarcia/5d4681c5fc0cae8b14e30ad86780ffb6
Captulo 3: Servidor de desarrollo 111

32 public navCtrl: NavController,


33 public navParams: NavParams,
34 public modalCtrl: ModalController,
35 public alertCtrl: AlertController,
36 public loadingCtrl: LoadingController,
37 private _contactoService: ContactoService
38 ) {}
39
40 ngOnInit() {
41 this.getContactos();
42 }
43
44 getContactos() {
45 this._contactoService.getContactos()
46 .subscribe(
47 result => {
48 this.contactos = result.data;
49 this.status = result.status;
50
51 if (this.status != "success") {
52 console.log("Error en el servidor");
53 }
54 },
55 error => {
56 this.errorMessage = <any>Error;
57 if (this.errorMessage != null) {
58 console.log(this.errorMessage);
59 }
60 }
61 );
62 }
63
64 ionViewDidLoad() {
65 console.log('ionViewDidLoad ContactosPage');
66 }
67
68 verContacto(contacto: Contacto) {
69 this.navCtrl.push(ContactoPage, {contacto});
70 }
71
72 mostrarEditarContacto(contacto: Contacto) {
73 let modal = this.modalCtrl.create(AddContactoModal, {contacto});
Captulo 3: Servidor de desarrollo 112

74 this.contactoOriginal = contacto;
75 modal.present();
76
77 modal.onDidDismiss(data => {
78 if (data) {
79 this.getContactos();
80 }
81 });
82 }
83
84 confirmarEliminarContacto(contacto: Contacto) {
85 let confirm = this.alertCtrl.create({
86 title: 'Eliminar contacto',
87 message: 'Realmente desea eliminar el contacto?',
88 buttons: [
89 {
90 text: 'Cancelar'
91 },
92 {
93 text: 'Eliminar',
94 handler: () => {
95 this.eliminarContacto(contacto);
96 }
97 }
98 ]
99 });
100 confirm.present();
101 }
102
103 eliminarContacto(contacto: Contacto) {
104 let loader = this.loadingCtrl.create();
105 loader.present();
106
107 this._contactoService.deleteContacto(contacto.id)
108 .subscribe(
109 result => {
110 this.status = result.status;
111
112 if (this.status != "success") {
113 console.log("Error en el servidor");
114 }
115 this.getContactos();
Captulo 3: Servidor de desarrollo 113

116 loader.dismiss();
117 },
118 error => {
119 this.errorMessage = <any>Error;
120 if (this.errorMessage != null) {
121 console.log(this.errorMessage);
122 }
123 loader.dismiss();
124 }
125 );
126 }
127
128 mostrarAgregarContacto() {
129 let modal = this.modalCtrl.create(AddContactoModal);
130 modal.present();
131
132 modal.onDidDismiss(data => {
133 if (data) {
134 this.getContactos();
135 }
136 });
137 }
138
139 }

Gist
Pgina de detalles de un contacto
contacto.html
Del libro:

1 <!--
2 Generated template for the Contacto page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar color="primary">

https://gist.github.com/vihugarcia/b7320c840ccb5c60e1d2e803cdd551c3
Captulo 3: Servidor de desarrollo 114

10 <ion-title>Contacto</ion-title>
11 </ion-navbar>
12
13 </ion-header>
14
15
16 <ion-content padding>
17 <div *ngIf="contacto">
18 <ion-card>
19 <ion-card-content>
20 <ion-card-title>
21 {{contacto.nombre}}
22 </ion-card-title>
23 <p>{{contacto.direccion}}</p>
24 <p>{{contacto.telefono}}</p>
25 <p>{{contacto.email}}</p>
26 </ion-card-content>
27 </ion-card>
28 </div>
29 </ion-content>

Gist
contacto.ts
Del libro:

1 import { Component } from '@angular/core';


2 import { NavController, NavParams } from 'ionic-angular';
3
4 import { Contacto } from '../../model/contacto';
5
6 /*
7 Generated class for the Contacto page.
8
9 See http://ionicframework.com/docs/v2/components/#navigation for more info on
10 Ionic pages and navigation.
11 */
12 @Component({
13 selector: 'page-contacto',
14 templateUrl: 'contacto.html'
15 })

https://gist.github.com/vihugarcia/e7f0a994c701ad7bd1baca907ac73608
Captulo 3: Servidor de desarrollo 115

16 export class ContactoPage {


17 public contacto: Contacto;
18
19 constructor(public navCtrl: NavController, public navParams: NavParams) {
20 this.contacto = this.navParams.get('contacto');
21 }
22
23 ionViewDidLoad() {
24 console.log('ionViewDidLoad ContactoPage');
25 }
26
27 }

Gist
Ventana modal
Del libro:

1 <!--
2 Generated template for the AddContactoModal page.
3
4 See http://ionicframework.com/docs/v2/components/#navigation for more info on
5 Ionic pages and navigation.
6 -->
7 <ion-header>
8
9 <ion-navbar color="primary">
10 <ion-title>{{titulo}}</ion-title>
11 <ion-buttons start>
12 <button ion-button (click)="dismiss()">
13 <span secondary showWhen="ios">Cancelar</span>
14 <ion-icon name="md-close" showWhen="android, windows"></ion-icon>
15 </button>
16 </ion-buttons>
17 </ion-navbar>
18
19 </ion-header>
20
21
22 <ion-content padding>
23 <form #formContacto="ngForm" class="container" (ngSubmit)="onSubmit()">

https://gist.github.com/vihugarcia/dcf7ab7bf60af7f907dc55969bdebb03
Captulo 3: Servidor de desarrollo 116

24 <ion-item>
25 <ion-input name="nombre" id="nombre" #nombre="ngModel"
26 [(ngModel)]="contacto.nombre" required="required"
27 placeholder="Nombre">
28 </ion-input>
29 </ion-item>
30 <ion-item danger [hidden]="nombre.valid || nombre.untouched">
31 El nombre es obligatorio
32 </ion-item>
33
34 <ion-item>
35 <ion-input name="direccion" id="direccion"
36 #direccion="ngModel" [(ngModel)]="contacto.direccion"
37 required="required" placeholder="Direccin"></ion-input>
38 </ion-item>
39 <ion-item danger [hidden]="direccion.valid || direccion.untouched">
40 La direccin es obligatoria
41 </ion-item>
42
43 <ion-item>
44 <ion-input name="telefono" id="telefono"
45 #telefono="ngModel" [(ngModel)]="contacto.telefono"
46 required="required" placeholder="Telfono"></ion-input>
47 </ion-item>
48 <ion-item danger [hidden]="telefono.valid || telefono.untouched">
49 El telfono es obligatorio
50 </ion-item>
51
52 <ion-item>
53 <ion-input name="email" id="email" #email="ngModel"
54 [(ngModel)]="contacto.email" placeholder="E-Mail">
55 </ion-input>
56 </ion-item>
57
58 <div class="submit-button">
59 <button ion-button block
60 type="submit" [disabled]="!formContacto.form.valid">
61 Enviar
62 </button>
63 </div>
64
65 </form>
Captulo 3: Servidor de desarrollo 117

66 </ion-content>

Gist
add-contacto-modal.ts
Del libro:

1 import { Component } from '@angular/core';


2 import { NavController, NavParams, ViewController } from 'ionic-angular';
3 import { Contacto } from '../../model/contacto';
4 import { ContactoService} from '../../services/contacto.service';
5
6 /*
7 Generated class for the AddContactoModal page.
8
9 See http://ionicframework.com/docs/v2/components/#navigation for more info on
10 Ionic pages and navigation.
11 */
12 @Component({
13 selector: 'page-add-contacto-modal',
14 templateUrl: 'add-contacto-modal.html'
15 })
16 export class AddContactoModal {
17 public contacto = new Contacto(0, '', '', '', '');
18 public titulo : String = 'Agregar Contacto';
19 public accion : String;
20 public status: String;
21 public errorMessage: String;
22 public id;
23
24 constructor(
25 public navCtrl: NavController,
26 public navParams: NavParams,
27 public viewCtrl: ViewController,
28 private _contactoService: ContactoService)
29 {
30 if (this.navParams.get('contacto')) {
31 this.contacto = Contacto.clone(this.navParams.get('contacto'));
32 this.titulo = 'Editar Contacto';
33 this.accion = 'editar';
34 this.id = this.contacto.id;

https://gist.github.com/vihugarcia/b0a10a9f10942bb73b494efbb6f033eb
Captulo 3: Servidor de desarrollo 118

35 }
36 }
37
38 ionViewDidLoad() {
39 console.log('ionViewDidLoad AddContactoModalPage');
40 }
41
42 onSubmit() {
43 let observable;
44 if (this.accion == "editar") {
45 observable =
46 this._contactoService.editContacto(this.id, this.contacto);
47 } else {
48 observable =
49 this._contactoService.addContacto(this.contacto);
50 }
51 observable.subscribe(
52 response => {
53 this.status = response.status;
54 if (this.status != "success") {
55 console.log("Error en el servidor");
56 }
57 },
58 error => {
59 this.errorMessage = <any>Error;
60 if (this.errorMessage != null) {
61 console.log(this.errorMessage);
62 }
63 }
64 );
65
66 this.viewCtrl.dismiss(this.contacto);
67 }
68
69 }

Gist
Modelo contacto
contacto.ts
Del libro:
https://gist.github.com/vihugarcia/821f02189b29fcb21f239e8b8cc7ea9a
Captulo 3: Servidor de desarrollo 119

1 export class Contacto{


2 constructor(
3 public id: number,
4 public nombre: string,
5 public direccion: string,
6 public telefono: string,
7 public email: string
8 ){}
9
10 static clone(contacto: Contacto) {
11 return new Contacto(contacto.id, contacto.nombre,
12 contacto.direccion, contacto.telefono, contacto.email);
13 }
14 }

Gist
Archivo de configuracin de la direccin de la api
Del libro:

1 export class AppSettings {


2 public static get API_ENDPOINT() {
3 return 'http://localhost/api-cmgr/contactos-api.php';
4 }
5 }

Gist
Servicio
contacto.service.ts
Del libro:

https://gist.github.com/vihugarcia/e53821bb37529027c2a219e11b6628b7
https://gist.github.com/vihugarcia/6693e3bbe3e76e6a76bedca91034c299
Captulo 3: Servidor de desarrollo 120

1 import {Injectable} from "@angular/core";


2 import {Http, Response, Headers} from "@angular/http";
3 import "rxjs/add/operator/map";
4 import {Observable} from "rxjs/Observable";
5 import {Contacto} from "../model/contacto";
6 import {AppSettings} from "../shared/app.settings";
7
8 @Injectable()
9 export class ContactoService {
10 constructor(private _http:Http) {
11
12 }
13
14 getContactos() {
15 return this._http.get(`${AppSettings.API_ENDPOINT}/contactos`)
16 .map(res => res.json());
17 }
18
19 addContacto(contacto: Contacto) {
20 let json = JSON.stringify(contacto);
21 let params = "json="+json;
22 let headers =
23 new Headers({"Content-Type":"application/x-www-form-urlencoded"});
24
25 return this._http.post(
26 `${AppSettings.API_ENDPOINT}/contactos`,
27 params, {headers: headers})
28 .map(res => res.json());
29 }
30
31 editContacto(id, contacto: Contacto) {
32 let json = JSON.stringify(contacto);
33 let params = "json="+json;
34 let headers =
35 new Headers({"Content-Type":"application/x-www-form-urlencoded"});
36
37 return
38 this._http.put(
39 `${AppSettings.API_ENDPOINT}/update-contacto/`+id,
40 params, {headers: headers})
41 .map(res => res.json());
42 }
Captulo 3: Servidor de desarrollo 121

43
44 deleteContacto(id) {
45 return
46 this._http.delete(
47 `${AppSettings.API_ENDPOINT}/delete-contacto/`+id)
48 .map(res => res.json());
49 }
50 }

Gist
Mdulo principal
Del libro:

1 import { NgModule, ErrorHandler } from '@angular/core';


2 import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
3 import { MyApp } from './app.component';
4 import { HomePage } from '../pages/home/home';
5 import { ContactosPage } from '../pages/contactos/contactos';
6 import { ContactoPage } from '../pages/contacto/contacto';
7 import { AddContactoModal }
8 from '../pages/add-contacto-modal/add-contacto-modal';
9 import { ContactoService } from '../services/contacto.service';
10
11 @NgModule({
12 declarations: [
13 MyApp,
14 HomePage,
15 ContactosPage,
16 ContactoPage,
17 AddContactoModal
18 ],
19 imports: [
20 IonicModule.forRoot(MyApp)
21 ],
22 bootstrap: [IonicApp],
23 entryComponents: [
24 MyApp,
25 HomePage,
26 ContactosPage,
27 ContactoPage,

https://gist.github.com/vihugarcia/62e1448ddd5c38fafc2fa37c6f58b56f
Captulo 3: Servidor de desarrollo 122

28 AddContactoModal
29 ],
30 providers: [
31 {provide: ErrorHandler, useClass: IonicErrorHandler},
32 ContactoService
33 ]
34 })
35 export class AppModule {}

Gist
Componente principal de la aplicacin
Del libro:

1 import { Component } from '@angular/core';


2 import { Platform } from 'ionic-angular';
3 import { StatusBar, Splashscreen } from 'ionic-native';
4
5 import { HomePage } from '../pages/home/home';
6 import { ContactosPage } from '../pages/contactos/contactos';
7
8
9 @Component({
10 templateUrl: 'app.html'
11 })
12 export class MyApp {
13 rootPage = ContactosPage;
14
15 constructor(platform: Platform) {
16 platform.ready().then(() => {
17 // Okay, so the platform is ready and our plugins are available.
18 // Here you can do any higher level native things you might need.
19 StatusBar.styleDefault();
20 Splashscreen.hide();
21 });
22 }
23 }

Gist
[1] https://es.wikipedia.org/wiki/Servicio_web
https://gist.github.com/vihugarcia/32cf5530d3d7f1c398738f85e419596f
https://gist.github.com/vihugarcia/1b27b634d7088a58e24208929e74799d
Captulo 4: Bsquedas y filtros
A lo largo de los captulos anteriores, hemos desarrollado una aplicacin totalmente funcional.
Esta aplicacin realiza las operaciones CRUD bsicas sobre un conjunto de recursos, contactos en
este caso, que son puestos a disposicin por medio de un servicio web.
Sin embargo, siempre debemos procurar lograr una interface lo ms usable posible, por lo que,
como habamos mencionado en el captulo anterior, vamos a agregar nueva funcionalidad y mejorar
cuestiones estticas que elevarn grandemente el valor de nuestra aplicacin.
Una caracterstica que ser de enorme utilidad para los usuarios, sobre todo si el nmero de contactos
es grande, es la posibilidad de buscar y/o filtrar contactos.
Vamos a ver como implementar dicha caracterstica.
En el archivo contactos.html, cuando explicamos el concepto de data binding, ubicamos un cuadro
de texto con las siguientes lneas:

1 <ion-item>
2 <ion-input type="text" placeholder="escriba algo..." [(ngModel)]="texto">
3 </ion-input>
4 </ion-item>

Vamos a reemplazar dichas lneas por la siguiente:

1 <ion-searchbar [(ngModel)]="terminoBusqueda"></ion-searchbar>

Estamos utilizando un nuevo componente, una barra de bsqueda.


Debemos agregar en contactos.ts, la variable que se est enlazando al modelo.

1 public terminoBusqueda: String = '';

Si ejecutamos ionic serve, el resultado ser el siguiente:


Captulo 4: Bsquedas y filtros 124

Barra de bsqueda

Muy bien. Ahora debemos construir la funcionalidad de bsqueda.


Para ello vamos a introducir un nuevo concepto.

Pipes
Las pipes son clases que permiten formatear datos para ser presentados en una interface.
Una pipe toma un dato de entrada, lo transforma segn se necesita, y devuelve el dato transformado
para su visualizacin.
Angular, el framework detrs de IONIC, viene con una serie de pipes predefinidas que son de utilidad.
Veamos algunos ejemplos.
Supongamos que en alguna de las clases que corresponden a nuestras pginas definimos una variable
denominada fechaNac:

1 public fechaNac = new Date(1982, 4, 12);

Y luego en la correspondiente plantilla incluimos:

1 <p>{{fechaNac}}</p>

La salida ser similar a esta:


Captulo 4: Bsquedas y filtros 125

Fecha sin formatear

Podemos utilizar una de las pipes de IONIC, denominada Date, para formatear la salida:

1 <p>{{fechaNac | date}}</p>

Noten la sintaxis. El dato que se quiere formatear, es seguido del operador | y de la pipe. Ahora la
salida es mucho ms agradable:

Fecha formateada

Pero las pipes son an ms potentes, ya que pueden recibir parmetros. Por ejemplo:

1 <p>{{fechaNac | date:"dd/MM/yyyy"}}</p>

La salida ahora es:

Pipe con parmetro

Ahora bien. No estamos limitados a las pipes predefinidas. Podemos construir las nuestras, y eso es
precisamente lo que haremos para implementar la funcin de bsqueda.
Creemos un directorio denominado pipes a la misma altura que el directorio pages.
Dentro de pipes, aadamos un archivo llamado contact-name-pipe.ts con el siguiente contenido.
Del libro:
Captulo 4: Bsquedas y filtros 126

1 import { PipeTransform, Pipe } from '@angular/core';


2
3 @Pipe({
4 name: 'contactNamePipe'
5 })
6 export class ContactNamePipe implements PipeTransform {
7 transform(contacts: Array<any>, searchTerm: string) : Array<any> {
8 if (contacts == null) {
9 return [];
10 }
11 if (searchTerm == null) {
12 return contacts;
13 }
14 return contacts.filter( contact => {
15 return `${contact.nombre}`.toLowerCase().indexOf(searchTerm) > -1;
16 });
17 }
18 }

Gist
Lo primero que tenemos es el import de los componentes necesarios para trabajar con pipes:

1 import { PipeTransform, Pipe } from '@angular/core';

Luego viene la seccin:

1 @Pipe({
2 name: 'contactNamePipe'
3 })

All estamos definiendo el nombre de la pipe, que es el que se utilizar al momento de transformar
datos. En este caso, para llamar a la pipe utilizaremos:

1 {{dato | contactNamePipe}}

Ya que ese es el nombre que definimos.


Luego viene el export de la clase:

https://gist.github.com/vihugarcia/138e12432f94fb0618b674f78ac0bb3d
Captulo 4: Bsquedas y filtros 127

1 export class ContactNamePipe implements PipeTransform

ContactNamePipe es el nombre de la clase que se deber usar en los imports.


Como nuestra pipe implementa la interface PipeTransform, debe forzosamente definir un mtodo
transform.
En este caso nuestra pipe acta sobre un arreglo (nuestra lista de contactos) y recibe como
parmetro una cadena (nuestro trmino de bsqueda), y devuelve un arreglo (la lista filtrada)
transform(contacts: Array<any>, searchTerm: string) : Array<any>
Si la lista recibida es nula, se devuelve un arreglo vaco:

1 if (contacts == null) {
2 return [];
3 }

Si el trmino de bsqueda es vaco, se devuelve la lista completa:

1 if (searchTerm == null) {
2 return contacts;
3 }

Si tenemos una lista y un trmino de bsqueda, entonces debemos aplicar el filtrado.

1 return contacts.filter( contact => {


2 return `${contact.nombre}`.toLowerCase().indexOf(searchTerm) > -1;
3 });

El mtodo filter en un arreglo, es un mtodo muy til que recibe una funcin como parmetro, y
devuelve los elementos para los cuales la funcin evale a verdadero.
La funcin recibe como parmetro cada uno de los elemento del arreglo. Dentro de la funcin, el
nombre se pasa a minsulas, y se busca mediante el mtodo indexOf la ocurrencia de la cadena
correspondiente al trmino de bsqueda.
indexOf, si encuentra la cadena, devolver la posicin a partir de la cual se encuentra dicha cadena
dentro del nombre. En caso de no encontrarse, devuelve -1.
Al preguntar si el valor de indexOf es mayor que -1 entonces, obtendremos verdadero cuando la
cadena se encuentre, y por lo tanto el contacto en cuestin ser devuelto como parte del arreglo
filtrado.
La pipe est definida ya, pero an no podemos utilizarla. Necesitamos declararle en app.module.ts
de lo contrario obtendremos un error.
Primero agregamos el import:
Captulo 4: Bsquedas y filtros 128

1 import { ContactNamePipe } from '../pipes/contact-name-pipe';

Luego debemos incluir la clase en la seccin declarations. No es necesario incluirla en entryCompo-


nents porque no se trata de un elemento de visualizacin.
Ahora si, podemos ir a contactos.html y modificar la seccin donde se itera a travs de los elementos:

1 <ion-item-sliding
2 *ngFor="let contacto of (contactos | contactNamePipe:terminoBusqueda)">

Ahora podemos poner en funcionamiento nuestra opcin de bsqueda:

Bsqueda

Bsqueda

Magnfico! Hemos agregado una pieza de funcionalidad que sin dudas los usuarios apreciarn.
Como un detalle final, si les desagrada el texto que aparece como placeholder (Search) este puede
ser fcilmente modificado de la siguiente manera:
Captulo 4: Bsquedas y filtros 129

1 <ion-searchbar [(ngModel)]="terminoBusqueda" placeholder="Nombre...">


2 </ion-searchbar>

Con lo que vern:

Placeholder

Gravatar
Vamos a ocuparnos ahora de una cuestin esttica. Si recuerdan del comienzo del captulo 2, cuando
diseamos la pantalla inicial llegamos a algo as:
Captulo 4: Bsquedas y filtros 130

Diseo de pantalla

Tenemos una imagen que se muestra para cada contacto. Las imgenes que vamos a mostrar para
cada contacto es un gravatar.
Para los que no estn al tanto de lo que es un gravatar, pueden visitar el sitio http://en.gravatar.com/
Para darnos una rpida idea de lo que queremos lograr, vamos a ubicar una placeholder como
imagen.
En la pgina de detalle de contacto, justo encima del nombre, coloquemos lo siguiente:

1 <img src="http://placehold.it/50x50">

El sitio placehold.it sirve imgenes del tamao solicitado para que acten como placeholders.
Si vamos al detalle de un contacto, veremos:
Captulo 4: Bsquedas y filtros 131

Placeholder

Muy bien. Ahora debemos incorporar a nuestro proyecto el paquete npm crypto-md5.
Desde la raz de nuestro proyecto ejecutemos:
npm install crypto-md5 save
El proceso puede demorar un par de minutos.
Ahora podemos usar el nuevo paquete desde cualquier lugar que lo necesitemos con el siguiente
import:
import md5 from crypto-md5;
Vamos a agregar dicho import en el archivo contacto.ts.
A continuacin, definamos una variable que contendr la imagen.

1 profilePicture: any;

Ahora modifiquemos la definicin del constructor de la siguiente manera:


Captulo 4: Bsquedas y filtros 132

1 constructor(public navCtrl: NavController, public navParams: NavParams) {


2 this.contacto = this.navParams.get('contacto');
3 this.profilePicture =
4 "https://www.gravatar.com/avatar/" +
5 md5(this.contacto.email.toLowerCase(), 'hex');
6 }

En el archivo contacto.html, cambiemos la disposicin de la ion-card como sigue:

1 <ion-card>
2 <ion-item>
3 <ion-avatar>
4 <img [src]="profilePicture">
5 </ion-avatar>
6 </ion-item>
7 <ion-card-content>
8 <ion-card-title>
9
10 {{contacto.nombre}}
11 </ion-card-title>
12 <p>{{contacto.direccion}}</p>
13 <p>{{contacto.telefono}}</p>
14 <p>{{contacto.email}}</p>
15 </ion-card-content>
16 </ion-card>

Si ahora vamos a ver un contacto, y en caso de que este tenga un gravatar, veremos algo as:

Gravatar
Captulo 4: Bsquedas y filtros 133

En caso de que el contacto no tenga un gravatar (al menos asociado a la direccin de email que se
introdujo) se ver una imagen genrica:

Sin gravatar

Para finalizar, vamos a darle un poco de estilo a la imagen.


Editemos el archivo contacto.scss de la siguiente manera:

1 page-contacto {
2 ion-avatar > img {
3 margin: auto;
4 width: 100px !important;
5 height: auto !important;
6 }
7 }

El resultado es el siguiente:
Captulo 4: Bsquedas y filtros 134

Gravatar con estilo


Captulo 5: Corriendo la aplicacin
desde el emulador
Hasta ahora hemos venido corriendo nuestra aplicacin desde el navegador. En este captulo,
veremos cmo ejecutar nuestra aplicacin en un emulador, para poder apreciar cmo se vera en
un ambiente real.
Puntos importantes para sealar:

Para poder realizar la emulacin (y posteriormente para generar la aplicacin que ser subida
a las app store), debemos tener en cuenta la plataforma de destino.
Para poder compilar el proyecto para IOS, necesitaremos una MAC. Alternativamente
podremos utilizar algn ambiente de desarrollo en la nube.

Vamos a trabajar con Android. Para esto debemos tener instalado el SDK de Android.
Desde la raz de nuestro proyecto, ejecutemos el siguiente comando:
ionic run android
Despus de unos minutos podremos ver el emulador en accin. Yla aplicacin devuelve un error.
Captulo 5: Corriendo la aplicacin desde el emulador 136

Error emulador

Es un excelente momento para introducir el proceso de depuracin de aplicaciones en ejecucin.


Cmo podemos depurar una aplicacin que se est ejecutando en el emulador?
Aqu es donde vemos la ventaja de utilizar Google Chrome.
Si abrimos una nueva pestaa y escribimos en la barra de direcciones: chrome://inspect podremos
ver la siguiente pantalla:
Captulo 5: Corriendo la aplicacin desde el emulador 137

Chrome Inspect

Si hacemos clic en inspect, se abrir una nueva ventana de herramienta de desarrollador:

Herramientas de desarrollador

Si nos dirigimos a la pestaa Console, podemos apreciar el siguiente error:


Captulo 5: Corriendo la aplicacin desde el emulador 138

Error de consola

Vemos que se ha rechazado la conexin a:


http://localhost/api-cmgr/contactos-api.php/contactos
Y aqu debemos sealar un detalle muy importante.
En este caso, estamos consumiendo datos de un web service que montamos en nuestra propia
mquina. Esto funciona perfectamente cuando corremos la aplicacin en el navegador utilizando
ionic serve, pero al correr desde un emulador, este no tiene idea de quin es localhost. Debemos
utilizar una direccin ip que pueda ser encontrada.
Por lo tanto, para poder correr nuestra aplicacin desde el emulador debemos hacer un cambio en
app.settings.ts.
Como primera medida, vamos a obtener la direccin ip local de nuestra mquina. Esto depender
del sistema operativo que tengamos.
En mi caso es 10.147.64.246
Editemos app.settings.ts de la siguiente manera:

1 export class AppSettings {


2 public static get API_ENDPOINT() {
3 //return 'http://localhost/api-cmgr/contactos-api.php';
4 return 'http://10.147.64.246/api-cmgr/contactos-api.php';
5 }
6 }

Ejecutemos nuevamente ionic run android.


Deberamos ver la aplicacin funcionando:
Captulo 5: Corriendo la aplicacin desde el emulador 139

App corriendo en el emulador


Captulo 5: Corriendo la aplicacin desde el emulador 140

App corriendo en el emulador

Fantstico.

APK
Observemos el contenido del directorio de nuestro proyecto. Como resultado de haber ejecutado el
comando ionic run android, vemos que hay presente un directorio platforms, y dentro de este un
directorio android. Este directorio android contiene una carpeta denominada build, y dentro de ella
hay otra llamada outputs.
Captulo 5: Corriendo la aplicacin desde el emulador 141

Finalmente, dentro de esta carpeta outputs tenemos un directorio llamado apk. Hay dos archivos
con extensin apk dentro de este directorio:
android-debug.apk y android-debug-unaligned.apk
Aunque se trata de archivos .apk, no es ninguno de ellos un archivo apto para poder subirlo a una
playstore. Tenemos que realizar un proceso para poder publicar nuestra aplicacin.

Publicando una aplicacin para Android


Como primera parte del proceso, vamos a editar el archivo config.xml que se encuentra en la raz
de nuestro proyecto. La informacin que se encuentre en este archivo ser tenida en cuenta por el
proceso de creacin de la versin release, por eso debemos ajustarlo.
En el encontraremos estas lneas:

1 <widget id="com.ionicframework.contactmgrapp628821" version="0.0.1"


2 xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0\
3 ">

Debemos cambiar el valor de id a algo nico que est relacionado con nosotros. Podra ser por
ejemplo algo basado en el nombre de nuestra compaa o nuestro sitio personal.
Por ejemplo: com.misuperempresa.contactmgrapp
El valor de versin puede quedar como est, pero hay que tener en cuenta que cada vez que subamos
una actualizacin de nuestra aplicacin deberemos incrementar dicho valor.
Despus tenemos las siguientes lneas:

1 <description>An awesome Ionic/Cordova app.</description>


2 <author email="hi@ionicframework" href="http://ionicframework.com/">
3 Ionic Framework Team</author>

Que tambin deberemos ajustar.


Muy bien. Hecho esto podemos comenzar a trabajar en la lnea de comandos.
Primero ejecutamos desde la raz de nuestro proyecto:
ionic build android release
Esto generar una versin release en el directorio outputs. El proceso puede demorar varios minutos.
Cuando concluya ver algo similar a esto:
Captulo 5: Corriendo la aplicacin desde el emulador 142

Build de la aplicacin

Y podrn encontrar un archivo denominado android-release-unsigned.apk


Esta es la versin release de nuestra apk, pero como su nombre lo indica, no est firmada an.
Para firmar una apk, debemos generar una key privada. Esta generacin slo tendremos que
realizarla una vez. Luego podremos usar esa misma key para firmar nuevamente cada actualizacin
de nuestra app. De esto se deduce que si por alguna razn llegamos a perder el archivo .keystore que
se generar, no seremos ya capaces de enviar actualizacin a la app store.
El comando para generar la key tiene la forma:
Del libro:

1 keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg


2 RSA -keysize 2048 -validity 10000

Gist
Debemos sustituir my-release-key.keystore por el nombre que deseamos tenga nuestro archivo,
por ejemplo contactmgrapp.keystore y alias_name por un nombre que haga referencia a nuestra
aplicacin, por ejemplo contactmgrapp.
Se inciar un asistente que nos ir pidiendo datos. En primer lugar una contrasea para el almacn
de claves.
Una vez finalizado el asistente, tendremos el archivo .keystore generado.
https://gist.github.com/vihugarcia/bec8b2483521886a85e953d26f081fa8
Captulo 5: Corriendo la aplicacin desde el emulador 143

Archivo de claves generados

Ahora debemos firmar nuestra aplicacin utilizando el archivo .keystore generado. Para simplificar
el proceso, vamos a copiar el archivo android-release-unsigned.apk del directorio apk al mismo lugar
donde est el archivo .keystore.
Luego ejecutamos:

1 jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1


2 -keystore my-release-key.keystore HelloWorld-release-unsigned.apk
3 alias_name

Necesitamos reemplazar my-release-key.keystore por el nombre de nuestro archivo .keystore, por


ejemplo contactmgrapp.keystore y HelloWorld-release-unsigned.apk por el nombre de nuestro
archivo unsigned, en este caso android-release-unsigned.apk. Tambin debemos cambiar alias_name
por el nombre de nuestra app, en este caso contactmgrapp.
El ltimo paso, es correr la herramienta de zip align, que tiene como funcin optimizar la aplicacin
para que pueda ser subida a las app store.

1 ruta/a/android/sdk/build-tools/version/zipalign -v 4
2 HelloWorld-release-unsigned.apk HelloWorld.apk

Lgicamente debemos reemplazar ruta/a/android/sdk/build-tools/versin/zipalign por la ruta co-


rrecta para nuestra configuracin. HelloWorld-release-unsigned.apk por android-release-unsig-
ned.apk y HelloWorld.apk por el nombre de la aplicacin. En mi caso el comando queda como:
c:/Android/sdk/build-tools/23.0.2/zipalign v 4 android-release-unsigned.apk contactmgrapp.apk
Si todo ha ido bien, debemos encontrar ahora un archivo contactmgrapp.apk en nuestro directorio:

apk lista para subir

Magnfico! Ya tenemos nuestra app firmada y alineada, lista para ser enviada a las app store.
El proceso para subir nuestra app es sencillo.
Para el caso de la app store de Google, primero debemos visitar la Consola de Desarrolladores de
Google. Si no contamos con una cuenta, deberemos crear una.
El registro no es gratuito, tiene un costo de 25 dlares. El costo del registro en Apple es de 99 dlares.
Ya estn listos para sacudir el mundo!
Captulo 5: Corriendo la aplicacin desde el emulador 144

Palabras de despedida
Eso es todo por ahora en lo que respecta a este libro. Tal vez no se hayan percatado an, pero han
adquirido una gran cantidad de conocimientos que les permitirn desarrollar aplicaciones mviles
de gran calidad.
Y remarco el por ahora, porque este libro seguir creciendo y actualizndose con nuevo material
para que ustedes puedan obtener el mximo provecho.
Espero por sobre todo haber despertado su curiosidad y sus ansias de aprender. No hay lmites para
lo que puedan lograr.
Hasta pronto!
Captulo 6: Apndices
## Instalando xampp en Linux
En primer lugar, debemos dirigirnos a la pgina de descargas de xampp, donde tendremos que elegir
la versin correcta para nuestro sistema operativo:
[pgina de descargas de xampp] (https://www.apachefriends.org/es/download.html)
Cuando hablo de la versin correcta para el sistema operativo, me refiero a que debemos saber si el
mismo es de 32 o 64 bits, de lo contrario nos encontraremos con un error que puede resultar difcil
de resolver.
Permtame explicarle a qu me refiero.
Una vez descargada la versin, y siguiendo lo establecido en las [FAQs de xampp para Linux]
(https://www.apachefriends.org/es/faq_linux.html), debemos ejecutar desde la consola en primer
lugar:

chmod

Lo que estamos haciendo aqu es asignar los permisos correctos (755) al archivo ejecutable, de lo
contrario la instalacin no podr llevarse a cabo. Podemos ver que el ejecutable corresponde a la
versin para 64 bits (como podemos ver por la partcula x64 en el nombre).
Luego, debemos iniciar la ejecucin del archivo con:

sudo

Deber introducir la contrasea de administrador, y presionar entery aqu es dnde puede


presentarse un problema:
Si observa el siguiente error:

1 ./xampp-linux-x64-5.6.3-0-installer.run: Syntax error: ( unexpected


Captulo 6: Apndices 146

Que como podemos ver no es para nada descriptivo, entonces probablemente sea debido a que est
tratando de instalar la versin de 64 bits sobre un sistema operativo de 32 bits. Esto puede ocurrir si
hemos SUPUESTO errneamente que nuestra arquitectura es de 64 bits. Para comprobarlo, podemos
escribir:

1 uname -m

uname

Si, como observamos arriba, obtenemos como respuesta i686, entonces eso nos mostrar que hemos
descargado e intentado ejecutar la versin equivocada. En ese caso debemos dirigirnos nuevamente
a la pgina de descargas y a obtener la versin correcta, luego procederemos nuevamente a asignar
los permisos adecuados:

chmod

A continuacin ejecutamos

1 sudo ./xampp-linux-5.6.3-0-installer.run

Si todo ha ido bien aparecer el asistente de instalacin:


Captulo 6: Apndices 147

Instalacin

Muy bien, despus de presionar Next unas cuantas veces, llegaremos al final:

Fin de instalacin

Al presionar Finish, aparecer la interfaz grfica de xampp:


Captulo 6: Apndices 148

Interface grfica

Desde la pestaa Manage Servers podremos acceder a los Servicios:

Servicios
Captulo 6: Apndices 149

Luego de verificar que Apache y MySQL estn activos, podr dirigirse al navegador e ingresar la
direccin localhost/phpmyadmin

PhpMyAdmin

Perfecto! ya tenemos un servidor con Apache y MySQL funcionando.

Vous aimerez peut-être aussi