On Github nicobytes / course-ionic2
{ name: 'Nicolas Molina Monroy', twitter: '@nicobytes', website: 'https://nicobytes.com', github: 'https://github.com/nicobytes' jobs: [ 'Hybrid App Developer', 'Front-End Developer' ], organizer: [ 'http://www.meetup.com/es/Hybrid-Apps-Colombia', 'http://www.meetup.com/es/Django-Bogota' ] }
(You'll feel right at home)
But a lot's changed since its release
Ionic was created 2 years ago....thats ages in web tech
Better/faster devices!
Fewer slow/bad devices!
Widely available web APIs!
Improved browser engines!
Ionic 1 was built with ES5
ES6 brings many new features
NG2 is pushing for ES6
Different look, but all JS Classes
ES6 + ES7 Decorators + Types
Ionic 2 and NG2 built using TS
Code completion in your editor
Why are we still coding for multiple platforms?
HTML5 that acts like native
Web wrapped in native layer
Direct access to native APIs
Familiar web dev environment
A single code base (web platform!)
http://techcrunch.com/2012/09/11/mark-zuckerberg-our-biggest-mistake-with-mobile-was-betting-too-much-on-html5/
class Person { name; age; constructor (name, age) { this.name = name; this.age = age; } getName(){ return this.name; } }
export function multiply (x, y) { return x * y } export var url = 'http://api.domain.com'; import { multiply, url } from "./math" console.log(multiply(2, 2))
doLogin().then((rta) => { console.log(rta); });
doLogin() .then((rta) => { console.log(rta); }) .then((rta) => { console.log(rta); }) .then((rta) => { console.log(rta); }) .catch((error) => { console.log(error); })
for (let i = 0; i < a.length; i++) { let x = a[i]; }
(function() { 'use strict'; //Code })();
doLogin((response) => { console.log(response); }); // doLogin(function(response){ console.log(response); });
class Person { private name: string; private age: number; private single: boolean; constructor (name, age) { this.name = name; this.age = age; this.sigle = false; } getName():string{ return this.name; } }
npm install -g ionic@beta cordova ionic info ionic start appBlank blank --v2 --ts ionic start appSide sidemenu --v2 --ts ionic start appTabs tabs --v2 --ts
ionic platform add android ionic platform add ios
ionic serve --lab
<input [value]="twitter"> <button (click)="doChange()"> <p> Hola {{twitter}} </p> <input [value]="twitter" (input)="twitter = $event.target.value"> <input [(ngModel)]="twitter">
<p #myVar></p> <button (click)="myVar.innerHTML = 'test.'">
ionic start myAwesomeApp --v2 --ts cd myAwesomeApp ionic info ionic serve
ionic build android ionic build ios ionic build android --release ionic build ios --release
ionic emulate android ionic emulate ios
ionic platform add android ionic platform add ios ionic platform add wp
ionic run android ionic run ios
ionic state save ionic state restore
ionic resources
ionic plugin add cordova-plugin-XXXXXX //Examples ionic plugin add cordova-plugin-camera ionic plugin add cordova-plugin-googlemaps ionic plugin add phonegap-plugin-push
npm install moment --save
npm install -g typings //Now npm install underscore --save typings install underscore --ambient --save
@Decorator({ //meta data }) export class MyClass { //Your class }
ionic g --list
ionic g page heroes
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; @Component({ templateUrl: 'build/pages/heroes/heroes.html', }) export class HeroesPage { constructor(private nav: NavController) { } }
ionic g component my-hero
import { Component, Input } from '@angular/core'; @Component({ selector: 'my-hero', templateUrl: 'build/components/my-hero/my-hero.html' }) export class MyHero { text: string; @Input() hero: any; constructor() { this.text = 'Hello World'; } }
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import {MyHero} from '../../components/my-hero/my-hero'; @Component({ templateUrl: 'build/pages/heroes/heroes.html', directives: [MyHero] }) export class HeroesPage {
<my-hero *ngFor="let hero of heroes" [hero]="hero"></my-hero>
import { Component, Input, ViewEncapsulation } from '@angular/core'; @Component({ selector: 'my-hero', templateUrl: 'build/components/my-hero/my-hero.html', encapsulation: ViewEncapsulation.Native })
ionic g directive my-highlight
import { Directive, ElementRef } from '@angular/core'; @Directive({ selector: '[my-highlight]' // Attribute selector }) export class MyHighlight { constructor(el: ElementRef) { el.nativeElement.style.backgroundColor = 'yellow'; } }
<h1 my-highlight>Hola</h1>
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import {MyHero} from '../../components/my-hero/my-hero'; import {MyHighlight} from '../../components/my-highlight/my-highlight'; @Component({ templateUrl: 'build/pages/heroes/heroes.html', directives: [MyHero, MyHighlight] }) export class HeroesPage {
ionic g pipe reserve
import { Injectable, Pipe } from '@angular/core'; @Pipe({ name: 'reserve' }) @Injectable() export class Reserve { transform(value: string, args: any[]) { return value.split('').reverse().join(''); } }
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import {MyHero} from '../../components/my-hero/my-hero'; import {MyHighlight} from '../../components/my-highlight/my-highlight'; import {Reserve} from '../../pipes/reserve'; @Component({ templateUrl: 'build/pages/heroes/heroes.html', directives: [MyHero, MyHighlight], pipes: [Reserve] }) export class HeroesPage {
<h1>{{ 'Hola' | reserve }}</h1>
ionic g provider heroes
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; @Injectable() export class Heroes { data: any; constructor(private http: Http) { this.data = null; } load() { //Code } }
import { Component } from '@angular/core'; import { NavController } from 'ionic-angular'; import {MyHero} from '../../components/my-hero/my-hero'; import {MyHighlight} from '../../components/my-highlight/my-highlight'; import {Reserve} from '../../pipes/reserve'; import {HeroesService} from '../../providers/heroes/heroes'; @Component({ templateUrl: 'build/pages/heroes/heroes.html', directives: [MyHero, MyHighlight], pipes: [Reserve], providers: [ HeroesService ] }) export class HeroesPage {
export class HeroesPage { heroes: any[]; constructor( private nav: NavController, private heroesService: HeroesService ) { this.heroesService.load(); this.heroes = [ { name: 'as' }, { name: 'as' } ]; } }
import {Component} from '@angular/core'; import {Platform, ionicBootstrap} from 'ionic-angular'; import {StatusBar} from 'ionic-native'; import {TabsPage} from './pages/tabs/tabs'; import {HeroesService} from './providers/heroes/heroes'; @Component({ template: '<ion-nav [root]="rootPage"></ion-nav>' }) export class MyApp { private rootPage: any; constructor( private platform: Platform, private heroesService: HeroesService ) { this.heroesService.load; } } ionicBootstrap(MyApp, [HeroesService]);
// 1. import { NavController } from 'ionic-angular'; // 2. constructor( private nav: NavController, private heroesService: HeroesService ) { // 3. this.nav.push( HeroPage );
// 1. import { NavController } from 'ionic-angular'; // 2. constructor( private nav: NavController, private heroesService: HeroesService ) { // 3. this.nav.pop();
this.nav.setRoot( page );
this.nav.push(somethingPage, { example1: data1, example2: data2 });
import {Component} from '@angular/core'; import {NavController, NavParams} from 'ionic-angular'; @Component({ templateUrl: 'build/pages/second/second.html' }) export class somethingPage { constructor(nav: NavController, navParams: NavParams){ this.navParams.get('example1'); } }
<ion-menu [content]="content"> <ion-content> <ion-list> <button menuClose ion-item (click)="openTabsPage()">Tabs</button> <button menuClose ion-item (click)="openHeroesPage()">Heroes</button> <button menuClose ion-item (click)="close()" >Cerrar </button > </ion-list> </ion-content> </ion-menu> <ion-nav id="nav" #content [root]="rootPage"></ion-nav>
export class MenuPage { private rootPage: any; constructor(private nav: NavController) { this.rootPage = TabsPage; } openHeroesPage(){ this.rootPage = HeroesPage; } close(){ this.nav.setRoot( LoginPage ); } }
<button menuToggle> <ion-icon name="menu"></ion-icon> </button>
$colors: ( primary: #387ef5, secondary: #32db64, danger: #f53d3d, light: #f4f4f4, dark: #222, favorite: #69BB7B );
$colors: ( // ... twitter: #55acee )
<button twitter>Twitter</button>
$colors: ( // ... twitter:( base: #55acee, contrast: #ffffff ) )
.home { p{ background: color($colors, twitter, base); } }
$my-padding: 10px;
.home { p{ background: color($colors, twitter, base); padding: $my-padding; } }
.md button { background: red; } .ios button { background: blue; } .wp button { background: green; }
<button [class.myclass]="true">Twitter</button> <button [attr.no-lines]="true">Twitter</button>
// App iOS Variables // -------------------------------------------------- // iOS only Sass variables can go here $button-ios-border-radius: 20px;
@import "../pages/home/home"; @import "../component/my-avatar/my-avatar";
@import "../pages/home/ios/home";
button[primary]{ background: black; }
<link href="build/font-awesome/css/font-awesome.min.css" rel="stylesheet" >
<form (ngSubmit)="saveData()"> <ion-input type="text" required name="username" [(ngModel)]="model.username"></ion-input> <ion-input type="text" required name="name" [(ngModel)]="model.name"></ion-input> <button primary block type="submit">Save</button> </form>
this.username = new Control( 'Default value', Validators.required /*Async*/ );
<input type="text" ngControl="username" [(ngModel)]="model.username" name="username" />
myForm: ControlGroup; // constructor( private formBuilder: FormBuilder ) {
this.username = new Control('', Validators.required); this.name = new Control('', Validators.required); this.myForm = this.formBuilder.group({ username: this.username, name: this.name, })
<form (ngSubmit)="saveData()" [ngFormModel]="myForm" novalidate> <ion-input type="text" name="username" [(ngModel)]="model.username" ngControl="username"></ion-input> <ion-input type="text" name="name" [(ngModel)]="model.name" ngControl="name"></ion-input> <button primary block type="submit">Save</button> </form>
<div *ngIf="username.dirty && !username.valid"> <p *ngIf="username.errors.required"> Este campo es requerido </p> </div>
Validators.compose([ Validators.required, Validators.minLength(4), Validators.maxLength(8), Validators.pattern('[a-zA-Z ]*') ])
this.username = new Control( '', Validators.compose([ Validators.required, Validators.minLength(4), Validators.maxLength(8), Validators.pattern('[a-zA-Z ]*') ]) );
this.myForm = this.formBuilder.group({ username: ['', Validators.compose([....])], name: ['', Validators.compose([....])], })
<div *ngIf="myForm.controls.username.dirty && !myForm.controls.username.valid"> <p *ngIf="myForm.controls.username.errors.required"> Este campo es requerido </p> </div>
<button primary block type="submit" [disabled]="!myForm.valid" >Save</button> //Or (ngSubmit)="myForm.valid && saveData()"
import {Control} from '@angular/common'; export class AgeValidator{ static isOld(control: Control){ let value = control.value; if(value > 18){ return { 'isOld': true } } return null; } }
import { AgeValidator } from '../../validators/age'; //Control age: ['', AgeValidator.isReal]
import { AgeValidator } from '../../validators/age'; //Control age: ['', AgeValidator.isReal]
return new Promise(resolve => { this.http.get('https://randomuser.me/api/?results=25') .map(res => res.json()) .subscribe(data => { this.data = data.results; resolve(this.data); }); });
getAllTodos() { return new Promise((resolve, reject) => { this.http.get(`${this.path}/todos?_expand=user`) .map(res => res.json()) .subscribe(data => { resolve(data); }, error =>{ reject(error); }) }); }
getTodo(id: number){ return new Promise((resolve, reject) => { this.http.get(`${this.path}/todos/${id}`) .map(res => res.json()) .subscribe(data => { resolve(data); }, error =>{ reject(error); }) }); }
createTodo(data: any){ return new Promise((resolve, reject) => { this.http.post(`${this.path}/todos`, data) .map(res => res.json()) .subscribe(data => { resolve(data); }, error =>{ reject(error); }) }); }
editTodo(data: any){ return new Promise((resolve, reject) => { this.http.post(`${this.path}/todos`, data) .map(res => res.json()) .subscribe(data => { resolve(data); }, error =>{ reject(error); }) }); }
deleteTodo(data: any){ return new Promise((resolve, reject) => { this.http.post(`${this.path}/todos`, data) .map(res => res.json()) .subscribe(data => { resolve(data); }, error =>{ reject(error); }) }); }
var headers = new Headers(); headers.append('Authorization', 'Basic ----------'); this.http.post('http://api.domain.com/users', data, { headers: headers })
"proxies": [ { "path": "/v1", "proxyUrl": "https://api.instagram.com/v1" } ]
import { Storage, LocalStorage } from 'ionic-angular'; @Injectable() export class TodosService { todos: Storage; constructor() { this.todos = new Storage(LocalStorage); } saveTodos( todos ){ this.todos.set('todos', JSON.stringify(todos)); } getAllTodos() { return this.todos.get('todos'); } }
import { Storage, SqlStorage } from 'ionic-angular'; @Injectable() export class TodosService { todos: Storage; constructor() { this.todos = new Storage( SqlStorage, {name: 'dbname'} ); }
createTable(){ let sql = "CREATE TABLE IF NOT EXISTS todos(id INTEGER PRIMARY KEY AUTOINCREMENT,title VARCHAR(32), done INTEGER)"; this.todos.query( sql ) .then(data => { console.log( data ); }) .catch(error => { console.log( error ); }) }
createTodo(data: any){ let sql = `INSERT INTO todos(title, done) values("${data.title}","${data.done}")`; this.todos.query( sql ) .then(data => { console.log( data ); }) .catch(error => { console.log( error ); }) }
getAllTodos():Promise<any>{ let sql = `SELECT * from todos`; return this.todos.query( sql ) .then(response => { let data = []; for (let index = 0; index < response.res.rows.length; index++) { data.push({ id: response.res.rows[index].id, title: response.res.rows[index].title, completed: response.res.rows[index].done == "true" ? true : false }); } return Promise.resolve( data ); }) .catch(error => { return Promise.reject(error) }) }
npm install angularfire2 --save npm install firebase --save
npm install typings -g typings install --save firebase
typings/index.d.ts
/// <reference path="../node_modules/angularfire2/firebase3.d.ts" />
import {FIREBASE_PROVIDERS, defaultFirebase} from 'angularfire2'; ionicBootstrap(MyApp, [ FIREBASE_PROVIDERS, defaultFirebase({ apiKey: "------------", authDomain: "------------", databaseURL: "------------", storageBucket: "------------", }), ]);
{ "rules": { ".read": true, ".write": true } }
import { Injectable } from '@angular/core'; import { FirebaseListObservable, AngularFire } from 'angularfire2'; @Injectable() export class TodosService { todos: FirebaseListObservable<any> constructor( private af: AngularFire ) { this.todos = this.af.database.list('/todos'); }
getAllTasks(){ return this.todos; }
getAllTodos(){ return this.todos; }
createTodo(todo: any){ return this.todos.push( todo ); }
updateTodo(task: any){ return this.todos.update( task.$key, { title: task.title, completed: task.completed }); }
updateTodo(task: any){ return this.todos.update( task.$key, { title: task.title, completed: task.completed }); }
removeTodo(task: any){ return this.todos.remove( task.$key ); }
android
ionic plugin add xxxx-xxx--xxxx --save
adb devices adb start-server adb kill-server
ionic run android ionic run ios
ionicBootstrap(MyApp, [ Providers ], { prodMode: true });
ionic resources
<?xml version='1.0' encoding='utf-8'?> <widget id="io.ionic.starter" version="0.0.1" xmlns="http://www.w3.org/ns/ widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <name>V2 Test</name> <description>An Ionic Framework and Cordova project.</description> <author email="hi@ionicframework" href="http://ionicframework.com/">Ionic Framework Team</author>
keytool -genkey -v -keystore my-release-key.keystore -alias upload -keyalg RSA -keysize 2048 -validity 10000
Ionic build android --release
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore platforms/android/build/outputs/apk/android-release-unsigned.apk upload
zipalign -v 4 platforms/android/build/outputs/apk/android-release-unsigned.apk upload.apk
openssl genrsa -out mykey.key 2048 openssl req -new -key mykey.key -out myCSR.certSigningRequest -subj "/emailAddress=you@yourdomain.com, CN=Your Name, C=AU"
Open Keychain Access Open .cer > Export .p12
openssl x509 -in ios_development.cer -inform DER -out app_pem_file.pem -outform PEM openssl pkcs12 -export -inkey mykey.key -in app_pem_file.pem -out app_p12.p12
ionic build ios