Nel mondo frontend da qualche settimana si parla sempre più spesso di Angular2, nuova versione del famosissimo framework di Google. Ad oggi siamo alla 2.0-rc5 e ci stiamo avvicinando alla versione 2.0 final. Parallelamente il team di Ionic sta portando avanti i lavori su Ionic 2, basato per l’appunto su Angular2. Di Ionic abbiamo già parlato in precedenza, ma per chi non ne avesse mai sentito parlare Ionic è un framework basato su Apache Cordova e AngularJS che vi permette di creare applicazioni mobile utilizzando tecnologie web.

Questo post non sarà una guida introduttiva. Non avrebbe senso in questo momento in cui il framework non è ancora stabile (siamo alla 2.0-beta11 e la final uscirà in concomitanza con l’uscita di Angular2). Ci occuperemo invece di analizzare alcune differenze strutturali tra la versione 1.X e la versione 2.0, che sicuramente non verranno stravolte prima della prossima versione stabile.

Navigazione

Secondo chi vi scrive, la navigazione tra le “pagine” è uno dei punti deboli di Ionic. Questo è uno schema degli elementi che formano il sistema di navigazione attuale di un’applicazione creata con questo framework.

Ionic Navigation System
Ionic Navigation System

Come vediamo gli elementi sono due. Il primo è ui-router, libreria che è diventata uno standard de facto nelle applicazioni AngularJS. Sopra a questo sistema Ionic ha creato il service $ionicHistory che permette di gestire le transition e una cache delle view per aumentare le performance. Ionic 2 avrebbe potuto utilizzare @angular/router, il pacchetto ufficiale di Angular2 per la gestione delle rotte. Invece ha optato per una soluzione completamente differente. Prima di tuffarci nel codice per capire come è stato affrontato questo argomento, analizziamo il flusso di navigazione delle normali applicazioni mobile. Se volessimo schematizzare (e semplificare) al massimo, il flusso di navigazione di un’app è uno stack di viste.

Schema di uno stack

Quando apriamo un’app ci troviamo in una home, cliccando ci muoviamo in una nuova vista (push), premendo “back” torniamo alla vista precedente (pop). In un’app mobile, anche se ibrida, gli URL sono praticamente inutili. Ad un’app mobile non è richiesto di essere RESTful, quindi sforzarsi di utilizzare un sistema simile a quello delle web applications classiche non solo è errato ma credo che sia anche dannoso per il design del codice.

Ionic 2 quindi utilizza un provider apposito per gestire la navigazione, chiamato NavController. Questo provider ha pochi metodi tra cui, per l’appunto, push e pop. Vediamolo subito in azione con un esempio, una piccola applicazione con un elenco di utenti e una view di dettaglio. Iniziamo la nostra analisi dalla lista.

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

import { Users } from '../../providers/users/users.service';

import { DetailPage } from '../detail/detail';

@Component({
  templateUrl: 'build/pages/list/list.html',
  providers:[Users]
})
export class ListPage {
  userList: any;

  constructor(private navCtrl: NavController, private users: Users) {
    this.userList = users.list();
  }

  onItemClick(user) {
    this.navCtrl.push(DetailPage, {
      user
    });
  }
}

Come vedete quando viene invocato il metodo onItemClick uso il NavController per far apparire la view DetailPage, inoltre le passo come parametro l’utente di cui si vuole visualizzare il dettaglio. Passiamo ora al codice del dettaglio.

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

@Component({
  templateUrl: 'build/pages/detail/detail.html'
})
export class DetailPage {
  user: any;

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

  goBack(){
    this.navCtrl.pop();
  }
}

Come vedete qui invece utilizzo pop per tornare indietro. Altra cosa molto interessante da notare è che grazie al provider NavParams posso ricevere l’utente corrente che mi è stato inviato dalla view precedente. Rispetto ad un’applicazione gestita tramite URL è un grosso passo avanti in quanto posso ricevere dati complessi. Nel sistema di rotte di Ionic 1.X avrei dovuto ricavare l’id dell’utente dalla rotta e caricarlo da un service in qualche modo.

Il nuovo ionic serve
Il nuovo ionic serve

Ionic Native

Altra novità è l’accesso alle feature del dispositivo. In Ionic 1.X si consiglia l’utilizzo di ngCordova, wrapper AngularJS dei plugin di cordova più famosi. Il progetto è sempre mantenuto dal team di Ionic, ma nello stub base di Ionic questo progetto non viene inserito. Di conseguenza molti sviluppatori alle prime armi utilizzano direttamente Apache Cordova creando un codice poco leggibile, o per lo meno poco Angular-style. Anche nella nuova versione l’accesso al dispositivo è riservato ad un progetto separato chiamato Ionic Native, ma questa volta il pacchetto viene installato automaticamente all’avvio di un nuovo progetto. Ionic Native non è legato ne ad Ionic, ne ad Angular2. Ma può essere utilizzato in qualsiasi progetto Apache Cordova che supporti Ecmascript6. Vediamo subito un’utilizzo di alcuni dei plugin disponibili.

@Component({
  templateUrl: 'build/pages/home/home.html'
})
export class HomePage {

  private pos;
  private watchGeolocation;
  private watchThreeDeeTouch;
  private isThreeDeeAvailable;
  private threeDeeData;
  private imageUrl;
  private watchAcceleration;
  private acceleration;

  constructor(private platform: Platform) {
    this.threeDeeData = {};
    this.pos = {};
    this.imageUrl = "http://www.e-xtrategy.net/wp-content/themes/ex2011/images/logo-extrategy-new.png";

    platform.ready().then(() => {
      this.watchGeolocation = Geolocation.watchPosition().subscribe((pos) => {
        this.pos = pos;
      });

      ThreeDeeTouch.isAvailable().then(isAvailable => {
        this.isThreeDeeAvailable = isAvailable;
        if(isAvailable){
          this.watchThreeDeeTouch = ThreeDeeTouch.watchForceTouches().subscribe((data) => {
            this.threeDeeData = data;
          });
        }
      });

      this.watchAcceleration = DeviceMotion.watchAcceleration().subscribe((acceleration: AccelerationData) => {
        this.acceleration = acceleration;
      });
    });
  }

  openActionSheet() {
    ActionSheet.show({
      'title': 'What do you want to do?',
      'buttonLabels': ['Destroy the world','Save the world'],
      'addCancelButtonWithLabel': 'Cancel'
    }).then((buttonIndex) => {
      console.log(buttonIndex);
    });
  }

  startCamera() {
    const options = {
      quality: 50,
      sourceType: Camera.PictureSourceType.CAMERA,
      encodingType: Camera.EncodingType.JPEG
    };

    Camera.getPicture(options).then((imageData) => {
      this.imageUrl = imageData;
    });
  }

  ngOnDestroy(){
    this.watchGeolocation.unsubscribe();
    this.watchAcceleration.unsubscribe();
    if(this.watchThreeDeeTouch){
      this.watchThreeDeeTouch.unsubscribe();
    }
  }
}

Come potete vedere gli oggetti che vengono utilizzati a comando (come la fotocamera) utilizzano le Promise, mentre tutti i comandi che sono in ascolto del sistema (come il gps) utilizzando gli stream di RxJs.

Esempio di applicazione con Ionic Native
Esempio di applicazione con Ionic Native

Conclusioni

Per quanto nutra dei dubbi su Angular2 e ne giustifico la complessità solo su applicazioni medio/grandi, Ionic 2 mi ha entusiasmato. Il fatto di appoggiarsi ad un framework basato su componenti e il nuovo sistema di navigazione lo rendono concettualmente simile ad altri framework in circolazione, uno su tutti React Native. Il consiglio che do è quindi di utilizzare Ionic 2 nei nuovi progetti appena sarà disponibile la final. Unico accorgimento che terrei è quello di affiancargli un framework per lo state management come Redux o MobX. In questo modo Ionic 2, come è giusto che sia, sarà solo uno strumento per gestire il layer di view della vostra architettura e in futuro potrà essere facilmente sostituito con qualcos’altro. Come di consueto i più curiosi possono trovare il codice completo degli esempi su GitHub. Alla prossima!

28 Posts

Faccio il Front-end engineer per extrategy dove mi occupo di applicazioni Angular, React e applicazioni mobile. Da sempre sono appassionato di Architetture software e cerco di applicarle in maniera innovativa allo sviluppo frontend in generale.