Web/Mobile/ Progressive

App Development - Part 2

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.

Apache Cordova

Architettura di Cordova

Cordova ci permette di creare applicazioni mobile tramite tecnologie web, effettuando un wrap di un browser, con all'interno la nostra web app.

Ci permette di accedere alle funzionalità native attraverso una serie di plugin.

Installazione

# Installare Node.js da nodejs.org
node -v # per vedere la versione di node
npm -v  # per vedere la versione di npm

# Installare git da git-scm.com
git --version # per vedere la versione di GIT

# Installare Cordova
npm install -g cordova

# Testare la versione
cordova -v

# Installare ios-deploy
npm install -g ios-deploy

# Installare ios-sim
npm install -g ios-sim

# Aggiungere una piattaforma
cordova platform add ios

# Aggiungere una piattaforma
cordova platform add android

# Vedere la lista delle piattaforme
cordova platform list

# Aggiungere un plugin
cordova plugin add cordova-plugin-camera

# Vedere la lista dei plugin
cordova plugin list

Configurazione


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<widget id="com.kaleidoscope.thefabgram" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">

	<name>TheFabGram</name>
	<description>Applicazione per il corso</description>
	<author email="filippo@kaleidoscope.it" href="http://www.kaleidoscope.it/">Kaleidoscope Srl</author>

	<content src="index.html"/>
	<access origin="*"/>
	<preference name="webviewbounce" value="false"/>
	<preference name="UIWebViewBounce" value="false"/>
	<preference name="DisallowOverscroll" value="true"/>
	<preference name="android-minSdkVersion" value="16"/>
	<preference name="BackupWebStorage" value="none"/>
	<preference name="SplashScreen" value="screen"/>
	<preference name="SplashScreenDelay" value="3000"/>

        <... />

</widget>

Ogni volta che creiamo un app con ionic, verrà creato un file xml, contenente la configurazione di base per l’app, la configurazione di base per ogni funzionalità nativa aggiunta tramite plugin e la configurazione delle risorse per ogni piattaforma utilizzata nell’app.

Configurazione - Features


	<feature name="StatusBar">
		<param name="ios-package" value="CDVStatusBar" onload="true"/>
	</feature>

Come potete vedere, ogni plugin avrà una sua configurazione generica all’interno di questo file, poi declinata per ogni piattaforma nella relativa cartella del plugin.

Plugin.xml


<plugin xmlns=“http://apache.org/cordova/ns/plugins/1.0" […] id=“org.apache.cordova.statusbar">

    <name>StatusBar</name>
    <description>Cordova StatusBar Plugin</description>

    <engines>
       <engine name="cordova" version=">=3.0.0" />
    </engines>

    <js-module src="www/statusbar.js" name="statusbar">
        <clobbers target="window.StatusBar" />
    </js-module>

    <platform name="ios">
        <config-file target="config.xml" parent="/*">
            <feature name="StatusBar">
                <param name="ios-package" value="CDVStatusBar" />
                <param name="onload" value="true" />
            </feature>
            <preference name="StatusBarOverlaysWebView" value="true" />
            <preference name="StatusBarStyle" value="lightcontent" />
        </config-file>
        <header-file src="src/ios/CDVStatusBar.h" />
        <source-file src="src/ios/CDVStatusBar.m" />
    </platform>

    <platform name="android">
        <source-file src="src/android/StatusBar.java" target-dir="src/org/apache/cordova/statusbar" />
        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="StatusBar">
                <param name="android-package" value="org.apache.cordova.statusbar.StatusBar" />
                <param name="onload" value="true" />
            </feature>
        </config-file>
    </platform>

</plugin>

Quella che vedete di seguito, invece, è la configurazione del plugin:

Configurazione - Risorse


	<platform name="ios">
		<icon src="resources/ios/icon/icon.png" width="57" height="57"/>
		<splash src="resources/ios/splash/Default-568h@2x~iphone.png" height="1136" width="640"/>
	</platform>

	<platform name="android">
		<icon src="resources/android/icon/drawable-ldpi-icon.png" density="ldpi"/>
		<splash src="resources/android/splash/drawable-land-ldpi-screen.png" density="land-ldpi"/>
	</platform>

Quella che vediamo di seguito ora, è la configurazione per le risorse, cioè quel set di immagini e icone usate in per l’app (splash, icone).

Ionic Framework

Installazione

# Installare Ionic
npm install -g ionic

# Creare una nuova app con uno specifico template (tabs | super | blank)
ionic start myApp [template] -a "Application Name" - i"it.kaleidoscope.myapp"

# Aggiungere una nuova piattaforma (ios | android)
ionic platform add [platform]

# Rimuovere una nuova piattaforma (ios | android)
ionic platform rm [platform]

# Aggiungere plugin
ionic plugin add [plugin_id]

# Rimuovere plugin
ionic plugin rm [plugin_id]

# Generatore di componenti per ionic ( page | component | directive | pipe | provider | tabs )
ionic generate [element] [name]

# ad esempio:
ionic generate page myPage

# preview dell'app
ionic serve

# preview con il supporto di Ionic Lab
ionic server --lab

# emulare su piattaforma (ios | android)
ionic emulate [platform]

# lanciare sul dispositivo
ionic run [platform]

# info sull'app
ionic info

Ionic Page

<ion-header>
    <ion-navbar>
	<ion-title>
	    About
	</ion-title>
    </ion-navbar>
</ion-header>

<ion-content padding>

    <p>Primo paragrafo app</p>

    <p>Secondo paragrafo app</p>

</ion-content>


page-about {

}

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

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

    constructor(public navCtrl: NavController) {

    }

}

Ogni pagina di Ionic viene fornita con un file .html, un file .scss e il relativo file .ts.

Ionic Components

<ion-card>
    <ion-card-header>
        Card Header
    </ion-card-header>

    <ion-card-content>
        <!-- Add card content here! -->
    </ion-card-content>
</ion-card>


<ion-button>Default</ion-button>


<ion-list>

    <ion-item>
        <ion-label>Username</ion-label>
        <ion-input type="text"></ion-input>
    </ion-item>

</ion-list>

Ionic fornisce tutta una serie di componenti per la UI dell'app. Ogni componente ha un suo scopo specifico.

Sass (Syntactically Awesome Style Sheets)

nav {
    ul {
        margin: 0;
        padding: 0;
        list-style: none;
        
        li {
            display: inline-block;
        }
    }

    a {
        display: block;
        color: $red;
    }
}

SASS è un preprocessore, un modo differente di scrivere CSS; che fornisce delle funzionalità non ancora disponibili in CSS, quali la possibilità di innestare il codice, usare le variabili e le mixins.

quando trasformato diventa così:

nav ul {
    margin: 0;
    padding: 0;
    list-style: none;
}
nav ul li {    
    display: inline-block;
}
nav a {
    display: block;
    color: #F00;
}

TypeScript

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

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

export class HomePage {

    constructor(public navCtrl: NavController) {

    }

}

Tutti i componenti di Ionic sono scritti utilizzando TypeScript, file avranno estensione .ts.

La nostra prima app

# Creare l'app
ionic start myApp blank

# entrare nella cartella
cd myApp

# aggiungere le piattaforme
ionic platform add android
ionic platform add ios

# lanciare l'app nel browser
ionic serve -c -l --lab

Creare la prima app

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
  <link rel="manifest" href="manifest.json">
  
  <!-- add to homescreen for ios -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">

  <!-- cordova.js required for cordova apps -->
  <script src="cordova.js"></script>

  <!-- un-comment this code to enable service worker
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js')
        .then(() => console.log('service worker installed'))
        .catch(err => console.error('Error', err));
    }
  </script>-->

  <link href="build/main.css" rel="stylesheet">
</head>
<body>

  <!-- Ionics root component and where the app will load -->
  <ion-app></ion-app>

  <!-- The polyfills js is generated during the build process -->
  <script src="build/polyfills.js"></script>

  <!-- The vendor js is generated during the build process
       It contains all of the dependencies in node_modules -->
  <script src="build/vendor.js"></script>

  <!-- The main bundle js is generated during the build process -->
  <script src="build/main.js"></script>

Il file index.html

La cartella app/

File Utilizzo
app.component.ts Componente base che la nostra app lancia
app.html L'HTML iniziale.
app.module.ts Il file dove dichiarare moduli, providers ed entry components.
app.scss Il file che definisce il CSS globale
main.ts Il file .ts usato durante lo sviluppo per caricare la nostra app.
import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';

@NgModule({
	declarations: [
		MyApp,
		HomePage
	],
	imports: [
		BrowserModule,
                // Definisce il primo componente da usare come componente di partenza,
                // Inoltre, è possibile passare un oggetto di configurazione 
                // per il testo del bottone indietro, ecc.
		IonicModule.forRoot(MyApp)
	],
	bootstrap: [IonicApp],

        // qui definiamo tutti i componenti usati nella nostra app; in questo modo il compilatore 
        // AoT (Ahead of Time), può usarlo e migliorare le performance di caricamento
	entryComponents: [
		MyApp,
		HomePage
	],
	providers: [
		StatusBar,
		SplashScreen,
		{ provide: ErrorHandler, useClass: IonicErrorHandler }
	]
})
export class AppModule {}

Il file app.module.ts

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

import { HomePage } from '../pages/home/home';
@Component({
	templateUrl: 'app.html'
})
export class MyApp {
	rootPage:any = HomePage;

	constructor(
            platform: Platform, 
            statusBar: StatusBar, 
            splashScreen: SplashScreen
        ) {
		platform.ready().then(() => {
			// Okay, so the platform is ready and our plugins are available.
			// Here you can do any higher level native things you might need.
			statusBar.styleDefault();
			splashScreen.hide();
		});
	}
}

Il file app.component.ts e app.html

<ion-nav [root]="rootPage"></ion-nav>

La cartella app/ e la pagina Home

Nella cartella app, troviamo ogni singola vista che creiamo nell'app.

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

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

	constructor(public navCtrl: NavController) {

	}

}
<ion-header>
	<ion-navbar>
		<ion-title>
			Ionic Blank
		</ion-title>
	</ion-navbar>
</ion-header>

<ion-content padding>
	The world is your oyster.
	<p>
		If you get lost, the <a href="http://ionicframework.com/docs/v2">docs</a> will be your guide.
	</p>
</ion-content>

La cartella www/, theme/, assets/

www

Il file index.html nella cartella www è la cartella che viene caricata all'interno della nostra app PhoneGap.

Ogni volta che modifichiamo i file .SCSS, il file main.css viene ricreato e ricaricato dentro la cartella www.

Lo stesso vale per i file .TS, che vengono compilati e vanno a ricreare il file main.js nella cartella www.

Il file vendor.js vengono compilati dalle dipendenze dei file nella cartella node_modules.

 

theme

La cartella theme contiene il file delle variabili del tema Ionic. Qui possiamo sovrascrivere tutte le variabili SCSS che definiscono il look&feel della nostra app.

 

assets

Qui trovano posto tutti gli assets (immagini, font, icone, ecc.) che poi verranno ottimizzate, compresse e caricate nell'app.

Avanti tutta!

ionic generate page TaskList --no-module
app.module.ts

import { TaskListPage } from '../pages/task-list/task-list'; // <<<=== importare la nuova pagina

@NgModule({
	declarations: [
            ...
	    TaskListPage // <<<=== aggiungere la pagina nelle declarations
	],
	[ ... ],
	entryComponents: [
            ...
	    TaskListPage // <<<=== e negli entryComponents
	]
})
app.component.ts

import { TaskListPage } from '../pages/task-list/task-list'; // <<<=== importare la nuova pagina


export class MyApp {
    rootPage:any = TaskListPage; // <<<=== impostare la pagina come rootPage

    [ ... ]
}

<ion-header>
     <ion-navbar>
          <ion-title>TaskList</ion-title>
          <ion-buttons end>
               <ion-button icon-left (click)="addItem()">
                    <ion-icon name="add"></ion-icon> Add Item
               </ion-button>
          </ion-buttons>
     </ion-navbar>
</ion-header>

Ionicons!

https://ionicons.com/

<ion-icon name="add"></ion-icon>

<ion-icon ios="logo-apple" md="logo-android"></ion-icon>

<ion-icon name="ios-map-outline"></ion-icon>

Ionicons è il sistema di icon fornito da Ionic.

Permette di specificare un'icona generica, un'icon per lo specifico sistema operativo su cui sta girando l'app oppure un'icona di un sistema operativo che vada bene per entrambi.

Visualizzare i tasks.

<ion-content>

    <ion-list>
        <ion-item *ngFor="let task of tasks">
            <ion-label>{{ task.title }}</ion-label>
        </ion-item>
    </ion-list>

</ion-content>
task-list.ts

export class TaskListPage {

	tasks: Array<any> = [];  // <<<=== aggiungere una variabile array per i tasks

	public constructor(
		public navCtrl: NavController, 
		public navParams: NavParams
	) {
		this.tasks = [
			{ title: 'Latte',      status: 'open' },
			{ title: 'Uova',       status: 'open' },
			{ title: 'Prosciutto', status: 'open' },
			{ title: 'Farina',     status: 'open' },
		];
	}

	public addItem() {
		let newTask: string = prompt('New task');
		if ( newTask !== '' ) {
			this.tasks.push( newTask, status: 'open' );
		}
	}
}

Slidings!

<ion-content>

	<ion-item-sliding *ngFor="let task of tasks">

        <ion-item-options side="start" (ionSwipe)="done(task)">
            <ion-item-option (click)="done(task)" expandable color="primary">Fatto</ion-item-option>
        </ion-item-options>

        <ion-item [ngClass]="{ taskDone: task.status == 'done' }">
				<ion-label>{{ task.title }}</ion-label>
        </ion-item>

        <ion-item-options side="end" (ionSwipe)="delete(task)">
            <ion-item-option (click)="delete(task)" expandable color="danger">Elimina</ion-item-option>
        </ion-item-options>

    </ion-item-sliding>

</ion-content>

Gestire lo stato dei tasks.

task-list.html
<ion-item [ngClass]="{ taskDone: task.status == 'done' }">
[...]
task-list.ts

[...]

done(task: any) {
    task.status = 'done';
}

delete(task: any) {
    task.status = 'removed';
    let index = this.tasks.indexOf(task);
    if ( index > -1 ) {
        this.tasks.splice(index, 1);
    }
}
// task-list.scss
.taskDone {
    font-weight: bold;
    text-decoration: line-through;
}

Ma lo slider resta aperto!

task-list.html
<ion-item-sliding *ngFor="let task of tasks" #item>
    [...]

    <ion-button icon-only (click)="done(item, task)" color="secondary">
        <ion-icon name="checkmark"></ion-icon>
    </ion-button>
    <ion-button icon-only (click)="delete(item, task)" color="danger">
        <ion-icon name="trash"></ion-icon>
    </ion-button>
task-list.ts

import { NavController, NavParams, ItemSliding } from '@ionic/angular';

[...]

public done(slidingItem: ItemSliding, task: any) {
	task.status = 'done';
	slidingItem.close();
}

public delete(slidingItem: ItemSliding, task: any) {
	task.status = 'removed';
	let index = this.tasks.indexOf(task);
	if ( index > -1 ) {
		this.tasks.splice(index, 1);
	}
	slidingItem.close();
}

Full-swipe gesture.

[...]
<ion-item-options side="right" (ionSwipe)="delete(item, task)">
[...]
    <ion-button icon-only (click)="delete(item, task)" expandable color="danger">
        <ion-icon name="trash"></ion-icon>
    </ion-button>

Il tema

Ionic ci fornisce  un tema semplificato con una serie di colori preimpostati.

<ion-navbar color="danger">
    [...]
</ion-navbar>

<ion-button icon-only (click)="done(item, ask)" color="secondary">
    <ion-icon name="checkmark"></ion-icon>
</ion-button>

Il typing

Ricordiamoci di sfruttare la vera potenza di typescript, cioè il suo sistema di tipizzazione delle variabili.

public tasks: Array<any> = [];

// diventa

public tasks: Array<{ title: string, status: string }> = [];

Firebase

Per salvare i dati su un database in cloud useremo un servizio, firebase: https://firebase.google.com/

Creiamo un account e poi un primo progetto.

 

Clicchiamo poi su "impostazioni" e su su "Le tue applicazioni" e copiamo la configurazione.

var config = {
    apiKey: "AIzaSyAYfsUNoK9iVaKz5Elucw7prvHgrnFWigU",
    authDomain: "ionic2do-5f1a1.firebaseapp.com",
    databaseURL: "https://ionic2do-5f1a1.firebaseio.com",
    projectId: "ionic2do-5f1a1",
    storageBucket: "ionic2do-5f1a1.appspot.com",
    messagingSenderId: "644350684336",
    appId: "1:644350684336:web:e55e8f4b277cffbdf7df75"
};

Clicchiamo poi su "Sviluppo > Database" e creiamo un database.

Come impostazioni di permesso, impostiamo il database come "in prova", senza necessità di impostare l'autenticazione.

Installiamo i moduli di NPM per firebase

npm install firebase --save

npm install @angular/fire --save

#npm install promise-polyfill --save

Modifichiamo il file app.module.ts .

// Import precedenti
import { environment } from '../environments/environment';
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule, FirestoreSettingsToken } from '@angular/fire/firestore';

@NgModule({
        [...]
	imports: [
            AngularFireModule.initializeApp(environment.firebase),
            AngularFirestoreModule
	],
	providers: [
             { provide: FirestoreSettingsToken, useValue: {} }
	]
})

Creiamo il primo modello e il primo servizio.

// Dentro src/models/task.ts
export interface Task {
	id: string;
	title: string;
	status: string;
}

import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument, DocumentReference } from '@angular/fire/firestore';
import { map, take } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { Task } from '../models/task';

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

	private tasks: Observable<Task[]>;
	private taskCollection: AngularFirestoreCollection<Task>;

	constructor(private afs: AngularFirestore) {
		this.taskCollection = this.afs.collection<Task>('tasks');
		this.tasks = this.taskCollection.snapshotChanges().pipe(
			map(actions => {
				return actions.map(a => {
					const data = a.payload.doc.data();
					const id = a.payload.doc.id;
					return { id, ...data };
				});
			})
		);
	}

	getTasks(): Observable<Task[]> {
		return this.tasks;
	}

	addTask(task: Task): Promise<DocumentReference> {
		return this.taskCollection.add(task);
	}

	updateTask(task: Task): Promise<void> {
		return this.taskCollection.doc(task.id).update({ title: task.title, status: task.status });
	}


	deleteTask(id: string): Promise<void> {
		return this.taskCollection.doc(id).delete();
	}

}

Modifichiamo la pagina dei tasks.

// Importiamo il modello e il servizio
import { Task } from '../../models/task';
import { TaskService } from '../../services/task.service';
import { Observable } from 'rxjs';
import { IonItemSliding } from '@ionic/angular';


export class TaskListPage {

    private tasks: Observable<Task[]>;

    public constructor(
        [...]
        private taskService:  TaskService
	) {
    }

    ngOnInit() {
        this.tasks = this.taskService.getTasks();
    }

    addItem() {
        let newTask: string = prompt('New task');
        if ( newTask !== '' ) {
            this.taskService.addTask( { title: newTask, status: 'open' } );
        }
    }

    changeStatus(slidingItem: IonItemSliding, task: any) {
        task.status = (task.status == 'done' ? 'open' : 'done');
        this.taskService
            .updateTask( task ).then(() => {
                console.log('Task aggiornato');
            }, err => {
                console.log('Problema nell\'aggiornamento del task');
            });
        slidingItem.close();
    }

    delete(slidingItem: IonItemSliding, task: any) {
        this.taskService.deleteTask( task.id ).then(() => {
            console.log('Task cancellato');
        }, err => {
            console.log('Problema nel cancellare il task');
        });
        slidingItem.close();
	}
}

E aggiungiamo la pipe async.

		<ion-item-sliding *ngFor="let task of tasks | async" #item>

			<ion-item-options side="start"
				(ionSwipe)="changeStatus(item, task)">
				<ion-item-option (click)="changeStatus(item, task)"
									expandable
									color="primary">
					<span *ngIf="task.status == 'open'">Fatto</span>
					<span *ngIf="task.status == 'done'">Sblocca</span>
				</ion-item-option>
			</ion-item-options>

			<ion-item [ngClass]="{ taskDone: task.status == 'done' }">
				<ion-label>{{ task.title }}</ion-label>
			</ion-item>

			<ion-item-options side="end"
				(ionSwipe)="delete(item, task)">
				<ion-item-option (click)="delete(item, task)"
									expandable
									color="danger">Elimina</ion-item-option>
			</ion-item-options>

		</ion-item-sliding>

Le pipes sono funzioni che permettono di trasformare i dati che ricevono.

Ne esistono di vario tipo, legate alla formattazione della valuta, date, ecc.

Ionic Native

E' arrivato il momento di iniziare ad utilizzare le funzionalità messe a disposizione dei dispositivi. Per farlo, usiamo il componente Ionic Native: https://ionicframework.com/docs/native/

 

Di default, Ionic Native è già installato all'interno della nostra app.

 

Iniziamo aggiungendo il componente Dialogs.

ionic cordova plugin add cordova-plugin-dialogs

npm install @ionic-native/dialogs

Modifichiamo poi l'app.module.ts per includere il nuovo componente nativo.

// Importare il componente
import { Dialogs } from '@ionic-native/dialogs/ngx';

// Aggiungiamo poi il componente nei providers
providers: [
    ...
    Dialogs
    ...
]

Usiamo il componente nativo!

// Modifichiamo la pagina task-list.ts
import { Dialogs } from '@ionic-native/dialogs/ngx';

export class TaskListPage {

	public constructor(
                [...],
		public dialogs: Dialogs
	) {
	}

	public addItem() {
		this.dialogs.prompt('Aggiungi un task', 'ionic2do', ['OK', 'Annulla'], '')
			.then(result => {
				if ( result.buttonIndex == 1 && result.input1 !== '' ) {
					this.taskService.addTask( { title: result.input1, status: 'open' } );
				}
			});
	}

        [...]

}

Per testarlo, ci vuole ovviamente un dispositivo fisico.

Possiamo usare l'app messa a disposizione da Ionic per testarla: Ionic DevApp con il comando ionic serve --devapp

Usiamo il componente sostitutivo di Ionic.

// Modifichiamo la pagina task-list.ts
import { AlertController } from '@ionic/angular';

export class TaskListPage {

	public constructor(
                [...],
                public alertCtrl: AlertController
	) {
	}

	addItem() {
		if ( window.cordova ) {
			// Native Dialog
		} else {
			this.addItemNoNative();
		}
	}

	async addItemNoNative() {

		const prompt = await this.alertCtrl.create({
			header: 'Ionic2Do',
			message: "Aggiungi un task",
			inputs: [
				{
					name: 'task',
					placeholder: 'Task'
				},
			],
			buttons: [{
				text: 'Annulla',
				handler: data => {
					console.log('Annulla cliccato');
				}
			}, {
				text: 'Add',
				handler: data => {
					this.taskService.addTask( { title: data.task, status: 'open' } );
				}
			}]
		});
		await prompt.present();
	}

}

Web/Mobile/Progressive Web App Development - Part 2

By Filippo Matteo Riggio

Web/Mobile/Progressive Web App Development - Part 2

  • 391