Développement d'applications mobiles hybrides avec IONIC 2 et Angular 2. Depuis l'installation jusqu'à l'utilisation de plugins natifs tels que stockage de données, géolocalisation, liaison serveur API Rest...
1. Développement
Mobile Web
avec IONIC2
Version du 29/03/2017
Jean David Olekhnovitch
📧 jd@olek.fr
@gidehault
2. Web et mobile :
deux mondes à part
• Le web est basé depuis la fin des années 90 sur un mode orienté
serveur :
• Un serveur HTTP qui centralise tous les traitements
• Des clients HTML/CSS qui affichent passivement l’information
• De multiples tentatives existent pour ‘dynamiser’ le côté
client, Javascript en tête
• Un handicap : ça rappelle furieusement le modèle mainframe/
client texte des années 60/70
• Un souhait : préserver un standard ouvert
2
3. Web et mobile :
deux mondes à part
• Le mobile s’appuie sur des outils de
développements plus sophistiqués, mais dédiés à
des plateformes incompatibles entre elles
3
Android
- Langage Java
- Environnement propriétaire
Google, sur base Linux
- Développement Android
Studio
- Distribution : Google Play
iOS
- Langage Swift
- Environnement propriétaire
Apple, sur base NetBSD
- Développement XCode
- Distribution : App Store
FirefoxOS
Windows
Phone
Blackberry
Ubuntu
Tizen
…
5. Mais la mixité subsiste
• D’un côté, il est possible de faire tourner des
choses assez évoluées dans le navigateur de son
téléphone
• De l’autre, il est aujourd’hui possible d’accéder à
des fonctions ‘natives’ depuis son navigateur
5
6. 1 - Historiquement : les
"sites mobiles"
• A l'époque des premiers smartphones, il était
nécessaire de faire une version spécifique des sites
Internet pour visualisation sur téléphone mobile
6
Avantages
‣ version légère et bien adaptée aux
smartphones peu performants
‣ facilité de développement
Inconvénients
‣ nécessité de gérer deux versions
en parallèle
‣ aucune exploitation des possibilités
natives des téléphones
7. 2 - Vers une version unique des
sites web : le responsive design
• Les possibilités d'élasticité (grille fluide) et d'adaptation aux
devices (media queries) des feuilles de style CSS permettent
de maintenir une version unique des sites web tout en
s'adaptant au format d'affichage
7
Avantages
‣ adaptation relativement simple (que
des hacks CSS)
‣ une seule version des sites web à
maintenir
Inconvénients
‣ aucune exploitation des possibilités
natives des téléphones
8. 3 - Un pas vers les fonctions
natives : HTML5
• La version HTML5 propose, de manière plus ou
moins aboutie, la possibilité d'accéder à certaines
fonctions natives depuis son navigateur web
8
Avantages
‣ possibilité de cumuler avec les
capacités de responsive design
‣ fonctions accessibles en quelques
balises
Inconvénients
‣ toutes les fonctions ne sont pas
présentes
‣ compatibilité aléatoire suivant les
navigateurs
9. Du HTML à l'app mobile : un
chemin semé d'embuches
9
Site
HTML/
CSS
Déclinaison
site mobile
Site
Responsive
Site HTML5
App mobile
native
App hybride
web/mobile
?
AppsSites
Progressive
Webapps
11. La barrière des appstore
• Google et Apple ont fait le choix du point d'entrée
unique vers l'application : l'app store (ou playstore)
• Pour diverses raisons de contrôle qualité, de
sécurité… et parfois de censure
• Cette barrière impose des critères techniques
nombreux et peu simples à franchir
11
12. 1ères solutions : Apache
Cordova/Adobe PhoneGap
• Idée : proposer des passerelles entre les fonctions
natives, et des pages conçues en HTML/CSS
• Naissance du concept d’Application Hybride
• Aujourd’hui intégralement en opensource et géré
par l’Apache Foundation
12
13. Limitations de Cordova
• Ne propose pas de solution pour l’interface
utilisateur (gestuelles tactiles, etc…)
• La publication vers les stores Google/Apple reste
complexe
• A voir plus comme une série de librairies que
comme un framework a part entière
13
14. Surcouche et structuration:
la solution d'un framework
• Idée : compléter la philosophie (et les briques
techniques) de Cordova par un véritable framework
capable de :
• faciliter le développement
• industrialiser la publication sur les stores
• donner la possibilité de créer des modules
optionnels
• ‘casser’ la logique de page au sens HTTP du terme
14
15. Côté Facebook : React
• Framework développé en interne
• Destiné au développement des applications
Facebook avant tout
• Essentiellement côté « front »
• Basé sur Javascript
• Fait appel à des composants natifs autant que
possible
15
16. Autres frameworks
• Onsen UI
• Intel XDK
• Sencha Touch
• Kendo UI
• Framework 7
16
• JQuery Mobile
• Mobile Angular UI
• Famo.us
• Monaca
• Trigger.IO
• Electron (pour applis desktop)
17. Ionic
• Créé en 2013
• Repose entièrement sur Cordova pour la partie native
• Et sur AngularJS pour la cinématique des pages
• v2 sortie fin 2016
• TypeScript remplace Javascript
• Angular2 remplace AngularJS
17
18. Angular 2
Architecture d’Ionic2
Apache Cordova
Angular 2
Ionic 2
HTML SCSS Typescript
NPM
NodeJS
Moteur HTML/CSS/JS
Natif
App
native Application
passerelle
vers le monde natif
cinématique des
écrans
framework
gestionnaire de paquets
environnement de
développement
affichage in-app
via un interpréteur web
19. Installation d’Ionic (1)
• Première étape : installation de NodeJS 6
• Inclut NPM (gestionnaire de paquet Javascript)
• https://nodejs.org/en/download/
19
20. Installation d’Ionic (2)
• Installation d’Ionic et de Cordova
• On se repose sur le gestionnaire de paquets
NPM
• Tout se passe en ligne de commande… comme
ça va être le cas pour toute la suite :
• npm install -g ionic cordova
20
21. Première app Ionic
• Génération du squelette :
• ionic start premierEssai super --v2
• On va dans le répertoire
• cd premierEssai
• Test dans un navigateur
• ionic serve -c --lab
21
Nom du
template qui sert de
modèle (blank, tabs,
sidemenu, tutorial …)
ionic peut à la fois créer
des apps ionic1 et ionic2
lancement avec
console de debug et choix de
l’émulateur
22. Visualisation sur un
téléphone
• Problème : on ne peut pas directement télécharger
une app sur un mobile (il faut passer par la
validation du constructeur)
• Contournement : l'app Ionic View qui permet de
visualiser un prototype de son app publié au
préalable sur les serveurs Ionic
22
23. Fonctionnement
d'Ionic View
23
Poste de travail
Serveur Ionic
Smartphone
App Ionic View
ionic upload
X
synchro
in-app
interdit
Nécessite la création d'un
compte sur http://ionic.io
Dispo sur AppStore
et GooglePlay
24. Installation d’Ionic (3)
• Modules supplémentaires
• Pour chaque module supplémentaire, là aussi on
utilisera npm :
• npm install ionic-native --save
• Les mises à jour se font elles aussi en ligne de
commande :
• npm update -g ionic
24
Gestion des
dépendances
Installation globale
25. Installation d’Ionic (4)
• Environnement iOS
• Il est obligatoire d’être sur Mac !
• Installation de XCode via le Mac App Store
• Déclaration dans ionic :
• sudo xcode-select -s /Applications/Xcode.app/
Contents/Developer
• sudo npm install -g ios-deploy --unsafe-perm=true --
allow-root
25
26. Installation d’Ionic (5)
• Environnement Android
• Passe par l’installation de Java (JDK, pas le JRE) :
• http://www.oracle.com/technetwork/java/javase/downloads/index.html
• Installation d’Android Studio :
• https://developer.android.com/studio/index.html
• Installation des packages via le Open Android SDK Manager (‘android’ en ligne de
commandes)
• Android Platform SDK for vXXX
• Android SDK build-tools
• Android Support Repository
• Créer un Android Virtual Device (AVD) pour définir un émulateur type qui servira par la suite
26
27. Génération de l'icône et du
splash screen
• Les fichiers suivants doivent être stockés dans le
répertoire /resources de l'application
• icon.png : taille de 192x192
• splash.png : taille de 2208x2208
• La génération de toutes les déclinaisons de ces
images se fait ensuite par l'instruction suivante, à
lancer avant le build :
ionic resources
27
28. Lancement de son application
dans un environnement natif
• ionic platform add android
• ionic platform add ios
• ionic build ios
• ionic build android
28
29. Préparer un build iOS
• Double-cliquer sur
• /platforms/ios/MonApp.xcodeproj
• Créer sur XCode un « Archive »
• Publier ensuite sur le compte (payant !) Developer Apple
créé via
• https://developer.apple.com
• Gestion de la publication sur
• https://itunesconnect.apple.com
29
30. Préparer un build Android
• Création d’une clé :
• keytool -genkey -v -keystore MonApp.keystore -alias
MonApp -keyalg RSA -keysize 2048 -validity 10000
• Création du build (fichier .apk) prêt à être importé :
• ionic build android --release
• Création d’un compte puis publication sur
• https://play.google.com/apps/publish/
30
31. Structuration d’une
app ionic
• /src : code source de l’application
• /resources : stockage de l’icône et du
splashscreen
• /platforms : version compilée par plateforme
• config.xml : fichier principal de configuration de
l’app
31
32. Structuration du
répertoire /src
• /app : fichiers principaux de l’application
• /pages : les pages de l’application
• /assets : divers fichiers statiques (css, js, images…)
• /providers : les ‘providers’ (‘moteurs’) de l’application
• /theme : définition d’un thème pour l’application
• index.html : fichier de démarrage de l'app
32
33. Création d’une nouvelle
page
• Création des fichiers :
• ionic g page MonEssai
• J’obtiens un dossier /src/pages/mon-essai,
• et 3 fichiers :
• mon-essai.html : interface de la page
• mon-essai.scss : style de la page
• mon-essai.ts : fonctions de traitement en amont et en aval
de la page (avec une classe qui s'appelle MonEssaiPage)
33
MonEssai
MonEssaiPage
nom de la classe
mon-essai
nom des fichiers
34. Déclaration d’une
nouvelle page
• Cette déclaration se fait dans le fichier global
/src/app/app.module.ts :
import { MonEssaiPage } from '../pages/mon-essai/mon-essai';
• Le nom « MonEssaiPage » doit également être
mentionné dans les paragraphes :
• ‘declarations’
• ‘entryComponents’
34
35. Structure d'un fichier .ts
• Chaque fichier .ts d'une page (ici : mon-essai.ts) est
avant tout une classe
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
@Component({
selector: 'monEssai',
templateUrl: 'mon-essai.html'
})
export class MonEssaiPage {
// attributs
// méthodes
constructor(public navCtrl: NavController) {
}
} 35
Import
de librairies et fichiers
externes utiles
Les attributs sont
les variables utilisées
dans la classe
Les
méthodes sont des
fonctions utilisées dans
la classe
Le constructeur est la
méthode appelée au
démarrage de la page
Divers
paramétrages
Fin de la
classe
36. Liens entre .ts et .html
• Le fichier mon-essai.ts contient une classe (et
donc des attributs et méthodes) représentant la
partie dynamique de la page
• Le fichier mon-essai.html contient la description
des éléments visuels de la page
• Les deux communiquent intimement par le biais
des mécanismes d’Angular
36
37. TS, HTML, et MVC
On est en fait ici très proche du pattern MVC bien connu des
développeurs d'interface utilisateur
• Mais le contrôleur est ici géré de manière transparente par le
framework Angular
37
Vue
mon-essai.html
Contrôleur
Ionic/Angular
Modèle
mon-essai.ts
38. Affichage d’une variable
dans la page
• Prenons l’exemple d’un attribut de la classe
(dans mon-essai.ts) :
export class MonEssaiPage {
unAttribut: string="un contenu";
• On affiche ensuite l'attribut au milieu du code de la page
mon-essai.html de la manière suivante :
<p>{{unAttribut}}</p>
• Nul besoin de préciser le nom de la classe ou de l’instance
• On verra plus tard que cette liaison est dynamique, et permet
la mise à jour du contenu de cette variable
38
39. Aller d’une page à l’autre (1)
• Pour aller à la nouvelle page, on commence par créer un
bouton dans le fichier .html de la page principale :
<button ion-button>Cliquez ici</button>
• On rajoute ensuite une mention '(click)' qui permet
d'indiquer le comportement à avoir lors d'un clic sur le
bouton
<button ion-button (click)="boutonClic()">Cliquez ici</button>
39
Fonction qui sera
appelée lors du 'clic'
40. Aller d’une page à l’autre (2)
• On crée la fonction correspondante dans le fichier .ts de la page
principale:
boutonClic() {
this.navCtrl.push(MonEssaiPage);
}
• Pour fonctionner, ce code a besoin au préalable de deux éléments :
• La page MonEssaiPage doit être importée pour être connue
import {MonEssaiPage} from '../mon-essai/mon-essai';
• Le contrôleur de navigation ‘navCtrl’ doit être connu et déclaré :
import { NavController} from 'ionic-angular';
constructor(public navCtrl: NavController) {
40
41. Schéma récapitulatif
Cliquez ici
home.html
class HomePage
{
boutonClic()
{
this.navCtrl.push
(MonEssaiPage);
}
home.ts
{{unAttribut}}
mon-essai.html mon-essai.ts
class MonEssaiPage
{
unAttribut : string;
}
Démarrage
de l'app
42. Le constructeur d'une
classe de page
• Ce constructeur a des paramètres d'entrée qui fonctionnent de manière
"magique" : il suffit de les mentionner pour qu'ils :
• soient renseignés avec des objets déjà initialisés
• remplissent un attribut du même nom (rendant l'objet accessible dans toute
la classe)
• Cette 'magie' est en fait le pattern injection, géré par le framework
• Ces providers sont :
• Soit des contrôleurs standards d'ionic-angular (ex : NavController)
• soit déclarés dans app.module.ts (providers globaux à l'app)
• soit dans le bloc @Component de la page
42
43. Afficher des traces dans la
console
• Lancer ionic serve avec le paramètre -c permet
d'avoir une trace de la console dans le terminal, ce
qui est bien pratique pour débugger
• Vous pouvez n'importe où dans le code afficher
des éléments dans la console :
console.log("valeur de x :"+x);
• Vous pouvez également afficher des structures
complexes avec JSON.stringify :
console.log("erreur :"+JSON.stringify(err));
43
44. Composants ionic
• ionic2 est fourni avec de très très très nombreux
composants, pour gérer à peu près toute l’interface de
son app mobile
• Nous allons voir sur la suite de ce cours quelques uns de
ces composants, mais une bonne idée est de
commencer par les découvrir en regardant la page :
• http://ionicframework.com/docs/v2/components/
44
45. Rafraichissement d'une
page
• Pour proposer un rafraichissement d'une page, on
passe par le composant <ion-refresher>, à
positionner en tête de son <ion-content> :
<ion-refresher (ionRefresh)="doRefresh($event)">
<ion-refresher-content
pullingIcon="arrow-dropdown"
pullingText="Tirer pour mettre à jour"
refreshingSpinner="circles"
refreshingText="Mise à jour...">
</ion-refresher-content>
</ion-refresher>
45
46. Récupérer la saisie d'un
champ de saisie
• On commence par créer dans la classe (côté .ts,
donc), un attribut nom qui récupérera le contenu
nom : string = "";
• Ensuite, dans la page (côté .html), on utilise le
component ion-input avec la propriété ngModel
qui va permettre de faire le lien avec l'attribut
<ion-input [(ngModel)]="nom"></input>
46
47. Créer une barre de
navigation (1)
• On peut passer par le template ‘tabs’ lors de la création de son
projet
• Mais il est également possible de construire manuellement sa
barre de navigation :
• On commence par créer une ‘page’ qui ne contiendra que les
données de la barre de navigation :
• ionic g page Tabs
• On crée ensuite deux pages qui compléteront Home en tant
que pages pointées par la barre de navigation (pour
l'exemple : Actus, Pratique)
47
48. Créer une barre de
navigation (2)
• Cette page Tabs contient côté HTML la liste des entrées de la
barre :
<ion-tabs>
<ion-tab [root]="tab1Root" tabTitle="Home" tabIcon="home"></ion-tab>
<ion-tab [root]="tab2Root" tabTitle="Actus" tabIcon="actus"></ion-tab>
<ion-tab [root]="tab3Root" tabTitle="Pratique" tabIcon="pratique"></ion-tab>
</ion-tabs>
• Le fichier .ts va permettre de lier à chaque entrée une page :
import { HomePage } from '../home/home';
import { ActusPage } from '../actus/actus';
import { PratiquePage } from '../pratique/pratique';
export class TabsPage {
tab1Root: any = HomePage;
tab2Root: any = ActusPage;
tab3Root: any = PratiquePage; 48
49. Créer une barre de
navigation (3)
• Dernière étape, cette page Tabs doit être déclarée
comme étant la page d’entrée de l’application, en
remplacement de Home
• Cette opération se fait dans le fichier
app.components.ts :
import { TabsPage } from '../pages/tabs/tabs';
export class MyApp {
rootPage = TabsPage;
// rootPage = HomePage; // ancienne page d'entrée
49
50. Créer un menu latéral
• On peut passer par le template ‘sidemenu’ lors de
la création de son projet
• Mais il est également possible de construire
manuellement son menu
50
51. API REST et JSON
• Une API REST est un moyen de faire communiquer
un serveur et un client d’une manière souple,
simple et légère, en se basant :
• Sur le protocole de transfert HTTP (compatible
sur tous réseaux, léger, sécurisable) via ses
diverses déclinaisons : GET, POST, PUT, DELETE
• Sur l’encodage JSON des trames, pour pouvoir
transférer tout type de structure de données
51
52. JSON
• Ce format est une alternative plus légère à XML pour structurer des
données
• donnée simple : "name" : "Jean Dupont"
• données multiples : "movies": ["titre1", "titre2"]
• structure complexe :
{
"name":"paul rudd",
"movies":[
"I Love You Man",
"Role Models"
],
}
52
53. Exemple d’API REST
• Nous allons partir sur l’exemple complet en ligne
https://reqres.in
• Cet exemple permet à la fois de trouver des exemples de
fonctionnement pour comprendre ce qu’est une API REST
• mais aussi de source pour pouvoir construire ses
premiers projets sans se soucier de la partie serveur
• NB : le CMS WordPress est depuis sa version 4.7 un
bon support pour diffuser facilement des données via
une API REST
53
54. Exemple de requête REST
simple
https://reqres.in/api/users/2
avec la méthode GET permet de récupérer les données
du user 2
Données retournées :
{
"data": {
"id": 2,
"first_name": "lucille",
"last_name": "bluth",
"avatar": "https://s3.amazonaws.com/jstein/128.jpg"
}
}
54
L’id est directement
présent dans l’URL
55. Exemple de requête REST
de liste
• https://reqres.in/api/users/ (sans préciser d’id user) permet de
récupérer une liste
"page": "1",
"per_page": 3,
"total": 12,
"total_pages": 4,
"data": [
{
"id": 4,
"first_name": "eve",
"last_name": "holt",
"avatar": "https://s3.amazonaws.com/marcoramires/128.jpg"
},
{
"id": 5,
"first_name": "gob",
"last_name": "bluth",
"avatar": "https://s3.amazonaws.com/stephenmoon/128.jpg"
},
{
"id": 6,
"first_name": "tracey",
"last_name": "bluth",
"avatar": "https://s3.amazonaws.com/bigmancho/128.jpg"
}
]
} 55
56. Méthodes HTTP
• Même si ces méthodes sont indépendantes des
traitements, on les utilise communément pour les
usages suivants dans le cadre d’une API REST :
• GET : lecture de données
• POST : création de données
• PUT : mise à jour de données
• DELETE : suppression de données
56
Les codes d'erreur sont également
mis à contribution :
• 200 : lecture ou traitement OK
• 201 : création OK
• 404 : aucune donnée
• 401 : connexion refusée
57. Lecture d’une API REST (1)
• On commence par créer un ‘provider’, classe
réservée à un traitement qui n’est pas un affichage
• ionic g provider ApiTest
• Tant qu'on y est, on va également créer la page qui
nous servira à afficher la liste des utilisateurs
• ionic g page ListUsers
57
58. Lecture d’une API REST (2)
• Le provider contient le code de base pour appeler
l’API :
import { Http } from '@angular/http';
export class ApiTest {
baseurl: string;
constructor(public http: Http) {
this.baseurl = ‘https://reqres.in/api';
}
getURL(url)
{
url=this.baseurl + url;
return this.http.get(url).map(res => res.json());
}
58
L’adresse de l’API est
importante car elle doit être
stockée en dur dans le
code
Ce traitement est
asynchrone ; on y reviendra
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
59. Lecture d’une API REST (3)
• On peut ensuite utiliser le provider dans une page pour
peupler une liste
import {ApiTest} from '../../providers/api-test';
@Component({
providers: [ApiTest]
})
export class UnePage {
users: Array<any>;
constructor(public navCtrl: NavController, public apiTest:ApiTest) {
}
59
toute
variable passée en
entrée du constructeur
est ensuite accessible
en tant qu’attribut
attribut
qui stockera
la liste
Utilisation du
provider
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
60. Lecture d’une API REST (4)
• On complète ensuite la classe d'une initialisation consistant
à lire les données
ngOnInit() {
this.apiTest.getURL('/users/').subscribe(
data => {
this.users = data.data;
},
err => {
console.log("erreur :"+JSON.stringify(err));
},
() => console.log('Load Complete')
);
}
60
les données lues sont
ensuite stockées dans
l'attribut users
Traitement
asynchrone
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
"data": [
{
"id": 4,
…
},
{
"id": 5,
…
},
…
]
61. Lecture d’une API REST (5)
• On gère ensuite l’affichage de la liste dans la partie HTML
de la page
• <button> est un élément fixe, mais l'attribut *ngFor va
provoquer sa répétition
<ion-list>
<button ion-item *ngFor="let user of users">
{{user.first_name}}
</button>
</ion-list>
61
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
"data": [
{
"id": 4,
"first_name": "eve",
"last_name": "holt",
"avatar": "…"
},
62. Rendre les items
de la liste cliquables (1)
• On va maintenant modifier nos <button> pour qu'ils
soient cliquables
• chaque clic lance un appel à la méthode itemTapped(),
en passant en paramètre le user courant
<button ion-item *ngFor="let user of users"
(click)="itemTapped($event, user)">
…
</button>
62
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
63. Rendre les items
de la liste cliquables (2)
• Un clic sur un élément provoquera l’appel à une
méthode de la classe
itemTapped(event, user) {
this.navCtrl.push(UserPage, {
param_user: user
});
}
63
C'est l'objet user
complet qui est envoyé en
paramètre d’entrée de la
page UserPage
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
64. Rendre les items
de la liste cliquables (3)
• On va ensuite créer une page User
• Cette page contiendra le code nécessaire pour récupérer
le paramètre qu'on lui a fourni
user:any;
constructor(public navCtrl:NavController, public navParams: NavParams)
{
this.user = navParams.get('param_user');
}
64
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
/pages/user/user.ts
/pages/user/user.html
65. Rendre les items
de la liste cliquables (4)
• La page va ensuite pouvoir afficher le contenu à partir de
l'objet user qu'on a reçu en paramètre, sans
rechargement nécessaire
<ion-card>
<ion-card-content>
<ion-card-title>
Bonjour, {{user.first_name}} {{user.last_name}} !
</ion-card-title>
</ion-card-content>
</ion-card>
65
/providers/api-test.ts
/pages/list-users/list-users.ts
/pages/list-users/list-users.html
/pages/user/user.ts
/pages/user/user.html
66. Schéma récapitulatif
Serveur
Héberge des API
REST
HTTPapi-test.ts
provider de
lecture de l'API
list-users.ts
• Appel du
provider
• Stockage
dans
l'attribut
'users'
list-users.html
boucle sur
'users'
user.ts
Stockage de
'user' en
attribut
data
param_user : useritemTapped(user)
user.html
affiche 'user'
67. Créer un traitement
asynchrone
• Contrairement à ce qui se passe avec un
développement web classique, tout dans Ionic/
Angular fonctionne de manière asynchrone :
• On commence par afficher la page
• On s'occupe ensuite d'afficher les éléments
dynamiques au fur et à mesure de leur récupération
• L'objectif est de fluidifier au maximum la réactivité de
l'application
67
68. Subscribe
• Les appels aux API REST étaient asynchrones :
this.apiTest.getURL('/users/').subscribe(
data => {
this.users = data;
},
err => {
},
);
}
68
Traitement
asynchrone
N'est exécuté
qu'une fois la
lecture achevée
<ion-card *ngFor="let user of users">
…
</ion-card> L'affichage
est lui aussi
asynchrone
69. Créer ses propres
traitements asynchrones
getMedia(id)
{
return Observable.create(s => {
// traitement prenant du temps
var retour = /* retour préparé */;
s.next(retour);
s.complete();
},
err => {
console.log(JSON.stringify(err));
},
() => console.log('getMedia terminé')
);
}
69
this.apiTest.getMedia(id).subscribe(
data => {
this.users = data;
},
err => {
},
);
}
s.next() permet
d'envoyer des données en
retour de getMedia()
70. Afficher du contenu HTML
• Dans certains cas (par exemple en récupérant un
article de Wordpress), l'API peut vous retourner du
code HTML
• Pour que ce code soit interprété (et non pas
affiché), il faut utiliser une balise particulière :
<p [innerHTML]="user.descriptif"></p>
70
Pas besoin de
mettre des accolades
pour préciser qu'il s'agit
d'un attribut
71. Gestion des styles
• Le look des pages s'effectue via des styles CSS
situés :
• de manière globale, dans /src/app/app.scss
• pour chacune des pages, dans nom-page.scss
• Il est possible de lier un thème à son application
71
72. SCSS
• Il s'agit d'une déclinaison du langage CSS, avec
quelques rajouts bien utiles
• Chaque code SCSS est ensuite transformé en CSS
• Par exemple :
72
table.hl {
margin: 2em 0;
td.ln {
text-align: right;
}
}
li {
font: {
family: serif;
weight: bold;
size: 1.3em;
}
}
table.hl {
margin: 2em 0;
}
table.hl td.ln {
text-align: right;
}
li {
font-family: serif;
font-weight: bold;
font-size: 1.3em;
}
74. Insertion d'animations
• On peut utiliser la bibliothèque animate.css
https://daneden.github.io/animate.css/
1. Téléchargement et stockage dans
/src/assets/css
2. Chargement dans /src/index.html
<link href="assets/css/animate.css" rel="stylesheet">
3. Utilisation dans le code HTML des pages. Ex :
<ion-card class="animated fadeInLeftBig">
74
75. Plugins natifs
• Ces plugins sont la raison d'être même d'Ionic :
permettre de tirer le meilleur du hardware des
smartphones tout en restant dans un contexte web
• C'est Cordova qui est en charge de la partie native,
par toute une série de plugins développés dans les
langages natifs des environnements (iOS, Android)
• Liste complète des plugins (plus de 2000 !)
disponibles à l'adresse suivante :
https://cordova.apache.org/plugins/
75
76. Prise de photo
• On utilise le plugin natif cordova-plugin-camera :
ionic plugin add cordova-plugin-camera
• La page dédiée à la prise de photo sera ainsi constituée :
import {Camera} from 'ionic-native';
export class CapturePicPage {
public base64Image: string;
}
• L'attribut base64Image permettra de stocker la dernière photo prise
sous forme binaire
• On pourra bien sûr stocker ensuite cette photo en base de
données
76
77. Interface de prise de photo
• Dans cet exemple minimaliste, on se contente d'un bouton
pour lancer le capteur, et pour afficher la dernière photo
prise :
<ion-card>
<ion-card-content>
<h1>Prise de photo</h1>
<button ion-button (click)="takePicture()">Prendre une photo</button>
<h2>Dernière photo prise :</h2>
<img *ngIf="base64Image" [src]="base64Image" />
</ion-card-content>
</ion-card>
77
Le "src" est ici à
prendre au sens large
(donnée binaire, pas nom
de fichier)
La balise ne sera
présente que si l'attribut
base64Image n'est pas
vide
78. Méthode de prise de photo
• Dernière étape : créer la méthode takePicture() qui
sera appelée lors du clic sur le bouton :
takePicture(){
Camera.getPicture({
destinationType: Camera.DestinationType.DATA_URL,
targetWidth: 1000,
targetHeight: 1000
}).then((data) => {
this.base64Image = "data:image/jpeg;base64," + data;
}, (err) => {
console.log(err);
});
}
78
79. Stockage local de données
• On commence par créer un Provider qui centralisera
l'accès aux données. Il aura pour rôle :
• de stocker de manière centralisée les principales
données de l'application pendant son fonctionnement
• d'accéder à la base de données pour stocker des
données de manière persistante
• Attention, pour écrire les tests permettant de déterminer
la source des données, il faudra en permanence penser
aux traitements asynchrones
79
80. Créer un provider global
• Pour stocker des données qui seront accessibles tout au long
du fonctionnement de l'application :
• Créer un provider doté d'attributs dans lesquels on stocke
les données
• Déclarer ce provider directement dans le bloc @NgModule
de app.module.ts
• On pourra ensuite créer dans ce même provider des
méthodes d'accès à la base de données, pour apporter une
pérennité aux données au delà de la présence en mémoire
de l'application
Mettre en priorité les moteurs les plus performants
80
81. Différents styles de
stockage
• Il existe plusieurs moteurs de stockage de données pour
une app Ionic :
• SQLite (mini-SGBD embarqué sous forme de plugin
natif)
• IndexedDB (mini-SGBD conçu par Mozilla)
• WebSQL (API vers un SGBD porté sur Chrome et Safari)
• localstorage (stockage de données key->value
standardisé dans HTML5)
81
82. Installation
• Le seul élément essentiel est le module gérant le
frontal d'accès au stockage de données
npm install --save @ionic/storage
• Vous pouvez également installer le plugin (natif)
SQLite
cordova plugin add cordova-sqlite-storage --save
82
83. Configuration du module
• Tout se passe dans app.module.ts
import { IonicStorageModule } from '@ionic/storage';
…
@NgModule({
declarations: ...,
imports: [
IonicStorageModule.forRoot({
name: '__mydb',
driverOrder: ['indexeddb', 'sqlite', 'websql','localstorage']
})
],
83
Mettre en priorité les
moteurs les plus
performants
84. Utilisation de base
• Le principe universel est celui dit "key/value" : on stocke
une valeur, indexée par une clé
import { Storage } from '@ionic/storage';
…
constructor(……, public storage:Storage) {
this.storage.ready().then(() => {
// stockage d'une valeur
this.storage.set('name', 'Jean David');
//Lecture d'une valeur par sa clé
this.storage.get('session_ID').then((val) => {
console.log('votre ID session :', val);
})
});
84
Key Value
name Jean David
client 1
session_ID
enGHumY%2C-2De-F-
TDzNHVmE%2ChY5
85. Utilisation avec SQL
• Lorsque le moteur de base de données le permet, on peut
utiliser des requêtes SQL (simplifiées) pour structurer de
manière plus complexe ses données
import {SqlStorage, Storage} from '@ionic/storage';
export class HomePage {
private storage: Storage;
public constructor() {
this.storage = new Storage(SqlStorage);
this.storage.query("CREATE TABLE IF NOT EXISTS users
(id INTEGER PRIMARY KEY AUTOINCREMENT,
first_name TEXT,
last_name TEXT)");
}
85
86. Insertion de données
avec SQL
• Les traitements sont asynchrones
this.storage.query("INSERT INTO users (first_name,
last_name) VALUES (?, ?)", ["JD", "Olek"]).then((data) => {
console.log("ok!");
}, (error) => {
console.log(error);
});
86
Cette partie n'est
exécutée qu'une fois
l'insertion terminée
87. Lecture de données
avec SQL
• Là aussi, la lecture est asynchrone
let liste:Array<Object>;
this.storage.query("SELECT * FROM users").then((data) => {
if(data.res.rows.length > 0) {
liste = [];
for(let i = 0; i < data.res.rows.length; i++) {
liste.push({
"id": data.res.rows.item(i).id,
"first_name": data.res.rows.item(i).first_name,
"last_name": data.res.rows.item(i).last_name,
});
}
}
}, (error) => {
console.log(error);
});
87
88. Géolocalisation
• Une fois encore, on va utiliser un plugin natif
ionic plugin add cordova-plugin-geolocation
• La récupération des coordonnées est un simple traitement asynchrone
import { Geolocation } from 'ionic-native';
export class MaPage {
latitude : number;
longitude : number;
…
ionViewDidLoad() {
Geolocation.getCurrentPosition().then((position) => {
this.latitude=position.coords.latitude;
this.longitude=position.coords.longitude;
}
}
88
On peut aussi
imaginer faire plutôt un
provider pour gérer la
localisation
89. Géoloc et Google Maps
• Cette partie va être l'occasion de montrer l'usage
d'une librairie Javascript directement au sein d'une
app Ionic
• Le chargement de la librairie Google Maps se fait
directement dans le fichier index.html :
<script src="http://maps.google.com/maps/api/js"></script>
89
En production, il faut
rajouter ?key=MACLE en
mentionnant une clé
d'utilisateur
de Google Maps
90. Affichage de la map
• L'affichage sera une simple zone avec un marqueur #map qui
permettra au traitement de remplir plus tard cette zone avec
la carte
<ion-content>
<div #map id="carte"></div>
</ion-content>
• Du côté du fichier .ts, on utilise ViewChild et ElementRef
pour pouvoir manipuler ce bloc #map plus tard
import { Component, ViewChild, ElementRef } from '@angular/core';
declare var google;
export class HomePage {
@ViewChild('map') mapElement: ElementRef;
map: any;
90
.scroll-content {
height: 100%
}
#carte {
width: 100%;
height: 100%;
}
Le CSS permettra de
s'assurer du bon
affichage de la carte
91. Génération de la map
• Il nous reste encore à afficher la map. On le fait à la
suite de la géolocalisation
ionViewDidLoad(){
…
let mapOptions = {
center: new google.maps.LatLng(this.latitude,this.longitude),
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
this.map = new google.maps.Map(
this.mapElement.nativeElement, mapOptions);
}
91
Coordonnées
précédemment obtenues
par le plugin de
géolocalisation
Pointage vers
l'emplacement #map de la
vue