1 of 124

The rise of web developers

@loicknuchel

2 of 124

?

3 of 124

Loïc Knuchel

Geek passionné

Freelance

Développeur web full-stack

Entrepreneur

Cookers / SalooN

loicknuchel@gmail.com

@loicknuchel

http://loic.knuchel.org/

4 of 124

L’histoire du smartphone

5 of 124

Juin 2007

Lancement de l’iPhone 1

6 of 124

Septembre 2008

Sortie du HTC G1

avec Android

7 of 124

8 of 124

  • Nouvelles possibilités
    • UX
    • Technique
    • Business

  • Un écosystème à bâtir

9 of 124

10 of 124

  • Nouvelle stack technique

  • Peu de personnes formées

  • Développer plusieurs fois la même chose

  • Environnements très spécifiques (offline, puissance, versions, diversité...)

11 of 124

12 of 124

La WebView

13 of 124

Idée

Coder des applications

entièrement dans la WebView

14 of 124

Idée

Coder des applications

entièrement dans la WebView

Avantages :

  • Technologies et environnements connus
  • Cross-platform

15 of 124

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)

16 of 124

17 of 124

Oups...

18 of 124

Oups...

  • UI moche
  • UI peu réactive
  • loin du look & feel natif

19 of 124

Oups...

  • UI moche
  • UI peu réactive
  • loin du look & feel natif

Mauvais support des standards du web dans la WebView

Peu d’outils / librairies

Téléphones peu puissants

20 of 124

Oups...

  • UI moche
  • UI peu réactive
  • loin du look & feel natif

Mauvais support des standards du web dans la WebView

Peu d’outils / librairies

Téléphones peu puissants

  • App de mauvaise qualité
  • Bugs

21 of 124

Oups...

  • UI moche
  • UI peu réactive
  • loin du look & feel natif

Mauvais support des standards du web dans la WebView

Peu d’outils / librairies

Téléphones peu puissants

  • App de mauvaise qualité
  • Bugs

Souvent pour des projets à petit budget...

22 of 124

Aujourd’hui

  • Téléphones puissants (et de + en +)
  • Très bon support des standards web dans la WebView
  • Beaucoup d’outils / librairies

23 of 124

24 of 124

Ionic c’est quoi ?

+

=

25 of 124

Ionic c’est quoi ?

+

=

26 of 124

Stack technologique

Natif

Web

Téléphone & APIs natives

Cordova : webview +

JavaScript bridges

Angular

Ionic

Application

27 of 124

Dév web = Dév mobile

28 of 124

DX

29 of 124

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

30 of 124

DX

#Platform continuity

31 of 124

DX

#Platform continuity

#CLI

32 of 124

DX

#Platform continuity

#CLI

#Ionic Market

33 of 124

DX

#Platform continuity

#CLI

#Backend services

#Ionic Market

34 of 124

DX

#Platform continuity

#CLI

#Backend services

#Push

#Deploy

#Package

#Auth

#Analytics

#Ionic Market

35 of 124

DX

#Platform continuity

#CLI

#Backend services

#Push

#Deploy

#Package

#Auth

#Analytics

#Ionic View

#Native plugins

#Ionic Market

#Ionic Creator

36 of 124

@ionitron

37 of 124

Ionic is a complete ecosystem !

38 of 124

One more thing...

39 of 124

Hybrid apps have superpowers...

40 of 124

Hybrid superpowers

41 of 124

App Store installs are broken !

42 of 124

App Store installs are broken !

?

43 of 124

App Store installs are broken !

?

44 of 124

App Store installs are broken !

?

45 of 124

App Store installs are broken !

46 of 124

App Store installs are broken !

47 of 124

App Store installs are broken !

48 of 124

App Store installs are broken !

49 of 124

App Store installs are broken !

Deep linking

Install tracking

50 of 124

Hybrid superpowers

Web App :

  • Cross-platform
  • Accès instantané
  • Deep link
  • Offline

51 of 124

Hybrid superpowers

Web App :

  • Cross-platform
  • Accès instantané
  • Deep link
  • Offline

Progressive Web App :

  • Installation instantanée
  • Lancement depuis la Home
  • Push notifications

52 of 124

Hybrid superpowers

Web App :

  • Cross-platform
  • Accès instantané
  • Deep link
  • Offline

Progressive Web App :

  • Installation instantanée
  • Lancement depuis la Home
  • Push notifications

Native App :

  • Accès complet au téléphone

53 of 124

Hybrid superpowers

54 of 124

App Store deployment is broken !

Apple : ~ 2 jours

Google : ~ 2 heures

55 of 124

Hybrid superpowers

56 of 124

57 of 124

58 of 124

59 of 124

60 of 124

61 of 124

62 of 124

63 of 124

64 of 124

65 of 124

66 of 124

67 of 124

Nouveautés depuis Ionic 1

  • Angular 2 (+ TypeScript)
  • Navigation push/pop
  • Nouveaux composants
    • DateTime
    • Toast
    • Searchbar
    • Segment

68 of 124

Getting started

Install nodejs & npm

69 of 124

Getting started

Install nodejs & npm

$ npm install -g ionic@beta

70 of 124

Getting started

Install nodejs & npm

$ npm install -g ionic@beta

$ ionic start demoApp --v2 --ts

71 of 124

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! ♬ ♫ ♬ ♫

72 of 124

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! ♬ ♫ ♬ ♫

73 of 124

Getting started

Install mobile sdk (Android ou iOS)

74 of 124

Getting started

Install mobile sdk (Android ou iOS)

$ sudo npm install -g cordova

75 of 124

Getting started

Install mobile sdk (Android ou iOS)

$ sudo npm install -g cordova

$ ionic platform add android

76 of 124

Getting started

Install mobile sdk (Android ou iOS)

$ sudo npm install -g cordova

$ ionic platform add android

$ ionic run android

77 of 124

Structure de fichiers

78 of 124

Structure de fichiers

79 of 124

Structure de fichiers

80 of 124

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>

81 of 124

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>

82 of 124

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();

});

}

}

83 of 124

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();

});

}

}

84 of 124

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();

});

}

}

85 of 124

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();

});

}

}

86 of 124

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>

87 of 124

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>

88 of 124

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>

89 of 124

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() {}

}

90 of 124

page1.ts

import {Page} from 'ionic-angular';

@Page({

styles: [`

.page1 h2 {

color: red;

}

`],

template: `...`

})

export class Page1 {

constructor() {}

}

91 of 124

Angular 1 vs Angular 2

  • bootstrap manuel
  • Filter => Pipe
  • Controllers => Components
  • Directives => Components
  • $scope => Class properties
  • Services => Injectables

92 of 124

Template

  • Bindings :

Your favorite hero is: {{favoriteHero}}

93 of 124

Template

  • Bindings :
  • Pipes :

Your favorite hero is: {{favoriteHero}}

<span>{{movie.title | uppercase}}</span>

94 of 124

Template

  • Bindings :
  • Pipes :
  • Propriétés / Input :

Your favorite hero is: {{favoriteHero}}

<span>{{movie.title | uppercase}}</span>

<a [href]="angularDocsUrl">Angular Docs</a>

95 of 124

Template

  • Bindings :
  • Pipes :
  • Propriétés / Input :
  • Evénements / Output :

Your favorite hero is: {{favoriteHero}}

<span>{{movie.title | uppercase}}</span>

<a [href]="angularDocsUrl">Angular Docs</a>

<button (click)="toggleMovie()"></button>

96 of 124

Template

  • Bindings :
  • Pipes :
  • Propriétés / Input :
  • Evénements / Output :
  • Double binding :

Your favorite hero is: {{favoriteHero}}

<span>{{movie.title | uppercase}}</span>

<a [href]="angularDocsUrl">Angular Docs</a>

<button (click)="toggleMovie()"></button>

<input [(ngModel)]="favoriteMovie" />

97 of 124

Template

  • Bindings :
  • Pipes :
  • Propriétés / Input :
  • Evénements / Output :
  • Double binding :
  • Templates :

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>

98 of 124

Template

  • Bindings :
  • Pipes :
  • Propriétés / Input :
  • Evénements / Output :
  • Double binding :
  • Templates :
  • Local variables :

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>

99 of 124

Template

  • Bindings :
  • Pipes :
  • Propriétés / Input :
  • Evénements / Output :
  • Double binding :
  • Templates :
  • Local variables :
  • Boucles :

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>

100 of 124

101 of 124

Créer une nouvelle page

102 of 124

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;

}

}

103 of 124

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;

}

}

104 of 124

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";

105 of 124

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>

106 of 124

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>

107 of 124

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>

108 of 124

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>

109 of 124

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>

110 of 124

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>

111 of 124

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>

112 of 124

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>

113 of 124

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>

114 of 124

115 of 124

Pour tester ce weekend...

116 of 124

Pour tester ce weekend...

117 of 124

Pour tester ce weekend...

118 of 124

Pour tester ce weekend...

119 of 124

Pour tester ce weekend...

120 of 124

Pour tester ce weekend...

121 of 124

Pour tester ce weekend...

122 of 124

Pour tester ce weekend...

123 of 124

124 of 124

Questions ?

@loicknuchel