Mobile App Development Primi passi con NativeScript e Angular 2

Filippo Matteo Riggio

Full Stack Developer

CTO @ Kaleidoscope Srl

@fmriggio

Scenario

1. Cordova-based app (ionic)

2. soluzioni middle (RN / NS)

3. Native

Altri strumenti disponibili

NativeScript

 

L'esempio userà le API prese da pokeapi.co.

Installare NativeScript

npm install -g nativescript
tns --version

Creare un nuovo progetto.

tns create pokeproject --ng --appid it.kaleidoscope.pokeproject
tns platform add ios
tns platform add android

Modulo Http

/**  ---- app.module.ts  ---- **/

// Import the library
import { NativeScriptHttpModule } from "nativescript-angular/http";

// Inject the module

@NgModule({
    [...]
    imports: [NativeScriptModule, NativeScriptHttpModule]
})


Logica dell'app

/**  ---- app.component.ts  ---- **/

// Imports
import { Component, OnInit } from "@angular/core";

@Component({
    selector: "my-app",
    templateUrl: "app.component.html"
})

export class AppComponent implements OnInit {

    public constructor() { ... }
    public ngOnInit() { ... }
    public showInformation(index: number) { ... }
    public showDialog(data: Array<string>) { ... }

}

Pokemon list!

/**  ---- app.component.ts  ---- **/

// Imports
import { Http } from "@angular/http";
import "rxjs/Rx";


export class AppComponent implements OnInit {

    public pokemon: Array<any>;

    public constructor(private http: Http) { ... }

    public ngOnInit() {
        this.http.get("https://pokeapi.co/api/v2/pokemon?limit=151")
            .map(result => result.json())
            .flatMap(result => result.results)
            .subscribe(result => {
                this.database.getDatabase().createDocument(result);
                this.pokemon.push(result);
            }, error => {
                console.error(error);
            });
    }

}

User Interface

/**  ---- app.component.html  ---- **/

<ActionBar title="PokeProject"></ActionBar>

<StackLayout>
</StackLayout>

I layout in NativeScript sono descritti utilizzando una serie di tag XML (https://docs.nativescript.org/ui/basics).

Il layout della nostra app è composto da un header e da un layout verticale.

L'header : <ActionBar/> e il vertical layout <StackLayout/>

User Interface - GridLayout

/**  ---- app.component.html  ---- **/

<GridLayout rows="auto" columns="auto, *, auto">

</GridLayout>

La nostra app presenta principalmente una lista.

Useremo il layout di tipo griglia, per mostrare la lista di pokemon .(https://docs.nativescript.org/cookbook/ui/layouts/grid-layout).

Il layout configurato come da codice, permette di avere rows con altezze diverse (che si adattano da sole) e colonne dove la prima e terza colonna hanno una larghezza fissa (auto) e la centrale ha una larghezza che si adatta in automatico allo spazio restante.

User Interface - ListView

/**  ---- app.component.html  ---- **/

<ListView [items]="pokemon">
    <template let-monster="item" let-index="index">
        <GridLayout/>
    </template>
</ListView>

Il tag template verrà ripetuto per ogni elemento dell'array pokemon e, durante il ciclo, viene assegnato un elemento dell'array all'oggetto monster.

/**  ---- app.component.html  ---- **/

<GridLayout rows="auto" columns="auto, *, auto" margin="15">
        <Label row="0" col="0" class="pokemon-number" text="{{ index + 1 }}."  marginRight="10"></Label>
        <Label row="0" col="1" class="pokemon-name" [text]="monster.name" ></Label>
        <Image row="0" col="2" class="pokemon-image" src="~/images/{{index + 1}}.png" ></Image>
</GridLayout>

A bit of style!

/**  ---- app.css ---- **/

.pokemon-number {
    font-weight: bold;
}

.pokemon-name {
    text-transform: capitalize;
}

.pokemon-image {
    animation-name: pokemon-image;
    animation-duration: 1s;
    animation-delay: 1s;
    opacity: 0;
}

@keyframes pokemon-image {
    from { opacity: 0; transform: rotate(0deg); }
    to { opacity: 1; transform: rotate(360deg); }
}

Aggiungere un event listener

/**  ---- app.component.html ---- **/

<GridLayout [...] (tap)="showInformation(index+1)">
[...]
</GridLayout>
/**  ---- app.component.ts ---- **/

[...]

public showInformation(index: number) {
    this.http.get("https://pokeapi.co/api/v2/pokemon/" + index)
        .map(result => result.json())
        .flatMap(result => result.types)
        .map(result => (<any> result).type.name)
        .toArray()
        .subscribe(result => {
            this.showDialog(result);
        }, error => {
            console.error(error);
        });
}

[...]

Native dialogs

/**  ---- app.component.ts ---- **/

// Import the library
// https://docs.nativescript.org/cookbook/ui/dialogs
import dialogs = require("ui/dialogs");

[...]

public showDialog(data: Array<string>) {
    dialogs.alert({
        title: "Information",
        message: "Questo pokemon è del tipo " + data.join(", "),
        okButtonText: "OK"
    });
}

[...]

Native plugins

tns plugin add nativescript-couchbase

NativeScript permette di aggiungere plugin nativi (come cordova) accessibili tramite API javascript. Qui una lista di possibili plugins: https://docs.nativescript.org/plugins/plugins.

Ogni plugin, inoltre, è provvisto dal file delle definizioni typescript, da aggiungere al file delle referenze.

/// <reference path="./node_modules/nativescript-couchbase/couchbase.d.ts" />

Preparare il provider del DB

/**  ---- database.ts ---- **/
// CREAZIONE DEL PROVIDER DEL DB
import { Couchbase } from 'nativescript-couchbase';

export class Database {

    private db: any;

    public constructor() {
        this.db = new Couchbase("db");
    }

    public getDatabase() {
        return this.db;
    }

}

/**  ---- app.component.ts ---- **/
// INIEZIONE NELLA APP
@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
    providers: [Database]
})
export class AppComponent implements OnInit {
[...]
public constructor(private http: Http, private database: Database) { [...] }
}

Cachare i dati dalle API

/**  ---- app.component.ts ---- **/

[...]
public ngOnInit() {
        let rows = this.database.getDatabase().executeQuery("pokemon");
        if (rows.length == 0) {
            this.http.get("https://pokeapi.co/api/v2/pokemon?limit=151")
                .map(result => result.json())
                .flatMap(result => result.results)
                .subscribe(result => {
                    this.database.getDatabase().createDocument(result);
                    this.pokemon.push(result);
                }, error => {
                    console.error(error);
                });
        } else {
            for (let i = 0; i < rows.length; i++) {
                this.pokemon.push(rows[i]);
            }
        }
    }
[...]

NoSQL MapReduce View

/**  ---- database.ts ---- **/

[...]
    public constructor() {
        this.db = new Couchbase("db");
        this.db.createView("pokemon", "1", (document, emitter) => {
            emitter.emit(document._id, document);
        });
    }
[...]

Dal sito di CouchBase:

A MapReduce View will emit a key-value pair. Logic can be placed around the emitter to make the views more specific.

Accesso alle API UI native

// NativeScript XML tag
<ActionBar title="Sign up"></ActionBar>

// Controller Obj-c (mappato in node_modules/tns-core-modules/ui/frame/frame.ios.js)
UINavigationController

// Componente UI Obj-c (mappato in node_modules/tns-core-modules/ui/action-bar/action-bar.ios.js)
UINavigationBar

// Modificare lo stile della ActionBar per iOS
if (page.ios) {
    var navigationBar = frameModule.topmost().ios.controller.navigationBar;
    navigationBar.barStyle = UIBarStyle.UIBarStyleBlack;
}

References

Documentazione di NativeScript

http://docs.nativescript.org/

 

Lista dei plugins (certificati da Telerik)

http://plugins.nativescript.org/

 

Plugin CouchBase

https://github.com/couchbaselabs/nativescript-couchbase


Performances (web-view)

https://github.com/mlynch/pgday-eu-2017-perf/blob/master/web-perf-2017.pdf

Grazie

Bonus pack

Bonus pack

ƒactotum CMS

A Laravel-based CMS, from web artisans to web artisans.

If you are a client, go fuck yourself!

 

PS: we hold the most nerdy claim
"We love KISSing the DRY PIE."

Features

Laravel-based

Multilanguage

Blazing fast

NativeScript

By Filippo Matteo Riggio