{
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
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
The course
“I want to build an app!”
Why are we still coding for multiple platforms?
“Is there an alternative?”
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!)
“Oh No! The Zuck has spoken!”
http://techcrunch.com/2012/09/11/mark-zuckerberg-our-biggest-mistake-with-mobile-was-betting-too-much-on-html5/
“Hybrid apps are slow!”
“The Times They Are a-Changin'”
“It's not 2007 anymore”
Year | Device | Processor | RAM |
---|---|---|---|
2007 | iPhone | 400 MHz | 128 MB |
2010 | iPhone 4 | 1 GHz | 512 MB |
2015 | iPhone 6 | 1.4 GHz dual-core | 1 GB |
var Person = function (name, age) {
this.name = name;
this.age = age;
};
Person.prototype.getName = function () {
return this.name;
};
class Person {
name;
age;
constructor (name, age) {
this.name = name;
this.age = age;
}
getName(){
return this.name;
}
}
// lib/math.js
LibMath = {};
LibMath.multiply = function (x, y) { return x * y };
// someApp.js
var math = LibMath;
console.log(math.multiply(2, 2));
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))
function doSomething (onDone) {
onDone();
}
doSomething(function(rta){
console.log(rta);
})
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);
})
(function() {
'use strict';
//Code
})();
for (let i = 0; i < a.length; i++) {
let x = a[i];
}
if(true) {
var twitter = "@nicobytes";
}
console.log(twitter); //twitter es global
//Const no existe en ES5
const PI = 3.14;
if(true) {
let twitter = "@nicobytes";
}
console.log(twitter); //Error porque twitter ha sido definida dentro de IF
console.log(PI); //3.14
PI = 3.12; //Error porque PI es un constante y no puede cambiar de valor una vez definida.
var numbers = [1,2,3,4];
numbers.map(function(item){
return item * 2;
}); //[2,4,6,8]
function Person(){
this.foo = function(){}
this.bar = function(){
var self = this;
document.addEventListener("click", function(e){
self.foo();
});
}
}
let numbers = [1,2,3,4];
numbers.map(item => item * 2); //[2,4,6,8]
class Person(){
foo(){}
bar(){
document.addEventListener("click", (e) => this.foo());
}
}
Install: https://ionicframework.com/docs/v2/setup/installation/
SO | Android | IOS |
---|---|---|
Window | ✓ | ✗ |
Mac | ✓ | ✓ |
Linux / Ubuntu | ✓ | ✗ |
For Android: http://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html
For IOS: http://cordova.apache.org/docs/en/latest/guide/platforms/ios/index.html
ionic start appBlank blank --v2
ionic start appSide sidemenu --v2
ionic start appTabs tabs --v2
cd yourApp
ionic serve
ionic start
Introducción a TypeScript: https://www.ion-book.com/blog/ionic2/intro-typescript/
<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 platform add android --save
ionic platform add ios --save
ionic platform add wp --save
ionic platform ls
ionic platform rm android
ionic run android
ionic run ios
ionic build android
ionic build ios
ionic build android --release
ionic build ios --release
ionic emulate android
ionic emulate ios
ionic state save
ionic state restore
ionic plugin add cordova-plugin-XXXXXX save
//Examples
ionic plugin add cordova-plugin-camera --save
ionic plugin add cordova-plugin-googlemaps --save
ionic plugin add phonegap-plugin-push --save
ionic plugin rm phonegap-plugin-push --save
ionic plugin ls
npm install moment --save
ionic resources
@Decorator({
//meta data
})
export class MyClass {
//Your class
}
ionic g --list
ionic g page users
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
@Component({
selector: 'users-page',
templateUrl: 'users.html',
})
export class UsersPage {
constructor(private nav: NavController) {
}
}
ionic g component my-user
import { Component, Input } from '@angular/core';
@Component({
selector: 'my-user',
templateUrl: 'my-user.html'
})
export class MyUser {
text: string;
@Input() hero: any;
constructor() {
this.text = 'Hello World';
}
}
<my-user *ngFor="let user of users" [hero]="user"></my-user>
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>
ionic g pipe reverse
import { Injectable, Pipe } from '@angular/core';
@Pipe({
name: 'reverse'
})
@Injectable()
export class Reverse {
transform(value: string, args: any[]) {
return value.split('').reverse().join('');
}
}
<h1>{{ 'Hola' | reserve }}</h1>
ionic g provider usersService
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
@Injectable()
export class usersService {
data: any;
constructor(private http: Http) {
this.data = null;
}
load() {
//Code
}
}
export class UsersPage {
users: any[];
constructor(
private nav: NavController,
private usersService: UsersService
) {
this.usersService.load();
this.users = [
{
name: 'as'
},
{
name: 'as'
}
];
}
}
// 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;
button[primary]{
background: black;
}
<link href="build/font-awesome/css/font-awesome.min.css" rel="stylesheet" >
En el vas a poder tener orden de tu CSS, automatizar trabajo, escalabilidad del proyecto y ahorro de tiempo.
Se puede utilizar dos tipos de sintaxis *.sass y *.scss
*.scss
body {
font: 100% $font-stack;
color: $primary-color;
}
*.sass
body
font: 100% $font-stack
color: $primary-color
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
Nos van a servir para declarar todos los archivos que pueden ser importados ejemplo.scss
@import 'reset';
body {
font: 100% Helvetica, sans-serif;
background-color: #efefef;
}
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
-ms-border-radius: $radius;
border-radius: $radius;
}
.box { @include border-radius(10px); }
.message {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
@extend .message;
border-color: green;
}
<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()" [formGroup]="myForm" novalidate>
<ion-input type="text"
name="username"
formControlName="username"></ion-input>
<ion-input type="text"
name="name"
formControlName="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 } from '@ionic/storage';
@NgModule({
declarations: [
// ...
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
// ...
],
providers: [
Storage
]
})
export class AppModule {}
import { Storage, LocalStorage } from 'ionic-angular';
@Injectable()
export class TodosService {
constructor(public storage: Storage) {}
saveTodos( todos ){
this.storage.set('todos', JSON.stringify(todos));
}
getAllTodos() {
return this.storage.get('todos');
}
}
cordova plugin add cordova-sqlite-storage --save
import { Injectable } from '@angular/core';
import { SQLite } from 'ionic-native';
@Injectable()
export class TasksService {
db: SQLite = null;
constructor() {
this.db = new SQLite();
}
}
openDatabase(){
return this.db.openDatabase({
name: 'data.db',
location: 'default' // the location field is required
});
}
createTable(){
let sql = 'CREATE TABLE IF NOT EXISTS tasks(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, completed INTEGER)';
return this.db.executeSql(sql, []);
}
getAll(){
let sql = 'SELECT * FROM tasks';
return this.db.executeSql(sql, [])
.then(response => {
let tasks = [];
for (let index = 0; index < response.rows.length; index++) {
tasks.push( response.rows.item(index) );
}
return Promise.resolve( tasks );
})
}
update(task: any){
let sql = 'UPDATE tasks SET title=?, completed=? WHERE id=?';
return this.db.executeSql(sql, [task.title, task.completed, task.id]);
}
delete(task: any){
let sql = 'DELETE FROM tasks WHERE id=?';
return this.db.executeSql(sql, [task.id]);
}
npm install angularfire2 --save
npm install firebase --save
export const firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxxxxxxx",
authDomain: "xxxxxxxxxxxxxxxxxx",
databaseURL: "xxxxxxxxxxxxxxxxxxxxxx",
storageBucket: "xxxxxxxxxxxxxxxxxxxxxxx",
messagingSenderId: "xxxxxxxxxxxxxxxxxxxxx"
};
@NgModule({
declarations: [
MyApp
],
imports: [
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp
],
providers: [
AuthService
]
})
export class AppModule {}
{
"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