Skip to content
Aprende a hacer apps móviles con Ionic 2 

Ionic DeepLinker I: Navegando por URLs

Últimamente siempre escucho la misma pregunta… ¿Como utilizo URLs para navegar en Ionic 2? La respuesta se llama DeepLinker. En este artículo voy a explicarte sus principios y cómo usarlo para navegar por URLs en tu web mobile o PWA.

Seguramente sabrás que Ionic 2 utiliza un sistema de navegación propio basado en un stack de vistas que componen el historial, muy similar al de las apps nativas. Si lo piensas tiene todo el sentido del mundo: es un framework mobile.

La siguiente animación es un ejemplo de la pila navegación en nativo.

native views stack

Quizá te estas preguntando… ¿y por qué no usar un router basado en URLs?

Pues por que la navegación en una app es diferente.

En nativo, el punto de entrada suele ser siempre el mismo (home screen), así que cuando llegas a una vista concreta existe un cierto historial… Esto no es así en la navegación por URLs, donde puedes ir directamente a cualquier página solo pegando su enlace en el navegador.

Esta es la diferencia más clara entre ambos casos. Navegar por URLs en nativo implica un cambio de mentalidad sobre lo que representa un enlace.

Como Ionic 2 permite hacer también PWA o simplemente mobile webs, es importante poder navegar también por URLs.

Desde Ionic nos invitan a pensar en las URLs como “migajas de pan” (breadcrums como ellos dicen) que nos lleven a navegar por la app, pero… ¿cómo conseguirlo?

DeepLinker

La solución de Ionic ha sido crear el DeepLinker. El DeepLinker se encarga de registrar y mostrar vistas específicas en base a su URL.

Ten en cuenta que el DeepLinker no funciona de forma autónoma, sino que se apoya en el NavController de la aplicación.

Mi consejo es que diseñes primero la navegación pensando en el patrón habitual de Ionic y añadas posteriormente el DeepLinker. A partir de ese momento, al navegar se actualizará la URL. Además, si refrescas una URL, se cargará el componente que has indicado.

Para utilizar el DeepLinker es imprescindible usar también un NavController que gestione el stack de navegación.

Como se usa

Para declarar los enlaces del DeepLinker, lo único que tienes que hacer es pasarle un objeto DeepLinkerConfig como tercer parámetro al método IonicModule.forRoot que se importa desde el NgModule.

Nada más. Nunca tocarás el DeepLinker directamente, de eso ya se encarga tu NavController.

El DeepLinkerConfig es un objeto con un array que incluye la información de los enlaces, siguiendo la estructura:

  • component: El componente de la página asociada a este enlace.
  • name: La referencia del enlace.
  • segment: El segmento de la ruta (URL) asociado al enlace.
  • defaultHistory (opcional): El array de componentes a cargar como historial del enlace.

Aquí tienes un ejemplo de objeto DeepLinkerConfig:

{
    links: [
        {component: ComponentA, name:'first-component', segment:'compA'},
        //...some more deep links
    ]
}

Aprendiendo con un ejemplo

Veamos un ejemplo, basado en el sidemenu starter. Puedes replicar el código ejecutando en terminal ionic start example sidemenu --v2.

Este starter carga un componente inicial (app.component.ts) que instancia el NavController. Su template contiene un menú lateral y el <ion-nav> sobre el que funcionará toda la navegación.

A través del menú puedes ir a 2 páginas distintas. ¡Vamos a llegar a ellas por URL!

Modifica la llamada a IonicModule.forRoot del archivo src/app/app.module.ts así:

//src/app/app.module.ts
import { NgModule, ErrorHandler } from '@angular/core';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { Page1 } from '../pages/page1/page1';
import { Page2 } from '../pages/page2/page2';

@NgModule({
  declarations: [
    MyApp,
    Page1,
    Page2
  ],

  imports: [
  //PAY ATTENTION HERE
    IonicModule.forRoot(MyApp, {}, {
      links: [
        {component: Page1, name:'Page 1', segment:'page1'},
        {component: Page2, name:'Page 2', segment:'page2'}
      ]
    })
  //OK, DONE

  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    Page1,
    Page2
  ],
  providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}]
})
export class AppModule {}

Como puedes ver, solo has tenido que cambiar esto:

IonicModule.forRoot(MyApp, {}, {
      links: [
        {component: Page1, name:'Page 1', segment:'page1'},
        {component: Page2, name:'Page 2', segment:'page2'}
      ]
    })

Así le indicas al DeepLinker que cuando la URL incluya el segmento page1, cargue el componente Page1 y lo mismo con la otra página.

Además, el NavController actualizará la barra de navegación para reflejar la URL en la que te encuentras en cada momento. Fíjate.

deeplinker

Si refrescas la página en esta URL, te cargará de nuevo este contenido (sin DeepLinker te habría llevado de vuelta a la home).

Links dinámicos

Ok, has visto lo básico, pero te preguntarás… ¿cómo paso parámetros en las URLs?

Fácil, solo tienes que usar la sintáxis :param en el segmento. Así:

links: [
        {component: CatalogDetail, name:'Catalog Detail', segment:'catalog/:itemID'}
      ]

Si navegas a esa ruta pasando un parámetro en formato string, podrás recoger el parámetro como siempre, usando el servicio NavParams.

Para que el paso de parámetros funcione al acceder directamente por URL, tienen que ser strings.

Actualizando el ejemplo

¿Y si al seleccionar un ítem en Page2, quieres ir a su de detalle?

Para eso, lo primero que tienes que hacer es crear la página de detalle:

ionic generate page page3

Ahora modifíca tu nueva página en src/pages/page3.ts para recuperar el argumento item:

//src/pages/page3.ts

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


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

  item:any;

  constructor(public navCtrl: NavController, public navParams: NavParams) {
    this.item = navParams.get('item');
  }
}

Actualiza también su template (src/pages/page3.html) para mostrar la información del item:

<ion-header>
  <ion-navbar>
    <ion-title>Item {{item.title}}</ion-title>
  </ion-navbar>
</ion-header>


<ion-content padding>
  <ion-icon [name]="item.icon" item-left></ion-icon>
  {{item.note}}
</ion-content>

Este item se tiene que pasar por algún lado. Actualiza también src/pages/page2.ts para navegar a Page3 y pasar el item seleccionado:

//src/pages/page2.ts

import { Page3 } from '../page3/page3';
//...some stuff...

  itemTapped(event, item) {
    // this method is already binded in the template
    this.navCtrl.push(Page3, {
      item: item
    });
  }

//...more stuff

Es el momento de que incorpores la nueva página al NgModule y de paso también a la configuración del DeepLinker.

Edita src/app/app.module.ts así:

//...some imports
import { Page3 } from '../pages/page3/page3';

@NgModule({
  declarations: [
    //...other declarations
    Page3
  ],
  imports: [
    IonicModule.forRoot(MyApp, {}, {
      links: [
        {component: Page1, name:'Page 1', segment:'page1'},
        {component: Page2, name:'Page 2', segment:'page2'},
        {component: Page3, name:'Page 3', segment:'page2/:item'},
      ]
    })
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    //...other entry components
    Page3
  ],
  //...more stuff

Parece que está listo, ¿no? Si lo pruebas, verás como navegas hasta la vista de detalle de un item, pero…

params with DeepLinker

Fíjate en la URL. La navegación “in-app” ha funcionado bien (has podido navegar) pero el parámetro en la URL ha pasado a ser [object Object]. Esto claramente no es lo que esperabas. Si refrescas la página, verás que sale vacía.

Recuerda, para navegar por URL con parámetros, deben estar en formato string, por lo que tienes 2 opciones:

  1. Usar JSON.stringify al crear el parámetro y JSON.parse al recuperarlo, para que en la URL se desmonte el objeto como un string. Ojo, esto te dará una URL muy fea, tipo: baseUrl/#/page2/%7B%22title%22%3A%22Item%2010%22%2C%22note%22%3A%22This%20is%20item%20%2310%22%2C%22icon%22%3A%22american-football%22%7D.

  2. Refactorizar el código para obtener el listado de items desde un servicio compartido por Page2 y Page3, de forma que solo tengas que pasar como parámetro el itemID.

Normalmente usarás la segunda estrategia, por lo que, aunque no es el objetivo del artículo, he hecho algunos cambios en el proyecto para que veas el resultado final.

He creado un servicio y he movido la lógica de generación de los items a su constructor. Luego lo incluyo en los providers del NgModule, lo paso por DI a Page2 y Page3, y actualizo ambos componentes para que se pase por parámetro el ID del item, en lugar del elemento completo.

Son todo cambios muy sencillos, pero si quieres ver el código, aquí tienes el repositorio:

Te dejo con una imagen del resultado, tanto para navegación clásica (stack navigation), como para navegación al refrescar la URL.

deepLinker navigation with params

¿Notas alguna diferencia?

SI, efectivamente, al navegar por URL no aparece el botón BACK.

Lo he comentado al principio. El DeepLinker sabe que tiene que cargar ese componente cuando llegas a través de una URL, pero no tiene ni idea del recorrido equivalente en la navegación in-app para llegar a la misma vista, así que no hay historial.

Eso sí, la solución es fácil…

Añadiendo historial a los enlaces

En la estructura del DeepLinkerConfig habías visto un campo opcional: defaultHistory. Este campo es justamente el que necesitas para indicarle al DeepLinker qué historial ficticio tiene que generar cuando carga la app a través de un enlace.

El campo defaultHistory espera un array, por lo que puedes pasarle varios componentes, que colocará el el stack view de forma ordenada para que puedas ir navegando atrás progresivamente.

A partir de aquí, ya es cosa tuya decidir si quieres que tu app siga una navegación idéntica por enlaces que en nativo, o no.

En un e-commerce por ejemplo, si entras directamente por un producto, igual te interesa que el botón atrás navegue a la página principal, en lugar de a un listado de productos de una categoría concreta.

Completando el ejemplo

Para completar el ejemplo que has ido siguiendo, sería buena idea que si navegas directamente al detalle, puedas volver atrás al listado de items.

Para ello, solo tienes que actualizar el DeepLinkConfig que pasas al método IonicModule.forRoot en src/app/app.module.ts:

links: [
        {component: Page1, name:'Page 1', segment:'page1'},
        {component: Page2, name:'Page 2', segment:'page2'},
        {component: Page3, name:'Page 3', segment:'page2/:item', defaultHistory:[Page2]},
      ]

Ahora sí, deberías ver como al cargar el detalle de un item desde URL tienes el botón back que te lleva a Page2.

Nota final

Te he mostrado los principios de la navegación por URLs en Ionic 2 y has trabajado con un ejemplo sencillo. Estás de subidón, lo sé, pero la cosa puede ser más complicada 😉

En mi próximo post (Ionic DeepLinker II) te explicaré casos más complejos del DeepLinker que se pueden dar cuando trabajas con Tabs.

Mientras tanto, dime… ¿qué opinas de la navegación por URLs en Ionic?

Published inAngular 2ionicIonic 2Ionic FrameworkJavascript

One Comment

Deja un comentario