Ionic 2 - Hybridapps auf Steroiden

  • View
    240

  • Download
    1

  • Category

    Mobile

Preview:

Citation preview

Ionic 2HYBRID APPS AUF STEROIDEN

InhalteProjektumgebung- Build- Debugging- Testautomatisierung- Externe Bibliotheken nutzen

Vorgehen- Bottom Up- Test First- Top Down

Ionic Services- Welche Dienste?- Wie eingesetzt?- Wie tragfähig?

Ionic- Projektstruktur- Wichtige Komponenten- Client Server Kommunikation- Promises- Lifecycle Hooks- Navigation- Tabs- Modals- Formulare- Toasts- Locale Storage- Plugins

Szenario

Search Favorites About

Details

Tabs

Input

Installation und Vorbereitung

http://cmder.net/

1. npm installieren: nodejs.org/en/download/

2. npm install –g cordova

3. npm install –g ionic

4. npm install –g typings

Was ist Ionic?

?!?

Die CLI

Bild durch Klicken auf Symbol hinzufügen

npm --version Version con NPM abrufennpm help {befehl} Ruft die Hilfe für den entsprechenden Befehl aufnpm install [-g] {Paketname} Installiert das Paket im aktuellen Projekt bzw. globalnpm install {Paketname}@{Version} Installiert das Paket mit der entsprechenden Version in das aktuelle Projekt.

--save Das Paket wird in die Liste der Abhängigkeiten des Projekts eingetragen

--save-dev Das Paket wird in die Liste der Abhängigkeiten des Projekts eingetragen die zur Entwicklungszeit gelten.

--force Das Paket wird installiert selbst wenn es schon vorhanden ist.npm list Zeigt alle installierten Pakete im aktuellen Projekt an.npm view {Paketname} Ruft diverse Informationen zum angegebenen Paket ab.npm view {Paketname} {Detailbezeichnung} Ruft nur die Detailinformationen ab (bspw. Versions)

npm update Aktualisiert alle Pakete des aktuellen Projekts die als Abhängigkeiten gespeichert wurden.

npm update [-g] {Paketname}[@{Version}] Aktualisiert das gegebene Paket ggf. auf eine bestimmte Version oder auf die neuste.

npm uninstall {Paketname} Deinstalliert das angegebene Paket.--save Entfernt das Paket aus den Projektabhängigkeiten.--save-dev Entfernt das Paket aus den Projektabhängigkeiten für die Entwicklungszeit.

Die wichtigsten Befehle für NPM im Kontext von Ionic

ionic start {AppName} blank --v2 Leeres Projekt für Ionic 2 erstellen.

ionic start {AppName} tabs Projekt mit Tab Template für Ionic 1 erstellen.ionic start {AppName} sidemenu Projekt mit Sidemenu Template für Ionic 1 erstellen.ionic platform add android/ios Android bzw. iOs als Plattform hinzufügen.ionic platform list Zeigt alle installierten Plattformen an.Ionic platform remove {platform} Entfern die angegebene Plattform.

ionic build android/ios Plattform bauen.ionic emulate android/ios Plattformversion im Emulator starten.ionic run android/ios Plattformversion auf Gerät starten.ionic serve

--lab--consolelogs |-c--serverlogs |-s

App im Browser ausführen.App im Browser mit Ansichten für iOS und Android ausführen.Ausgabe des Consolen loggings in der CLI.Server Logs in der CLI ausgeben.

ionic login Bei den Ionic Services anmelden.ionic upload Die aktuelle App in die Ionic Cloud hochladen.ionic share Die aktuelle App über die Ionic Cloud mit anderen Personen teilen.ionic --help Auflistung und Erläuterung aller CLI Befehle.

cordova clean Entfernt alle

Die wichtigsten Befehle für Ionic und Cordova

Die App bauen

Infrastruktur aufsetzen

Ionic start myApp –v2 blank

cd {app name} ionic serve --lab

Jede Seite erhält ein Verzeichnis mit: - Darstellung .html- Styles .scss- Logik .ts

Applikationsweite Einstellungen können plattformspezifisch konfiguriert werden.

Das Bootstrapping wird an zentraler Stelle verwaltet und beim Programmstart eingeleitet.

Cordova plugins werden zentral gespeichert und direkt über Ionic verwendet.

Es gibt kein ngCordova!

Cordova Plugins u.ä. werden über Hooks in die Applikation eingebunden.

Dies geschieht weitestgehend automatisch.

Ionic speichert alle wichtigen Bibliotheken und Frameworks zunächst im node_modues Verzeichnis.

Zusätzliche Pakete sollten IMMER über NPM installiert werden damit der Buildprozess korrekt fuktioniert!

Splash screens und App Icons können für jedes plattform gesondert angegeben werden.

Sie werden über die CLI aus einer Vorlage heraus generiert.

Type Script 1.x benötigt Typeings um Javascript Frameworks ansprechen zu können.Diese sind separat mit jedem Framework herunter zu laden.

Die eigentliche App wird nach einem ionic build im www Verzeichnis zur Verfügung gestellt.

Im www Verzeichnis sollte nicht entwickelt werden!!!

Konfiguration aller wichtigen Umgebungsparameter der App (Version, App Package, API Version, …)

Konfiguration des Buildprozesses

Konfiguration der Ionic Parameter (welche Ionic Version, wird typscript verwendet, …)

NPM und Cordova Config (welche Pakete werden verwendet, Plattformen addressiert, Plugins verwendet)

Konfiguration des Typescript Transpilings (in welche JS Version, welche Dateien, wo liegen die Typings)

Konfiguration des Linters zur statischen Codeanalyse

Registrierung der eigentlichen Typings

<?xml version="1.0" encoding="UTF-8"?><widget xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" id="de.MovieManiaProductions.MoviewMania" version="1.0.0"> <name>movieMania</name> <description>An Ionic Framework and Cordova project.</description> <author email="hi@ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author> <content src="index.html" /> <access origin="*" /> <allow-intent href="http://*/*" /> <allow-intent href="https://*/*" /> <allow-intent href="tel:*" /> <allow-intent href="sms:*" /> <allow-intent href="mailto:*" /> <allow-intent href="geo:*" /> <platform name="android"> <allow-intent href="market:*" /> </platform> <platform name="ios"> <allow-intent href="itms:*" /> <allow-intent href="itms-apps:*" /> </platform> <preference name="webviewbounce" value="false" /> <preference name="UIWebViewBounce" value="false" /> <preference name="DisallowOverscroll" value="true" /> … <feature name="StatusBar"> <param name="ios-package" onload="true" value="CDVStatusBar" /> </feature> <plugin name="cordova-plugin-device" spec="~1.1.3" /> <plugin name="cordova-plugin-console" spec="~1.0.4" /> …</widget>

Package ID darf sich zukünftig nicht mehr ändern!!!

Entspricht der Version im Store!

Testinfrastruktur hinzufügen

npm install karma --save-dev

npm install -g karma-cli

karma init karma.conf.js

1. Ersetze karma.conf.js durch: https://github.com/HerrLoesch/IonicWorkshop/blob/master/karma.conf.js

2. Füge folgende Abhänigkeiten in der package.json hinzu: "jasmine-core": "^2.4.1",

"karma": "^0.13.22",

"karma-chrome-launcher": "^1.0.1",

"karma-jasmine": "^1.0.2",

"run-sequence": "1.1.5",

"karma-browserify": "5.0.5",

"browserify": "13.0.0",

"browserify-istanbul": "2.0.0",

"tsify": "0.16.0",

"isparta": "4.0.0"

"scripts": {

"test": "karma start karma.conf.js --browsers=Chrome --single-run=false --debug"

},

3. Ergänze folgendes Script in package.json:

Komponente erzeugen mit der wir die Daten auslesen:

ionic g provider Movies

import {beforeEachProviders, it, describe, expect, inject} from '@angular/core/testing';import {Movies} from './movies'; describe('movie services', () => { it('should return true', () => { expect(true).toBeTruthy(); }); });

1. Füge neue Datei hinzu movies.spec.ts

2. Schreibe folgenden Inhalt in die Datei:

3. npm test-> Test sollte erfolgreich sein

4. Ändere im test expect(true) zu expect(false)-> Test sollte fehlschlagen

npm install karma-htmlfile-reporter --save-dev

karma.conf.js erweitern:

module.exports = function(config) {  config.set({    reporters: ['progress', 'html'],     htmlReporter: {      outputFile: 'test/results.html',                  // Optional       pageTitle: ‚Moview Mania',      subPageTitle: 'Test results',      groupSuites: true,      useCompactStyle: true,      useLegacyStyle: true    }  });};

Html Report

Debuggen mit Chromenpm test

F12

ts Datei wählen

Break Point setzen

F5

Augury

Externe Bibliotheken laden

Beispiel lodash

npm install -g typings

npm install lodash --savetypings install lodash –save

import {[Funktionen die verwendet werden]} from 'lodash';

Überall wo es verwendet wird

Die SucheTest First & Bottom Up

Search Favorites About

Details

Tabs

Input

http://www.omdbapi.com/

import {beforeEachProviders, it, describe, expect, inject} from '@angular/core/testing';import {Movies} from './movies';import {includes, find} from 'lodash'; describe('movie search', () => { beforeEachProviders(() => [Movies]);

it('should return an array of movies for the search text lo', inject([Movies], (movieRepository) =>{ var movies = movieRepository.search("lo");

expect(Array.isArray(movies)).toBeTruthy; expect(movies.length).toBeGreaterThan(0); }));

[2. Test]});

Der 1. Test

Der (Fake)Provider + Domain Objectimport { Injectable } from '@angular/core';import 'rxjs/add/operator/map';

@Injectable()export class Movies {

constructor() {}

public search(searchText: string) { let data = new Array();

let movie = {}; movie.Title = "olo";

data.push(movie); return data;

}}

export class Movie { public Title : string; public Year : number; public imdbID: string; public Type: string; public Poster: string;

}

Beispiel: http://www.omdbapi.com/?s=lo

{ "Title":"The Hi-Lo Country", "Year":"1998", "imdbID":"tt0120699", "Type":"movie", "Poster":"https://images-na.ssl-images-...},

Quelle: http://www.joshmorony.com/an-in-depth-explanation-of-providers-in-ionic-2/

Was ist ein Provider

Der echte Providerimport { Injectable } from '@angular/core';import 'rxjs/add/operator/map';import { Http } from '@angular/http';

@Injectable()export class Movies {

constructor(private http: Http) {}

public search(searchText: string) {

return new Promise(resolve => { let url = 'http://www.omdbapi.com/?s=' + searchText; console.log('start searching:' + url);

this.http.get(url) .map(result => result.json()) .subscribe(data => resolve(data.Search));

});}

}

it('should return movies which titles contain the text lo', inject([Movies], (movieRepository) => {

var movies = movieRepository.search("lo"); var foundSearchPhrase = find(movies, function(movie) {

var missmatchFound = !includes(movie.Title, 'lo'); return missmatchFound; });

expect(movies.length).toBeGreaterThan(0); expect(foundSearchPhrase).toBeFalsy("Found at least one element which does not contain lo."); }));

Der 2. Test

ionic g page Search

Die Search Page – noch ohne Suche (1)View:

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

<ion-content padding> <ion-list > <ion-item *ngFor="let movie of movies"> {{movie.Title}} </ion-item> </ion-list></ion-content>

Logik:

import { Component } from '@angular/core';import { Movies } from '../../providers/movies/movies';

@Component({ templateUrl: 'build/pages/search/search.html', providers: [Movies]})

export class SearchPage { public movies: any;

constructor(private movieRepository: Movies) { this.movieRepository.search("lo").then(data => {

this.movies = data; }); }}

Die Search Page – noch ohne Suche (2)import { Component } from '@angular/core';import { ionicBootstrap, Platform } from 'ionic-angular';import { StatusBar } from 'ionic-native';import { SearchPage } from './pages/Search/Search';

@Component({ template: '<ion-nav [root]="rootPage"></ion-nav>‚})

export class MyApp { rootPage: any = SearchPage;

constructor(public platform: Platform) {platform.ready().then(() => {

StatusBar.styleDefault(); }); }}

ionicBootstrap(MyApp);

app.ts anpassen

Die Search Page – noch ohne Suche (3)ionic serve

Die Search Page – mit On-Input Suche<ion-header> …</ion-header>

<ion-content padding> <ion-searchbar name="searchBar"

[(ngModel)]="searchText" (ionInput)="onSearchInput($event)" >

<!-- [showCancelButton]="shouldShowCancel" (ionCancel)="onCancel($event)"> --> </ion-searchbar>

<ion-list > <ion-item *ngFor="let movie of movies"> {{movie.Title}} </ion-item> </ion-list>

</ion-content>

export class SearchPage { public movies: any; public searchText: string;

public onSearchInput() { this.search(this.searchText);

}

constructor(private movieRepository: Movies) { }

private search(text: string){ this.movieRepository.search(text)

.then(data => this.movies = data) .catch(error => console.log(error));

}}

Binding Typen<ion-header> …</ion-header>

<ion-content padding> <ion-searchbar name="searchBar"

[(ngModel)]="searchText" [showCancelButton]="shouldShowCancel"

(ionInput)="onSearchInput($event)" (ionCancel)="onCancel($event)"> </ion-searchbar>

<ion-list > <ion-item *ngFor="let movie of movies"> {{movie.Title}} </ion-item> </ion-list>

</ion-content>

One Way Binding

Interpolation

Event Binding

One Way Binding

Die Search Page mit Thumbnails<ion-content padding>…

<ion-list> <ion-item *ngFor="let movie of movies"> <ion-thumbnail item-left> <img *ngIf="movie.Poster !== 'N/A'" src="{{movie.Poster}}"> </ion-thumbnail>

<h2>{{movie.Title}}</h2> <p>{{movie.Year}}</p> <p>{{movie.Type}}</p>

</ion-item> </ion-list></ion-content>

Die Search Page mit Thumbnails und Sliding<ion-content padding>…

<ion-list> <ion-item-sliding *ngFor="let movie of movies"> <ion-item> … </ion-item>

<ion-item-options side="right"><button primary>

<ion-icon ios="ios-star" md="md-star"></ion-icon></button>

</ion-item-options > </ion-item-sliding> </ion-list></ion-content>

Die DetailansichtTop Down entwickeln

Search Favorites About

Details

Tabs

Input

ionic g page Details

Die Details Page Styling (1)Logik:import { Component } from '@angular/core';

@Component({ templateUrl: 'build/pages/details/details.html',})export class DetailsPage {

public movie: any;

constructor() { this.movie = { "Title": "Lo", "Year": "2009", "Rated": "N/A", "Released": "24 Oct 2009", "Runtime": "80 min",

… } }}

App.ts

export class MyApp { rootPage: any = DetailsPage;

constructor(public platform: Platform) { platform.ready().then(() => {

StatusBar.styleDefault(); }); }}

Die Details Page Styling (2)<ion-content padding> <ion-card> <h2>{{movie.Title}}</h2> <p>{{movie.Year}}</p> <img *ngIf="movie.Poster !== 'N/A'" src="{{movie.Poster}}"> <ion-card-content> <p>{{movie.Plot}}</p> </ion-card-content> <ion-row> <ion-col> <ion-badge>

<ion-icon name="star-half">{{movie.imdbRating}}</ion-icon></ion-badge>

</ion-col> <ion-col> <ion-badge>

<ion-icon name="text"> {{movie.imdbVotes}}</ion-icon></ion-badge>

</ion-col> </ion-row> </ion-card></ion-content>

Die Details Page Daten

Provider

public getMovie(imdbID: string) {

return new Promise(resolve => { let url = 'http://www.omdbapi.com/?i=' + imdbID; console.log('start getting movie:' + url);

this.http.get(url) .map(result => result.json()) .subscribe(data => resolve(data)); }); }

Logik

import { Component } from '@angular/core';import { Movies } from '../../providers/movies/movies';

@Component({ templateUrl: 'build/pages/details/details.html', providers: [Movies]})export class DetailsPage {

public movie: any = {};

constructor(private movieRepository: Movies) { this.setMovie("tt1047490"); }

private setMovie(imdbId: string){ this.movieRepository.getMovie(imdbId) .then(data => this.movie = data ); }}

Die Details Page Daten mit OnInit import { Component, OnInit } from '@angular/core';import { Movies } from '../../providers/movies/movies';

@Component({ templateUrl: 'build/pages/details/details.html', providers: [Movies]})

export class DetailsPage implements OnInit {

public movie: any = {};

constructor(private movieRepository: Movies) { }

ngOnInit() { this.setMovie("tt1047490"); }

private setMovie(imdbId: string){ … }}

Quelle: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html

Die Navigation per Navigation Controller

Search Favorites About

Details

Tabs

Input

Navigation & Parameterübergabe programmatischView… <ion-list> <ion-item-sliding *ngFor="let movie of movies"> <button ion-item (click)="showDetails(movie.imdbID)"> <ion-thumbnail item-left> <img *ngIf="movie.Poster !== 'N/A'" src="{{movie.Poster}}"> </ion-thumbnail> <h2>{{movie.Title}}</h2> <p>{{movie.Year}}</p> <p>{{movie.Type}}</p> </button>

<ion-item-options side="right"> <button primary> <ion-icon ios="ios-star" md="md-star"></ion-icon> </button> </ion-item-options> </ion-item-sliding> </ion-list>…

Logik…import { NavController } from 'ionic-angular';import { DetailsPage } from '../details/details';

@Component({ templateUrl: 'build/pages/search/search.html', providers: [Movies]})export class SearchPage {

public showDetails(imdbID: string) { this.navCtrl.push(DetailsPage, {"id": imdbID); }

constructor(private movieRepository: Movies, public navCtrl: NavController) {

}

Ergebnis der Anpassung der Suche

Navigation & Parameterübergabe deklarativ

<button ion-item [navPush]="detailsPage" [navParams]="movie" > <ion-thumbnail item-left> <img *ngIf="movie.Poster !== 'N/A'" src="{{movie.Poster}}"> </ion-thumbnail> <h2>{{movie.Title}}</h2> <p>{{movie.Year}}</p> <p>{{movie.Type}}</p> </button>

Navigationsparameter abrufenimport { Component, OnInit } from '@angular/core';import { Movies } from '../../providers/movies/movies';import { NavParams } from 'ionic-angular';

@Component({ templateUrl: 'build/pages/details/details.html', providers: [Movies]})export class DetailsPage implements OnInit{

public movie: any = {};

constructor(private movieRepository: Movies, private params: NavParams) { }

ngOnInit() { this.setMovie(this.params.get("id")); }…

Zurück navigierenLogik…import { NavController } from 'ionic-angular';

@Component({ templateUrl: 'build/pages/search/search.html', providers: [Movies]})export class SearchPage {

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

constructor(private movieRepository: Movies, public navCtrl: NavController) {

}

<ion-navbar>…</ion-navbar>

Navigation ohne Stackimport { App, ViewChild } from '@angular/core';import { NavController } from 'ionic-angular';

@App({ template: '<ion-nav #myNav [root]="rootPage"></ion-nav>'})export class MyApp { @ViewChild('myNav') nav: NavController private rootPage = TabsPage;

// Wait for the components in MyApp's template to be initialized // In this case, we are waiting for the Nav with id="my-nav" ngAfterViewInit() { // Let's navigate from TabsPage to Page1 this.nav.push(Page1); }}

Favorites Pageionic g page Favorites

<ion-header>

<ion-navbar> <ion-title>Favorites</ion-title> </ion-navbar>

</ion-header>

<ion-content padding> Hier finden sich irgend wann einmal die beliebtesten Filme des Nutzers.</ion-content>

Die Tabs

Search Favorites About

Details

Tabs

Input

About Pageionic g page About

<ion-header>

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

</ion-header>

<ion-content padding> <p>Diese App wurde von Hendrik Lösch gebaut...</p> <p> Alle Quellen finden sich <a href="https://github.com/HerrLoesch/MoviewMania">hier</a></p>

</ion-content>

Die Tabs

App.html<ion-tabs selectedIndex="1"> <ion-tab tabTitle="Search" tabIcon="search" [root]="searchRoot"></ion-tab> <ion-tab tabTitle="Favorites" tabIcon="star" [root]="favoritesRoot" tabBadge="5" tabBadgeStyle="danger"></ion-tab> <ion-tab tabTitle="About" tabIcon="information" [root]="aboutRoot"></ion-tab></ion-tabs>

Anpassung der App.tsApp.tsimport { SearchPage } from './pages/Search/Search';import { FavoritesPage } from './pages/favorites/favorites';import { AboutPage } from './pages/about/about';

@Component({ templateUrl: 'build/app.html'})export class MyApp {

searchRoot = SearchPage; favoritesRoot = FavoritesPage; aboutRoot = AboutPage;

constructor(public platform: Platform) { platform.ready().then(() => { StatusBar.styleDefault(); }); }}

ionicBootstrap(MyApp);

Modals

Search Favorites About

Details

Tabs

Input

Anpassungen an der SucheView:

<ion-item-options side="right"> <button primary (click)="addFavorite(movie)"> <ion-icon ios="ios-star" md="md-star"></ion-icon> </button> </ion-item-options>

Logik:import { NavController, ModalController } from 'ionic-angular';import { FavoriteInput } from '../favorite-input/favorite-input';

@Component({ templateUrl: 'search.html', providers: [Movies]})export class SearchPage { constructor(... private modalCtrl: ModalController) { }

public addFavorite(movie: any){ console.log("start adding favorite:"); console.log(movie);

let modal = this.modalCtrl.create(FavoriteInput, movie); modal.present(); }…

Die Eingabemaske<ion-header> <ion-toolbar> <ion-title> Add Favorite </ion-title> <ion-buttons start> <button ion-button (click)="dismiss()"> <span color="primary" showWhen="ios">Cancel</span> <ion-icon name="md-close" showWhen="android,windows"></ion-icon> </button> </ion-buttons> </ion-toolbar></ion-header>

<ion-content padding> <h2>{{title}}</h2> <p>{{year}}</p>

<ion-item> <ion-textarea placeholder="comments" rows="5"></ion-textarea> </ion-item>

<button (click)="save()" ion-button full>Save</button></ion-content>

Die Logikimport { Component, OnInit } from '@angular/core';import { NavParams, ViewController } from 'ionic-angular';

@Component({ selector: 'page-favorite-input', templateUrl: 'favorite-input.html'})export class FavoriteInput implements OnInit {

title:string; year:string;

ngOnInit() { this.title = this.params.get("Title"); this.year = this.params.get("Year"); }

constructor(private params: NavParams, private viewController: ViewController) {}

public dismiss() { this.viewController.dismiss(); }

public save(){ }}

Geht nicht für komplexe Datentypen

Forms mit FormBuilder

Search Favorites About

Details

Tabs

Input

?

View:<form (ngSubmit)="save()" [formGroup]="formData"> <ion-item> <ion-textarea placeholder="comments"rows="5" formControlName="comments"></ion-textarea> </ion-item>

<button type="submit" [disabled]="!formData.valid" ion-button full>Save</button></form>

Anpassungen an Favorite InputLogik:import {Validators, FormBuilder, FormGroup } from '@angular/forms';...export class FavoriteInput implements OnInit {

formData: FormGroup;

constructor(... private formBuilder: FormBuilder) {

this.formData = this.formBuilder.group({comments: ['', Validators.minLength(5)]});

}

public save(){ let favorite = { "comments": this.formData.value.comments, "imdbID": this.params.get("imdbID") };

this.viewController.dismiss(); }

Native Storage & PluginsWhy?This plugin is created because of the non-persistent property of LocalStorage in the WebView of Android and iOS. In iOS stored data from LocalStorage can be removed by the OS, when running out of memory.

When to use the plugin•Simple: Uniform and convenient way of organizing, storing, and accessing the data•Fast: Less than 1 milisecond to save or retrieve an object (in general)•Persistence: Save data over multiple sessions, i.e. holds the data till the application is removed from the device•Small data: Store small amounts of persistent data (less than a few hundred kilobytes)

• It is possible to store more than a few megabytes, but that's not the intended usage of the plugin.

Quelle: https://github.com/TheCocoaProject/cordova-plugin-nativestorage

Plugin hinzufügennpm install –g cordova

ionic plugin add cordova-plugin-nativestorage

ionic g provider store

Nur falls Cordova noch nicht installiert ist.

Fügt es nur der config.xml aber nichtpackage.json hinzu!!!

"cordovaPlugins": [ "cordova-plugin-whitelist", "cordova-plugin-console", "cordova-plugin-statusbar", "cordova-plugin-device", "cordova-plugin-splashscreen", "ionic-plugin-keyboard", "cordova-plugin-nativestorage" ],

Provider erzeugenionic g provider favoriteStore

import { Injectable, OnInit, OnDestroy } from '@angular/core';import 'rxjs/add/operator/map';import { NativeStorage } from 'ionic-native';

@Injectable()export class FavoritesStore implements OnInit, OnDestroy {

constructor(private nativeStorage: NativeStorage) { }

private dataKey: "favorites";

private favorites: Array<any>;

public ngOnInit() { this.loadFavorites(); }

public ngOnDestroy(){ this.saveFavorites(); }

private loadFavorites() { NativeStorage.getItem(this.dataKey) .then( data => this.favorites = data, error => console.error(error) ); }

private saveFavorites() { NativeStorage.setItem(this.dataKey, {property: this.favorites}) .then( () => { console.log('Stored favorites'); console.log(this.favorites); }, error => console.error('Error storing item', error) ); }

public addFavorite(favorite: any) { this.favorites.push(favorite); }

public getFavorites(){ return this.favorites; }}

Provider bereitstellenapp.module.ts

import { FavoritesStore } from '../providers/favoritesStore';import { NativeStorage } from 'ionic-native';

@NgModule({ declarations: [… ], imports: [ IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [… ], providers: [FavoritesStore, NativeStorage]})

Dadurch steht die gleiche Instanz des Providers allen zur Verfügung.

Provider Nutzenimport { FavoritesStore } from '../../providers/favoritesStore';

@Component({ selector: 'page-favorite-input', templateUrl: 'favorite-input.html'})export class FavoriteInput implements OnInit {

constructor(…, private store: FavoritesStore) { }

public save(){ let favorite = { "comments": this.formData.value.comments, "imdbID": this.params.get("imdbID") };

this.store.addFavorite(favorite);

Alerts

Search Favorites About

Details

Tabs

Input

Anpassungen an Favorite Input let confirmation = this.alertController.create({ title: 'Add Favorite?', message: 'Do you want to add ' + this.title + ' as favorite?', buttons: [ { text: 'Disagree', handler: () => { } }, { text: 'Agree', handler: () => { this.addFavorite(); this.viewController.dismiss(); } } ] }); confirmation.present();

Toasts

Search Favorites About

Details

Tabs

Input

Anpassungen an Favorite Input

showScuessToast() { let toast = this.toastCtrl.create({ message: 'Favorite was added successfully', duration: 3000, position: 'top' });

toast.present(); }

Die Schei#e geht net!

Probleme beim Erstellen eines neuen Projekts weil npm kein install ausführen kann.

Lösung: Aller neuste Node Version installieren und den Rechner neu starten.

Man kann selbst in neuen Projekten keine Ionicons sehen(betrifft vor allem RC0)

^ Symbol entfernen oder auf Version 2.0.0 wechseln

Danach noch einmal npm install ausführen.

Es ist nicht möglich zu einer bestimmten Seite zu wechseln

Die Seite muss in declarations und(!) entryComponents der app.module.ts eingetragen werden.

Recommended