Post on 16-Apr-2017
transcript
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
Webstorm (€) Visual Studio Code Visual Studio (€) Atom Chrome
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
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.