SlideShare ist ein Scribd-Unternehmen logo
1 von 125
Ionic
HYBRID APPS AUF STEROIDEN
Inhalte
Projektumgebung
- Android
- iOS
- Typescript
- Build
- Debugging
- Externe Bibliotheken nutzen
- Hilfreiche externe Bibliotheken
- Lodash
- MomentJS
- Font Awsome
- Chart.js
Vorgehen
- Bottom Up
- Top Down
Ionic Services
- Welche Dienste?
- Wie eingesetzt?
- Wie tragfähig?
Ionic
- Projektstruktur
- Wichtige Komponenten
- Client Server
Kommunikation
- Promises
- Lifecycle Hooks
- Navigation
- SideMenu
- Tabs
- Modals
- Formulare
- Toasts
- Storage
- Plugins / Native
- Translate
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?
Hybrid Apps
Wrapper
App
Native Apps
App
Web Apps
Browser
App
Hybrid Apps
Wrapper
App
Native Apps
App
Web Apps
Browser
App
Nutzung von Geräte spezifischen Features
Portabilität über Plattformgrenzen hinweg
Performance und Nutzungserlebnis
Aktualisierbarkeit
CSS / HTML
Java Script / Type Script
Hybrid Apps
Wrapper
App
Model View ViewModel
View ViewModel Model
DataBinding
ViewModel
DataBinding
ViewModel
DataBinding
ViewModel
ViewModel
ViewModel
Die CLI
npm --version Version con NPM abrufen
npm help {befehl} Ruft die Hilfe für den entsprechenden Befehl auf
npm install [-g] {Paketname} Installiert das Paket im aktuellen Projekt bzw. global
npm 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 Leeres Projekt für Ionic 3 erstellen.
ionic start {AppName} tabs –v1 Projekt mit Tab Template für Ionic 1 erstellen.
ionic start {AppName} sidemenu Projekt mit Sidemenu Template für Ionic 2 erstellen.
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 generate component
directive
page
pipe
provider
{name}
Erstellt die Struktur für den entsprechenden Bestandteil basierend auf dem
Namen. Aus einer Page Details wird bspw. DetailsPage.
ionic login Bei den Ionic Services anmelden.
ionic link Eine App mit den Ionic Services verbinden
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.
Ionic telemetry [on|off] Telemetriedaten ein uns ausschalten
Die wichtigsten Befehle für Ionic
(ionic) cordova platform add android/ios Android bzw. iOs als Plattform hinzufügen.
(ionic) cordova platform list Zeigt alle installierten Plattformen an.
(ionic) cordova platform remove {platform} Entfern die angegebene Plattform.
(ionic) cordova build android/ios Plattform bauen.
(ionic) cordova emulate android/ios Plattformversion im Emulator starten.
(ionic) cordova run android/ios Plattformversion auf Gerät starten.
Die wichtigsten Befehle für Cordova
Die App bauen
Infrastruktur aufsetzen
Ionic start myApp blank
cd {app name}
ionic serve --lab
<?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!
App.module.ts
@NgModule({
declarations: [
MyApp,
…
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
…
],
providers: […]
})
Hier müssen Pages und Components eingetragen werden
die in der App genutzt werden sollen.
Hier müssen alle Pages eingetragen werden.
Hier müssen alle Provider eingetragen werden die als Singleton
genutzt werden.
Debuggen mit Chrome
npm test
F12
ts Datei unter webpack
wählen
Break Point setzen
F5
Augury
Externe Bibliotheken laden
Beispiel lodash
npm install lodash --save
import {[Funktionen die verwendet werden]} from 'lodash';
Überall wo es verwendet wird
Die Suche
Test 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
Erstellt die Testinstanz und all ihre Abhängigkeiten
Der (Fake)Provider + Domain Object
import { 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 Provider
import { 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/?apikey=8c94451e&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 (1)
<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 (2)
<ion-content padding>
…
<ion-list>
<ion-item *ngFor="let movie of movies">
<ion-thumbnail item-left>
<div [ngSwitch]=" movie.Poster">
<span *ngSwitchCase="'N/A‘“ ></span>
<span *ngSwitchDefault>
<img *ngIf="movie.Poster !== 'N/A'" src="{{movie.Poster}}">
</span>
</div>
</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 (3)
<ion-content padding>
…
<ion-list>
<ion-item *ngFor="let movie of movies">
<ion-thumbnail item-left>
<ion-img src="{{movie.Poster}}"></ion-img>
</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 Detailansicht
Top 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/?apikey=8c94451e&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
export class MyClass{
constructor(
public navCtrl: NavController
){}
pushPage(){
this.navCtrl.push(DetailPage);
}
ionViewCanLeave(): boolean{
// here we can either return true or false
// depending on if we want to leave this view
if(isValid(randomValue)){
return true;
} else {
return false;
}
}
}
Die Navigation per Navigation Controller
Search Favorites About
Details
Tabs
Input
Navigation & Parameterübergabe programmatisch
View
…
<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
Navigationsparameter abrufen
import { 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 navigieren
Logik
…
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 Stack
import { 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 Page
ionic 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 Page
ionic 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.ts
App.ts
import { 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 Suche
View:
<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 Logik
import { 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 Input
Logik:
import {Validators, FormBuilder, FormGroup } from '@angular/for
...
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 & Plugins
Why?
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ügen
npm install –g cordova
ionic plugin add cordova-plugin-nativestorage --save
ionic g provider store
Nur falls Cordova noch nicht installiert ist.
Evtl. kommt es zu einem Fehler, dass das Plugin
Nicht in der package.json registriert wird.
"cordovaPlugins": [
"cordova-plugin-whitelist",
"cordova-plugin-console",
"cordova-plugin-statusbar",
"cordova-plugin-device",
"cordova-plugin-splashscreen",
"ionic-plugin-keyboard",
"cordova-plugin-nativestorage"
],
Provider erzeugen
ionic g provider favoriteStore
import { Injectable, OnInit, OnDestroy } from '@angular/core';
import 'rxjs/add/operator/map';
import { NativeStorage } from '@ionic-native/native-storage';
@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 bereitstellen
app.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 Nutzen
import { 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();
}
Das Side Menu
Search Favorites About
Details
Tabs
Input
<ion-menu [content]="content">
<ion-header>
<ion-toolbar>
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<button menuClose ion-item *ngFor="let p of pages" (click)="openPage(p)">{{p.title}}</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav #content [root]=“rootPage“></ion-nav>
@Component({
templateUrl: 'app.html'
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = Page1;
pages: Array<{title: string, component: any}>;
constructor(public platform: Platform) {
this.initializeApp();
// used for an example of ngFor and navigation
this.pages = [
{ title: 'Page One', component: Page1 },
{ title: 'Page Two', component: Page2 }
];
}
initializeApp() {
this.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();
});
}
openPage(page) {
// Reset the content nav to have just this page
// we wouldn't want the back button to show in this scenario
this.nav.setRoot(page.component);
}
}
Übersetzung/i18n
@NgModule({
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
HttpModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [Http]
}
})
],
…
export function createTranslateLoader(http: Http) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
Anpassungen an app.module.ts
sonst wird an der falschen Stelle gesucht
import { HttpModule, Http } from '@angular/http';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { FavoriteInputPage } from "../pages/favorite-input/favorite-input";
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
Anpassungen von app.component.ts
import { TranslateService } from "@ngx-translate/core";
@Component({
templateUrl: 'app.html'
})
export class MyApp {
rootPage:any = HomePage;
constructor(platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen,
translate: TranslateService) {
platform.ready().then(() => {
statusBar.styleDefault();
translate.setDefaultLang('en');
splashScreen.hide();
Anlegen eines Verzeichnisses i18n in assets das die
Übersetzungen enthält
DE:
{
"SEARCH": "Suche",
"BUTTONS": {
"SAVE" : "speichern"
}
}
EN:
{
"SEARCH": "Search",
"BUTTONS": {
"SAVE" : "save"
}
}
Anpassungen an den Seiten
Search.html
<ion-title>
{{ 'SEARCH' | translate }}
</ion-title>
<button outline ion-button>{{'BUTTONS.SAVE' | translate}}</button>FavoriteInput.html
translateService.use('en')Wo immer die Sprachumschaltung geschehen soll:
translateService.use('de')
Ionic Events
Publisher Subscriber
Subscriber
Subscriber
Publisher
Publisher
Publisher Event Aggregator Subscriber
import { Events } from 'ionic-angular‘;
class firstClass {
// first page (publish an event when a user is created)
constructor(public events: Events) {}
createUser(user) {
events.publish('user:created', user, Date.now());
}
}
class secondClass {
// second page (listen for the user created event after function is called)
constructor(public events: Events) {
events.subscribe('user:created', (user, time) => {
// user and time are the same arguments passed in `events.publish(user, time)`
console.log('Welcome', user, 'at', time);
});
}
}
Theming
Standard Farben verändern
Standardeinstellungen können über das Theme in
variables.scss geändert werden.
Neue Variablen anlegen
Neue Farben können einfach in Colors eingefügt werden.
Sie haben einen Base- (Hintergrund) und einen Contrast-
Wert (Text- oder Vordergrund).
<button color="monochrom">Black is beautifull</button>
let background : color($colors, monochrom, base)
Splashscreen & Icon
Splash Screen erstellen
Templates für Icon und Splashscreen befinden sich im
Ressource Verzeichnis. Diese müssen ersetzt werden, dann
können sie für die Platformen generiert werden die in der
config.xml angegeben sind.
ionic resources
ionic resources --icon
ionic resources --splash
Android
1. Java SDK installieren: http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html
2. Android SDK installieren: https://developer.android.com/studio/index.html
3. Umgebungsvariablen setzen:
%JAVA_HOME%=„C:Program FilesJavajdk7bin“
%ANDROID_HOME%=„c:androidandroid-sdk“
4. Build Tools zur PATH Variable hinzufügen:
%ANDROID_HOME/build-tools/VERSION/
Installation notwendiger SDKs
App Signing Doku: https://developer.android.com/studio/publish/app-signing.html
Signatur erstellen
$ keytool -genkey -v -keystore [name].keystore -alias [alias_name] -keyalg RSA -keysize 2048 -validity 10000
Anschließend Fragen beantworten und Passwort vergeben.
Alternative
Keystore Explorer: http://www.keystore-explorer.org/
App Signing Doku: https://developer.android.com/studio/publish/app-signing.html
App bauen und signieren
$ ionic platform add android
$ cordova build --release android
$ cd platformsandroidbuildoutputsapk
$ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore [name].keystore [AppName]-release-unsigned.apk [alias_name]
Bei der Erstellung des Keystores angegeben
In der config.xml angegeben.
$ zipalign -v 4 [AppName]-release-unsigned.apk AppName.apk
=> Upload zu play.google.com
Components
Komponenten sind Erweiterungen des bestehenden Html Markups um eigene Tags.
ionic g component LabledRadioButton
import { Component } from '@angular/core';
@Component({
selector: 'labled-radio-button',
template: '<input type="radio" [id]="id" [name]="name" [value]="value"> <label [for]="name">{{label}}</label>'
})
export class LabledRadioButton {
public id: string;
public name: string;
public value: string;
public label: string;
constructor() {
}
}
Verwendung im Html: <labled-radio-button id="discButton" name="discbutton" value="disc“ label="Disc"></labled-radio-button>
Beispiel typscript code:
Tagbezeichnung im Html
Directives
Quelle: https://angular.io/guide/attribute-directives
Verwendung im Html: <div test> Hallo Welt </div>
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[test]' // Attribute selector
})
export class TestDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow‘;
}
}
Sehr ungünstiger Name!
Besser ein Patter ala ion-test verwenden
Pipes
Quelle: https://angular.io/guide/pipes
<ion-title>{{ 'HELLO' | translate }}</ion-title>
<div *ngFor="let movie of (movies | filterMovies)"> {{movie.Title}} </div>
<span>Movie was published {{ movie.Date | date | uppercase}}</span>
Movie was published APR 1999
chained pipe
Quelle: https://angular.io/guide/pipes
import { Pipe, PipeTransform } from '@angular/core';
/*
* Raise the value exponentially
* Takes an exponent argument that defaults to 1.
* Usage:
* value | exponentialStrength:exponent
* Example:
* {{ 2 | exponentialStrength:10}}
* formats to: 1024
*/
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent: string): number {
let exp = parseFloat(exponent);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}
Quelle: https://angular.io/guide/pipes
Pure Pipes
- Werden nur aufgerufen wenn sich am direkten Wert etwas ändert.
- Stellen den Standard dar
Impure Pipes
- Werden immer aufgerufen wenn sich etwas im Bereich des DOMs ändert
- Müssen explizit konfiguriert werden
@Pipe({
name: 'name',
pure: false
})
Domain Layer mit Typescript
export class Movie {
public title : string;
public year : number;
public id: string;
public type: string;
public posterUrl: 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-...
},
Datenmodel vom Server Internes Datenmodell
Das interne Datenmodell unterscheidet sich in Details. Es ermöglicht die App unabhängiger vom Server zu gestalten,
damit Änderungen am Datenmodel des Servers leicht in der App nachgepflegt werden können.
Daten kopieren
Auch wenn alle Views angepasst werden müssen, wird nachfolgend nur der Provider gezeigt, da bei ihm wichtig ist wie die
Konvertierung geschieht. Dieser Ansatz verwendet die lodash Funktion forEach.
.
@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(this.map)
.subscribe(data => resolve(data));
});
}
map(data:any) : Movie {
let result = new Array<Movie>();
let jsonData = data.json().Search;
forEach(jsonData, raw => {
let movie = Movie.create(raw);
result.push(movie);
}
return result.
}
Objekte erzeugen
Erstellen der Klasse MovieConverter um Konvertierungsfunktionen zu bündeln.
import {Movie} from './Movie';
export class MovieConverter {
public static create(data:any) : Movie {
let movie = new Movie();
movie.title = data.Title;
movie.year = data.Year;
movie.type = data.Type;
movie.id = data.imdbID;
if(data.Poster != 'N/A') {
movie.posterUrl = data.Poster;
}
return movie;
}
}
ACHTUNG: Nicht vergessen in der UI die korrekten
Eigenschaften der Movie Klasse zu verwenden!
Chart.js
Chart JS nutzen
npm install ng2charts –save
Npm install chart.js --save
<canvas baseChart
[data]="doughnutChartData"
[labels]="doughnutChartLabels"
chartType="doughnut"
[legend]="legend">
</canvas>
public doughnutChartLabels:string[] = ['',''];
public doughnutChartData:number[] = [0, 0];
public legend = false;
Der Mist geht nicht!
Es ist nicht möglich zu einer bestimmten Seite zu wechseln
Die Seite muss in declarations und(!) entryComponents der app.module.ts
eingetragen werden.
Es ist nicht möglich eine Android Version zu bauen
Dieser Fehler kann sehr viele Gründe haben.
Häufig entsteht er weil Ressourcen Namen mit
Sonderzeichen besitzen bspw.:
„Ansicht Hauptstraße.jpg“
Dies muss in:
„Ansicht-Hauptstrasse.jpg“
geändert werden.
Eine genaue Fehlerbeschreibung erhält man in dem in das Verzeichnis platformsandroid wechselt und dort
gradlew.bat --info build clean
ausführt.

Weitere ähnliche Inhalte

Was ist angesagt?

Spiele entwickeln mit dem Adobe AIR SDK
Spiele entwickeln mit dem Adobe AIR SDKSpiele entwickeln mit dem Adobe AIR SDK
Spiele entwickeln mit dem Adobe AIR SDKIndieOutpost
 
iOS Apps mit Webtechnologien erstellen
iOS Apps mit Webtechnologien erstelleniOS Apps mit Webtechnologien erstellen
iOS Apps mit Webtechnologien erstellenMichael Kühnel
 
Mobile Anwendungen mit Apache Cordova
Mobile Anwendungen mit Apache CordovaMobile Anwendungen mit Apache Cordova
Mobile Anwendungen mit Apache CordovaYvette Teiken
 
Aber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-Apps
Aber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-AppsAber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-Apps
Aber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-AppsGregor Biswanger
 
Webentwicklung für das IPhone
Webentwicklung für das IPhoneWebentwicklung für das IPhone
Webentwicklung für das IPhonereinhardh
 
Hybrid App Development mit Intel XDK
Hybrid App Development mit Intel XDKHybrid App Development mit Intel XDK
Hybrid App Development mit Intel XDKHans Rudolf Tremp
 
Android Ice Cream Sandwich WJAX 2011
Android Ice Cream Sandwich WJAX 2011Android Ice Cream Sandwich WJAX 2011
Android Ice Cream Sandwich WJAX 2011Dominik Helleberg
 
Electron.NET: Cross-Platform Desktop Software mit ASP.NET Core
Electron.NET: Cross-Platform Desktop Software mit ASP.NET CoreElectron.NET: Cross-Platform Desktop Software mit ASP.NET Core
Electron.NET: Cross-Platform Desktop Software mit ASP.NET CoreGregor Biswanger
 
Mobile Webentwicklung mit HTML5
Mobile Webentwicklung mit HTML5Mobile Webentwicklung mit HTML5
Mobile Webentwicklung mit HTML5kkramhoeft
 
Rhomobile
RhomobileRhomobile
RhomobileJan Ow
 
Ruboto - Ruby on Android
Ruboto - Ruby on AndroidRuboto - Ruby on Android
Ruboto - Ruby on AndroidHeiko Seebach
 
Apple iOS - GUI Entwicklung
Apple iOS - GUI EntwicklungApple iOS - GUI Entwicklung
Apple iOS - GUI Entwicklungmesseb
 
Hochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/Fuse
Hochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/FuseHochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/Fuse
Hochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/FuseJens Becker
 
Jalimo Slides Linuxtag2007
Jalimo Slides Linuxtag2007Jalimo Slides Linuxtag2007
Jalimo Slides Linuxtag2007smancke
 

Was ist angesagt? (16)

Spiele entwickeln mit dem Adobe AIR SDK
Spiele entwickeln mit dem Adobe AIR SDKSpiele entwickeln mit dem Adobe AIR SDK
Spiele entwickeln mit dem Adobe AIR SDK
 
iOS Apps mit Webtechnologien erstellen
iOS Apps mit Webtechnologien erstelleniOS Apps mit Webtechnologien erstellen
iOS Apps mit Webtechnologien erstellen
 
Vaadinator erweitern
Vaadinator erweiternVaadinator erweitern
Vaadinator erweitern
 
Mobile Anwendungen mit Apache Cordova
Mobile Anwendungen mit Apache CordovaMobile Anwendungen mit Apache Cordova
Mobile Anwendungen mit Apache Cordova
 
Aber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-Apps
Aber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-AppsAber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-Apps
Aber schnell! Top HTML5 Performance Tipps für Hybrid- und Web-Apps
 
Webentwicklung für das IPhone
Webentwicklung für das IPhoneWebentwicklung für das IPhone
Webentwicklung für das IPhone
 
Hybrid App Development mit Intel XDK
Hybrid App Development mit Intel XDKHybrid App Development mit Intel XDK
Hybrid App Development mit Intel XDK
 
Android Ice Cream Sandwich WJAX 2011
Android Ice Cream Sandwich WJAX 2011Android Ice Cream Sandwich WJAX 2011
Android Ice Cream Sandwich WJAX 2011
 
Electron.NET: Cross-Platform Desktop Software mit ASP.NET Core
Electron.NET: Cross-Platform Desktop Software mit ASP.NET CoreElectron.NET: Cross-Platform Desktop Software mit ASP.NET Core
Electron.NET: Cross-Platform Desktop Software mit ASP.NET Core
 
Mobile Webentwicklung mit HTML5
Mobile Webentwicklung mit HTML5Mobile Webentwicklung mit HTML5
Mobile Webentwicklung mit HTML5
 
Rhomobile
RhomobileRhomobile
Rhomobile
 
Ruboto - Ruby on Android
Ruboto - Ruby on AndroidRuboto - Ruby on Android
Ruboto - Ruby on Android
 
Apple iOS - GUI Entwicklung
Apple iOS - GUI EntwicklungApple iOS - GUI Entwicklung
Apple iOS - GUI Entwicklung
 
Hochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/Fuse
Hochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/FuseHochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/Fuse
Hochschule Darmstadt: NZSE/ENA Gastvortrag Xamarin/Fuse
 
Jalimo Slides Linuxtag2007
Jalimo Slides Linuxtag2007Jalimo Slides Linuxtag2007
Jalimo Slides Linuxtag2007
 
Workshop Vue js
Workshop Vue jsWorkshop Vue js
Workshop Vue js
 

Ähnlich wie Ionic 3

Augmented Reality Workshop
Augmented Reality WorkshopAugmented Reality Workshop
Augmented Reality Workshopargency
 
German: Softwareprodukte aus einem Source Code mit Javascript
German: Softwareprodukte aus einem Source Code mit JavascriptGerman: Softwareprodukte aus einem Source Code mit Javascript
German: Softwareprodukte aus einem Source Code mit JavascriptRalf Schwoebel
 
Top 10 Internet Trends 2006
Top 10 Internet Trends 2006Top 10 Internet Trends 2006
Top 10 Internet Trends 2006Jürg Stuker
 
Das Android Open Source Project
Das Android Open Source ProjectDas Android Open Source Project
Das Android Open Source Projectinovex GmbH
 
Programmierung von Mobiltelefonen mit Python
Programmierung von Mobiltelefonen mit PythonProgrammierung von Mobiltelefonen mit Python
Programmierung von Mobiltelefonen mit PythonAndreas Schreiber
 
Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)
Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)
Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)greenrobot
 
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...Gregor Biswanger
 
Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2
Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2
Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2Manfred Steyer
 
Die Zukunft der Webstandards - Webinale 31.05.2010
Die Zukunft der Webstandards - Webinale 31.05.2010Die Zukunft der Webstandards - Webinale 31.05.2010
Die Zukunft der Webstandards - Webinale 31.05.2010Patrick Lauke
 
elemente websolutions - Zusammenfassung T3DD09
elemente websolutions - Zusammenfassung T3DD09elemente websolutions - Zusammenfassung T3DD09
elemente websolutions - Zusammenfassung T3DD09elemente websolutions
 
Niemals nach Mitternacht füttern - Grüne Roboter überall!
Niemals nach Mitternacht füttern - Grüne Roboter überall!Niemals nach Mitternacht füttern - Grüne Roboter überall!
Niemals nach Mitternacht füttern - Grüne Roboter überall!inovex GmbH
 
Java und Python - Das Beste aus beiden Welten nutzen
Java und Python - Das Beste aus beiden Welten nutzenJava und Python - Das Beste aus beiden Welten nutzen
Java und Python - Das Beste aus beiden Welten nutzenAndreas Schreiber
 
2. Technologie-Tag - Frontend Architektur
2. Technologie-Tag - Frontend Architektur2. Technologie-Tag - Frontend Architektur
2. Technologie-Tag - Frontend ArchitekturNico Steiner
 
Automatisierung von Windows-Anwendungen
Automatisierung von Windows-AnwendungenAutomatisierung von Windows-Anwendungen
Automatisierung von Windows-AnwendungenAndreas Schreiber
 
Android Apps mit Xamarin entwickeln
Android Apps mit Xamarin entwickelnAndroid Apps mit Xamarin entwickeln
Android Apps mit Xamarin entwickelnAndré Krämer
 
WTC 2019 – Flutter
WTC 2019 – FlutterWTC 2019 – Flutter
WTC 2019 – Flutterwebconia
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT CoreJens Siebert
 
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan ScheidtAutomatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan ScheidtOPITZ CONSULTING Deutschland
 
Microservices mit Rust
Microservices mit RustMicroservices mit Rust
Microservices mit RustJens Siebert
 

Ähnlich wie Ionic 3 (20)

Augmented Reality Workshop
Augmented Reality WorkshopAugmented Reality Workshop
Augmented Reality Workshop
 
German: Softwareprodukte aus einem Source Code mit Javascript
German: Softwareprodukte aus einem Source Code mit JavascriptGerman: Softwareprodukte aus einem Source Code mit Javascript
German: Softwareprodukte aus einem Source Code mit Javascript
 
Top 10 Internet Trends 2006
Top 10 Internet Trends 2006Top 10 Internet Trends 2006
Top 10 Internet Trends 2006
 
Das Android Open Source Project
Das Android Open Source ProjectDas Android Open Source Project
Das Android Open Source Project
 
Programmierung von Mobiltelefonen mit Python
Programmierung von Mobiltelefonen mit PythonProgrammierung von Mobiltelefonen mit Python
Programmierung von Mobiltelefonen mit Python
 
Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)
Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)
Android Entwicklung (App Entwickler Konferenz 2010 der Telekom)
 
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
Kuck mal, Node.js! Einstieg für .NET Entwickler mit Visual Studio Code und Ty...
 
Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2
Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2
Offlinefähige Browseranwendungen: Progressive Web-Apps mit Angular 2
 
Die Zukunft der Webstandards - Webinale 31.05.2010
Die Zukunft der Webstandards - Webinale 31.05.2010Die Zukunft der Webstandards - Webinale 31.05.2010
Die Zukunft der Webstandards - Webinale 31.05.2010
 
elemente websolutions - Zusammenfassung T3DD09
elemente websolutions - Zusammenfassung T3DD09elemente websolutions - Zusammenfassung T3DD09
elemente websolutions - Zusammenfassung T3DD09
 
Niemals nach Mitternacht füttern - Grüne Roboter überall!
Niemals nach Mitternacht füttern - Grüne Roboter überall!Niemals nach Mitternacht füttern - Grüne Roboter überall!
Niemals nach Mitternacht füttern - Grüne Roboter überall!
 
Java und Python - Das Beste aus beiden Welten nutzen
Java und Python - Das Beste aus beiden Welten nutzenJava und Python - Das Beste aus beiden Welten nutzen
Java und Python - Das Beste aus beiden Welten nutzen
 
2. Technologie-Tag - Frontend Architektur
2. Technologie-Tag - Frontend Architektur2. Technologie-Tag - Frontend Architektur
2. Technologie-Tag - Frontend Architektur
 
Automatisierung mit grunt
Automatisierung mit gruntAutomatisierung mit grunt
Automatisierung mit grunt
 
Automatisierung von Windows-Anwendungen
Automatisierung von Windows-AnwendungenAutomatisierung von Windows-Anwendungen
Automatisierung von Windows-Anwendungen
 
Android Apps mit Xamarin entwickeln
Android Apps mit Xamarin entwickelnAndroid Apps mit Xamarin entwickeln
Android Apps mit Xamarin entwickeln
 
WTC 2019 – Flutter
WTC 2019 – FlutterWTC 2019 – Flutter
WTC 2019 – Flutter
 
Windows 10 IoT Core
Windows 10 IoT CoreWindows 10 IoT Core
Windows 10 IoT Core
 
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan ScheidtAutomatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
 
Microservices mit Rust
Microservices mit RustMicroservices mit Rust
Microservices mit Rust
 

Mehr von Hendrik Lösch

Why (most) softwareprojects fail silently
Why (most) softwareprojects fail silentlyWhy (most) softwareprojects fail silently
Why (most) softwareprojects fail silentlyHendrik Lösch
 
We (don't) need a software architect!?!
We (don't) need a software architect!?!We (don't) need a software architect!?!
We (don't) need a software architect!?!Hendrik Lösch
 
Restrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen GroßapplikationRestrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen GroßapplikationHendrik Lösch
 
Vom Monolith zum Modulith
Vom Monolith zum ModulithVom Monolith zum Modulith
Vom Monolith zum ModulithHendrik Lösch
 
Der Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die ArchitekturbewertungDer Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die ArchitekturbewertungHendrik Lösch
 
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als SoftwarearchitektHendrik Lösch
 
Software ist was du draus machst!
Software ist was du draus machst!Software ist was du draus machst!
Software ist was du draus machst!Hendrik Lösch
 
Einstieg in das Vueniverse
Einstieg in das VueniverseEinstieg in das Vueniverse
Einstieg in das VueniverseHendrik Lösch
 
Survivalkit für Codehausmeister
Survivalkit für CodehausmeisterSurvivalkit für Codehausmeister
Survivalkit für CodehausmeisterHendrik Lösch
 
Confessions of a Codehausmeister
Confessions of a CodehausmeisterConfessions of a Codehausmeister
Confessions of a CodehausmeisterHendrik Lösch
 
WPF Dos n Don'ts - der WPF Rundumschlag
WPF Dos n Don'ts - der WPF RundumschlagWPF Dos n Don'ts - der WPF Rundumschlag
WPF Dos n Don'ts - der WPF RundumschlagHendrik Lösch
 
Clean mit visual studio
Clean mit visual studioClean mit visual studio
Clean mit visual studioHendrik Lösch
 
Advanced Refactoring Patterns
Advanced Refactoring PatternsAdvanced Refactoring Patterns
Advanced Refactoring PatternsHendrik Lösch
 
Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018Hendrik Lösch
 
Der Healthcheck für Softwareprojekte
Der Healthcheck für SoftwareprojekteDer Healthcheck für Softwareprojekte
Der Healthcheck für SoftwareprojekteHendrik Lösch
 

Mehr von Hendrik Lösch (20)

Why (most) softwareprojects fail silently
Why (most) softwareprojects fail silentlyWhy (most) softwareprojects fail silently
Why (most) softwareprojects fail silently
 
We (don't) need a software architect!?!
We (don't) need a software architect!?!We (don't) need a software architect!?!
We (don't) need a software architect!?!
 
Restrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen GroßapplikationRestrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen Großapplikation
 
Vom Monolith zum Modulith
Vom Monolith zum ModulithVom Monolith zum Modulith
Vom Monolith zum Modulith
 
Der Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die ArchitekturbewertungDer Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
 
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
 
Software ist was du draus machst!
Software ist was du draus machst!Software ist was du draus machst!
Software ist was du draus machst!
 
Modular mit .NET
Modular mit .NETModular mit .NET
Modular mit .NET
 
.NET zu .NET Core
.NET zu .NET Core.NET zu .NET Core
.NET zu .NET Core
 
Migrationsstrategien
MigrationsstrategienMigrationsstrategien
Migrationsstrategien
 
Einstieg in das Vueniverse
Einstieg in das VueniverseEinstieg in das Vueniverse
Einstieg in das Vueniverse
 
Survivalkit für Codehausmeister
Survivalkit für CodehausmeisterSurvivalkit für Codehausmeister
Survivalkit für Codehausmeister
 
Confessions of a Codehausmeister
Confessions of a CodehausmeisterConfessions of a Codehausmeister
Confessions of a Codehausmeister
 
Hey, wie geht es dir?
Hey, wie geht es dir?Hey, wie geht es dir?
Hey, wie geht es dir?
 
WPF Dos n Don'ts - der WPF Rundumschlag
WPF Dos n Don'ts - der WPF RundumschlagWPF Dos n Don'ts - der WPF Rundumschlag
WPF Dos n Don'ts - der WPF Rundumschlag
 
Clean mit visual studio
Clean mit visual studioClean mit visual studio
Clean mit visual studio
 
Advanced Refactoring Patterns
Advanced Refactoring PatternsAdvanced Refactoring Patterns
Advanced Refactoring Patterns
 
Codesmells
CodesmellsCodesmells
Codesmells
 
Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018
 
Der Healthcheck für Softwareprojekte
Der Healthcheck für SoftwareprojekteDer Healthcheck für Softwareprojekte
Der Healthcheck für Softwareprojekte
 

Ionic 3

  • 2.
  • 3. Inhalte Projektumgebung - Android - iOS - Typescript - Build - Debugging - Externe Bibliotheken nutzen - Hilfreiche externe Bibliotheken - Lodash - MomentJS - Font Awsome - Chart.js Vorgehen - Bottom Up - Top Down Ionic Services - Welche Dienste? - Wie eingesetzt? - Wie tragfähig? Ionic - Projektstruktur - Wichtige Komponenten - Client Server Kommunikation - Promises - Lifecycle Hooks - Navigation - SideMenu - Tabs - Modals - Formulare - Toasts - Storage - Plugins / Native - Translate
  • 6. Webstorm (€) Visual Studio Code Visual Studio (€) Atom Chrome
  • 8. 1. npm installieren: nodejs.org/en/download/ 2. npm install –g cordova 3. npm install –g ionic 4. ( npm install –g typings )
  • 11. Hybrid Apps Wrapper App Native Apps App Web Apps Browser App Nutzung von Geräte spezifischen Features Portabilität über Plattformgrenzen hinweg Performance und Nutzungserlebnis Aktualisierbarkeit
  • 12. CSS / HTML Java Script / Type Script Hybrid Apps Wrapper App
  • 13.
  • 14.
  • 15.
  • 16.
  • 25. npm --version Version con NPM abrufen npm help {befehl} Ruft die Hilfe für den entsprechenden Befehl auf npm install [-g] {Paketname} Installiert das Paket im aktuellen Projekt bzw. global npm 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
  • 26. ionic start {AppName} blank Leeres Projekt für Ionic 3 erstellen. ionic start {AppName} tabs –v1 Projekt mit Tab Template für Ionic 1 erstellen. ionic start {AppName} sidemenu Projekt mit Sidemenu Template für Ionic 2 erstellen. 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 generate component directive page pipe provider {name} Erstellt die Struktur für den entsprechenden Bestandteil basierend auf dem Namen. Aus einer Page Details wird bspw. DetailsPage. ionic login Bei den Ionic Services anmelden. ionic link Eine App mit den Ionic Services verbinden 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. Ionic telemetry [on|off] Telemetriedaten ein uns ausschalten Die wichtigsten Befehle für Ionic
  • 27. (ionic) cordova platform add android/ios Android bzw. iOs als Plattform hinzufügen. (ionic) cordova platform list Zeigt alle installierten Plattformen an. (ionic) cordova platform remove {platform} Entfern die angegebene Plattform. (ionic) cordova build android/ios Plattform bauen. (ionic) cordova emulate android/ios Plattformversion im Emulator starten. (ionic) cordova run android/ios Plattformversion auf Gerät starten. Die wichtigsten Befehle für Cordova
  • 31. cd {app name} ionic serve --lab
  • 32. <?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!
  • 33. App.module.ts @NgModule({ declarations: [ MyApp, … ], imports: [ IonicModule.forRoot(MyApp) ], bootstrap: [IonicApp], entryComponents: [ MyApp, … ], providers: […] }) Hier müssen Pages und Components eingetragen werden die in der App genutzt werden sollen. Hier müssen alle Pages eingetragen werden. Hier müssen alle Provider eingetragen werden die als Singleton genutzt werden.
  • 34. Debuggen mit Chrome npm test F12 ts Datei unter webpack wählen Break Point setzen F5
  • 37. Beispiel lodash npm install lodash --save import {[Funktionen die verwendet werden]} from 'lodash'; Überall wo es verwendet wird
  • 38. Die Suche Test First & Bottom Up Search Favorites About Details Tabs Input
  • 40. 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 Erstellt die Testinstanz und all ihre Abhängigkeiten
  • 41. Der (Fake)Provider + Domain Object import { 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-... },
  • 43. Der echte Provider import { 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/?apikey=8c94451e&s=' + searchText; console.log('start searching:' + url); this.http.get(url) .map(result => result.json()) .subscribe(data => resolve(data.Search)); }); } }
  • 44. 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
  • 45. 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; }); }}
  • 46. 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
  • 47. Die Search Page – noch ohne Suche (3) ionic serve
  • 48. 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)); } }
  • 49. 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
  • 50. Die Search Page mit Thumbnails (1) <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>
  • 51. Die Search Page mit Thumbnails (2) <ion-content padding> … <ion-list> <ion-item *ngFor="let movie of movies"> <ion-thumbnail item-left> <div [ngSwitch]=" movie.Poster"> <span *ngSwitchCase="'N/A‘“ ></span> <span *ngSwitchDefault> <img *ngIf="movie.Poster !== 'N/A'" src="{{movie.Poster}}"> </span> </div> </ion-thumbnail> <h2>{{movie.Title}}</h2> <p>{{movie.Year}}</p> <p>{{movie.Type}}</p> </ion-item> </ion-list> </ion-content>
  • 52. Die Search Page mit Thumbnails (3) <ion-content padding> … <ion-list> <ion-item *ngFor="let movie of movies"> <ion-thumbnail item-left> <ion-img src="{{movie.Poster}}"></ion-img> </ion-thumbnail> <h2>{{movie.Title}}</h2> <p>{{movie.Year}}</p> <p>{{movie.Type}}</p> </ion-item> </ion-list> </ion-content>
  • 53. 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>
  • 54. Die Detailansicht Top Down entwickeln Search Favorites About Details Tabs Input
  • 55. 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(); }); } } …
  • 56. 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>
  • 57. Die Details Page Daten Provider public getMovie(imdbID: string) { return new Promise(resolve => { let url = 'http://www.omdbapi.com/?apikey=8c94451e&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 ); } }
  • 58. 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
  • 59. export class MyClass{ constructor( public navCtrl: NavController ){} pushPage(){ this.navCtrl.push(DetailPage); } ionViewCanLeave(): boolean{ // here we can either return true or false // depending on if we want to leave this view if(isValid(randomValue)){ return true; } else { return false; } } }
  • 60. Die Navigation per Navigation Controller Search Favorites About Details Tabs Input
  • 61. Navigation & Parameterübergabe programmatisch View … <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) { } …
  • 63. Navigationsparameter abrufen import { 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")); } …
  • 64. Zurück navigieren Logik … 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>
  • 65. Navigation ohne Stack import { 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); } }
  • 66. Favorites Page ionic 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>
  • 67. Die Tabs Search Favorites About Details Tabs Input
  • 68. About Page ionic 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>
  • 69. 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>
  • 70. Anpassung der App.ts App.ts import { 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);
  • 72.
  • 73. Anpassungen an der Suche View: <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(); } …
  • 74. 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>
  • 75. Die Logik import { 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
  • 76. Forms mit FormBuilder Search Favorites About Details Tabs Input ?
  • 77. 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 Input Logik: import {Validators, FormBuilder, FormGroup } from '@angular/for ... 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(); }
  • 78. Native Storage & Plugins Why? 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
  • 79. Plugin hinzufügen npm install –g cordova ionic plugin add cordova-plugin-nativestorage --save ionic g provider store Nur falls Cordova noch nicht installiert ist. Evtl. kommt es zu einem Fehler, dass das Plugin Nicht in der package.json registriert wird. "cordovaPlugins": [ "cordova-plugin-whitelist", "cordova-plugin-console", "cordova-plugin-statusbar", "cordova-plugin-device", "cordova-plugin-splashscreen", "ionic-plugin-keyboard", "cordova-plugin-nativestorage" ],
  • 80. Provider erzeugen ionic g provider favoriteStore import { Injectable, OnInit, OnDestroy } from '@angular/core'; import 'rxjs/add/operator/map'; import { NativeStorage } from '@ionic-native/native-storage'; @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; } }
  • 81. Provider bereitstellen app.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.
  • 82. Provider Nutzen import { 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);
  • 84. 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();
  • 86. Anpassungen an Favorite Input showScuessToast() { let toast = this.toastCtrl.create({ message: 'Favorite was added successfully', duration: 3000, position: 'top' }); toast.present(); }
  • 87. Das Side Menu Search Favorites About Details Tabs Input
  • 88. <ion-menu [content]="content"> <ion-header> <ion-toolbar> <ion-title>Menu</ion-title> </ion-toolbar> </ion-header> <ion-content> <ion-list> <button menuClose ion-item *ngFor="let p of pages" (click)="openPage(p)">{{p.title}}</button> </ion-list> </ion-content> </ion-menu> <ion-nav #content [root]=“rootPage“></ion-nav>
  • 89. @Component({ templateUrl: 'app.html' }) export class MyApp { @ViewChild(Nav) nav: Nav; rootPage: any = Page1; pages: Array<{title: string, component: any}>; constructor(public platform: Platform) { this.initializeApp(); // used for an example of ngFor and navigation this.pages = [ { title: 'Page One', component: Page1 }, { title: 'Page Two', component: Page2 } ]; } initializeApp() { this.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(); }); } openPage(page) { // Reset the content nav to have just this page // we wouldn't want the back button to show in this scenario this.nav.setRoot(page.component); } }
  • 91. @NgModule({ imports: [ BrowserModule, IonicModule.forRoot(MyApp), HttpModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: (createTranslateLoader), deps: [Http] } }) ], … export function createTranslateLoader(http: Http) { return new TranslateHttpLoader(http, './assets/i18n/', '.json'); } Anpassungen an app.module.ts sonst wird an der falschen Stelle gesucht import { HttpModule, Http } from '@angular/http'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { FavoriteInputPage } from "../pages/favorite-input/favorite-input"; import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
  • 92. Anpassungen von app.component.ts import { TranslateService } from "@ngx-translate/core"; @Component({ templateUrl: 'app.html' }) export class MyApp { rootPage:any = HomePage; constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, translate: TranslateService) { platform.ready().then(() => { statusBar.styleDefault(); translate.setDefaultLang('en'); splashScreen.hide();
  • 93. Anlegen eines Verzeichnisses i18n in assets das die Übersetzungen enthält DE: { "SEARCH": "Suche", "BUTTONS": { "SAVE" : "speichern" } } EN: { "SEARCH": "Search", "BUTTONS": { "SAVE" : "save" } }
  • 94. Anpassungen an den Seiten Search.html <ion-title> {{ 'SEARCH' | translate }} </ion-title> <button outline ion-button>{{'BUTTONS.SAVE' | translate}}</button>FavoriteInput.html translateService.use('en')Wo immer die Sprachumschaltung geschehen soll: translateService.use('de')
  • 98. import { Events } from 'ionic-angular‘; class firstClass { // first page (publish an event when a user is created) constructor(public events: Events) {} createUser(user) { events.publish('user:created', user, Date.now()); } } class secondClass { // second page (listen for the user created event after function is called) constructor(public events: Events) { events.subscribe('user:created', (user, time) => { // user and time are the same arguments passed in `events.publish(user, time)` console.log('Welcome', user, 'at', time); }); } }
  • 100. Standard Farben verändern Standardeinstellungen können über das Theme in variables.scss geändert werden.
  • 101. Neue Variablen anlegen Neue Farben können einfach in Colors eingefügt werden. Sie haben einen Base- (Hintergrund) und einen Contrast- Wert (Text- oder Vordergrund). <button color="monochrom">Black is beautifull</button> let background : color($colors, monochrom, base)
  • 103. Splash Screen erstellen Templates für Icon und Splashscreen befinden sich im Ressource Verzeichnis. Diese müssen ersetzt werden, dann können sie für die Platformen generiert werden die in der config.xml angegeben sind. ionic resources ionic resources --icon ionic resources --splash
  • 105. 1. Java SDK installieren: http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html 2. Android SDK installieren: https://developer.android.com/studio/index.html 3. Umgebungsvariablen setzen: %JAVA_HOME%=„C:Program FilesJavajdk7bin“ %ANDROID_HOME%=„c:androidandroid-sdk“ 4. Build Tools zur PATH Variable hinzufügen: %ANDROID_HOME/build-tools/VERSION/ Installation notwendiger SDKs
  • 106. App Signing Doku: https://developer.android.com/studio/publish/app-signing.html Signatur erstellen $ keytool -genkey -v -keystore [name].keystore -alias [alias_name] -keyalg RSA -keysize 2048 -validity 10000 Anschließend Fragen beantworten und Passwort vergeben. Alternative Keystore Explorer: http://www.keystore-explorer.org/
  • 107. App Signing Doku: https://developer.android.com/studio/publish/app-signing.html App bauen und signieren $ ionic platform add android $ cordova build --release android $ cd platformsandroidbuildoutputsapk $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore [name].keystore [AppName]-release-unsigned.apk [alias_name] Bei der Erstellung des Keystores angegeben In der config.xml angegeben. $ zipalign -v 4 [AppName]-release-unsigned.apk AppName.apk => Upload zu play.google.com
  • 109. Komponenten sind Erweiterungen des bestehenden Html Markups um eigene Tags. ionic g component LabledRadioButton import { Component } from '@angular/core'; @Component({ selector: 'labled-radio-button', template: '<input type="radio" [id]="id" [name]="name" [value]="value"> <label [for]="name">{{label}}</label>' }) export class LabledRadioButton { public id: string; public name: string; public value: string; public label: string; constructor() { } } Verwendung im Html: <labled-radio-button id="discButton" name="discbutton" value="disc“ label="Disc"></labled-radio-button> Beispiel typscript code: Tagbezeichnung im Html
  • 112. Verwendung im Html: <div test> Hallo Welt </div> import { Directive, ElementRef } from '@angular/core'; @Directive({ selector: '[test]' // Attribute selector }) export class TestDirective { constructor(el: ElementRef) { el.nativeElement.style.backgroundColor = 'yellow‘; } } Sehr ungünstiger Name! Besser ein Patter ala ion-test verwenden
  • 113. Pipes
  • 114. Quelle: https://angular.io/guide/pipes <ion-title>{{ 'HELLO' | translate }}</ion-title> <div *ngFor="let movie of (movies | filterMovies)"> {{movie.Title}} </div> <span>Movie was published {{ movie.Date | date | uppercase}}</span> Movie was published APR 1999 chained pipe
  • 115. Quelle: https://angular.io/guide/pipes import { Pipe, PipeTransform } from '@angular/core'; /* * Raise the value exponentially * Takes an exponent argument that defaults to 1. * Usage: * value | exponentialStrength:exponent * Example: * {{ 2 | exponentialStrength:10}} * formats to: 1024 */ @Pipe({name: 'exponentialStrength'}) export class ExponentialStrengthPipe implements PipeTransform { transform(value: number, exponent: string): number { let exp = parseFloat(exponent); return Math.pow(value, isNaN(exp) ? 1 : exp); } }
  • 116. Quelle: https://angular.io/guide/pipes Pure Pipes - Werden nur aufgerufen wenn sich am direkten Wert etwas ändert. - Stellen den Standard dar Impure Pipes - Werden immer aufgerufen wenn sich etwas im Bereich des DOMs ändert - Müssen explizit konfiguriert werden @Pipe({ name: 'name', pure: false })
  • 117. Domain Layer mit Typescript
  • 118. export class Movie { public title : string; public year : number; public id: string; public type: string; public posterUrl: 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-... }, Datenmodel vom Server Internes Datenmodell Das interne Datenmodell unterscheidet sich in Details. Es ermöglicht die App unabhängiger vom Server zu gestalten, damit Änderungen am Datenmodel des Servers leicht in der App nachgepflegt werden können.
  • 119. Daten kopieren Auch wenn alle Views angepasst werden müssen, wird nachfolgend nur der Provider gezeigt, da bei ihm wichtig ist wie die Konvertierung geschieht. Dieser Ansatz verwendet die lodash Funktion forEach. . @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(this.map) .subscribe(data => resolve(data)); }); } map(data:any) : Movie { let result = new Array<Movie>(); let jsonData = data.json().Search; forEach(jsonData, raw => { let movie = Movie.create(raw); result.push(movie); } return result. }
  • 120. Objekte erzeugen Erstellen der Klasse MovieConverter um Konvertierungsfunktionen zu bündeln. import {Movie} from './Movie'; export class MovieConverter { public static create(data:any) : Movie { let movie = new Movie(); movie.title = data.Title; movie.year = data.Year; movie.type = data.Type; movie.id = data.imdbID; if(data.Poster != 'N/A') { movie.posterUrl = data.Poster; } return movie; } } ACHTUNG: Nicht vergessen in der UI die korrekten Eigenschaften der Movie Klasse zu verwenden!
  • 122. Chart JS nutzen npm install ng2charts –save Npm install chart.js --save <canvas baseChart [data]="doughnutChartData" [labels]="doughnutChartLabels" chartType="doughnut" [legend]="legend"> </canvas> public doughnutChartLabels:string[] = ['','']; public doughnutChartData:number[] = [0, 0]; public legend = false;
  • 123. Der Mist geht nicht!
  • 124. Es ist nicht möglich zu einer bestimmten Seite zu wechseln Die Seite muss in declarations und(!) entryComponents der app.module.ts eingetragen werden.
  • 125. Es ist nicht möglich eine Android Version zu bauen Dieser Fehler kann sehr viele Gründe haben. Häufig entsteht er weil Ressourcen Namen mit Sonderzeichen besitzen bspw.: „Ansicht Hauptstraße.jpg“ Dies muss in: „Ansicht-Hauptstrasse.jpg“ geändert werden. Eine genaue Fehlerbeschreibung erhält man in dem in das Verzeichnis platformsandroid wechselt und dort gradlew.bat --info build clean ausführt.