Archivo de la etiqueta: libro ionic en español

Tutorial de IONIC: Formularios reactivos

Hola a todos, hoy vamos a hablar de los formularios reactivos.

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

En la aplicación de acertar números utilizamos [(ngModel)] para hacer  Data Binding entre la vista y el controlador.

Aunque este enfoque es sencillo y es una característica que viene heredando angular desde sus primeras versiones, en formularios complejos puede dificultar la gestión de los campos, además está previsto que se marque como deprecated en futuras versiones de Angular. 

Por otro lado Angular nos ofrece los formularios reactivos que nos permite gestionar de una manera más organizada  y escalable los campos de un formulario facilitando la validación.

Como siempre la mejor forma de comprender como funciona es con un ejemplo, así que vamos a crear un nuevo proyecto al que llamaremos formulario:

ionic start formulario blank

Seleccionamos Angular como framework y para este ejemplo no es necesario integrar nuestra app con Capacitor.

El primer paso que debemos dar es importar ReactiveFormsModule en el módulo de la página donde vayamos a utilizar un formulario, en este caso en home.module.ts.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HomePage } from './home.page';
 
import { HomePageRoutingModule } from './home-routing.module';
 
 
@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    IonicModule,
    HomePageRoutingModule
  ],
  declarations: [HomePage]
})
export class HomePageModule {}

Crear un FormControl

Ahora en home.page.ts vamos a importar la clase FormControl y a crear una instancia para un campo que en este caso llamaremos nombre:

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
 
@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
 
  nombre = new FormControl('');
 
  constructor() {
  }
 
}

Vemos que al constructor FormControl le pasamos como parámetro una cadena vacía (‘’), este es el valor inicial que tomará el campo, si queremos que el campo tenga un valor de inicio podemos pasarle este valor en el constructor.

Veamos ahora cómo se utiliza un Form control en la plantilla, editamos el archivo home.page.html, eliminamos todo lo que hay dentro de ion-content y añadimos lo siguiente:

<ion-header>
  <ion-toolbar>
    <ion-title>
      Blank
    </ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content>
  <ion-item>
    <ion-label>Nombre</ion-label>
    <ion-input [formControl]="nombre"></ion-input>
  </ion-item>
</ion-content>

Simplemente en el campo ion-input indicamos que este campo va a utilizar el formControl nombre. 

Si queremos mostrar en tiempo real el valor de nuestro campo podemos utilizar el atributo value, para verlo vamos a añadir una etiqueta p donde se mostrará el valor de nuestro campo a medida que vamos escribiendo:

<ion-header>
  <ion-toolbar>
    <ion-title>
      Blank
    </ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content>
  <ion-item>
    <ion-label>Nombre:</ion-label>
    <ion-input [formControl]="nombre"></ion-input>
  </ion-item>
  <p class="ion-padding">{{ nombre.value }}</p>
</ion-content>

Si ejecutamos ahora nuestro proyecto al escribir algo en el campo nombre veremos como va apareciendo debajo los caracteres a medida que vamos escribiendo:

Los formularios reactivos tienen métodos que nos permiten cambiar el valor de un control mediante programación, lo que permite actualizar el valor sin la interacción del usuario.

Por ejemplo vamos a crear un método en home.page.ts llamado cambiarNombre() que nos permita cambiar el valor del campo nombre:

cambiarNombre(){
  this.nombre.setValue('Ornitorrinco');
}

Con el método setValue asignamos el valor que deseemos al campo, en este caso el campo tomará el valor ‘Ornitorrinco’.

Para llamar a este método vamos a crear un botón en home.page.html:

<ion-content>
  <ion-item>
    <ion-label>Nombre:</ion-label>
    <ion-input [formControl]="nombre"></ion-input>
  </ion-item>
  <p class="ion-padding">{{ nombre.value }}</p>
  <p class="ion-text-center">
    <ion-button (click)="cambiarNombre()">Cambiar nombre</ion-button>
  </p>
</ion-content>

Hemos metido el componente ion-button dentro de una etiqueta p con la clase ion-text-center para que salga centrado.

Si ejecutamos ahora nuestra aplicación veremos que al pulsar el botón Cambiar nombre el campo nombre cambiará su valor por la palabra Ornitorrinco.

Agrupando FormControls: 

Los formularios generalmente contienen varios controles relacionados. Los formularios reactivos nos permiten agrupar múltiples controles relacionados en un solo formulario de entrada.

FormGroup

Vamos a ver cómo podemos agrupar varios controles utilizando FormGroup.

Lo primero que debemos hacer es editar home.page.ts e importar FormGroup:

import { Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';

Ahora en lugar de el campo nombre que teníamos vamos a crear una propiedad llamada usuario que será un FormGroup que contendrá a su vez dos instancias tipo FormControl, una para el nombre y otra para el email, quedando de la siguiente manera:

...
export class HomePage {
 
  usuario = new FormGroup({
    nombre: new FormControl(''),
    email: new FormControl(''),
  });
 
  constructor() {
  }
...

Los controles de formulario individuales ahora se recopilan dentro de un grupo.

Vamos a modificar la vista en home.page.html para ver cómo se utilizar un FormGroup en la plantilla:

<ion-content>
  <form [formGroup]="usuario">
    <ion-item>
      <ion-label>Nombre:</ion-label>
      <ion-input formControlName="nombre"></ion-input>
    </ion-item>
    <ion-item>
      <ion-label>Email:</ion-label>
      <ion-input formControlName="email"></ion-input>
    </ion-item>
 </form>
 <p class="ion-padding">{{ usuario.controls.nombre.value }}</p>
 <p class="ion-text-center">
   <ion-button (click)="cambiarNombre()">Cambiar nombre</ion-button>
 </p>
</ion-content>

Bien, vayamos por partes:

En primer lugar hemos añadido una etiqueta form a la cual mediante el parámetro [formGroup] le indicamos que los campos del formularios van a estar asociados al FormGroup usuario que hemos creado en el controlador.

En el campo nombre ya no indicamos mediante el parámetro [formControl] que lo asociamos con el control nombre sino que al pertenecer a un FormGroup necesitamos utilizar el parámetro formControlName.

Hemos añadido otro campo para recoger el email del usuario, la estructura es identica al campo nombre, solo que como formControlName le asignamos email.

Dentro de la etiqueta p donde mostramos el contenido del campo nombre ya no podemos acceder directamente a nombre.value, ahora para acceder al valor del campo que pertenece a un FormGroup debemos especificarlo accediendo al control dentro del formgroup usuario de la siguiente manera:

<p class="ion-padding">{{ usuario.controls.nombre.value }}</p>

Si lo intentamos ejecutar nos dará un error porque en el método cambiarNombre() estamos accediendo al valor del nombre directamente, debemos modificar este método en home.page.ts para que quede de la siguiente manera:

cambiarNombre(){
  this.usuario.controls.nombre.setValue('Ornitorrinco');
}

Guardar los datos del formulario:

Lo habitual es tener un un botón en el formulario que al pulsarlo se procesan los datos del formulario, ya sea para enviarlos al servidor para guardarlos en una base de datos o para realizar cualquier operación con ellos.

Vamos a añadir ngSubmit a la etiqueta form para detectar cuando es lanzado el formulario y procesar los campos, en este caso le diremos que ejecute el método guardarDatos() que definiremos posteriormente en el controlador, por lo tanto la etiqueta form deberá quedar así:

 <form [formGroup]="usuario" (ngSubmit)="guardarDatos()">

Ahora para poder lanzar el formulario necesitamos incluir en el formulario un botón de tipo submit:

<ion-content>
  <form [formGroup]="usuario" (ngSubmit)="guardarDatos()">
    <ion-item>
      <ion-label>Nombre:</ion-label>
      <ion-input formControlName="nombre"></ion-input>
    </ion-item>
    <ion-item>
      <ion-label>Email:</ion-label>
      <ion-input formControlName="email"></ion-input>
    </ion-item>
    <p class="ion-text-center">
      <ion-button type="submit">Guardar</ion-button>
    </p>  
  </form>
    <p class="ion-padding">{{ usuario.controls.nombre.value }}</p>
    <p class="ion-text-center">
      <ion-button (click)="cambiarNombre()">Cambiar nombre</ion-button>
    </p>
</ion-content>

Bien, ahora solo nos quedaría recoger los datos de nuestro formulario en la función guardarDatos() y hacer lo que necesitemos con ellos, en este caso simplemente vamos a mostrar en consola el contenido de los campos, por lo que en home.page.ts crearemos la función guardarDatos que quedará de la siguiente forma:

 guardarDatos(){
   console.log(this.usuario.value);
 }

Si ejecutamos nuestra aplicación en el navegador, introducimos el nombre y el email y posteriormente pulsamos en el botón Guardar veremos en la consola del navegador se mostrará un objeto como este:

{ nombre: “Eduardo”, email: “edu.revilla.vaquero@gmail.com }

Con los datos del formulario contenidos en this.usuario.value realizaremos las operaciones que necesitemos.

Validar campos

Podemos validar los campos que introduce el usuario de una manera sencilla.

Lo primero que necesitamos es importar Validators de @angular/forms:

import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
...

Ahora vamos a hacer que el campo nombre sea obligatorio y que además tenga que tener como mínimo 4 caracteres, además vamos a hacer que se compruebe que el campo email contiene una dirección de email válida.

Para ello vamos a modificar los FormsControls para que queden de la siguiente manera:

usuario = new FormGroup({
  nombre: new FormControl('', [Validators.required, Validators.minLength(4)]),
  email: new FormControl('', Validators.email),
});

Como vemos al crear el FormControl nombre le pasamos como segundo parámetro un array con dos Validators, con Validators.required indicamos que el campo es obligatorio, y con Validators.minLength(4) le estamos diciendo que el campo tiene que tener al menos 4 caracteres.

Por otro lado al crear el FormControl email le pasamos como segundo parámetro Validators.email que hará una comprobación de si el campo cumple con la estructura de un email válido. Como el campo email solo tiene un único validador no es necesario que esté contenido en un array.

Ahora vamos a hacer que el botón de guardar sólo esté activo cuando se cumplan las condiciones que le hemos marcado en los validadores. Editamos home.page.html y modificamos el botón de guardar para que quede de la siguiente manera:

<ion-button type="submit" [disabled]="!usuario.valid">Guardar</ion-button>

Con esto le estamos indicando que el botón esté deshabilitado si los campos del formGroup usuario no son válidos.

Si ejecutas ahora la aplicación verás que debemos cumplir con los requisitos que le hemos indicado para que se active el botón de guardar.

Por último vamos a modificar la plantilla para mostrar mensajes de error cuando no se cumplan las validaciones, editamos home.page.html y modificamos el formulario para incluir lo siguiente:

<form [formGroup]="usuario" (ngSubmit)="guardarDatos()">
    <ion-item>
      <ion-label>Nombre:</ion-label>
      <ion-input formControlName="nombre"></ion-input>
    </ion-item>
    <ion-label color="danger" *ngIf="usuario.controls.nombre.errors?.required && (usuario.touched || usuario.dirty)">* El nombre es obligatorio </ion-label>
    <ion-label color="danger" *ngIf="usuario.controls.nombre.errors?.minlength && (usuario.touched || usuario.dirty)">* El nombre tiene que tener al menos 4 caracteres.</ion-label>
    <ion-item>
      <ion-label>Email:</ion-label>
      <ion-input formControlName="email"></ion-input>
    </ion-item>
    <ion-label color="danger" *ngIf="usuario.controls.email.errors?.email && (usuario.touched || usuario.dirty)">* El email no es válido.</ion-label>
    <p class="ion-text-center">
      <ion-button type="submit" [disabled]="!usuario.valid">Guardar</ion-button>
    </p>  
  </form>

En el campo nombre hemos añadido dos componentes ion-label. En el primero mostramos un mensaje advirtiendo que el campo nombre es obligatorio, se mostrará cuando se cumpla la condición que le endicamos enla directiva ngIf:

<ion-label color="danger" *ngIf="usuario.controls.nombre.errors?.required && (usuario.touched || usuario.dirty)">* El nombre es obligatorio </ion-label>

En esta directiva comprobamos primero si se ha producido el error required, es decir que el campo esté vacío. 

Accedemos al control a través de usuario.controls.nombre, dentro de este, en errors se encuentran los tipos de errores de validación que se hayan producido, observa que hemos puesto una interrogación después de errors, esto es porque si no se ha producido ningún error errors valdrá null y dará un error al intentar acceder a la propiedad required. Poniendo una interrogación solo accede a esta propiedad si existe errors.

Además hemos añadido otra condición que se debe cumplir: (usuario.touched || usuario.dirty) esta comprobación es para  evitar que el validador muestre errores antes de que el usuario tenga la oportunidad de editar el formulario.

En el segundo label comprobamos que haya pasado el validador minlength, para mostrar el error de que el nombre tiene que tener al menos 4 caracteres:

<ion-label color="danger" *ngIf="usuario.controls.nombre.errors?.minlength && (usuario.touched || usuario.dirty)">* El nombre tiene que tener al menos 4 caracteres.</ion-label>

Finalmente en el campo email mostramos un error si el email no es válido:

<ion-label color="danger" *ngIf="usuario.controls.email.errors?.email && (usuario.touched || usuario.dirty)">* El email no es válido.</ion-label>

FormBuilder

Para facilitar la tarea de crear formularios Angular nos proporciona el servicio FormBuilder.

Para utilizar FormBuilder debemos importarlo de @angular/forms e inyectarlo en el constructor:

import { FormBuilder } from '@angular/forms';
 
…

 constructor(private fb: FormBuilder) {
  }

FormBuilder nos permite definir los controles del formulario en forma de array haciendo mucho más cómodo.

En home.page.ts tenemos la definición del nuestro FormGroup usuario:

usuario = new FormGroup({
  nombre: new FormControl('', [Validators.required, Validators.minLength(4)]),
  email: new FormControl('', Validators.email),
});

Lo vamos a sustituir por lo siguiente:

usuario = this.fb.group({
  nombre: ['', [Validators.required, Validators.minLength(4)]],
  email: ['', Validators.email],
});

Como vemos no necesitamos llamar a new FormControl en cada control, simplemente le asignamos un array con sus propiedades, en este caso el valor por defecto que es una cadena vacía y los validadores.

Puede que al tener solo dos campos en este pequeño ejemplo no le veas mucho ahorro, pero en formularios complejos con muchos campos hace que sea mucho más cómodo de definir los campos y facilita la lectura del código.

Si quieres saber más sobre el desarrollo de aplicaciones con IONIC puedes adquirir mi libro, es esta entrada puedes ver el índice de contenidos del libro:

Libro: Desarrollo de aplicaciones móviles multiplataforma y PWAs con Ionic y Firebase desde cero.

Muchas gracias

Tutorial de IONIC: Peticiones http

Hola a todos, hoy vamos a ver cómo podemos comunicar una aplicación desarrollada con ionic con una API REST, para ello vamos a aprender cómo realizar peticiones a un servidor remoto a través de http.

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

En esta pequeña prueba vamos acceder a una API REST para obtener desde un servidor remoto un listado de usuarios. Para este pequeño ejemplo vamos a utilizar RANDOM USER GENERATOR que como se indica en su web es una API libre de código abierto para generar datos de usuario aleatorios para realizar pruebas. Cómo Lorem Ipsum, pero con personas.

Lo que vamos a hacer es simplemente realizar una llamada a esta API donde recibiremos como respuesta un listado de usuarios que mostraremos en nuestra vista.

Antes de nada vamos a crear una nueva aplicación de prueba:

ionic start pruebahttp1 blank

Seleccionamos Angular como framework pulsando enter y le podemos decir que no queremos integrarlo con Capacitor, si deseas probarlo desde un dispositivo móvil entonces le diremos que sí, aunque puedes integrarlo después si lo deseas.

Una vez generado el proyecto como siempre desde el terminal con cd pruebahttp1 entramos dentro de la carpeta que acabamos de crear.

Ahora vamos a crear un servicio donde gestionaremos la comunicación con el servidor remoto:

ionic g service services/http

Se habrá creado una carpeta services y dentro  un archivo .ts con el nombre http.service.ts.

Por defecto contendrá el siguiente código:

import { Injectable } from '@angular/core';
 
@Injectable({
 providedIn: 'root'
})
export class HttpService {
 
 constructor() { }
}

Para realizar las peticiones al servidor vamos a utilizar un paquete de angular llamado HttpClient, así que vamos a importarlo en nuestro servicio:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class HttpService {

  constructor(public http: HttpClient) { }
}

Ahora debemos importar y declarar como provider en app.module.ts el servicio que acabamos de crear, también debemos importar y declarar en los imports HttpClientModule:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { HttpService } from './services/http.service';
import { HttpClientModule } from '@angular/common/http';


@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    HttpService,
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

http.get

Vamos a añadir un método que llamaremos loadUsers a nuestro servicio para obtener la lista de usuarios desde el servidor, por lo tanto editamos el archivo http.service.ts y añadimos el siguiente función después del constructor:

 loadUsers() {
   return this.http
   .get('https://randomuser.me/api/?results=25')
 }

 En este caso llamamos a randomuser y le decimos que devuelva 25 resultados (?results=25).

http.get devuelve el resultado de la solicitud en forma de un observable.

Como resultado de la petición obtendremos un JSON con un formato parecido a este:

{
 "results":[
   {
     "gender":"male",
     "name":{"title":"mr","first":"denis","last":"vieira"},
     "location":{
       "street":"666 rua sete de setembro ",
       "city":"rondonópolis",
       "state":"minas gerais",
       "postcode":44819,
       "coordinates":{"latitude":"-73.8339","longitude":"-19.7138"},
       "timezone":{"offset":"-2:00","description":"Mid-Atlantic"}
     },
     "email":"denis.vieira@example.com",
     "login":{
       "uuid":"4f51bc76-8f49-4043-841d-7b2978bd7665",
       "username":"beautifulswan509",
       "password":"down",
       "salt":"I9rLbtmc",
       "md5":"3689e6b1cb08345ac5ab67b179358250",
       "sha1":"4f0e8bf0fea756dbaa8f4e7e01b7c6a8de328abf",
       "sha256":"a23a5af4e8622e69c464a2a30f608066e77aa8d36f037ecdb171d452b20e5c96"
     },
     "dob":{"date":"1984-02-04T02:47:36Z","age":35},
     "registered":{"date":"2004-07-29T23:10:42Z","age":14},
     "phone":"(45) 7844-4365",
     "cell":"(73) 5854-2157",
     "id":{"name":"","value":null},
     "picture":{
       "large":"https://randomuser.me/api/portraits/men/62.jpg",
       "medium":"https://randomuser.me/api/portraits/med/men/62.jpg",
       "thumbnail":"https://randomuser.me/api/portraits/thumb/men/62.jpg"
     },
     "nat":"BR"
   }, ...

Ahora vamos a crear la vista en home.page.html para mostrar un botón que llamará a la función cargarUsuarios, y un listado de items con los usuarios que crearemos recorriendo con *ngFor el array usuarios que posteriormente vamos a crear en el controlador:

<ion-header>
 <ion-toolbar>
   <ion-title>
     Usuarios
   </ion-title>
 </ion-toolbar>
</ion-header>

<ion-content>
 <ion-list>
   <ion-item *ngFor="let usuario of usuarios">
     <ion-avatar slot="start">
       <img [src]="usuario.picture.medium">
     </ion-avatar>
     <ion-label>
     <h2>{{ usuario.name.first }}</h2>
     <p>{{ usuario.email }}</p>
   </ion-label>
   </ion-item>
 </ion-list>
 <ion-button expand="block" (click) = "cargarUsuarios()">Cargar Usuarios</ion-button>
</ion-content>

Vamos a modificar home.page.ts para obtener los datos desde el servicio y mostrarlos en la vista para ello editamos home.page.ts e importamos el service httpProvider que acabamos de crear:

import { HttpService } from '../services/http.service';

Para poder utilizarlo debemos inyectarlo en el constructor:

constructor(private http: HttpService) {}

Justo antes del constructor vamos a definir una variable miembro donde guardaremos el array de usuarios que recibamos desde el servidor:

...
export class HomePage {

 usuarios: any[];

 constructor(private http: HttpService) {}
...

Ahora vamos a crear un método en home.page.ts que que a su vez llamará al método loadUsers de nuestro provider para recibir los datos de los usuarios:

cargarUsuarios(){
  this.http.loadUsers().subscribe(
    (res: any) => {
      this.usuarios = res.results;
    },
    (error) =>{
      console.error(error);
    }
  );
}

Como podemos observar llamamos al método loadUsers que hemos definido en el servicio, pero no utilizamos then sino subscribe, esto es porque http no devuelve una promesa si no que devuelve un observable. Un observable se queda a la espera de recibir datos y nosotros nos “suscribimos” recibiendo estos datos en cuanto estén disponibles.

Esta cualidad se puede utilizar para suscribirnos a una url y observar si ha habido cambios, como por ejemplo si es un sitio de noticias donde se están continuamente renovando.

La diferencia entre promesas y observables a groso modo es que la promesa devuelve los datos una única vez cuando estos son recibidos mientras que un observable se queda “vigilando” si se han producido cambios y se ejecuta cada vez que un cambio se produce, aunque hasta que no te suscribes a un observable éste no se ejecutará.

Si solo necesitamos recibir los datos una única vez sin necesidad de observar si se han producido cambios podemos utilizar promesas. Una promesa se ejecuta una vez que se haya resuelto la llamada y recibimos los datos en la función .then(res=>{… }).

El código del provider en el archivo http.ts utilizando una promesa quedaría de la siguiente manera:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
 
 
@Injectable({
 providedIn: 'root'
})
export class HttpService {
 
 constructor(public http: HttpClient) { }
 
 loadUsers() {
   return this.http
   .get('https://randomuser.me/api/?results=25').toPromise();
 
 }
 
}

Simplemente  debemos añadir toPromise() después del get.

Ahora en home.ts solo tenemos que sustituir subscribe por then:

cargarUsuarios(){
  this.http.loadUsers().then(
    (res: any) => {
      this.usuarios = res.results;
    },
    (error) =>{
      console.error(error);
    }
  );
}

Si ejecutamos la aplicación con ionic serve y pulsamos en el botón Cargar Usuarios podremos ver algo como esto:

 

Podéis observar como cada vez que pulsemos el botón la lista de usuarios cambia ya que Random User Generator como su propio nombre indica devuelve una lista aleatoria de usuarios.

http.post

RANDOM USER GENERATOR  nos ha servido para aprender ha hacer una petición get a una API REST para recibir datos, en este caso una lista de usuarios.

Si queremos conectarnos con un servicio que tengamos corriendo en un servidor remoto y necesitamos pasarle datos desde la aplicación para que se guarden en el servidor tenemos que usar http.post.

Vamos a imaginar que tenemos corriendo un servicio en PHP cuya url sea https://www.miservicio.com/adduser/ que está programado para recibir vía post un nuevo usuario para guardarlo en la base de datos del servidor.

En nuestro servicio tendremos que crear una función similar a esta:

postDatos() {
  const datos = { nombre: 'Edu', email: 'edu.revilla.vaquero@gmail.com'};

  const options = {
     headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  };

  const url = 'https://www.miservicio.com/adduser/';

  return this.http.post(url, JSON.stringify(datos), options).toPromise();
}

En la variable datos  tenemos un objeto con los datos del usuario que queremos enviar al servidor, en este caso nombre y email.

Después definimos la variable options donde a su vez definimos las cabeceras en la variable headers, dependiendo de la configuración del servidor podría necesitar parámetros diferentes en la cabecera.

Como vemos http.post es bastante parecido a http.get, solo que como segundo parámetro le pasamos la variable que contiene los datos que queremos enviar convertida a formato JSON con JSON.stringify(datos), como tercer parámetro le pasamos options.

En data recibiremos la respuesta que nos de el servidor.

Vamos a ver un ejemplo de cómo obtendremos los datos enviados desde nuestra aplicación con un servicio desarrollado en PHP en el servidor:

<?php
  header('Access-Control-Allow-Origin: *');
 
  $postdata = file_get_contents("php://input");
  if (isset($postdata)) {
   $request = json_decode($postdata);
   $request->recibido = 'OK';
   echo json_encode($request);
  }
 
?>

Por seguridad muchos sitios solo admiten peticiones que se hagan desde su propio dominio,  para no tener problemas de Cross-Origin en la primera línea le asignamos la opción header para indicarle que está permitido cualquier origen:

header('Access-Control-Allow-Origin: *');

También hay que tener en cuenta que el navegador puede bloquear peticiones a dominios que no sean https, por lo que sí estás intentando acceder a un servidor que no tenga activado https puede que el navegador bloquee la petición y te muestre un error en consola.

Si vas a hacer pruebas en un servidor local existen complementos para Chrome que evita el bloqueo de las peticiones Cross-Origin.

 

En este ejemplo tan sencillo lo único que hacemos en la siguiente línea es recoger los datos que hemos enviado desde la aplicación:

$postdata = file_get_contents("php://input");

Después miramos si no está vacío $postdata y en $request guardamos el objeto resultante al decodificar el json.

$request = json_decode($postdata);

Le añadimos una propiedad más al objeto para indicar que hemos recibido la petición:

$request->recibido = 'OK';

Por último simplemente sacamos un echo de $request convertido de nuevo a json.

Es decir que recibiremos como resultado en la app lo mismo que hemos enviado más un campo recibido que contendrá la palabra ‘OK’.

Evidentemente en otros lenguajes de programación del lado del servidor el código para recibir los datos sería diferente, pero se escapa del propósito de este libro el abordar como sería en cada lenguaje.

Para probar esto podemos añadir un botón al al home.page.ts del ejemplo anterior y que llame a una función de definiremos después en el controlador:

<ion-header>
 <ion-toolbar>
   <ion-title>
     Usuarios
   </ion-title>
 </ion-toolbar>
</ion-header>
 
<ion-content>
 <ion-list>
   <ion-item *ngFor="let usuario of usuarios">
     <ion-avatar slot="start">
       <img [src]="usuario.picture.medium">
     </ion-avatar>
     <ion-label>
     <h2>{{ usuario.name.first }}</h2>
     <p>{{ usuario.email }}</p>
   </ion-label>
   </ion-item>
 </ion-list>
 <ion-button expand="block" (click) = "cargarUsuarios()">Cargar Usuarios</ion-button>
 <ion-button expand="block" (click) = "post()">Peticion POST</ion-button>
</ion-content>

Definimos entonces la función post en home.page.ts:

post() {
    this.http.postDatos().then(res=>{
      alert(JSON.stringify(res));
    })
 }

Esta función simplemente va a realizar una llamada a la función postDatos que tendremos creada en nuestro servicio http y saca un alert del resultado obtenido convirtiéndolo a texto utilizando el método stringify de JSON.

Si no tienes la posibilidad de tener un servidor para hacer pruebas puedes utilizar algún servicio online que te permite hacer pruebas como por ejemplo http://httpbin.org, pudiendo quedar el método postDatos de nuestro servicio de la siguiente manera:

postDatos() {
   const datos = { nombre: 'Edu', email: 'edu.revilla.vaquero@gmail.com'};
 
   const options = {
     headers: {
       'Content-Type': 'application/x-www-form-urlencoded'
     }
   };
 
   const url = 'http://httpbin.org/post';
 
   return this.http.post(url, JSON.stringify(datos), options).toPromise();
 }

Si quieres saber más sobre el desarrollo de aplicaciones con IONIC puedes adquirir mi libro, es esta entrada puedes ver el índice de contenidos del libro:

Libro: Desarrollo de aplicaciones móviles multiplataforma y PWAs con Ionic y Firebase desde cero.

Muchas gracias

Tutorial de IONIC: Componentes personalizados

Hola a todos, en posts anteriores hemos aprendido a crear apps multiplataforma con Ionic.

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

Ionic nos ofrece un montón de componentes ya creados para utilizar en nuestras apps y realmente nos permiten con poco esfuerzo crear una interfaz funcional para nuestras aplicaciones.

Sin embargo hay momentos que puede interesarnos crear nuestros propios componentes personalizados.

Un componente es simplemente algo que podemos mostrar las veces que queramos en la pantalla, como si fuese una etiqueta html, solo que a su vez un componente puede estar formado por varias etiquetas html y otros componentes.

Para ver mejor cómo podemos crear nuestro propios componentes personalizados en Ionic vamos a crear un proyecto de prueba a que vamos a llamar miComponente:

ionic start ejemploTabs tabs

 

Seleccionamos Angular como framework y una vez creado el proyecto si  entramos en home.page.ts veremos esto:

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

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})

export class HomePage {

  constructor() {}

}

En Ionic todo son componentes, de hecho las páginas de nuestra aplicación son componentes, si nos fijamos tiene un decorador @Component por lo que podemos ver que la propia página es un componente  que tiene el selector ‘app-home’ y como plantilla utiliza el archivo home.page.html.

Si inspeccionamos el código que genera ionic en el navegador veremos que tenemos un elemento llamado ion-router-outlet que es la etiqueta principal donde se renderizan las páginas, y dentro de esta tenemos una etiqueta app-home.

Tal y como hemos comentado las páginas también son componentes y para mostrar componentes en la plantilla se utiliza la etiqueta con el nombre del selector del componente que en este caso es app-home, por defecto los componentes llevan el prefijo app- por delante del nombre del componente.

Vamos a crear un sencillo componente al que vamos a llamar saluda, para ello vamos a echar mano de ionic generator, lo primero que vamos a crear es un módulo para declarar nuestros componentes y poder luego invocarlos desde cualquier página donde los necesitemos.

Nos situamos dentro la carpeta de  nuestro proyecto en la consola de comandos y tecleamos lo siguiente:

ionic g module components

Esto nos habrá creado una carpeta llamada components y dentro un archivo llamado components.module.ts con el siguiente contenido:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';



@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ]
})
export class ComponentsModule { }

Ahora vamos a crear nuestro nuevo componente saluda:

 ionic g component components/saluda

 

Esto nos creará una carpeta llamada saluda dentro de components con nuestro componente.

En la carpeta de nuestro componente tendremos un archivo .html para la plantilla, un archivo .scss para los estilos y un archivo .ts con el controlador.

Si observamos lo que contiene el archivo saluda.component.html vemos que simplemente muestra un párrafo con el texto “saluda works!”:

<p>
 saluda works!
</p>

Veamos ahora que tenemos en el archivo  saluda.component.ts:

import { Component, OnInit } from '@angular/core';
 
@Component({
 selector: 'app-saluda',
 templateUrl: './saluda.component.html',
 styleUrls: ['./saluda.component.scss'],
})
export class SaludaComponent implements OnInit {
 
 constructor() { }
 
 ngOnInit() {}
 
}

Como vemos el controlador de un componente es prácticamente igual que el de una página.

Tenemos el decorador @Component donde se indica que su selector es ‘app-saluda‘, que la ruta de la plantilla que utiliza es ./saluda.component.html  y la ruta de los estilos que utilizará es ./saluda.component.scss.

Ahora vamos a importar SaludaComponent en el módulo components.module.ts y lo vamos a declarar en la sección declarations y en la sección exports:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SaludaComponent } from './saluda/saluda.component';
 
@NgModule({
 declarations: [SaludaComponent],
 imports: [
   CommonModule
 ],
 exports: [SaludaComponent]
})
export class ComponentsModule { }

Ahora para poder utilizar nuestro componente en cualquier página solo tenemos que importar ComponentModule en el módulo de nuestra página y declararlo en los imports, por lo tanto vamos a editar home.module.ts e importar el módulo ComponentsModule:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { ComponentsModule } from '../components/components.module';

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

@NgModule({
 imports: [
   CommonModule,
   FormsModule,
   IonicModule,
   ComponentsModule,
   RouterModule.forChild([
     {
       path: '',
       component: HomePage
     }
   ])
 ],
 declarations: [HomePage]
})
export class HomePageModule {}


Para mostrar nuestro componente en la página home editamos el archivo home.page.html, eliminamos todo lo que hay dentro de ion-content y añadimos la etiqueta con el componente que acabamos de crear:

<ion-header>
 <ion-toolbar>
   <ion-title>
     Ionic Blank
   </ion-title>
 </ion-toolbar>
</ion-header>
 
<ion-content>
 <div class="ion-padding">
   <app-saluda></app-saluda>
 </div>
</ion-content>

Así de fácil. Si ejecutamos nuestra app de ejemplo veremos algo similar a esto:

A nuestro componente le podemos añadir también atributos personalizados.
Por ejemplo podemos pasarle un atributo nombre de esta manera:

<ion-header>
 <ion-toolbar>
   <ion-title>
     Ionic Blank
   </ion-title>
 </ion-toolbar>
</ion-header>
 
<ion-content>
 <div class="ion-padding">
   <app-saluda nombre="Eduardo"></app-saluda>
 </div>
</ion-content>

Luego en el controlador de nuestro componente (saluda.component.ts) definimos el parámetro de entrada con el decorador Input de la siguiente manera:

import { Component, OnInit, Input } from '@angular/core';
 
@Component({
 selector: 'app-saluda',
 templateUrl: './saluda.component.html',
 styleUrls: ['./saluda.component.scss'],
})
export class SaludaComponent implements OnInit {
 
 @Input() nombre: string;
 constructor() { }
 
 ngOnInit() {}
 
}

Para poder utilizar el decorador Input debemos importarlo primero.

Ahora podemos hacer que en lugar de mostrar en pantalla “saluda works!” salude a la persona que recibamos en el parámetro nombre, para ello vamos a crear una variable que llamaremos text y a la que en el constructor le daremos el valor ‘¡Hola’ concatenando el nombre que recibe como input:

import { Component, OnInit, Input } from '@angular/core';
 
@Component({
 selector: 'app-saluda',
 templateUrl: './saluda.component.html',
 styleUrls: ['./saluda.component.scss'],
})
export class SaludaComponent implements OnInit {
 
 @Input() nombre: string;
 text: string;
 
 constructor() {
   this.text = '¡Hola '+this.nombre+'!';
 }
 
 ngOnInit() {}
 
}

Ahora en la plantilla saluda.component.html vamos a hacer que se muestre el contenido de la variable text:

<p>
{{ text }}
</p>

Bien, si como en el ejemplo hemos pasado “Eduardo” al parámetro nombre cabría esperar ver en pantalla “¡Hola Eduardo!”, sin embargo comprobamos que muestra “¡Hola undefined!”, es evidente que hay algo que no está funcionando.

Esto ocurre porque estamos accediendo a la variable this.nombre desde el constructor, y en el momento que se ejecuta el constructor aún no están accesibles los parámetros que recibimos en nuestro componente, para ello utilizamos ngOnInit que se ejecuta cuando nuestro componente se ha inicializado y tenemos acceso a los parámetros:

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

@Component({
  selector: 'app-saluda',
  templateUrl: './saluda.component.html',
  styleUrls: ['./saluda.component.scss'],
})
export class SaludaComponent implements OnInit {

  @Input() nombre: string;
  text: string;

  constructor() {
  }

  ngOnInit() {
    this.text = '¡Hola ' + this.nombre + '!';
  }

}

Ahora podemos comprobar que muestra el nombre que le hayamos pasado.

También podemos utilizar la la variable nombre directamente en la plantilla de nuestro componente de esta manera:

<p>
¡Hola {{ nombre }}!
</p>

Por último si en lugar de pasarle directamente el nombre al componente queremos utilizar una variable debemos poner el parámetro entre corchetes, por ejemplo imaginemos que tenemos un array de usuarios  y queremos saludarles a todos, en home.page.ts definimos un array de usuarios:

import { Component } from '@angular/core';
 
@Component({
 selector: 'app-home',
 templateUrl: 'home.page.html',
 styleUrls: ['home.page.scss'],
})
export class HomePage {
 
 usuarios: any = [
   {
     nombre: 'Eduardo',
     edad: 41
   },
   {
     nombre: 'Pedro',
     edad: 28
   },
   {
     nombre: 'Francisco',
     edad: 34
   },
   {
     nombre: 'Maria',
     edad: 43
   }
 ];
 
 constructor() {}
 
}

Ahora en home.page.html podemos recorrer el array con *ngFor y mostrar nuestro componente saludo pasándole la variable usuario.nombre:

<ion-header>
 <ion-toolbar>
   <ion-title>
     Ionic Blank
   </ion-title>
 </ion-toolbar>
</ion-header>
 
<ion-content>
 <div class="ion-padding">
   <app-saluda  *ngFor="let usuario of usuarios" [nombre]="usuario.nombre"></app-saluda>
 </div>
</ion-content>

Como podemos ver en este caso el parámetro nombre va entre corchetes []  ya que lo que le pasamos no es un texto literal sino una variable.

Si probamos este ejemplo veremos algo como esto:

Este ejemplo es muy sencillo y es solo para explicar cómo funcionan los componentes en Ionic, evidentemente no merece la pena crear un componente que solo contenga un div y un texto, pero podemos ampliar nuestro componente añadiendo una imagen o una ficha completa con los datos del usuario, o cualquier otro elemento que se nos ocurra.

Puedes descargar o clonar este proyecto desde GitHub en el siguiente link: 

Este ejemplo es muy sencillo y es solo para explicar cómo funcionan los componentes en Ionic, evidentemente no merece la pena crear un componente que solo contenga un div y un texto, pero podemos ampliar nuestro componente añadiendo una imagen o una ficha completa con los datos del usuario, o cualquier otro elemento que se nos ocurra.

Puedes descargar o clonar este proyecto desde GitHub en el siguiente link: 

https://github.com/edurevilla/libro-ionic-5-componentes-personalizados

 

Tutorial de IONIC: Navegación por Tabs

Hoy vamos a aprender cómo se genera una aplicación con varias páginas en forma de tabs o pestañas y veremos cómo podemos navegar entre ellas, para ello vamos a crear un nuevo proyecto de ejemplo.

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

Vamos a utilizar la plantilla tabs para crear nuestra aplicación lo que nos generará un proyecto con una estructura ya creada para utilizar tabs.

Desde la consola de comandos o terminal escribimos:

 ionic start ejemploTabs tabs

Como siempre elegimos Angular como framework y a la pregunta “Integrate your new app with Capacitor to target native iOS and Android?” respondemos que No.

Al crear nuestra aplicación con la plantilla tabs nos genera por defecto tres tabs.

Nos situamos como siempre dentro de la carpeta del proyecto que acabamos de crear desde la consola, si ejecutamos el comando ionic serve -l podemos ver algo como esto:

Recuerda que al ejecutar ionic serve con el parámetro -l te preguntará si quieres instalar @ionic/lab.

También puedes comprobar como quedaría en un dispositivo móvil sin necesidad de instalar @ionic/lab, utilizando el inspector de Chrome (boton derecho en cualquier parte de la página y seleccionando inspeccionar),  y seleccionando la vista móvil en el panel:

Veamos ahora el código que se ha generado al iniciar el proyecto:

Lo primero que podemos observar es que que se han generado 5 carpetas dentro de src/app, una carpeta llamada explorer-container que contiene un componente que se va a utilizar en las páginas de los tabs, veremos que son los componentes y cómo se crean más adelante, después una por cada tab (tab1,tab2,y tab3) y por último otra llamada tabs

tab1, tab2 y tab3 son páginas normales como las que ya conocemos que simplemente contienen lo que queramos que se muestre en cada pestaña.

La página tabs es la página maestra donde vamos a mostrar los tabs con los iconos y controlar la ruta para que se muestre el contenido de la página correspondiente al tab activo.

Si vemos el contenido de la carpeta tabs podemos observar que contiene los archivos típicos de cualquier página en ionic, pero además contiene otro archivo llamado tabs.router.module.ts que es módulo donde se define las ruta de cada tab:

Vamos antes de nada a ver como es el html para crear los tabs con sus correspondientes iconos, si abrimos en el editor el archivo tabs.page.html vemos que contiene el siguiente código:

 
<ion-tabs>
 
  <ion-tab-bar slot="bottom">
    <ion-tab-button tab="tab1">
      <ion-icon name="triangle"></ion-icon>
      <ion-label>Tab 1</ion-label>
    </ion-tab-button>
 
    <ion-tab-button tab="tab2">
      <ion-icon name="ellipse"></ion-icon>
      <ion-label>Tab 2</ion-label>
    </ion-tab-button>
 
    <ion-tab-button tab="tab3">
      <ion-icon name="square"></ion-icon>
      <ion-label>Tab 3</ion-label>
    </ion-tab-button>
  </ion-tab-bar>
 
</ion-tabs>

Es bastante intuitivo, tenemos la etiqueta ion-tabs que es el contenedor general para los tabs.

Después tenemos el componente ion-tab-bar donde definimos la barra donde se va a mostrar los iconos de los tabs, vemos que además tiene el atributo slot=”bottom”, con este atributo le estamos indicando que la barra se tiene que mostrar en la parte inferior de la pantalla.

Si queremos que los tabs se muestren en la parte superior deberemos cambiar slot=”bottom” por slot=”top”.

Por último definimos el botón de cada tab con el componente  ion-tab-button que como vemos lleva el atributo tab=”tab1″ donde le indicamos el nombre del tab que se debe activar al pulsar sobre él, este nombre es importante para posteriormente indicar en las rutas.

Dentro cada tab contiene un ion-icon donde indicamos que icono se debe mostrar con el atributo name, y un ion-label con el texto que queremos que se muestre en cada pestaña.

Si solo queremos que se muestre el icono sin ningún texto solo tenemos que eliminar la etiqueta ion-label.

El listado de iconos disponibles lo podéis consultar el la documentación oficial de ionic desde el siguiente enlace: https://ionicframework.com/docs/ionicons/

Los iconos a su vez puede tener tres estilos diferentes:

  • Outline.
  • Filled.
  • Sharp

Bien, veamos ahora el contenido del archivo tabs.routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TabsPage } from './tabs.page';
 
const routes: Routes = [
  {
    path: 'tabs',
    component: TabsPage,
    children: [
      {
        path: 'tab1',
        loadChildren: () => import('../tab1/tab1.module').then(m => m.Tab1PageModule)
      },
      {
        path: 'tab2',
        loadChildren: () => import('../tab2/tab2.module').then(m => m.Tab2PageModule)
      },
      {
        path: 'tab3',
        loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule)
      },
      {
        path: '',
        redirectTo: '/tabs/tab1',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/tab1',
    pathMatch: 'full'
  }
];
 
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TabsPageRoutingModule {}

Lo interesante aquí es la declaración de las rutas de los tabs:

Vemos que se declara la constante routes y dentro indicamos que el path ‘tabs’ está asociado al componente TabsPage, y luego como “hijos” (children) están definidos cada uno de los tabs con su ruta indicada en path y a su vez en children se carga con loadChildren el módulo de la página a mostrar. 

Puede resultar un poco engorroso, pero no debes preocuparte demasiado, el trabajo de crear las rutas ya lo hace ionic por tí, solo debes hacer algunos cambios siguiendo esta estructura si quieres añadir un nuevo tab a tu proyecto, vamos a ver como se hace a continuación.

Como el contenido que se muestra en cada tab es una página, si queremos añadir un nuevo tab a nuestro proyecto crearemos una nueva página, por lo tanto vamos a crear una nueva página llamada tab4:

ionic g page tab4

Al crear una página nueva ionic automáticamente añade la ruta de la página a app-routing.module.ts, en este caso no nos interesa porque la página tab4 va a depender de la página maestra tabs y que tiene su propio módulo para definir las rutas (tabs.router.module.ts), tal y como hemos visto, por lo tanto vamos a editar el archivo app-routing.module.ts y eliminamos la siguiente línea que nos ha generado:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
 { path: '', loadChildren: './tabs/tabs.module#TabsPageModule' },
 // { path: 'tab4', loadChildren: './tab4/tab4.module#Tab4PageModule' } // debemos eliminar esta línea
];
@NgModule({
 imports: [RouterModule.forRoot(routes)],
 exports: [RouterModule]
})
export class AppRoutingModule {}

Ahora vamos a definir la ruta del nuevo tab en tabs.routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TabsPage } from './tabs.page';
 
const routes: Routes = [
  {
    path: 'tabs',
    component: TabsPage,
    children: [
      {
        path: 'tab1',
        loadChildren: () => import('../tab1/tab1.module').then(m => m.Tab1PageModule)
      },
      {
        path: 'tab2',
        loadChildren: () => import('../tab2/tab2.module').then(m => m.Tab2PageModule)
      },
      {
        path: 'tab3',
        loadChildren: () => import('../tab3/tab3.module').then(m => m.Tab3PageModule)
      },
      {
        path: 'tab4',
        loadChildren: () => import('../tab4/tab4.module').then(m => m.Tab4PageModule)
      },
      {
        path: '',
        redirectTo: '/tabs/tab1',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/tab1',
    pathMatch: 'full'
  }
];
 
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TabsPageRoutingModule {}
 

Simplemente hemos copiado la estructura de los otros tabs para el tab4:
... 
 {
        path: 'tab4',
        loadChildren: () => import('../tab4/tab4.module').then(m => m.Tab4PageModule)
 },

Ya solo nos queda modificar el html del archivo tabs.page.html para añadir el botón de la nueva pestaña:

<ion-tabs>
 
  <ion-tab-bar slot="bottom">
    <ion-tab-button tab="tab1">
      <ion-icon name="triangle"></ion-icon>
      <ion-label>Tab 1</ion-label>
    </ion-tab-button>
 
    <ion-tab-button tab="tab2">
      <ion-icon name="ellipse"></ion-icon>
      <ion-label>Tab 2</ion-label>
    </ion-tab-button>
 
    <ion-tab-button tab="tab3">
      <ion-icon name="cube"></ion-icon>
      <ion-label>Tab 3</ion-label>
    </ion-tab-button>
 
    <ion-tab-button tab="tab4">
      <ion-icon name="alarm"></ion-icon>
      <ion-label>Tab 4</ion-label>
    </ion-tab-button>
 
  </ion-tab-bar>
 
</ion-tabs>

Ya tenemos en funcionamiento nuestra aplicación con los cuatro tabs:

Puedes descargar o clonar este proyecto desde GitHub en el siguiente link:

https://github.com/edurevilla/libro-ionic-5-tabs.git

 

Eso es todo por hoy.

Tutorial de IONIC: Menú lateral

Hola a todos, por diversos motivos he tenido abandonado el blog durante una temporada, por fin estoy de vuelta y me dispongo a seguir con el tutorial de Ionic que dejé a medias.

En el último post vimos cómo navegar entre páginas con Ionic, hoy vamos a seguir con el tutorial aprendiendo a utilizar los menús laterales.

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

Podemos crear un  proyecto de ionic utilizando la plantilla sidemenu y ya nos crearía la estructura de una aplicación con un menú lateral y dos páginas de ejemplo.

Sin embargo, para entender mejor cómo funciona vamos a crear un proyecto con la plantilla blank y vamos a añadir nosotros los componentes necesarios para generar un menú lateral.

Lo primero que vamos ha hacer es crear un nuevo proyecto:

ionic start menu blank

Seleccionamos Angular y le podemos decir que no queremos integrar Capacitor, para este ejemplo no lo necesitamos.

Bien, una vez se haya generado el proyecto nos situamos desde la consola dentro de la carpeta menú que se acaba de crear.

Como por defecto ya nos crea la página home, vamos a crear otra página que se llame listado:

ionic g page listado

La idea es tener dos opciones en el menú, una que nos lleve a la página de inicio (home) y otra que nos lleve a una segunda página, en este caso la página listado que acabamos de crear.

La plantilla principal de nuestra aplicación, la que se va a cargar siempre al inicio se encuentra en el archivo app.component.html.

Si echamos un vistazo al contenido de app.component.html vemos que contiene lo siguiente:

<ion-app>
 <ion-router-outlet></ion-router-outlet>
</ion-app>

Dentro de ion-router-outlet se carga el contenido de la página que esté activa en ese momento, es decir la que indique la ruta actual.

Aquí vamos a añadir el menú, que dependiendo de la opción seleccionada cambiará la ruta para mostrar dentro de ion-router-oulet la página que hayamos seleccionado.

Editamos app.component.html y añadimos el siguiente código:

<ion-app>
  <ion-split-pane contentId="main-content">
    <ion-menu contentId="main-content">
      <ion-header>
        <ion-toolbar>
          <ion-title>Menu</ion-title>
        </ion-toolbar>
      </ion-header>
      <ion-content>
        <ion-list>
          <ion-menu-toggle auto-hide="false">
            <ion-item [routerDirection]="'root'" [routerLink]="'/listado'">
              <ion-icon slot="start" [name]="'list'"></ion-icon>
              <ion-label>
                Listado
              </ion-label>
            </ion-item>
            <ion-item [routerDirection]="'root'" [routerLink]="'/home'">
              <ion-icon slot="start" [name]="'home'"></ion-icon>
              <ion-label>
                Inicio
              </ion-label>
            </ion-item>
          </ion-menu-toggle>
        </ion-list>
      </ion-content>
    </ion-menu> 
    <ion-router-outlet id="main-content"></ion-router-outlet>
  </ion-split-pane>
</ion-app>

Lo primero que hacemos es envolver todo en una etiqueta ion-split-pane, esto permite adaptarse a pantallas más grandes (como tabletas) y muestra el menú justo al lado de su contenido. Le asignamos la propiedad contentId=”main-content” que serà el id que le vamos a asignar a la etiqueta ion-router-outlet donde irá el contenido de la aplicación.

Después tenemos ion-menu que es el componente que contiene el menú y al que asignaremos tambien el parámetro contentId=”main-content”, dentro del mismo añadimos su contenido igual que si sería una página, hemos añadido un ion-header con un toolbar y dentro el título del menú.

Después tenemos un ion-content y dentro hemos creado un listado con dos items con las opciones del menú.

Como vimos en el anterior capítulo routerDirection sirve para indicar la dirección de la animación.

En el parámetro routerLink de cada item definimos la ruta que tenemos que cargar al pulsar en el.

Observa que la ruta está entre comillas simples dentro de las comillas dobles, esto es para que lo tome como un literal en lugar de una variable.

También podríamos definir una variable con un array de opciones con sus rutas y hacer un bucle para mostrar las opciones, pero para simplificar hemos creado las dos opciones a mano directamente.

Si ejecutamos la aplicación en un navegador de escritorio con ionic serve se mostrará el menú siempre visible a la izquierda del contenido, sin embargo si ejecutamos ahora la aplicación con ionic serve -l a tamaño móvil no veremos nada especial, solo la página home, para poder ver el menú tenemos que crear un botón para hacer que se muestre el menú. Para ello vamos a modificar el archivo home.page.html,  y añadimos un botón especial llamado ion-menu-button que se encarga de abrir y cerrar el menú, de paso vamos a eliminar todo lo que hay dentro de ion-content y a añadir un texto en el contenido para indicar que es la página de inicio:

<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>     
    <ion-title>
      Ionic Blank
    </ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content>
  <div class="ion-padding">
      <p>Yo soy la página de inicio</p>
  </div>
</ion-content>

Para finalizar vamos a modificar también la plantilla de la página listado para añadir a la cabecera el botón del menú y de paso añadimos un pequeño listado en el contenido para que haga honor a su nombre, editamos el archivo listado.page.html y añadimos lo siguiente:

<ion-header>
  <ion-toolbar>
      <ion-buttons slot="start">
          <ion-menu-button></ion-menu-button>
        </ion-buttons>
    <ion-title>listado</ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content>
    <ion-list>
        <ion-item>
          <ion-label>
            Item 1 del listado
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-label>
            Item 2 del listado
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-label>
            Item 2 del listado
          </ion-label>
        </ion-item>
        <ion-item>
          <ion-label>
            Item 3 del listado
          </ion-label>
        </ion-item>
      </ion-list>
</ion-content>

Ahora si ejecutamos ionic serve -l veremos nuestro menú en acción:

Puedes descargar o clonar este proyecto desde GitHub en el siguiente link:

https://github.com/edurevilla/libro-ionic-5-menu.git

 

Eso es todo por hoy, el el siguiente post seguiremos aprendiendo más sobre Ionic.

Tutorial de IONIC: Navegación

Hola a todos, en el post anterior vimos  una app simple, un minijuego de adivinar números que nos sirvió para aprender cómo se programa la lógica de una página en Ionic.

La aplicación era extremadamente sencilla y toda la lógica se desarrollaba en la misma página, sin embargo lo normal en cualquier aplicación que sea mínimamente completa es que tenga varias vistas o páginas y haya que navegar entre ellas.

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

Vamos a crear un nuevo proyecto de prueba para observar cómo funciona la navegación entre páginas en ionic.

Nos situamos en la carpeta donde queramos crear nuestro proyecto y desde consola escribimos el siguiente comando:

 ionic start navegacion blank

Seleccionamos Angular como framework y esperamos a que se termine de crear el proyecto.

Nos situamos dentro de la carpeta del proyecto que acabamos de crear:

cd navegacion

Ahora vamos a crear una nueva página para poder navegar entre dos páginas, pero antes de seguir vamos a conocer Ionic Generator, una herramienta de nos ofrece Ionic para crear componentes automáticamente.

Ionic Generator

Nosotros podemos crear las páginas a mano creando una carpeta para nuestra página con su vista html y su controlador .ts, y su archivo de estilo .scss, también podemos crear y configurar otros elementos a mano, pero el cli (command line interface o interfaz de línea de comandos) de ionic nos facilita muchísimo el trabajo. Ionic dispone de una herramienta llamada ionic generator.

Ionic generator nos permite generar plantillas con los componentes que queramos.

Con el siguiente comando obtenemos la lista de elementos disponible que podemos generar con ionic generator:

ionic g

La lista de elementos que podemos generar automáticamente con ionic generator son:

  • page: Páginas.
  • component: Los componentes son un conjunto de html, con su css y su comportamiento que podemos reutilizar en cualquier lugar sin tener que reescribir de nuevo todo.
  • service: Los services son proveedores que se encargan del manejo de datos, bien extraídos de la base de datos, desde una api, etc. En versiones anterioes a la 4 de ionic se llamaban providers.
  • module: Nos permite crear un módulo.
  • class: Nos permite crear una clase.
  • directive: Una directiva sirve para modificar atributos de un elemento.
  • guard: Genera un “guardián” que nos permite crear una protección para permitir que los componentes solo se activen si se cumple alguna condición, como por ejemplo si el usuario está logueado.
  • pipe: Los pipes nos permiten crear filtros para aplicar a la información que mostramos en la plantilla, por ejemplo podemos aplicar un filtro que convierta un texto en mayúsculas.
  • interface: Nos permite crear una interfaz.
  • enum: genera una enumeración.

Veremos con más detalle cada elemento según lo vayamos necesitando, para este ejemplo de momento nos interesa  page así que con las flechas del cursor seleccionamos page y pulsamos enter, después nos pregunta el nombre que queremos darle a la página, le llamamos por ejemplo Pagina2 y pulsamos enter.

También podríamos haber creado la página directamente escribiendo:

ionic g page Pagina2 

Está es la manera que utilizaremos a partir de ahora para crear páginas.

Podemos observar que en la carpeta app se ha creado una nueva carpeta llamada pagina2:

Vemos que al crear una página con ionic generator además del archivo pagina2.page.ts también se genera un archivo pagina2.module.ts y otro pagina2-routing.module.ts en cada página generada, este archivo se utiliza para poder realizar lo que se conoce como Lazy Loading, que permite cargar páginas y recursos bajo demanda acelerando así la carga de las páginas.

En versiones anteriores a ionic 4, utilizábamos algo parecido a esto para navegar hacia una página:

this.navCtrl.push(‘miPagina’);

Aunque todavía se puede utilizar este sistema lo recomendable es utilizar el routing de angular basado en diferentes URL para cada página o componente a mostrar.

Si echamos un vistazo al archivo app-routing.module.ts vemos que contiene lo siguiente:

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)},
  {
    path: 'pagina2',
    loadChildren: () => import('./pagina2/pagina2.module').then( m => m.Pagina2PageModule)
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Cómo podemos observar contiene una constante llamada Routes donde se han generado automáticamente las rutas a nuestras dos páginas home y Pagina2.

El primer path que se define está vacío y redirecciona a home, esto indica que home será la página por defecto cuando no se especifique ninguna.

Vemos también que la ruta que se especifica en loadChildren de las dos páginas no apunta a componente de la página si no a su módulo, de esta manera se definen las rutas para hacer lazy loading.

Vamos a ver a continuación las diferentes opciones que tenemos para navegar entre páginas.

Navegar entre páginas utilizando routerLink en la plantilla html

Podemos utilizar routerLink directamente para indicar la ruta de la página que queremos mostrar.

Vamos a editar home.page.html, eliminamos el contenido que se genera por defecto y vamos a crear una página sencilla con un botón para ir a la página 2.

<ion-header>
 <ion-toolbar>
   <ion-title>
     Ionic Blank
   </ion-title>
 </ion-toolbar>
</ion-header>
 
<ion-content>
  <div class="ion-padding">
    <ion-button routerLink="/pagina2" routerDirection="forward">Ir a la página 2</ion-button>
  </div>
</ion-content>

Ahora vamos a editar pagina2.page.html y lo dejamos de la siguiente manera:

<ion-header>
  <ion-toolbar>
    <ion-title>Pagina2</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <div class="ion-padding">
    <p>Soy la página 2</p>
    <ion-button routerLink="/home" routerDirection="back">Volver a home</ion-button>
  </div>
</ion-content>

Aunque no es obligatorio podemos utilizar como en el ejemplo el parámetro routerDirection para indicar la dirección de la animación. Los valores posibles son tres: fordward, back y root, puedes hacer pruebas cambiando el valor de routerDirection para que entiendas mejor qué hace cada uno. 

Vemos que simplemente poniendo la ruta de la página dentro de href podemos navegar entre dos páginas, si ejecutamos ionic serve -l veremos algo como esto:

Navegar entre páginas programáticamente

Si bien podemos navegar entre las distintas páginas de nuestra aplicación utilizando href en un botón o en un ítem de nuestra plantilla html, a veces necesitaremos que se muestre una página desde la lógica del controlador. 

Lo primero que vamos a hacer es modificar el botón de la página home.page.html, en lugar de la propiedad href le vamos a asignar un evento (click) que llame a una función del controlador que vamos a llamar goToPagina2:

<ion-content>
  <div class="ion-padding">
    <ion-button (click)="goToPagina2()" routerDirection="forward">Ir a la página 2</ion-button>
  </div>
</ion-content>

Ahora vamos a editar home.page.ts para crear esta función que nos permita navegar hasta la página 2.

Aunque podríamos importar y utilizar el  Router de angular con el método navigateByUrl, este método no nos permite definir la dirección de la navegación, por lo que es mejor utilizar NavController.

Lo primero que debemos hacer es importar NavController:

import { NavController } from '@ionic/angular';

Ahora debemos inyectarlo en el constructor:

constructor(private navCtrl: NavController){

}

Al inyectar un elemento en el constructor nos permite acceder a sus métodos desde cualquier sitio de la página.

Para finalizar vamos a crear la función goToPagina2 a la que hacíamos referencia desde el botón de la plantilla:

goToPagina2(){
  this.navCtrl.navigateForward('/pagina2');
}

Tenemos tres métodos que podemos utilizar para navegar según la dirección al igual que lo hacíamos cuando hemos utilizado href, estos tres métodos son: navigateForward, navigateBack y navigateRoot.

Una vez más te animo a que experimentes con ellos para que entiendas mejor lo que hace cada uno.

El código completo de home.page.ts quedaría de la siguiente manera:

import { Component } from '@angular/core';
import { NavController } from '@ionic/angular';

@Component({
 selector: 'app-home',
 templateUrl: 'home.page.html',
 styleUrls: ['home.page.scss'],
})
export class HomePage {

 constructor(private navCtrl: NavController){

 }
 goToPagina2(){
   this.navCtrl.navigateForward('/Pagina2');
 }

}

Volver a la página anterior

Para volver a la página anterior podemos utilizar el componente ion-back-button en nuestra plantilla, lo podemos utilizar para poner el típico botón de volver en la cabecera.

Para probarlo vamos a editar el archivo pagina2.page.html y vamos a añadir lo siguiente en la cabecera:

<ion-header>
 <ion-toolbar>
   <ion-title>Pagina2</ion-title>
   <ion-buttons slot="start">
     <ion-back-button defaultHref="/home"></ion-back-button>
    </ion-buttons>
 </ion-toolbar>
</ion-header>

<ion-content>
  <div class="ion-padding">
    <p>Soy la página 2</p>
    <ion-button routerLink="/home" routerDirection="back">Volver a home</ion-button>
  </div>
</ion-content>

Podemos observar que hemos puesto una propiedad llamada defaultHref, con esto le estamos indicando que la ruta por defecto sea /home. Al pulsar este botón vamos a navegar hacia atrás a la página anterior en el historial, si por algún motivo no encontrase la página anterior por defecto cargaría home. 

En este caso como solo tenemos dos páginas coincide que la página anterior siempre va a ser home.

Por otro lado hemos creado el elemento ion-back-button dentro del contenedor ion-buttons, esto nos sirve para contener botones. Podemos observar que ion-buttons tiene un atributo llamado slot  al que hemos asignado el valor start. Con esto le indicamos que los botones que contenga se van a mostrar al inicio, es decir a la izquierda.

El atributo slot indica para indicar la posición o el estilo que van a tener los elementos.

Existen diferentes valores que podemos utilizar con slot, como por ejemplo: item-left, end, start, icon-left, range-left, range-right, icon-only, etc, dependiendo del componente puede aceptar o no diferentes tipos de slot por lo deberemos revisar la documentación del componente para ver qué posibilidades nos ofrece.

Como podemos ver si ejecutamos ionic serve ion-back-button nos ha creado un botón de volver atrás en la cabecera:

Pasando datos entre páginas

Muchas veces nos interesa pasar datos al llamar a una página.

Por ejemplo podemos tener un listado de productos y al pulsar en un producto queremos llamar a una página donde se muestre el detalle de ese producto.

Al utilizar el router de Angular debemos pasar los datos que necesitemos en la url.

Aunque podríamos pasar toda la información del producto en la propia url convertido en json no es lo más práctico, lo más usual es pasar el id del producto y con el id utilizar un servicio para obtener los datos relacionados con el producto seleccionado. Aprenderemos más adelante a crear y utilizar servicios, de momento vamos a ver cómo podemos pasar un id a la página a la que llamamos.

Vamos a seguir haciendo pruebas con el proyecto navegación, lo primero que vamos a hacer es editar el archivo app-routing.module.ts que es el archivo donde de definen las rutas de nuestras páginas. 

Al crear una página con ionic generator ya se crea automáticamente la ruta en app-routing.module.ts , sin embargo para poder añadir datos a la url debemos especificar el nombre del dato que vamos a enviar, en este ejemplo queremos mandar un dato llamado id así que debemos añadir /:id a la ruta de la página 2:

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: 'home', loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)},
  {
    path: 'pagina2/:id',
    loadChildren: () => import('./pagina2/pagina2.module').then( m => m.Pagina2PageModule)
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

 

Como podemos observar hemos hemos definido la el path de la ruta de la Pagina2 como:  pagina2/:id  añadiendo /:id por detrás, así después podremos hacer referencia a id desde la página 2.

Ahora vamos a pasarle por ejemplo el id 14 cuando llamamos a la página 2 desde la página Home, para ello editamos el archivo home.page.ts y modificamos la función goToPagina2() añadiendo el número 14 a la url:

goToPagina2(){
  this.navCtrl.navigateForward('/pagina2/14');
}

Por último vamos a ver cómo obtenemos el id en Pagina2, editamos el archivo pagina2.page.ts y lo primero que necesitamos hacer es importar ActivatedRoute para poder obtener los parámetros recibidos en la url:

import { ActivatedRoute } from '@angular/router';

Ahora debemos inyectarlo en el constructor:

constructor(private route: ActivatedRoute) { }

Finalmente en la función ngOnInit que se ejecuta al iniciar la página recogemos el id y simplemente lo vamos a mostrar a través de un alert:

ngOnInit() {
   let id = this.route.snapshot.paramMap.get('id');
   alert(id);
 }

Para recoger el id utilizamos this.route.snapshot.paramMap.get(‘id’).

El código completo de pagina2.page.ts por lo tanto quedaría de la siguiente manera:

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

@Component({
  selector: 'app-pagina2',
  templateUrl: './pagina2.page.html',
  styleUrls: ['./pagina2.page.scss'],
})
export class Pagina2Page implements OnInit {

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    let id = this.route.snapshot.paramMap.get('id');
    alert(id);
  } 

}

Si corremos la aplicación con ionic serve veremos que al pasar a la página 2 saca un alert con el número 14.

Puedes descargar o clonar este proyecto desde GitHub en el siguiente link:

https://github.com/edurevilla/libro-ionic-navegacion

Para no hacer este post demasiado largo por hoy lo vamos a dejar aquí. En el próximo post veremos cómo utilizar menús laterales, navegación por tabs etc.

 

Eso es todo por hoy,  nos vemos en el próximo post :).

Tutorial de IONIC:Mini Juego de acertar números (actualizado)

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

Después de ver en el post anterior como modificar la plantilla de una página para simplemente mostrar el texto “Hola Mundo”, vamos a avanzar un poco más en nuestro aprendizaje y vamos a programar nuestra primera aplicación interactiva.

Se trata de un simple juego de acertar el número secreto. La aplicación creará un número aleatorio que nosotros debemos adivinar.

Por primera vez vamos a programar con Ionic. Por el camino aprenderemos conceptos como Data Binding, que nos permite actualizar valores entre el html y el controlador y recoger las acciones del usuario como  pulsar un botón.

Bien, sin más preámbulos  vamos a empezar:

Vamos a crear un nuevo proyecto Ionic que en este caso le vamos a llamar adivina: Abrimos el terminal o consola de comandos y situándonos en la carpeta donde queremos crear nuestro proyecto escribimos:

ionic start adivina blank

 

Seleccionamos Angular como framework y esperamos a que se terminen de instalar los paquetes.

Nos situamos dentro de la carpeta del proyecto de acabamos de crear y una vez dentro ejecutamos ionic serve para ver en el navegador los cambios que vayamos haciendo en nuestra aplicación:

 ionic serve -l

 

Recuerda que si utilizas la opción -l con ionic serve puedes visualizar como queda la app en todas las plataformas, la primera vez que ejecutes el comando en un proyecto nuevo te pedirá que instales @ionic/lab.

Ahora abrimos la carpeta del proyecto que acabamos de crear en el editor, entramos en src/app/home y abrimos el archivo home.page.html.

Editamos su contenido para que quede de la siguiente manera:

<ion-header>
  <ion-toolbar>
    <ion-title>
      Adivina el número secreto
    </ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content>
  <div class="ion-padding">
    <ion-input type="number" min="1" max="100" [(ngModel)]="num" placeholder="Introduce un número del 1 al 100"></ion-input>
    <p>El número secreto es {{ mayorMenor }} {{ num }}</p>
    <ion-button expand="block" (click)="compruebaNumero()">Adivina</ion-button>     
  </div>
</ion-content>

 

Si nunca antes has programado en angular ni en ionic es posible que el código te resulte extraño, no te preocupes, ahora vamos a ver lo que es cada cosa, si por el contrario ya has programado en angular o en versiones anteriores de ionic es probable este código te resulte familiar.

Tenemos una plantilla básica de una página con ion-header, ion-navbar y ion-title donde hemos puesto el título de nuestra aplicación “Adivina el número secreto”. Hasta aquí ninguna novedad con lo que ya habíamos visto en el capítulo anterior.

Dentro de ion-content que como sabemos es donde va el contenido de la página tenemos un div al que le aplicamos la clase ion-padding que deja un espacio entre el borde de la pantalla y el contenido dentro de este div es donde está lo interesante:

<ion-input type="number" min="1" max="100" [(ngModel)]="num" placeholder="Introduce un número del 1 al 100"></ion-input>


Primero tenemos un
ion-input que es muy parecido a un input de html, le decimos que sea de tipo number, le ponemos un rango mínimo a 1 y máximo a 100.

El siguiente parámetro [(ngModel)] es el encargado de hacer el Data Binding entre el valor del input y una variable llamada num que estará definida como luego veremos en el controlador de la página. Este es un Data Binding bidireccional ya que si se introduce un valor en el input automáticamente este será reflejado en la variable del controlador, del mismo modo si algún proceso cambia el valor de esta variable en el controlador éste automáticamente aparecerá reflejado como valor del input. 

Por último el input tiene un placeholder indicando que se introduzca un valor entre 1 y 100.

La siguiente línea es un párrafo que contiene lo siguiente:

 <p>El número secreto es {{ mayorMenor }} {{ num }}</p>

Si ya conoces ionic o angular sabes de qué se trata. En este caso con las dobles llaves “{{ }}”  hacemos un Data Binding unidireccional, mayorMenor es una variable que estará definida en el controlador y con las dobles llaves muestra su valor. 

La variable contendrá el texto “mayor que” en caso de que el número secreto sea mayor al introducido, “menor que” en caso de ser menor e “igual a” en caso de ser igual.

En las últimas llaves mostramos el valor de la variable num, veremos cómo al introducir un número este se actualiza automáticamente.

Por último tenemos un botón para comprobar si el número introducido coincide con el número secreto:

<ion-button expand=”block” (click)=”compruebaNumero()”>Adivina</ion-button>

En la documentación oficial de ionic podéis ver más sobre cómo crear distintos tipos de botones con ionic:https://ionicframework.com/docs/api/button

De momento esto es todo lo que necesitamos en la plantilla de nuestro juego.

Ahora necesitamos programar el comportamiento en el controlador de la página.

Si abrimos el archivo home.page.ts que se genera automáticamente al crear un proyecto en blanco veremos el siguiente código:

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

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {

  constructor() {}

}

 

Este código esta es typeScript. Como ya hemos comentado TypeScript permite utilizar elementos de las últimas versiones del estándar ECMAscript aunque todavía no estén implementadas en el navegador que estemos utilizando. También nos permite utilizar variables tipadas.

Como hemos comentado anteriormente con Ionic vamos a utilizar Angular que a su vez utiliza TypeScript, así que es muy beneficioso conocer Angular y typescript.

Aquí vamos a explicar a groso modo las cuestiones más importantes para poder desenvolvernos con ionic pero para saber más sobre Angular y sacarle el máximo podéis encontrar en la red muchos recursos para aprender Angular.

Analicemos el código de home.page.ts:

Lo primero que vemos es una línea import:

import { Component } from ‘@angular/core’;

Import se utiliza para importar módulos que contienen librerías y clases para poder utilizarlas en nuestro proyecto. Podemos importar módulos propios de Ionic que ya se incorporan al crear un proyecto, librerías de Angular, librerías de terceros que podemos instalar o nuestras propias  librerías.

En este caso se importa el elemento Component de @angular/core. 

Las páginas son componentes de Angular, por eso necesitamos importar Component.

Después vemos el siguiente código:

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
}) 

 

@Component es un decorador de Angular. Angular usa los decoradores para registrar un componente.

Los decoradores empiezan con @.

Los decoradores se colocan antes de la clase y definen cómo se va a comportar esta.

Existen los siguientes  decoradores:

  • @Component
  • @Injectable
  • @Pipe
  • @Directive

De momento nos interesa @Component.

En el código vemos que @Component contiene un objeto con dos atributos, selector:’app-home’, que es el selector que se va a aplicar a la página,  templateUrl:’home.page.html’ que es la plantilla html que va a renderizar la página y styleUrls: [‘home.page.scss’] donde se indica dónde se encuentran los estilos que se van a aplicar al componente, es decir,  en este caso a la página.

Por último se exporta una clase llamada HomePage:

export class HomePage {

  constructor() {}

}

 

Podemos añadir a la clase un constructor en caso de que necesitemos iniciar atributos de la clase, inyectar dependencias o realizar alguna operación en el momento de crear la clase, en este ejemplo no lo vamos a necesitar, lo veremos más adelante en otros ejemplos.

En TypeScript la creación de clases es muy similar a cómo sería en otros lenguajes de programación orientado a objetos como Java.

Todos los datos que se muestran en la plantilla y todas las funciones a las que se llame por ejemplo al hacer clic en un botón deben formar parte de la clase HomePage que es el controlador de la página.

Como ya hemos visto en la plantilla home.page.html utilizamos [(ngModel)]=”num” para recoger en la variable num el valor que se introduzca en el campo input, también mostramos otra variable llamada mayorMenor que indicará  si el número introducido es mayor o menor que el número que hay que adivinar y mostramos la variable num que se actualizará instantáneamente al introducir un número.

<ion-input type="number" min="1" max="100" [(ngModel)]="num" placeholder="Introduce un número del 1 al 100"></ion-input>
<p>El número secreto es {{ mayorMenor }} {{ num }}</p>


Por lo tanto estas variables deben de estar definidas en el controlador dentro de la clase
HomePage del archivo home.page.ts:

import { Component } from '@angular/core';
 
@Component({
 selector: 'app-home',
 templateUrl: 'home.page.html',
 styleUrls: ['home.page.scss'],
})
export class HomePage {
 
 num:number;
 mayorMenor = '...';
 
  constructor() {}
 
}

 

Cómo podemos observar para definir una variable ponemos el nombre de la variable y seguido de dos puntos “:” el tipo de valor que va a contener. Si lo deseamos podemos inicializar la variable con un valor en el momento de declararla. 

En este caso num es de tipo number y mayorMenor de tipo string  aunque no es necesario ponerlo ya que al inicializarla con una cadena de texto automáticamente se le asigna el tipo string.

La variable mayorMenor va a contener un texto con la palabra “Mayor”, “Menor” o “Igual” según sea el caso. Inicializamos la variable mayorMenor con tres puntos suspensivos “…”.

Los tipos primitivos  de variable que podemos definir son:

  • number (Numérico).
  • string (cadenas de texto).
  • boolean (Booleano: true o false).
  • any (cualquier tipo).
  • Array.

También vamos a necesitar otra variable que contenga el número secreto que debemos adivinar, le vamos a llamar numSecret y cómo valor le vamos a asignar la respuesta a la llamada a una función llamada numAleatorio que crearemos a continuación.

Vamos a definir la variable numSecret de tipo number debajo de las dos variables anteriores:

numSecret: number = this.numAleatorio(0,100);

 

Hacemos referencia a la  función numAleatorio con this porque va a ser un método de la propia clase.

Ahora vamos a  crear la función numAleatorio que como su nombre indica nos devolverá un número aleatorio entre dos números:

numAleatorio(a,b)
{
    return Math.round(Math.random() * (b - a) + parseInt(a, 10));
}

 

Esta función recibe como parámetros dos valores que serán el rango mínimo y el máximo para el número aleatorio, es este caso le estamos pasando 0 y 100 por lo que obtendremos un número aleatorio entre 0 y 100.

Por último vamos a crear la función compruebaNumero que se llama al pulsar el botón Adivina que en la parte html como recordarás lo hemos definido así:

<ion-button expand="block" (click)="compruebaNumero()">Adivina</ion-button>

 

Y ahora en home.page.ts definimos la función dentro de la clase HomePage:

compruebaNumero(){
    if(this.num)
    {
      if(this.numSecret < this.num)
      {
        this.mayorMenor = 'menor que';
      }
      else if(this.numSecret > this.num)
      {
        this.mayorMenor = 'mayor que';
      }
      else{
        this.mayorMenor = '';
      }
    }
  }

 

Esta función compara el número secreto que está contenido en la variable numSecret con el número introducido por el usuario que se aloja en la variable num y le asigna a la variable mayorMenor el texto “menor que”, “mayor que” o “igual a” en función de si es menor, mayor o igual que esta.

Observa que debemos utilizar this para las variables ya que son atributos de la propia clase.

El código completo de home.page.ts quedará de esta manera:

import { Component } from '@angular/core';
 
@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
 
  num: number;
  mayorMenor = '...';
  numSecret: number = this.numAleatorio(0,100);
 
  constructor() {}
 
  numAleatorio(a, b) {
    return Math.round(Math.random() * (b - a) + parseInt(a, 10));
  }
 
  compruebaNumero(){
    if(this.num)
    {
      if(this.numSecret < this.num)
      {
        this.mayorMenor = 'menor que';
      }
      else if(this.numSecret > this.num)
      {
        this.mayorMenor = 'mayor que';
      }
      else{
        this.mayorMenor = '';
      }
    }
  }
}

 

Ahora si lo ejecutamos en el navegador con ionic serve -l (si no lo tenías ya en marcha), podemos ver un campo para introducir un número, y debajo un texto que dice “el número secreto es … ”, en estos momentos  no hay introducido ningún número por lo que aparecen los puntos suspensivos.

Si introducimos un número vemos que automáticamente en el texto aparece ese número el mismo tiempo que lo escribimos. Eso es porque estamos realizando un Data Binding con la variable num. Al utilizar [(ngModel)]=”num” en el campo input le estamos indicando que la variable coja el valor que introducimos en el input y lo refresque automáticamente en todos los sitios donde se utilice, por ejemplo en el texto de debajo.Lo mismo ocurre si cambiamos el valor de la variable desde el código.

Por últimos si pulsamos el botón adivina obtendremos algo como esto:

Al pulsar el botón se ha ejecutado la función compruebaNumero(), dentro de la función se ha comprobado que el número secreto es mayor a 12 por lo que a la variable mayorMenor se le asigna el texto “mayor que”.

Cuando acertemos el número secreto el texto dirá que el número secreto es el número introducido. Esto es un poco soso, vamos a hacer algunos cambios para que cuando acertemos el número nos muestre un mensaje felicitándonos por haber acertado.

Este mensaje lo ponemos en la plantilla html pero solo se debe mostrar cuando se cumpla una condición y es que la variable mayorMenor no contenga ningún texto, eso ocurre cuando hemos acertado ya que si el número introducido no es ni mayor ni menor, es decir que es es igual al número secreto hacemos la siguiente asignación this.mayorMenor = ”.

Vamos a añadir lo siguiente en home.page.html justo antes del botón “Adivina”:

<ion-card *ngIf="mayorMenor==''">
  <ion-card-header>
  ¡¡¡Enhorabuena!!!
</ion-card-header>
<ion-card-content>
    Has acertado, el número secreto es el {{ num }}
</ion-card-content>
</ion-card>

 

Aprovecho la ocasión para introducir otro componente de ionic: ion-card, las cards o “tarjetas” son componentes que muestran la información en un recuadro. Como vemos dentro contiene otros dos componentes <ion-card-header> y <ion-card-content>, Como habrás adivinado el primero permite mostrar una cabecera dentro de la tarjeta y el segundo el contenido que deseemos.

En la documentación oficial de ionic podéis ver todas las posibilidades que tiene ion-card: https://ionicframework.com/docs/v2/components/#cards

Queremos que está card se muestre solo cuando la variable de nuestro controlador mayorMenor contenga una cadena vacía después de hacer la comprobación.

Aquí entra en juego la directiva condicional *ngIf:

<ion-card *ngIf=“mayorMenor==””>

La directiva *ngIf es una directiva estructural, lo que significa que nos permite alterar el DOM (Document Object Model), estas directivas llevan un asterisco “*” delante.

Con *ngIf le estamos indicando que el elemento donde se encuentra, ion-card en este caso, solo se muestre si se cumple la condición que tiene definida entre las dobles comillas. En este caso le estamos diciendo que solo se muestre el elemento ion-card cuando mayorMenor==’’.

Sabiendo esto vamos a darle otra vuelta de tuerca más y vamos a añadir un botón para volver a jugar una vez que hemos acertado, este botón llamará a una función para volver a generar un nuevo número aleatorio y reiniciará las variables para que se oculte el ion-card. Este botón debe permanecer oculto hasta que hayamos acertado, y cuando se muestra debe ocultarse el botón Adivina para que no podamos pulsarlo después de haber adivinado el número hasta que empecemos una nueva partida.

Editamos home.page.html y añadimos *ngIf al botón Adivina para que solo se  muestre cuando no hemos acertado el número:

<ion-button *ngIf="mayorMenor!=''" expand="block" (click)="compruebaNumero()">Adivina</ion-button>

 

Ahora añadimos el botón “Volver a Jugar” justo después del de Adivina en home.page.html:

<ion-button *ngIf="mayorMenor==''" expand="block" (click)="reinicia()">Volver a Jugar</ion-button>

 

Con *ngIf indicamos que solo se muestre cuando hayamos acertado el número, es decir cuando la variable mayorMenor esté vacía, y en el evento (click) le indicamos que ejecute la función reinicia().

Por lo tanto ahora  vamos a editar nuestro controlador (home.page.ts) y añadimos la función reinicia(), que como hemos dicho debe reiniciar las variables para poder comenzar el juego:

 reinicia(){
   // reiniciamos las variables
   this.num = null;
   this.mayorMenor = '...';
   this.numSecret = this.numAleatorio(0,100);
 }

 

Si acertamos el número veremos algo como esto:

Vista de nuestro juego al acertar.
Vista de nuestro juego al acertar.

Ahora ya podemos volver a jugar una vez de que hemos adivinado el número.

Este juego se podría mejorar añadiendo un contador de intentos. Podríamos limitar el número de intentos y mostrar un mensaje si se han consumido todos los intentos sin acertar el número secreto. Esto lo dejo como deberes para el que quiera practicar más, sin duda experimentar y tratar de modificar el juego es una de las mejores formas de aprender.

Puedes descargar o clonar este proyecto desde GitHub en el siguiente link:

https://github.com/edurevilla/libro-ionic-adivina.git.

  Eso es todo por hoy,  nos vemos en el próximo post :).

Tutorial de IONIC: Estructura de un proyecto en IONIC

Estructura

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

Vamos a ver la estructura de carpetas y archivos que se generan al crear un proyecto ionic.

Al crear un proyecto con ionic se crea una carpeta con el nombre del proyecto y dentro de ella una estructura de archivos y directorios que contiene todos los elementos del proyecto. Vamos a echar un vistazo a la estructura de archivos y carpetas que se ha generado al crear nuestro proyecto hola-mundo:

Veamos qué contiene cada carpeta:

e2e: Aquí se encuentra el código para escribir tests end to end que prueben la aplicación, no nos vamos a preocupar de esta carpeta.


node_modules: La carpeta node_modules se genera automáticamente al instalar las dependencias npm con “npm install”. Este comando explora el archivo package.json para todos los paquetes que necesitan ser instalados. No necesitamos tocar nada en esta carpeta.

src: Esta es la carpeta más importante y donde realizaremos la mayor parte de nuestro trabajo. Aquí es donde están los archivos con el contenido de nuestra aplicación, donde definimos las pantallas, el estilo y el comportamiento que tendrá nuestra aplicación.
Dentro de src tenemos las siguientes subcarpetas: 

La carpeta app, que es donde se ubicará las páginas que creemos para la aplicación, los servicios y toda la lógica de programación.

La carpeta assets donde almacenaremos aquellos recursos que necesitemos para nuestra aplicación, como imágenes etc.

La carpeta environments contiene un archivo llamado environment.prod.ts y un archivo llamado environment.ts. Ambos archivos exportan un objeto llamado environment donde podemos definir variables de entorno, por defecto contienen la variable variable production que podemos utilizar para diferenciar cuando vamos a ejecutar la aplicación en producción en en modo desarrollo. De momento no nos vamos a preocupar por esto.

La carpeta theme contiene el archivo variables.scss donde se definen las variables css de ionic. Por defecto vienen definidos algunos colores que podemos utilizar.

El archivo global.scss: En este archivo podemos colocar css que se utilice globalmente en cualquier sitio de nuestra aplicación.

index.html: Es el punto de entrada de nuestra aplicación, la mayoría de las veces no necesitaremos cambiar nada aquí.

karma.conf.js: Es el archivo de configuración de karma, se utiliza para la realización de test unitarios.

main.ts: Es el punto de entrada del módulo principal de nuestra aplicación. No necesitaremos cambiar nada aquí.

polyfills.ts: Es un archivo de configuración que se genera al crear un proyecto angular, no necesitaremos modificar nada en el.

test.ts: Este archivo es requerido por karma.conf.js que como hemos comentado se utiliza para realizar tests unitarios. 

tsconfig.app.json, tsconfig.spec.json y typings.d.ts:Contienen configuraciones typescript para los tests.

.editorconfig y .gitignore son dos archivos ocultos, así que dependiendo de tu sistema puede que no los veas, están relacionados con la configuración del editor de código y Git, en principio no tenemos que preocuparnos por ellos.

angular.json: Archivo de configuración de la app.

 

Ionic.config.json:  Contiene información básica sobre la configuración nuestro proyecto.

package.json: Contiene paquetes y dependencias de nodeJS.

tsconfig.json y tslint.json: Son archivos que contienen información necesaria a la hora de compilar TypeScript, no necesitamos editar estos archivos.

Aunque pueda parecer complicado en realidad la mayoría de los elementos los gestiona automáticamente Ionic y nosotros solo tenemos que preocuparnos de la carpeta src que es donde se va a situar nuestro código.

Modificando nuestro hola mundo

Ahora que ya hemos visto la estructura de un proyecto en ionic vamos a modificar nuestro hola-mundo para que realmente diga “hola mundo”.

Si desplegamos el directorio src podemos ver la carpeta app, en esta carpeta es donde se van a alojar todas la páginas que contenga nuestra aplicación. Para que nos entendamos una página será como una vista o un pantalla de nuestra aplicación.

Al crear un proyecto con la plantilla blank ionic genera por defecto una página llamada home, que como su propio nombre indica es la página inicial que se va a mostrar al iniciar nuestra aplicación. Esta página la podemos mantener como página principal y modificarla, o podemos eliminarla y crear otra con el nombre que nosotros queramos. De momento vamos a mantener la que nos ha creado por defecto y vamos a modificar su contenido.

Como podemos ver dentro de la carpeta de la página home que nos ha creado hay cinco archivos:

El archivo home.module.ts: En este archivo se importan los módulos y funcionalidades requeridas.

El archivo home.page.html que contiene la plantilla html de la página.

El archivo home.page.scss que contiene el archivo sass donde podremos modificar el estilo de los componentes de la página.

home.page.spec.ts este archivo se utiliza para las pruebas unitarias, no nos vamos a preocupar de él por ahora.

El archivo home.page.ts que es el archivos typescript que contiene el controlador de la página, donde definiremos el comportamiento de la misma, como por ejemplo la función con la lógica a ejecutarse cuando se pulse sobre un botón de la página etc. Veremos más adelante en profundidad cada una de las partes de una página.

Si abrimos el archivo home.page.html veremos que contiene algo como esto:

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Blank
    </ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">Blank</ion-title>
    </ion-toolbar>
  </ion-header>
 
  <div id="container">
    <strong>Ready to create an app?</strong>
    <p>Start with Ionic <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
  </div>
</ion-content>


Las páginas se pueden crear utilizando html puro, sin embargo aquí podemos ver algunas etiquetas que no corresponden con las etiquetas html “estándar”. Lo que vemos aquí son componentes de ionic.

Ionic nos ofrece una amplia gama de componentes listos para utilizar y que nos facilitarán la labor de crear nuestra interfaz de usuario con un estilo atractivo y profesional.

Iremos viendo  diferentes componentes de ionic según los vayamos necesitando a lo largo de este libro. En este enlace podéis consultar en la documentación oficial de ionic los componentes disponibles con pequeños ejemplos de como implementarlos: https://ionicframework.com/docs/components/

Todos los componentes de ionic comienzan con el prefijo “ion-“.

Cómo ionic está basado en Web components si en algún caso no nos es suficiente con los componentes que nos ofrece ionic podríamos crear nuestros propios componentes personalizados, aunque en la mayoría de los casos no será necesario ya que ionic nos ofrece una amplia gama de componentes para poder desarrollar nuestras aplicaciones.

Veremos más sobre componentes en posteriores capítulos.

En la página principal (y única de momento) de nuestro proyecto hola-mundo vemos que tenemos los siguientes componentes:

  • ion-header: Cabecera.
  • ion-navbar: Barra de navegación.
  • ion-title: Título.
  • ion-content: Contenido de la página.

Como puedes ver ion-header aparece dos veces, una dentro de la etiqueta ion-content con el parámetro collapse=”condense”, esto es una nueva funcionalidad que trae ionic 5 y que solo es visible en IOS, al hacer scroll la cabecera se colapsa.
En los ejemplos de este libro no vamos a utilizar esta funcionalidad, pero si necesitas saber más sobre cómo funcionan las cabeceras desplegables para iOS en ionic puedes consultar la documentación oficial de ionic al respecto en el siguiente link:

https://ionicframework.com/docs/api/title#collapsible-large-titles

Bien, vamos a cambiar el contenido de ion-title por “Hola Mundo”, también vamos a borrar todo lo que hay dentro de la etiqueta ion-content y vamos a poner orgullosos “<h1>¡¡¡¡Hola mundo!!!!<h1>”, así el código de home.html debería quedar de la siguiente manera:

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
     Hola Mundo
    </ion-title>
  </ion-toolbar>
</ion-header>
 
<ion-content [fullscreen]="true">
  <div class="ion-padding">
    <h1>¡¡¡¡Hola mundo!!!!</h1>
  </div>
</ion-content>

Si desde el terminal,escribimos ionic serve -l  para ver desde el navegador como queda en las tres plataformas nuestra aplicación veremos algo como esto:

 

Eso es todo por hoy,  en el próximo post crearemos un mini juego para seguir aprendiendo a crear aplicaciones con este potente framework llamado IONIC.

Tutorial de Ionic – Mini Juego de acertar números en ionic 2, el controlador de la página y Data Binding y *ngIF

En el siguiente enlace tienes el índice para acceder al resto de entradas de este tutorial:

 

¡¡Atención!! este tutorial se basa en ionic 3 y está desactualizado por lo que es posible que los ejemplos no funcionen en la última versión de ionic, haz click aquí para acceder a un tutorial mas actual de Ionic.

Hola a todos:

Después de ver en el capitulo anterior cómo modificar la plantilla de una página para simplemente mostrar el texto “Hola Mundo”, hoy vamos a avanzar un poco más en nuestro aprendizaje y vamos a programar nuestra primera aplicación interactiva.

Se trata de un simple juego de acertar el número secreto. La aplicación creará un número aleatorio que nosotros debemos adivinar.

Por primera vez vamos a programar con ionic 2. Por el camino aprenderemos conceptos como Data Binding, que nos permite actualizar valores entre el html y el controlador y recoger las acciones del usuario como  pulsar un botón.

Bien, sin más preámbulos  vamos a empezar:

Vamos a crear un nuevo proyecto ionic 2 que en este caso le vamos a llamar adivina: abrimos el terminal o consola de comandos y escribimos:

ionic start adivina blank

Nos situamos dentro de la carpeta del proyecto de acabamos de crear:

cd adivina

Y una vez dentro ejecutamos ionic serve para ver en el navegador los cambios que vayamos haciendo en nuestra aplicación.

ionic serve

Recuerda que si utilizas la opción -l con ionic serve puede visualizar como queda la app en todas las plataformas.

Ahora abrimos la carpeta del proyecto que acabamos de crear en el editor, entramos en src/pages/home y abrimos el archivo home.html.

Editamos su contenido para que quede de la siguiente manera:

<ion-header>
  <ion-navbar>
    <ion-title>
      Adivina el número secreto
    </ion-title>
  </ion-navbar>
</ion-header>
<ion-content padding>
    <ion-input type="number" min="1" max="100" [(ngModel)]="num" placeholder="Introduce un número del 1 al 100"></ion-input>
    <p>El número secreto es {{ mayorMenor }}</p>
   <button ion-button block (click)="compruebaNumero()">Adivina</button>
</ion-content>

Si nunca antes has programado en angular ni en ionic es posible que el código te resulte extraño, no te preocupes, ahora vamos a ver lo que es cada cosa, si por el contrario ya has programado en angular o ionic 1 es probable este código te resulte familiar.

Tenemos una plantilla básica de una página con ion-header, ion-navbar y ion-title donde hemos puesto el título de nuestra aplicación “Adivina el número secreto”. Hasta aquí ninguna novedad con lo que ya habíamos visto en el capítulo anterior.

Dentro de ion-content que como sabemos es donde va el contenido de la página es donde está lo interesante:

 <ion-input type="number" min="1" max="100" [(ngModel)]="num" placeholder="Introduce un número del 1 al 100"></ion-input>

Primero tenemos un ion-input que es muy parecido a un input de html, le decimos que sea de tipo number, le ponemos un rango mínimo a 1 y máximo a 100.

El siguiente parámetro [(ngModel)] es el encargado de hacer el Data Binding entre el valor del input y una variable llamada num que estará definida como luego veremos en el controlador de la página. Este es un Data Binding bidireccional ya que si se introduce un valor en el input automáticamente este será reflejado en la variable del controlador, del mismo modo si algún proceso cambia el valor de esta variable en el controlador este automáticamente aparecerá reflejado como valor del input. Por último el input tiene un placeholder indicando que se introduzca un valor entre 1 y 100.

La siguiente línea es un párrafo que contiene lo siguiente:

<p>El número secreto es {{ mayorMenor }}</p>

Si ya conoces ionic o angular sabes de que se trata. En este caso las con las dobles llaves “{{ }}”  hacemos un Data Binding unidireccional, mayorMenor es una variable que estará definida en el controlador y con las dobles llaves muestra su valor. En este caso la variable contendrá el texto “mayor” en caso de que el número secreto sea mayor al introducido, “menor” en caso de ser menor e “igual” en caso de ser igual.

Por último tenemos un botón para comprobar si el número introducido coincide con el número secreto.

<button ion-button block (click)="compruebaNumero()">Adivina</button>

En la documentación oficial de ionic podéis ver más sobre como crear distintos tipos de botones con ionic 2: https://ionicframework.com/docs/v2/components/#buttons

De momento esto es todo lo que necesitamos en la plantilla de nuestro juego.

Ahora necesitamos programar el comportamiento en el controlador de la página.

Si abrimos el archivo home.ts que se genera automáticamente al crear un proyecto en blanco veremos el siguiente código:

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

import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(public navCtrl: NavController) {

  }

}

Este código esta es typeScript. Como ya hemos comentado TypeScript permite utilizar elementos de las últimas versiones del estándar ECMAscript aunque todavía no estén implementadas en el navegador que estemos utilizando. También nos permite utilizar variables tipadas.

Ionic 2 se basa Angular 2 que a su ver utiliza TypeScript, así que es muy beneficioso conocer angular 2 y typescript.
Aquí vamos a explicar a groso modo las cuestiones imprescindibles para poder desenvolvernos con ionic 2 pero para saber más sobre angular y sacarle el máximo partido podéis consultar en http://learnangular2.com/  así como otros muchos recursos que se pueden encontrar en la red para aprender Angular 2.

Analicemos el código de home.ts:

Lo primero que vemos son dos lineas import:

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

import { NavController } from 'ionic-angular';

Import se utiliza para importar módulos que contienen librerías y clases para poder utilizarlas en nuestro proyecto. Podemos importar módulos propios de Ionic que ya se incorporan al crear un proyecto, librerías de AngularJS, librerías de terceros que podemos instalar o nuestras propias  librerías.

En este caso se importa el elemento Component de Angular y el elemento NavController de ionic-angular.

NavController lo utilizaremos más adelante para poder navegar entre distintas páginas, de momento en este ejemplo no lo necesitamos.

Las páginas son componentes de Angular, por eso necesitamos importar Component.

Después vemos el siguiente código:

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

@Component es un decorador de Angular. Angular 2 usa los decoradores para registrar un componente.

Los decoradores empiezan con @.

Los decoradores se colocan antes de la clase y definen cómo se va a comportar esta.

Existen los siguientes  decoradores:

  • @Component
  • @Injectable
  • @Pipe
  • @Directive

De momento nos interesa @Component.

En el código vemos que @Component contiene un objeto con dos atributos, selector:’page-home’, que es el selector css que se va a aplicar a la página, y templateUrl:’home.html’ que es la plantilla html que va a renderizar la página.

Por último se exporta una clase llamada HomePage:

export class HomePage {

  constructor(public navCtrl: NavController) {

  }

}

Tenemos que exportar la clase para luego poderla importar si queremos llamar a  la página desde cualquier otro sitio de la aplicación, por ejemplo cuando navegamos entre páginas.
Vemos que la clase tiene un constructor, en TypeScript la creación de clases es muy similar a como sería en otros lenguajes de programación orientado a objetos como Java.

Todos los datos que se muestran en la plantilla y todas las funciones a las que se llame por ejemplo al hacer click en un botón deben formar parte de la clase HomePage qué es el controlador de la página.

Como ya hemos visto en la plantilla home.html utilizamos (ngModel)]=”num” para recoger en la variable num el valor que se introduzca en el campo input, también mostrábamos otra variable llamada mayorMenor que indicará  si el número introducido es mayor o menor que el número que hay que adivinar:

 <ion-input type="number" min="1" max="100"[(ngModel)]="num" placeholder="Introduce un número del 1 al 100"></ion-input>
    <p>El número secreto es {{ mayorMenor }}</p>

Por lo tanto estas variables deben de estar definidas en el controlador dentro de la clase HomePage del archivo home.ts:

export class HomePage {

  num:number;
  mayorMenor: string = '...';

  constructor(public navCtrl: NavController) {

  }

}

Como podemos observar para definir una variable ponemos el nombre de la variable y seguido de dos puntos “:” el tipo de valor que va a contener. Si lo deseamos podemos inicializar la variable con un valor en el momento de declararla. En este caso num es de tipo number y mayorMenor de tipo string ya que va a contener un texto con la palabra “Mayor”, “Menor” o “Igual” según sea el caso. Inicializamos la variable mayorMenor con tres puntos suspensivos “…”.

Los tipos primitivos  de variable que podemos definir son:

  • number (Numérico).
  • string (cadenas de texto).
  • boolean (Booleano: true o false).
  • any (cualquier tipo).
  • Array.

También vamos a necesitar otra variable que contenga el número secreto que debemos adivinar, le vamos a llamar numSecret y como valor le vamos a asignar la respuesta a la llamada a una función llamada numAleatorio que crearemos a continuación.

Definimos la variable numSecret de tipo number:

numSecret: number = this.numAleatorio(0,100);

Hacemos referencia a la  función numAleatorio con this porque va a ser un método de la propia clase.

Ahora vamos a  crear la función numAleatorio que nos devolverá un número aleatorio.

numAleatorio(a,b)
{
      return Math.round(Math.random()*(b-a)+parseInt(a));
}

Esta función recibe como parámetros dos valores que serán el rango mínimo y el máximo para el número aleatorio, es este caso le estamos pasando 0 y 100 por lo que obtendremos un número aleatorio entre 0 y 100.

Por último vamos a crear la función compruebaNumero que se llama al pulsar el botón Adivina que en la parte html lo hemos definido así:

<button ion-button block (click)="compruebaNumero()">Adivina</button>

Y ahora en home.ts definimos la función dentro de la clase HomePage:

compruebaNumero(){
    if(this.num)
    {
      if(this.numSecret < this.num)
      {
        this.mayorMenor = 'menor';
      }
      else if(this.numSecret > this.num)
      {
        this.mayorMenor = 'mayor';
      }
      else{
        this.mayorMenor = 'igual';
      }
    }
  }

Esta función compara el número secreto que esta contenido en la variable numSecret con el número introducido por el usuario que se aloja en la variable num y le asigna a la variable mayorMenor el texto “menor”, “mayor” o “igual” en función de si es menor, mayor o igual que esta.
Observa que debemos utilizar this para las variables ya que son atributos de la propia clase.

El código completo de home.ts quedará de esta manera:

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

import { NavController } from 'ionic-angular';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  num:number;
  mayorMenor: string = '...';
  numSecret: number = this.numAleatorio(0,100);

  constructor(public navCtrl: NavController) {

  }

  compruebaNumero(){
    if(this.num)
    {
      if(this.numSecret < this.num)
      {
        this.mayorMenor = 'menor';
      }
      else if(this.numSecret > this.num)
      {
        this.mayorMenor = 'mayor';
      }
      else{
        this.mayorMenor = 'igual';
      }
    }
  }

  numAleatorio(a,b)
  {
        return Math.round(Math.random()*(b-a)+parseInt(a));
  }

}

Ahora si lo ejecutamos en el navegador con ionic serve (si no lo tenías ya en marcha), podemos ver un campo para introducir un número, y debajo un texto que dice “el número secreto es … de”, en estos momentos  no hay introducido ningún número por lo que no aparece ningún número en el texto.

Si introducimos un número vemos que automáticamente en el texto aparece ese número el mismo tiempo que lo escribimos. Eso es porque estamos realizando un Data Binding con la variable num. Al utilizar [(ngModel)]=”num” en el campo input le estamos indicando que la variable coja el valor que introducimos en el input y lo refresque automáticamente en todos los sitios donde se utilice, por ejemplo en el texto de debajo.Lo mismo ocurre si cambiamos el valor de la variable desde el código.

Por últimos si pulsamos el botón adivina obtendremos algo como esto:

Vista de nuestro Juego en acción
Vista de nuestro Juego en acción

Al pulsar el botón se ha ejecutado la función compruebaNumero(), dentro de la función se ha comprobado que el número secreto es mayor a 12 por lo que a la variable mayorMenor se le asigna el texto “mayor”.

Cuando acertemos el número secreto el texto dirá que el número secreto es igual al número introducido. Esto es un poco soso, vamos a hacer algunos cambios para que cuando acertemos el número nos muestre un mensaje felicitándonos por haber acertado.

Este mensaje lo ponemos en la plantilla html pero solo se debe mostrar cuando se cumpla una condición y es que la variable mayorMenor contenga el texto ‘igual’.

Vamos a añadir lo siguiente en home.html justo antes del botón “Adivina”:

<ion-card *ngIf="mayorMenor=='igual'">
     <ion-card-header>
        ¡¡¡Enhorabuena!!!
      </ion-card-header>
      <ion-card-content>
          Has acertado, el número secreto es el {{ num }}
      </ion-card-content>
</ion-card>

Aprovecho la ocasión para introducir otro componente de ionic: ion-card, las cards o “tarjetas” son componentes que muestran la información en un recuadro. Como vemos dentro contiene otros dos componentes <ion-card-header> y <ion-card-content>, Como habrás adivinado el primero permite mostrar una cabecera dentro de la tarjeta y el segundo el contenido que deseemos.
En la documentación oficial de ionic podéis ver todas las posibilidades que tiene ion-cardhttps://ionicframework.com/docs/v2/components/#cards

Queremos que está card se muestre solo cuando la variable de nuestro controlador mayorMenor contenga el texto ‘igual’ después de hacer la comprobación.

Aquí entra en juego la directiva condicional *ngIf:

<ion-card *ngIf="mayorMenor=='igual'">

La directiva *ngIf es una directiva estructural, lo que significa que nos permite alterar el DOM (Document Object Model), estas directivas llevan un asterisco “*” delante.
Con *ngIf le estamos indicando que el elemento donde se encuentra, ion-card en este caso, solo se muestre si se cumple la condición que tiene definida entre las dobles comillas. En este caso le estamos diciendo que solo se muestre el elemento ion-card cuando mayorMenor==’igual’.

Sabiendo esto vamos a darle otra vuelta de tuerca más y vamos a añadir un botón para volver a jugar una vez que hemos acertado, este botón llamará a una función para volver a generar un nuevo número aleatorio y reiniciará las variables para que se oculte el ion-card. Este botón debe permanecer oculto hasta que hayamos acertado, y cuando se muestra debe ocultarse el botón Adivina para que no podamos pulsarlo después de haber adivinado el número hasta que empecemos una nueva partida.

Editamos home.html y añadimos *ngIf al botón Adivina para que solo se  muestre cuando no hemos acertado el número:

<button *ngIf="mayorMenor!='igual'" ion-button block (click)="compruebaNumero()">Adivina</button>

Ahora añadimos el botón “Volver a Jugar” a home.html:

<button *ngIf="mayorMenor=='igual'" ion-button block (click)="reinicia()">Volver a Jugar</button>

Con *ngIf indicamos que solo se muestre cuando hayamos acertado el número, y en el evento (click) le indicamos que ejecute la función reinicia().

Por lo tanto ahora  vamos a editar nuestro controlador (home.ts) y añadimos la función reinicia(), que como hemos dicho debe reiniciar las variables para poder comenzar el juego:

reinicia(){
    // reiniciamos las variables
    this.num = null;
    this.mayorMenor = '...';
    this.numSecret = this.numAleatorio(0,100);
  }

Ahora ya podemos volver a jugar una vez de que hemos adivinado el número.

Este juego se podría mejorar añadiendo un contador de intentos. Podríamos limitar el número de intentos y mostrar un mensaje si se han consumido todos los intentos sin acertar el número secreto. Esto lo dejo como deberes para el que quiera practicar más, si queréis ver como sería la solución completa con contador de intentos Luis Jordán me ha hecho llegar su solución, en el siguiente enlace podéis encontrar el código de la página home que ha mejorado realizado Luis: https://drive.google.com/file/d/0B7M15FxlLmQJZHBSTUlfMmFkSmM/view?usp=sharing

De momento este simple ejemplo nos ha servido para aprender algunos conceptos básicos sobre Ionic 2, en el siguiente capitulo veremos como navegar entre distintas páginas de una aplicación.

 

Si necesitas desarrollar una aplicación móvil no dudes en solicitarme un presupuesto sin compromiso: