Académique Documents
Professionnel Documents
Culture Documents
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.
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
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
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.
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.
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:
Por qu ocurre esto? Para comprenderlo abramos nuestro proyecto en nuestro editor de texto. La
estructura es la siguiente:
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 }
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
Pestaa Network
Directorio src
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;
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>
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
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.
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:
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:
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:
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:
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
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:
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>
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
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
Errores
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:
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
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 <ion-title>{{titulo}}</ion-title>
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.
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 },
Primera pantalla
Como primera medida, agregaremos una variable para contener el ttulo que mostraremos en la
pgina:
Luego de export class ContactosPage { agregamos:
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
19 HomePage,
20 ContactosPage
21 ],
22 providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
23 })
24 export class AppModule {}
Gist
Ahora lancemos ionic serve.
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:
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.
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
Gist
Sin embargo, ahora nos encontraremos con que la lnea de comandos nos muestra mensajes de error:
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 ];
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>
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
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
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
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:
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 mostrarAgregarContacto() {
2
3 }
Podemos ver que ahora nuestra pantalla principal presenta un botn en la esquina inferior derecha.
Captulo 2: Primera Aplicacin Completa 51
Desde luego, el botn no hace nada an. Mejor dicho, convoca a un mtodo que todava no realiza
ninguna funcin.
Vamos a resolver eso.
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"
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>
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
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 }
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:
Por:
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
1 mostrarAgregarContacto() {
2 let modal = this.modalCtrl.create(AddContactoModal);
3 modal.present();
4
5 modal.onDidDismiss(data => {});
6 }
1 import { AddContactoModal }
2 from '../pages/add-contacto-modal/add-contacto-modal';
Ventana modal
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>
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
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.
Por:
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 #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
Contacto en consola
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
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:
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:
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
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:
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
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
Editar contacto
Consola
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
Contacto editado
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:
Por:
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
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
Gist
contactos.ts
Del libro:
https://gist.github.com/vihugarcia/8331f50647bde1a3051ce59dbcbd14af
Captulo 2: Primera Aplicacin Completa 77
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
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:
https://gist.github.com/vihugarcia/6c8615e9bd2e0f9d9c12366a0221a47c
Captulo 2: Primera Aplicacin Completa 83
Gist
Mdulo principal
app.module.ts
Del libro:
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:
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
Una vez creada la tabla, insertemos unos cuantos registros. Podemos utilizar los mismos datos de
nuestro arreglo contactos.
Insertar contactos
Registros insertados
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:
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
Datos de contactos
Postman
http://localhost/api-cmgr/contactos-api.php/contactos
Captulo 3: Servidor de desarrollo 91
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:
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 constructor(
2 public navCtrl: NavController,
3 public navParams: NavParams,
4 public modalCtrl: ModalController,
5 public alertCtrl: AlertController,
6 private _contactoService: ContactoService
7 ) {}
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 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.
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 );
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
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:
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 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 }
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();
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
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;
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:
Por:
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
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
Gist
contactos.ts
Del libro:
https://gist.github.com/vihugarcia/5d4681c5fc0cae8b14e30ad86780ffb6
Captulo 3: Servidor de desarrollo 111
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:
https://gist.github.com/vihugarcia/e7f0a994c701ad7bd1baca907ac73608
Captulo 3: Servidor de desarrollo 115
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:
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
Gist
Archivo de configuracin de la direccin de la api
Del libro:
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
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:
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:
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>
1 <ion-searchbar [(ngModel)]="terminoBusqueda"></ion-searchbar>
Barra de bsqueda
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 <p>{{fechaNac}}</p>
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>
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
Gist
Lo primero que tenemos es el import de los componentes necesarios para trabajar con pipes:
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}}
https://gist.github.com/vihugarcia/138e12432f94fb0618b674f78ac0bb3d
Captulo 4: Bsquedas y filtros 127
1 if (contacts == null) {
2 return [];
3 }
1 if (searchTerm == null) {
2 return contacts;
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 <ion-item-sliding
2 *ngFor="let contacto of (contactos | contactNamePipe:terminoBusqueda)">
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
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;
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
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
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
Chrome Inspect
Herramientas de desarrollador
Error de consola
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.
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:
Build de la aplicacin
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
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 ruta/a/android/sdk/build-tools/version/zipalign -v 4
2 HelloWorld-release-unsigned.apk HelloWorld.apk
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
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
Instalacin
Muy bien, despus de presionar Next unas cuantas veces, llegaremos al final:
Fin de instalacin
Interface grfica
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