The rise of web developers
@loicknuchel
?
Loïc Knuchel
Geek passionné
Freelance
Développeur web full-stack
Entrepreneur
Cookers / SalooN
L’histoire du smartphone
Juin 2007
Lancement de l’iPhone 1
Septembre 2008
Sortie du HTC G1
avec Android
La WebView
Idée
Coder des applications
entièrement dans la WebView
Idée
Coder des applications
entièrement dans la WebView
Avantages :
PhoneGap / Cordova
Mars 2009 :
Lancement de PhoneGap par Nitobi
Octobre 2011 :
Rachat de Nitobi par Adobe
Séparation de la technologie (Cordova) et des services commerciaux (PhoneGap)
Oups...
Oups...
Oups...
Mauvais support des standards du web dans la WebView
Peu d’outils / librairies
Téléphones peu puissants
Oups...
Mauvais support des standards du web dans la WebView
Peu d’outils / librairies
Téléphones peu puissants
Oups...
Mauvais support des standards du web dans la WebView
Peu d’outils / librairies
Téléphones peu puissants
Souvent pour des projets à petit budget...
Aujourd’hui
Ionic c’est quoi ?
+
=
Ionic c’est quoi ?
+
=
Stack technologique
Natif
Web
Téléphone & APIs natives
Cordova : webview +
JavaScript bridges
Angular
Ionic
Application
Dév web = Dév mobile
DX
DX
“We want to cater to the 99% who just want to build something functional quickly and not break the bank to do it.” - Max Lynch
DX
#Platform continuity
DX
#Platform continuity
#CLI
DX
#Platform continuity
#CLI
#Ionic Market
DX
#Platform continuity
#CLI
#Backend services
#Ionic Market
DX
#Platform continuity
#CLI
#Backend services
#Push
#Deploy
#Package
#Auth
#Analytics
#Ionic Market
DX
#Platform continuity
#CLI
#Backend services
#Push
#Deploy
#Package
#Auth
#Analytics
#Ionic View
#Native plugins
#Ionic Market
#Ionic Creator
@ionitron
Ionic is a complete ecosystem !
One more thing...
Hybrid apps have superpowers...
Hybrid superpowers
App Store installs are broken !
App Store installs are broken !
?
App Store installs are broken !
?
App Store installs are broken !
?
App Store installs are broken !
App Store installs are broken !
App Store installs are broken !
App Store installs are broken !
App Store installs are broken !
Deep linking
Install tracking
Hybrid superpowers
Web App :
Hybrid superpowers
Web App :
Progressive Web App :
Hybrid superpowers
Web App :
Progressive Web App :
Native App :
Hybrid superpowers
App Store deployment is broken !
Apple : ~ 2 jours
Google : ~ 2 heures
Hybrid superpowers
Nouveautés depuis Ionic 1
Getting started
Install nodejs & npm
Getting started
Install nodejs & npm
$ npm install -g ionic@beta
Getting started
Install nodejs & npm
$ npm install -g ionic@beta
$ ionic start demoApp --v2 --ts
Getting started
Install nodejs & npm
$ npm install -g ionic@beta
$ ionic start demoApp --v2 --ts
$ cd demoApp && ionic serve
♬ ♫ ♬ ♫ Your Ionic app is ready to go! ♬ ♫ ♬ ♫
Getting started
Install nodejs & npm
$ npm install -g ionic@beta
$ ionic start demoApp --v2 --ts
$ cd demoApp && ionic serve
♬ ♫ ♬ ♫ Your Ionic app is ready to go! ♬ ♫ ♬ ♫
Getting started
Install mobile sdk (Android ou iOS)
Getting started
Install mobile sdk (Android ou iOS)
$ sudo npm install -g cordova
Getting started
Install mobile sdk (Android ou iOS)
$ sudo npm install -g cordova
$ ionic platform add android
Getting started
Install mobile sdk (Android ou iOS)
$ sudo npm install -g cordova
$ ionic platform add android
$ ionic run android
Structure de fichiers
Structure de fichiers
Structure de fichiers
index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<link ios-href="build/css/app.ios.css" rel="stylesheet">
<link md-href="build/css/app.md.css" rel="stylesheet">
<link wp-href="build/css/app.wp.css" rel="stylesheet">
</head>
<body>
<!-- this Ionic's root component and where the app will load -->
<ion-app></ion-app>
<!-- cordova.js required for cordova apps -->
<script src="cordova.js"></script>
<!-- Polyfill needed for platforms without Promise and Collection support -->
<script src="build/js/es6-shim.min.js"></script>
<!-- Zone.js and Reflect-metadata -->
<script src="build/js/Reflect.js"></script>
<script src="build/js/zone.js"></script>
<!-- the bundle which is built from the app's source code -->
<script src="build/js/app.bundle.js"></script>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Ionic</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<link ios-href="build/css/app.ios.css" rel="stylesheet">
<link md-href="build/css/app.md.css" rel="stylesheet">
<link wp-href="build/css/app.wp.css" rel="stylesheet">
</head>
<body>
<!-- this Ionic's root component and where the app will load -->
<ion-app></ion-app>
<!-- cordova.js required for cordova apps -->
<script src="cordova.js"></script>
<!-- Polyfill needed for platforms without Promise and Collection support -->
<script src="build/js/es6-shim.min.js"></script>
<!-- Zone.js and Reflect-metadata -->
<script src="build/js/Reflect.js"></script>
<script src="build/js/zone.js"></script>
<!-- the bundle which is built from the app's source code -->
<script src="build/js/app.bundle.js"></script>
</body>
</html>
app.ts
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
@App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
rootPage: any = TabsPage;
constructor(platform: Platform) {
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();
});
}
}
app.ts
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
@App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
rootPage: any = TabsPage;
constructor(platform: Platform) {
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();
});
}
}
app.ts
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
@App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
rootPage: any = TabsPage;
constructor(platform: Platform) {
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();
});
}
}
app.ts
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
@App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
rootPage: any = TabsPage;
constructor(platform: Platform) {
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();
});
}
}
page1.ts
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
constructor() {
}
}
<ion-navbar *navbar>
<ion-title>Tab 1</ion-title>
</ion-navbar>
<ion-content padding class="page1">
<h2>Welcome to Ionic!</h2>
<p>
This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
</p>
<p>
Take a look at the <code>app/</code> directory to add or change tabs, update any existing page or create new pages.
</p>
</ion-content>
page1.ts
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
constructor() {
}
}
<ion-navbar *navbar>
<ion-title>Tab 1</ion-title>
</ion-navbar>
<ion-content padding class="page1">
<h2>Welcome to Ionic!</h2>
<p>
This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
</p>
<p>
Take a look at the <code>app/</code> directory to add or change tabs, update any existing page or create new pages.
</p>
</ion-content>
page1.ts
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/page1/page1.html'
})
export class Page1 {
constructor() {
}
}
<ion-navbar *navbar>
<ion-title>Tab 1</ion-title>
</ion-navbar>
<ion-content padding class="page1">
<h2>Welcome to Ionic!</h2>
<p>
This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
</p>
<p>
Take a look at the <code>app/</code> directory to add or change tabs, update any existing page or create new pages.
</p>
</ion-content>
page1.ts
import {Page} from 'ionic-angular';
@Page({
template: `
<ion-navbar *navbar>
<ion-title>Tab 1</ion-title>
</ion-navbar>
<ion-content padding class="page1">
<h2>Welcome to Ionic!</h2>
<p>
This starter project comes with simple tabs-based layout for apps that are going to primarily use a Tabbed UI.
</p>
</ion-content>`
})
export class Page1 {
constructor() {}
}
page1.ts
import {Page} from 'ionic-angular';
@Page({
styles: [`
.page1 h2 {
color: red;
}
`],
template: `...`
})
export class Page1 {
constructor() {}
}
Angular 1 vs Angular 2
Template
Your favorite hero is: {{favoriteHero}}
Template
Your favorite hero is: {{favoriteHero}}
<span>{{movie.title | uppercase}}</span>
Template
Your favorite hero is: {{favoriteHero}}
<span>{{movie.title | uppercase}}</span>
<a [href]="angularDocsUrl">Angular Docs</a>
Template
Your favorite hero is: {{favoriteHero}}
<span>{{movie.title | uppercase}}</span>
<a [href]="angularDocsUrl">Angular Docs</a>
<button (click)="toggleMovie()"></button>
Template
Your favorite hero is: {{favoriteHero}}
<span>{{movie.title | uppercase}}</span>
<a [href]="angularDocsUrl">Angular Docs</a>
<button (click)="toggleMovie()"></button>
<input [(ngModel)]="favoriteMovie" />
Template
Your favorite hero is: {{favoriteHero}}
<span>{{movie.title | uppercase}}</span>
<a [href]="angularDocsUrl">Angular Docs</a>
<button (click)="toggleMovie()"></button>
<input [(ngModel)]="favoriteMovie" />
<div *ngIf="movies.length === 0">No movies</div>
Template
Your favorite hero is: {{favoriteHero}}
<span>{{movie.title | uppercase}}</span>
<a [href]="angularDocsUrl">Angular Docs</a>
<button (click)="toggleMovie()"></button>
<input [(ngModel)]="favoriteMovie" />
<div *ngIf="movies.length === 0">No movies</div>
<video-player #player></video-player>
<button (click)="player.pause()">Pause</button>
Template
Your favorite hero is: {{favoriteHero}}
<span>{{movie.title | uppercase}}</span>
<a [href]="angularDocsUrl">Angular Docs</a>
<button (click)="toggleMovie()"></button>
<input [(ngModel)]="favoriteMovie" />
<div *ngIf="movies.length === 0">No movies</div>
<video-player #player></video-player>
<button (click)="player.pause()">Pause</button>
<ul *ngFor="let movie of movies">
<li>{{movie.title}}</li>
</ul>
Créer une nouvelle page
todo.page.ts todo.html todo.scss
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
constructor() { }
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-content class="todo-page">
</ion-content>
.todo-page {
.done {
text-decoration: line-through;
}
}
todo.page.ts todo.html todo.scss
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
constructor() { }
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-content class="todo-page">
</ion-content>
.todo-page {
.done {
text-decoration: line-through;
}
}
app.ts theme/app.core.scss
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {TabsPage} from './pages/tabs/tabs';
import {TodoPage} from "./pages/todo/todo.page";
@App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
config: {}
})
export class MyApp {
rootPage: any = TodoPage;
constructor(platform: Platform) {
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
}
// http://ionicframework.com/docs/v2/theming/
// App Shared Imports
// --------------------------------------------------
// These are the imports which make up the design of this app.
// By default each design mode includes these shared imports.
// App Shared Sass variables belong in app.variables.scss.
@import "../pages/page1/page1";
@import "../pages/page2/page2";
@import "../pages/page3/page3";
@import "../pages/todo/todo";
Ionic2 TODO list
import {Page} from 'ionic-angular';
class Todo {
constructor(
public name: string,
public done: boolean = false
) {}
}
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
constructor() { }
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-content class="todo-page">
</ion-content>
Ionic2 TODO list
import {Page} from 'ionic-angular';
class Todo {
constructor(
public name: string,
public done: boolean = false
) {}
}
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
todos: Todo[] = [
new Todo('todo 1'),
new Todo('todo 2'),
new Todo('todo 3')
];
constructor() { }
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-content class="todo-page">
</ion-content>
Ionic2 TODO list
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-content class="todo-page">
<ion-list>
<ion-item *ngFor="let todo of todos" [hidden]="todo.done">
<ion-label>{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done"></ion-checkbox>
</ion-item>
<ion-item *ngFor="let todo of todos" [hidden]="!todo.done">
<ion-label class="done">{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done" disabled="true"></ion-checkbox>
</ion-item>
</ion-list>
</ion-content>
Ionic2 TODO list
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-content class="todo-page">
<ion-list>
<ion-item *ngFor="let todo of todos" [hidden]="todo.done">
<ion-label>{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done"></ion-checkbox>
</ion-item>
<ion-item *ngFor="let todo of todos" [hidden]="!todo.done">
<ion-label class="done">{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done" disabled="true"></ion-checkbox>
</ion-item>
</ion-list>
</ion-content>
Ionic2 TODO list
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-content class="todo-page">
<ion-list>
<ion-item *ngFor="let todo of todos" [hidden]="todo.done">
<ion-label>{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done"></ion-checkbox>
</ion-item>
<ion-item *ngFor="let todo of todos" [hidden]="!todo.done">
<ion-label class="done">{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done" disabled="true"></ion-checkbox>
</ion-item>
</ion-list>
</ion-content>
Ionic2 TODO list
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
newTodo: string = '';
todos: Todo[] = [
new Todo('todo 1'),
new Todo('todo 2'),
new Todo('todo 3')
];
constructor() { }
create(text: string): void {
this.todos.push(new Todo(text));
this.newTodo = '';
}
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-toolbar>
<ion-input type="text" [(ngModel)]="newTodo" placeholder="Nouvelle tâche">
</ion-input>
<ion-buttons end>
<button (click)="create(newTodo)">
<ion-icon name="send"></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
<ion-content class="todo-page">
<ion-list>
...
</ion-list>
</ion-content>
Ionic2 TODO list
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
newTodo: string = '';
todos: Todo[] = [
new Todo('todo 1'),
new Todo('todo 2'),
new Todo('todo 3')
];
constructor() { }
create(text: string): void {
this.todos.push(new Todo(text));
this.newTodo = '';
}
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-toolbar>
<ion-input type="text" [(ngModel)]="newTodo" placeholder="Nouvelle tâche">
</ion-input>
<ion-buttons end>
<button (click)="create(newTodo)">
<ion-icon name="send"></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
<ion-content class="todo-page">
<ion-list>
...
</ion-list>
</ion-content>
Ionic2 TODO list
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
newTodo: string = '';
todos: Todo[] = [...];
constructor() { }
create(text: string): void { ... }
delete(todo: Todo): void {
const i = this.todos.indexOf(todo);
if(i >= 0) {
this.todos.splice(i, 1);
}
}
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-toolbar>
...
</ion-toolbar>
<ion-content class="todo-page">
<ion-list>
<ion-item *ngFor="let todo of todos" [hidden]="todo.done">
<ion-label>{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done"></ion-checkbox>
</ion-item>
<ion-item *ngFor="..." [hidden]="!todo.done" (click)="delete(todo)">
<ion-label class="done">{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done" disabled="true"></ion-checkbox>
</ion-item>
</ion-list>
</ion-content>
Ionic2 TODO list
import {Page} from 'ionic-angular';
@Page({
templateUrl: 'build/pages/todo/todo.html'
})
export class TodoPage {
newTodo: string = '';
todos: Todo[] = [...];
constructor() { }
create(text: string): void { ... }
delete(todo: Todo): void {
const i = this.todos.indexOf(todo);
if(i >= 0) {
this.todos.splice(i, 1);
}
}
}
<ion-navbar *navbar>
<ion-title>Todos</ion-title>
</ion-navbar>
<ion-toolbar>
...
</ion-toolbar>
<ion-content class="todo-page">
<ion-list>
<ion-item *ngFor="let todo of todos" [hidden]="todo.done">
<ion-label>{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done"></ion-checkbox>
</ion-item>
<ion-item *ngFor="..." [hidden]="!todo.done" (click)="delete(todo)">
<ion-label class="done">{{todo.name}}</ion-label>
<ion-checkbox [(ngModel)]="todo.done" disabled="true"></ion-checkbox>
</ion-item>
</ion-list>
</ion-content>
Pour tester ce weekend...
Pour tester ce weekend...
Pour tester ce weekend...
Pour tester ce weekend...
Pour tester ce weekend...
Pour tester ce weekend...
Pour tester ce weekend...
Pour tester ce weekend...
Questions ?
@loicknuchel