Web/Mobile/ Progressive

App Development

Chi sono

Filippo Matteo Riggio

CTO @ Kaleidoscope Srl

Full Stack Developer

ECMAscript 6 & Typescript

Super set di JavaScript, contiene funzionalità già proprie di ES6, con la possibilità di aggiungere variabili tipizzate.

Angular 2+

Angular è un framework per lo sviluppo di web app, basato sulla logica a componenti.
Creato in collaborazione tra Google e Microsoft.

Apache Cordova & IonicFramework 3+

Ionic Framework è un framework per la creazione di interfacce mobile/desktop, basato su TS / ng2+. Con l'aiuto di Apache Cordova, andremo a creare un app mobile ibrida.

Progressive Web App?

Una PWA è un applicazione realizzata con moderni standard web, che fornisce una UX simile alle app.

 

Progressive perchè, se una funzionalià non è fornita dal dispositivo, viene fornita in modo progressivo dalla web app.

 

Le PWA implementano, a differenza delle app native, i concetti di discoverable e linkable.

Possono lavorare offline attraverso i service workers, essere aggiunte allo schermo dello smartphone, ricevere notifiche push,ecc.

ECMAscript 6 & TypeScript

Variabili in ES6

for ( var i = 0; i < 10; i++ ) {
    console.log(i); // 0,1,2...9
}
console.log(i); // 10

for ( let i = 0; i < 10; i++ ) {
    console.log(i); // 0,1,2...9
}
console.log(i); // Uncaught Reference Error: i is not defined

let

Con l'uso della parola chiave let, invece di var, possiamo contenere lo scope della variabile al solo contesto.

Classi in ES6

class Rocket {

    public landing(location) {
        // [...]
    }

}

class Falcon extends Rocket {

    public constructor() {
        super();

        this.manufacturer = 'SpaceX';
        this.stages = 2;
    }

    public landing(location) {
        console.log('On land');
    }

}

class Antares extends Rocket {
    public constructor() {
        super();

        this.manufacturer = 'OrbitalATK';
        this.stages = 2;
    }

    public landing(location) {
        console.log('In the ocean');
    }
}

Promises in ES6

var greetingPromise = sayHello();

greetingPromise.then(function(greeting) {

    console.log(greeting);

}, function(error) {

    console.error('Uh-oh, ', error); // ERRORE!

});

Serve per gestire le richieste asincrone, senza utilizzare le callbacks.

Una promise può trovarsi in 3 stati:

- pending: la promise non è ancora stata determinata, perchè la richiesta HTTP è ancora in stato pending.

- fulfilled: la richiesta HTTP è conclusa e la promise ha un valore

- rejected: la richiesta HTTP è fallita e la promise non verrà mai fulfilled.

Arrow Functions

// ES 5
var multiply = function(x, y) {
    return x * y;
};

// ES 6
var multiply = (x, y) => { return x * y };


// Un caso con gli array
var missions = [
    { name: 'Mercury', flights: 6  },
    { name: 'Gemini',  flights: 3  },
    { name: 'Apollo',  flights: 11 },
    { name: 'ASTP',    flights: 17  },
];

// ES 5
console.log(
    missions.map( function(mission) {
        return mission.flights;
    });
); // [ 6, 3, 11, 17]

// ES 6
console.log(
    missions.map(
        mission => mission.flights   
    )
); // [ 6, 3, 11, 17]

Una nuova modalità per scrivere funzioni, più concise.

Tipi

let num: number;
let str: string;
let bool: boolean;

num = 123;
num = 123.456;
num = '123'; // Errore

str = '123';
str = 123; // Errore

bool = true;
bool = false;
bool = 'false'; // Errore

Con TypeScript, possiamo specificare la tipologia delle veriabili, così da effettuare il type-checking a compile time.

E' inoltre possibile specificare anche degli array con i tipi

let booleanArray: boolean[];
let stringArray: string[];

Tipi speciali e funzioni tipizzate

let someVar: any;
someVar = 123;
someVar = '123';

// null e undefined sono trattati come il tipo any
var str: string;
var num: number;
str = undefined;
num = null;

function sayHello(name: string) : string {
    return 'Hello ' + name;
}

:void

// Usiamo il tipo void per specificare che la funzione non deve ritornare nulla
function logMessage(message: string) : void {
    console.log(message);
}

Template Strings

let user = {
    first_name: 'Filippo',
    last_name: 'Riggio'
};

let template = `
    <div>
        <h1>I tuoi dati</h1>
        <p>${ user.first_name } - ${ user.last_name }</p>
    </div>
`;

Con ES6 è possibile dichiarare template HTML direttamente in JS.

Per farlo si usano i backticks se il codice HTML è su più righe, o i singoli apici se solo su una riga; mentre con il simbolo ${nome_variabile} possiamo interpolare valori al template.

Approfondimento: La sostituzione viene effettuate con l'algoritmo di distanza minima di Levenshtein:

https://it.wikipedia.org/wiki/Distanza_di_Levenshtein

Angular 2+

Installation

# Install Angular CLI
npm install -g @angular/cli

# Craft new application
ng new my-app

# Serve the app
cd my-app
ng serve --open

Components

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

@Component({
    selector: 'my-first-component',
    template: `<div>Hello, my name is {{name}}.
               <button (click)="sayMyName()">Log my name</button>
               </div>`,
});

export class MyComponent {

    public constructor() {

        this.name = 'Inigo Montoya';

    }

    public sayMyName = () {

        console.log('Hello. My name is ', this.name, '. You killed my father. Prepare to die.0);

    }

}

Nel passaggio da Angular 1 alla 2, la decisione è stata di migrare tutto verso una struttura a componenti.

Di seguito come si definisce un componente:

Components

<my-first-component></my-first-component>

Importiamo il modulo "Component" dalla libreria di Angular e usiamo il decoratore @Component.

Il selettore ci fornirà il nostro tag html personalizzato da inserire nell'html.

Dopo il decorator, esporteremo la classe che rappresenta il componente.

Approfondimento: il pattern decorator è uno dei tanti pattern di sviluppo.

https://it.wikipedia.org/wiki/Decorator

Inputs

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

@Component({
    selector: 'current-user',
    template: '<div>{{ user.name }}</div>',
});

export class UserProfile {

    @Input user;

    public constructor() {

    }

}

Importiamo il modulo "Input" dalla libreria di Angular e usiamo il decoratore @Input all'interno della nostra classe.

Con il decoratore @Input andiamo a definire quali parametri possono essere passati in ingresso al nostro componente.

<current-user [user]="currentUser"></current-user>

Data binding (templates)

// {}: per il RENDERING
<div>
Hello, my name is {{ name }}.
</div>


// []: per il BINDING delle proprietà
<card-header [themeColor]="currentColor"></card-header>


// (): per la GESTIONE EVENTI
<my-component (click)="onUserClick($event)"></my-component>


// [()]: per il DATA BINDING BI-DIREZIONALE
// La variabile this.userName sarà sempre aggiornata tra il componente e il template
<input [(ngModel)]="userName">


// *: per DIRETTIVE PROPRIETARIE DI ANGULAR
<my-component *ngFor="let item of items">
<my-component>

Per la fase di comunicazione tra il codice TS e il template HTML del componente, si usa il binding (collegamento), che può essere eseguito attraverso l'uso di parentesi speciali.

Eventi e Eventi Custom

<button (click)="clicked($event)">Click</button>

@Component(...)
export class MyComponent {
    public clicked(event) {
    }
}


// CUSTOM EVENTS
import { Component, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'user-profile',
    template: '<div>Hi, my name is </div>'
});

export class UserProfile {
    @Output userDataUpdated = new EventEmitter();

    public constructor() {
        // Update user...
        this.userDataUpdated.emit(this.user);
    }
}

// <user-profile (userDataUpdated)="userProfileUpdated($event)"></user-profile>

import { Component } from '@angular/core';
import { UserProfile } from './user-profile';

export class SettingdPage {
    constructor() {}
   
    public userProfileUpdated(user) {
        // .... DO STUFF
    }
}

Life Cycle Events

@Component(...)
export class MyComponent {

    public ngOnInit() {
        // Evento chiamato prima dell'inizializzazione del componente
    }

    public ngOnDestroy() {
        // Evento chiamato prima della distruzione del componente
    }

    // ALTRI EVENTI
    public ngDoCheck() { // Evento chiamato per effettuare una custom change detection }
    public ngOnChanges(changes) { ... }
    // ....

}

Angular mette a disposione svariati eventi per la gestione del ciclo di vita del componente.

Principalmente per gestire 3 aspetti: creation, rendering e distruzione del componente.

Bootstrap dell'applicazione Angular

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app-component';

@NgModule({
    imports:      [ BrowserModule ],
    declarations: [ AppComponent ],
    bootstrap:    [ AppComponent ]
})

export class AppModule { }

Rispetto alla prima versione di Angular, nella versione 2+ è stato cambiato il modo di dichiarare le dipendente.

Le dipendenze vengono dichiarate solamente nel @NgModule.

Il modulo verrà poi inizializzato attraverso il main.ts.

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule }  from './app-module';

const platform = platformBrowserDynamic();

platform.bootstrapModule(AppModule);

Pipes

<p>Il compleanno è {{ birthday | date }}</p>

<p>Totale dovuto {{ valore | currency }}</p>

<p>Minuscolo {{ 'test' | lowercase }}</p>

<p>Maiuscolo {{ 'test' | uppercase }}</p>

<p>Guadagno {{ valore | percent }}</p>

Le pipes (o filtri) sono delle funzioni utili per la trasformazione dei valori nei template.

​Ne esistono di svariati tipi, legati alla formattazione del testo, alla valuta, ecc.

@ViewChild

import { Component, ViewChild } from '@angular/core';
import { UserProfile } from '../user-profile';

@Component({
    template: '<user-profile (click)="update()"></user-profile>',
    directives: [ UserProfile ]
})

export class MasterPage {

    // Assegniamo il componente che vogliamo recuperare, ad una variabile pubblica con il tipo di variabile
    public @ViewChild(UserProfile) userProfile: UserProfile;

    public constructor() { }

    public update() {
        this.userProfile.sendData();
    }

}

Con il decoratore @ViewChild possiamo leggere e scrivere i componenti figli.

Per farlo, iniettiamo il componente figlio nel padre come ViewChild.

Web/Mobile/Progressive Web App Development - Part 1

By Filippo Matteo Riggio

Web/Mobile/Progressive Web App Development - Part 1

  • 414