Dans le précédent article on a vu comment construire l’interface d’une application de quiz avec Ionic Creator. On a vu également comment importer sa création et créer un projet avec Intel XDK. Dans le présent article on va maintenant voir comment coder l’application avec cet IDE.
Le projet final complet XDK est disponible ici en téléchargement.
On fait le point du code
On va voir d’abord comment est organisé le code qu’on a obtenu. Voilà les dossiers :
Faisons un peu le point :
- img : prêt pour recevoir les images, à la base on n’en a pas
- js : notre application est ici avec les fichiers Javascript pour AngularJS
- lib : toutes les librairies d’Ionic
- templates : le code Html de nos pages (à la mode Ionic)
- xdk : des informations propres à l’IDE pour référencer le projet
Analysons de plus près les fichiers où on va devoir intervenir…
Mais dans un premier temps je constate que Ionic arrive en version 1.1.1 (dans lib/ionic) alors que la dernière version stable est la 1.2.4 comme on peut le voir ici. Alors tant qu’à faire je vais chercher la dernière version pour remplacer celle que Ionic Creator a envoyé. On pourra ainsi profiter des plus récentes possibilités.
index.html
C’est le fichier principal qui donne l’architecture de l’application. Voici le code obtenu :
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"> <title></title> <link href="lib/ionic/css/ionic.css" rel="stylesheet"> <script src="lib/ionic/js/ionic.bundle.js"></script> <!-- cordova script (this will be a 404 during development) --> <script src="cordova.js"></script> <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above <link href="css/ionic.app.css" rel="stylesheet"> --> <script src="js/app.js"></script> <script src="js/controllers.js"></script> <script src="js/routes.js"></script> <script src="js/services.js"></script> <script src="js/directives.js"></script> <!-- Only required for Tab projects w/ pages in multiple tabs <script src="lib/ionicuirouter/ionicUIRouter.js"></script> --> </head> <body ng-app="app" animation="slide-left-right-ios7"> <div style=""> <div style=""> <ion-nav-bar class="bar-stable"> <ion-nav-back-button class="button-icon icon ion-ios-arrow-back">Back</ion-nav-back-button> </ion-nav-bar> <ion-nav-view></ion-nav-view> </div> </div> </body> </html>
On trouve les références à Ionic et Cordova ainsi que l’appel du Javascript.
Ensuite l’application AngularJS est définie avec ng-app. On trouve aussi deux <div> qui me laissent un peu perplexe et que je vais supprimer rapidement si je n’en trouve pas l’utilité. Ensuite on a le composant ion-nav-bar pour la barre de navigation (documentation ici). Enfin on trouve le composant ion-nav-view qui permet de générer les vues et d’utiliser le routeur d’AngularJS (documentation ici). On va voir comment s’articule tout ça plus loin…
AngularJS
On trouve les fichiers d’AngularJS dans le dossier js :
app.js
C’est là que sont définis l’application et ses modules. On y trouve aussi les initialisations :
angular.module('app', ['ionic', 'app.controllers', 'app.routes', 'app.services', 'app.directives']) .run(function($ionicPlatform) { $ionicPlatform.ready(function() { // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard // for form inputs) if(window.cordova && window.cordova.plugins.Keyboard) { cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true); } if(window.StatusBar) { // org.apache.cordova.statusbar required StatusBar.styleDefault(); } }); })
controllers.js
Là on a deux contrôleurs tout prêts pour gérer nos deux pages :
angular.module('app.controllers', []) .controller('questionsCtrl', function($scope) { }) .controller('rSultatsCtrl', function($scope) { })
directives.js
Si on a des directives à créer c’est ici (mais on n’en aura pas besoin pour le quiz) :
angular.module('app.directives', []) .directive('blankDirective', [function(){ }]);
routes.js
Là c’est intéressant on a les routes de l’application :
angular.module('app.routes', []) .config(function($stateProvider, $urlRouterProvider) { // Ionic uses AngularUI Router which uses the concept of states // Learn more here: https://github.com/angular-ui/ui-router // Set up the various states which the app can be in. // Each state's controller can be found in controllers.js $stateProvider .state('tabsController.questions', { url: '/page2', views: { 'tab1': { templateUrl: 'templates/questions.html' } } }) .state('tabsController.rSultats', { url: '/page3', views: { 'tab2': { templateUrl: 'templates/rSultats.html' } } }) .state('tabsController', { url: '/page1', abstract:true, templateUrl: 'templates/tabsController.html' }) ; // if none of the above states are matched, use this as the fallback $urlRouterProvider.otherwise('/page1/page3'); });
Tout est organisé en états. Pour chaque état on a une url et une vue. Le générateur aurait pu associer les contrôleurs !
services.js
Ici on peut créer des services (on va s’en servir pour les questions et les résultats) :
angular.module('app.services', []) .factory('BlankFactory', [function(){ }]) .service('BlankService', [function(){ }]);
Les templates
On a un dossier pour les templates :
Très logiquement on retrouve ce qu’on a créé avec Ionic Creator : les deux pages et les onglets. On va devoir intervenir à ce niveau également pour coder l’application.
Les vues
On va commencer par compléter les templates pour obtenir le résultat désiré.
La page index
Voici la page d’index modifiée :
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"> <title></title> <link href="lib/ionic/css/ionic.css" rel="stylesheet"> <link href="css/style.css" rel="stylesheet"> <script src="lib/ionic/js/ionic.bundle.js"></script> <!-- cordova script (this will be a 404 during development) --> <script src="cordova.js"></script> <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above <link href="css/ionic.app.css" rel="stylesheet"> --> <script src="js/app.js"></script> <script src="js/controllers.js"></script> <script src="js/routes.js"></script> <script src="js/services.js"></script> <!-- Only required for Tab projects w/ pages in multiple tabs <script src="lib/ionicuirouter/ionicUIRouter.js"></script> --> </head> <body ng-app="app" animation="slide-left-right-ios7"> <ion-nav-bar class="bar-stable bar-assertive"> <ion-nav-back-button class="button-icon icon ion-ios-arrow-back">Back</ion-nav-back-button> </ion-nav-bar> <ion-nav-view></ion-nav-view> </body> </html>
J’ai ajouté la référence d’une page de style et une classe pour l’aspect.u
tabsController
Là je ne change pas grand chose, juste une petite classe pour la couleur :
<ion-view style="" title="Tabs Controller"> <ion-tabs style="" class="tabs-stable tabs-icon-top tabs-assertive"> <ion-tab title="Questions" icon="ion-android-list" href="#/page1/page2"> <ion-nav-view name="tab1"></ion-nav-view> </ion-tab> <ion-tab title="Résultats" icon="ion-ios-checkmark" href="#/page1/page3"> <ion-nav-view name="tab2"></ion-nav-view> </ion-tab> </ion-tabs> </ion-view>
questions
Là j’ai un peu plus de travail :
<ion-view title="Questions"> <ion-content class="has-header" padding="true"> <div class="list card"> <div class="item item-text-wrap">{{question}}</div> <div class="item item-body"> <div class="list"> <ion-radio ng-class="{ red: red[$index], green: green[$index] }" ng-click="choix($index)" ng-disabled="disabled" ng-repeat="reponse in reponses">{{reponse}}</ion-radio> </div> </div> </div> <button ng-show="bouton" ng-click="suivant()" class="button button-dark button-block icon-right" ng-class="iconeBouton">{{texteBouton}}</button> </ion-content> </ion-view>
Il faut prévoir l’interaction avec AngularJS.
Pour les questions/réponses
- emplacement pour le texte de la question
- répétition du bouton radio en fonction du nombre de réponses disponibles (ng-repeat)
- adaptation de la classe des boutons radio pour les rendre vert ou rouge selon que la réponse est vraie ou fausse (ng-class)
- mise en place d’un événement pour le choix de la réponse (ng-click)
- possibilité de rendre les boutons inactifs lorsqu’un choix a été fait (ng-disabled)
Pour le bouton :
- possibilité de le rendre invisible (ng-show)
- mise en place d’un événement pour le clic (ng-click)
- emplacement pour le texte du bouton
- classe pour pourvois changer l’icône (ng-class)
resultats
Là c’est plus simple :
<ion-view title="Résultats" ng-init="setResults()"> <ion-content class="has-header" padding="true"> <ion-list></ion-list> <ion-list> <ion-item>Vrai : {{vrai}}</ion-item> <ion-item>Faux : {{faux}}</ion-item> </ion-list> </ion-content> </ion-view>
On a besoin à chaque fois que la page apparaît de mettre les valeurs à jour (ng-init).
On prévoit les deux emplacements pour les valeurs des réponses (vrai et faux).
Les routes
Là aussi je n’ai pas changé grand chose :
angular.module('app.routes', []) .config(function($stateProvider, $urlRouterProvider) { $stateProvider .state('tabsController.questions', { url: '/page2', views: { 'tab1': { templateUrl: 'templates/questions.html', controller: 'questionsCtrl' } } }) .state('tabsController.resultats', { cache: false, url: '/page3', views: { 'tab2': { templateUrl: 'templates/resultats.html', controller: 'resultatsCtrl' } } }) .state('tabsController', { url: '/page1', abstract: true, templateUrl: 'templates/tabsController.html' }) ; $urlRouterProvider.otherwise('/page1/page2'); });
J’ai juste ajouté le nom des contrôleurs et annulé le cache pour la vue des résultats pour avoir à chaque fois l’initialisation.
Les services
J’ai prévu deux services, le premier qui gère les questions et les réponses :
.factory('quiz', [function(){ var questions = [ { question: 'Qui a écrit La Chartreuse de Parme ?', reponses: [ 'Balzac', 'Stendhal', 'Flaubert', 'Marivaux' ], vrai: 1 }, { question: 'Le Parthénon se trouve :', reponses: [ 'En Grèce', 'En Crète', 'En Sicile' ], vrai: 0 }, { question: 'Qui a composé Le Boléro ?', reponses: [ 'Camille Saint-Saens', 'Maurice Ravel', 'Serge Prokofiev' ], vrai: 1 }, { question: 'Qui a peint le plafond de la chapelle Sixtine ?', reponses: [ 'Léonard de Vinci', 'Michel-Ange', 'Véronèse' ], vrai: 1 }, { question: 'Quel cinéaste a réalisé le film E.T. en 1982 ?', reponses: [ 'James Cameron', 'Georges Lucas', 'David Lynch', 'Steven Spielberg' ], vrai: 3 } ]; return { getQuestion: function (index) { return questions[index]; }, getMax: function () { return questions.length; } }; }])
J’ai juste mis 5 questions pour faire tourner l’application.
Le second service est pour les résultats, étant donné qu’on doit les atteindre avec les deux contrôleurs :
.service('resultats', [function(){ var vrai = 0; var faux = 0 return { getVrai: function () { return vrai; }, incrementeVrai: function () { ++vrai; }, resetVrai: function () { vrai = 0; }, getFaux: function () { return faux; }, incrementeFaux: function () { ++faux; }, resetFaux: function () { faux = 0; } } }])
Les contrôleurs
questionsCtrl
C’est le contrôleur principal qui doit gérer les page des questions :
.controller('questionsCtrl', function($scope, quiz, resultats) { $scope.question = ''; $scope.reponses = {}; $scope.vrai = 0; $scope.red = []; $scope.green = []; $scope.bouton = false; $scope.disabled = false; $scope.texteBouton = 'Question suivante'; $scope.iconeBouton = 'ion-android-arrow-forward'; resultats.resetVrai(); resultats.resetFaux(); var step = 0; $scope.choix = function (index) { $scope.disabled = true; $scope.bouton = true; if(index == $scope.vrai) { // Bonne réponse $scope.green[index] = true; resultats.incrementeVrai(); } else { // Mauvaise réponse $scope.red[index] = true; $scope.green[$scope.vrai] = true; resultats.incrementeFaux(); } if(quiz.getMax() == step) { $scope.iconeBouton = 'ion-wand'; $scope.texteBouton = 'Recommencer'; step = -1; } }; $scope.suivant = function () { if(step == -1) { step = 0; $scope.texteBouton = 'Question suivante'; $scope.iconeBouton = 'ion-android-arrow-forward'; resultats.resetVrai(); resultats.resetFaux(); } getQuestion(); }; var getQuestion = function () { var item = quiz.getQuestion(step); $scope.bouton = false; $scope.disabled = false; $scope.question = item.question; $scope.reponses = item.reponses; $scope.vrai = item.vrai; $scope.red = []; $scope.green = []; for (var i = 0; i < item.reponses.length; i++) { $scope.red.push(false); $scope.green.push(false); } ++step; } $scope.suivant(); })
Je ne vais pas entrer dans le détail du code, je vous laisse l’analyser…
resultatsCtrl
Là c’est tout léger parcequ’on doit juste renseigner les résultats :
.controller('resultatsCtrl', function($scope, resultats) { $scope.setResults = function () { $scope.vrai = resultats.getVrai(); $scope.faux = resultats.getFaux(); } })
Le css
J’ai établi quelques règles css pour l’esthétique :
.scroll-content { background-color: brown; } .red { color: red; } .green { color: green; } .item { background-color: darksalmon; } .item-radio .item-content { background-color: #efbbb0; } .overflow-scroll { overflow-y: auto; }
Utilisation de Intel XDK
Emulation
On a vu dans le précédent article comment créer un projet pour le quiz avec Intel XDK. Si on utilise l’onglet Emulate on peut déjà prévisualiser l’application :
Fonctionnement
Si on choisi la bonne réponse elle change en vert et le bouton apparaît :
En cas de mauvaise réponse elle apparaît en rouge et la bonne en vert :
Vous voyez également qu’on a du choix dans le mobile émulé.
A tout moment on peut aller voir les résultats :
Lorsqu’on atteint la dernière question le bouton change d’aspect :
Bon, l’application est évidemment très rudimentaire mais largement suffisante pour se faire une idée du système !
Test en réel
L’émulation c’est pratique mais faire un essai sur un vrai appareil c’est mieux. Intel XDK propose de construire l’application sur leur serveur cloud :
Pour ça il faut créer un compte mais c’est gratuit.
Ensuite il faut installer l’application de prévisualisation sur son appareil à partir d’un store (Google, iOS ou Windows) et on peut ainsi lancer l’application sur son appareil. Simple et efficace !
On peut aussi faire du débogage en local.
Mais on peut surtout générer l’application pour tous les supports :
Mais évidemment il faudra bien renseigner les paramètres pour chaque plateforme (sur la page d’accueil) :
Voilà ce petit tour d’horizon des possibilités d’ionic est terminé. A vous de jouer si ça vous a donné envie !