Ionic : Comment créer une application mobile avec AngularJS et Sqlite

Dans cet article on va créer une application mobile de prise de notes ( à la evernote mais plus rudimentaire) en utilisant ionic Framework.

Pourquoi Ionic ?

Ionic est un framework pour la création d’applications mobiles cross platform qui se base sur HTML5, AngularJS et Cordova.

En plus du framework Javascript basé sur AngularJS, Ionic contient une suite d’outils qui permettent de configurer cordova, le but est de permettre à tout les développeurs web de créer rapidement une application mobile.

Je vais donc vous présenter dans cet article comment créer une simple application de TodoList afin de voir ce qu’Ionic à sous le ventre.

Préparation de l’environnement de dev

Avant de commencer

Avant de commencer à développer sur Ionic, il faut s’assurer que vous avez installé NodeJS.

Assurez vous aussi d’avoir bien installé les plateformes de destinations de votre choix, les 2 plateformes les plus répandues sont Android et IOS.

Installation des outils

On commence par installer Cordova :

npm install -g cordova

Après on installe Ionic :

sudo npm install -g gulp ionic

Génération de l’application

On va commencer par générer le squelette de base de notre application, pour ce faire entrez la commande suivante :

  ionic start appDemo blank
  

Cette commande va générer une application avec une template vide, Ionic propose 2 autres templates qui sont :

  • sidemenu : une application avec un menu.
  • tabs : application avec des onglets.

Ajout des plateformes de destination

Pour ce tuto je choisis Android comme plateforme de destination parce que c’est ce que j’ai sous la main actuellement, cependant vous pouvez remplacer android par ios si vous êtes sur Mac et que vous voulez tester votre application sur un appareil IOS.

cd appDemo
ionic platform add android 
cordova plugin add ionic-plugin-keyboard
ionic build android 
ionic emulate android

Dans le code ci dessus j’ajoute la plateforme android, puis installe le plugin keyboard ( necessaire pour les applications ionic), puis finalement lance l’application dans l’émulateur android.

Si tout ce passe bien, ionic lancera l’émulateur d’android avec votre application dedans, c’est bien beau tout ça mais pour le moment notre application ne fait rien du tout, donc commençons a développer notre application.

Vous avez aussi la possibilité de tester sur votre navigateur web avec la commande suivante :

  ionic serve --lab
  

le –lab sert à montrer sur votre navigateur web les versions ios et Android sur une seule fenêtre.

Création d’une application de prise de notes

Structure

On va créer une simple application de type CRUD pour la gestion de notes ( un peu comme evernote), notre application va contenir deux écrans:

  • Liste : ça va contenir la liste de toutes les notes
  • Edition : ça va contenir un formulaire pour éditer

Nos vues en HTML

Pour commencer on va mettre en place une barre de navigation qui contiendra le titre et un bouton pour revenir en arrière, pour ce faire, remplacez la balise body dans le fichier index.html par le code suivant :

  <body ng-app="starter">
    <ion-pane>
      <ion-nav-bar class="bar-positive">
        <ion-nav-back-button></ion-nav-back-button>
      </ion-nav-bar>
      <ion-nav-view></ion-nav-view>
    </ion-pane>
  </body>

Après, on va mettre en place l’écran qui va contenir la liste de toutes les notes :

dans le fichier: templates/list.html

<ion-view view-title="Liste des notes">
  <ion-content >

    <div class="card" ng-repeat="item in itemsList">
      <div class="item item-divider" ng-click="gotoEdit(item.id)">
        {{item.title}}
      </div>
      <div class="item item-text-wrap">
        {{item.content}}
      </div>
    </div>

    <a href="#/form/" class="button button-block button-positive">
      <i class="icon ion-plus-circled"></i> Ajouter
    </a>
  </ion-content>
</ion-view>

Puis la page de formulaire pour la création et modification d’une note :

dans le fichier: templates/form.html


<ion-view view-title="Edition d'une note">
  <ion-content>
    <div class="list">
      <label class="item item-input">
        <input type="text" placeholder="Title" ng-model="noteForm.title">
      </label>
      <label class="item item-input">
        <textarea placeholder="Comments" ng-model="noteForm.content"></textarea>
      </label>
    </div>

    <button ng-show="!noteForm.id" class="button button-full button-positive ng-hide" ng-click="saveNote()">
      Sauvegarder
    </button>

    <div ng-show="noteForm.id" class="button-bar">
      <a class="button button-positive" ng-click="saveNote()">
        <i class="icon ion-checkmark"></i>
      </a>
      <a class="button button-assertive" ng-click="confirmDelete(noteForm.id)">
        <i class="icon ion-trash-b"></i>
      </a>
    </div>
  </ion-content>
</ion-view>

Création des routes

Notre petite application va contenir deux routes, une pour la liste, et une autre pour acceder au formulaire d’édition, voici à quoi ressemble mon fichier app.js :

dans le fichier: js/app.js


angular.module('starter', ['ionic','starter.controllers', 'starter.services'])

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true)
      cordova.plugins.Keyboard.disableScroll(true)
    }
    if(window.StatusBar) {
      StatusBar.styleDefault()
    }
  })
})
.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider

    .state('list', {
      url: '/list',
      templateUrl: 'templates/list.html',
      controller: 'ListCtrl'
    })

    .state('form', {
      url: '/form/{id}',
      templateUrl: 'templates/form.html',
      controller: 'FormCtrl',
      params: {
        id: {value: null},
      },
    })

  $urlRouterProvider.otherwise('/list')
})

Stocker les données avec Sqlite

Pour le stockage de nos données j’ai choisit d’utiliser une base de données Sqlite, pour ce faire il faudra commencer par installer le plugin cordova suivant :

cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin.git

On va aussi profiter du fait que notre application ionic utilise AngularJS pour ajouter la librarie ngCordova, elle permet de faciliter l’utilisation des plugins cordova pour une application mobile hybride tournant sous AngularJS, dans notre cas on va l’utiliser pour faciliter l’utilisation de notre base de donnée Sqlite.

Pour faire cela je vais utiliser bower avec la commande suivante :

bower install --save ngCordova

Puis Ajouter la librairie dans le fichier index.html, au final le fichier index.html va ressembler à ça :

dans le fichier: index.html


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

    <!-- ionic/angularjs js -->
    <script src="lib/ionic/js/ionic.bundle.js"></script>
    <script src="lib/ngCordova/dist/ng-cordova.min.js"></script>

    <!-- cordova script (this will be a 404 during development) -->
    <script src="cordova.js"></script>

    <!-- your app's js -->
    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
    <script src="js/services.js"></script>
  </head>
  <body ng-app="starter">
  <ion-pane>
    <ion-nav-bar class="bar-positive">
      <ion-nav-back-button></ion-nav-back-button>
    </ion-nav-bar>
    <ion-nav-view></ion-nav-view>
  </ion-pane>
  </body>
</html>


Un service pour les opérations CRUD

Notre application aura besoin de faire des opérations de CRUD sur la base de données, afin de faire ça proprement on va mettre toute la partie gestion des données dans un service qu’on va nommer NotesDataService.

Je vais mettre ci dessous le code source du service de gestion de données, puis expliquer ce que ça fait ensuite:

dans le fichier: js/services.js


angular.module('starter.services', ['ngCordova'])
  .factory('NotesDataService', function ($cordovaSQLite, $ionicPlatform) {
    var db, dbName = "noteDemo.db"

    function useWebSql() {
      db = window.openDatabase(dbName, "1.0", "Note database", 200000)
      console.info('Using webSql')
    }

    function useSqlLite() {
      db = $cordovaSQLite.openDB({name: dbName, location : 1})
      console.info('Using SQLITE')
    }

    function initDatabase(){
      $cordovaSQLite.execute(db, 'CREATE TABLE IF NOT EXISTS T_NOTE (id integer primary key, title, content)')
        .then(function(res){

        }, onErrorQuery)
    }

    $ionicPlatform.ready(function () {
      if(window.cordova){
        useSqlLite()
      } else {
        useWebSql()
      }
      
      initDatabase()
    })

    function onErrorQuery(err){
      console.error(err)
    }

    return {
      createNote: function (note) {
        return $cordovaSQLite.execute(db, 'INSERT INTO T_NOTE (title, content) VALUES(?, ?)', [note.title, note.content])
      },
      updateNote: function(note){
        return $cordovaSQLite.execute(db, 'UPDATE T_NOTE set title = ?, content = ? where id = ?', [note.title, note.content, note.id])
      },
      getAll: function(callback){
        $ionicPlatform.ready(function () {
          $cordovaSQLite.execute(db, 'SELECT * FROM T_NOTE').then(function (results) {
            var data = []

            for (i = 0, max = results.rows.length; i < max; i++) {
              data.push(results.rows.item(i))
            }

            callback(data)
          }, onErrorQuery)
        })
      },

      deleteNote: function(id){
        return $cordovaSQLite.execute(db, 'DELETE FROM T_NOTE where id = ?', [id])
      },

      getById: function(id, callback){
        $ionicPlatform.ready(function () {
          $cordovaSQLite.execute(db, 'SELECT * FROM T_NOTE where id = ?', [id]).then(function (results) {
            callback(results.rows.item(0))
          })
        })
      }
    }
  })


Dans le service ci dessus, on va commencer par créer notre base de données en utilisant Sqlite, ce dernier etant indisponible sur le navigateur web de votre pc, on utilisera WebSql pour garder l’application utilisable sur l’environnement de developpement, pour détécter si l’application est executée sur mobile, on va tester si la variable window.cordova contient une valeur.

Après avoir établit la connexion avec la base de donnée, la prochaine étape est de créer la table qui va contenir nos données ( les notes).

finalement on retourne le service contenant la liste des fonctions exposées pour faire les opérations CRUD.

Important à noter

Avant d’appeler le plugin Sqlite, on doit s’assurer qu’il est déjà chargé, dans notre cas on risque de rencontrer ce problème lors du chargement des informations à partir de la base, car c’est la première chose que notre application fait, du coup le contr^oleur risque d’appeler sqlite avant que le plugin soit pr^et, c’est pourquoi on enveloppe les appèles à la base de données dans un $ionicPlatform.ready.

Controleurs

Il ne reste plus qu’a mettre en place nos controlleurs, dans notre cas on se limitera aux controlleurs suivants :

  • ListCtrl : relié à l’ecran qui va lister les éléments
  • FormCtrl : relié à l’ecran du formulaire, on va gérer ici l’ajout la modification et la suppression

angular.module('starter.controllers', [])

  .controller('ListCtrl', function ($scope,$ionicPlatform, $state, NotesDataService) {
    $scope.$on('$ionicView.enter', function(e) {
        NotesDataService.getAll(function(data){
          $scope.itemsList = data
        })
    })

    $scope.gotoEdit = function(idNote){
      $state.go('form', {id: idNote})
    }
  })

  .controller('FormCtrl', function ($scope, $stateParams, $ionicPopup, $state, NotesDataService) {
    $scope.$on('$ionicView.enter', function(e) {
      initForm()
    })

    function initForm(){
      if($stateParams.id){
        NotesDataService.getById($stateParams.id, function(item){
          $scope.noteForm = item
        })
      } else {
        $scope.noteForm = {}
      }
    }
    function onSaveSuccess(){
      $state.go('list')
    }
    $scope.saveNote = function(){

      if(!$scope.noteForm.id){
        NotesDataService.createNote($scope.noteForm).then(onSaveSuccess)
      } else {
        NotesDataService.updateNote($scope.noteForm).then(onSaveSuccess)
      }
    }

    $scope.confirmDelete = function(idNote) {
      var confirmPopup = $ionicPopup.confirm({
        title: 'Supprimer une note',
        template: 'êtes vous sûr de vouloir supprimer ?'
      })

      confirmPopup.then(function(res) {
        if(res) {
          NotesDataService.deleteNote(idNote).then(onSaveSuccess)
        }
      })
    }


  })

Avec les controlleurs en place, il ne reste plus qu’à tester l’application, pour rappel on peux le tester avec les commandes suivantes :

Dans le navigateur

ionic serve

Dans l’émulateur android

ionic emulate android

directement sur votre smartphone

ionic run android

Petite astuce : Débugger votre application avec chrome

Vous pouvez utiliser les outils de développement de chrome m^eme quand vous lancez votre application sur l’émulateur ou un smartphone android, pour ce faire dans la barre d’adresse ouvrez l’URL suivante :

  chrome://inspect/#devices
  

Conclusion

Comme vous avez pu le voir, ionic permet de créer des applications mobiles très rapidement, mais avant de vous emballer n’oublier pas qu’avec ionic on crée des applications hybrides, il faudra faire des efforts dans l’optimisation de votre code pour avoir des performances proches d’un développement natif, ce qui peux pousser pas mal de gens à préférer le développement natif, cependant rien que pour la rapidité de développement, Ionic peux ^etre utilisé comme outils pour créer des prototypes rapides de votre application.

Pour finir je vais mettre ici quelques captures d’écran du résultat final :

Voilà c’est tout pour ce tuto, n’hésitez pas à poser des questions dans la zone de commentaire.

50 commentaires

  • Amidou ZABRE

    Tres bon tuto ,vraiment instructif.Merci infiniment

  • Ouadie

    Meci pour le tuto, el code source est partagé quelque part ?

  • FunnyCrafteur

    Merci super article !

  • frank

    Merci beaucoup. Avec ça, je vais pouvoir commencer.

  • joeletoutffu

    Bonjour !!
    Merci pour ce tuto mais j’ai un soucis, tout fonctionne bien sur chrome mais quand je l’exporte pour tester sur mon téléphone l’enregistrement des notes ne se fait pas, quand je tape sur « sauvegarder » rien ne se passe (alors que ça fonctionne sur chrome)…
    Quelqu’un aurai un lien github avec une version qui fonctionne après l’export que je cherche mon erreur svp

    Merci !

  • ccaseau

    Bonjour,
    Et merci pour ce tuto

    Le code fonctionne bien quand je teste avec ionic serve sur chrome. Par contre une fois l’application exporté en .apk et testé sur mobile rien ne marche…
    En faisant tourné un débuggeur je reçois l’erreur : « Cannot call method ‘openDatabase’ of undefined »…

    Quelqu’un à t’il une idée de ce que je peux faire pour réparer ça ? ou un code source que je pourrais tester ?

  • Andrianimanana

    très bon tuto!!! mais je une problème lorsque je lance la commande « bower install –save ngCordova » il y des messages d’erreurs… merçi

  • zaineb lamine

    Je vous remercie énormément pour ce tuto!

  • Ysidore

    Tres bon tutoriel mon cher!! J’ai appris de bonne choses!
    Courage!

  • Lyon

    NO working in my cellphone Samsung galaxy s4, i believe that is’nt create bd in davice

  • sinen

    import io.liteglue.SQLCode;
    import io.liteglue.SQLColumnType;
    import io.liteglue.SQLiteConnector;
    import io.liteglue.SQLiteConnection;
    import io.liteglue.SQLiteOpenFlags;
    import io.liteglue.SQLiteStatement;

    unused library,.. !!! what’s the .jar file ??

  • Njara

    Très bon tuto, ça marche très bien.
    Mais juste une question, tu sais où il sauvegarde la bdd sqlite ?
    Je veux l’exploiter après via un autre programme, merci

  • Njara

    C’est bon j’ai trouvé, en fait, lors du test dans le navigateur chrome, il sauvegarde les données dans le webSql du navigateur.

    F12 > onglet application > storage > web SQL > ma_bdd > ma_table

  • Thierry Laurent

    Tres bon tutoriel, je vous remercie

  • ah

    Enfin un exemple avec sqlite ,merci pour ce tuto

  • maro

    external backup of the SQLite database in sd card for example, please

  • lamine sarr

    merci pour ce tuto mais j’ai un probleme d’installation du ploggin : cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin.git
    il me genere cet erreur :

    C:\Users\lamine\mesprojets\MesTest1>cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin.git
    Fetching plugin « https://github.com/brodysoft/Cordova-SQLitePlugin.git » via git clone
    Error: Failed to fetch plugin https://github.com/brodysoft/Cordova-SQLitePlugin.git via git.
    Either there is a connection problems, or plugin spec is incorrect:
    Error: « git » command line tool is not installed: make sure it is accessible on your PATH.

  • Mohamed

    Merci pour ce superbe tuto. Franchement c’est bien pour nous les débutants. Mais j’ai l’erreur suivante lorsque je fais cette commande: cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin.git

    npm install of external dependencies ok
    Failed to install ‘cordova-sqlite-storage’:CordovaError: Failed to find ‘ANDROID_HOME’ environment variable. Try setting setting it manually.
    Failed to find ‘android’ command in your ‘PATH’. Try update your ‘PATH’ to include path to valid SDK directory.
    at C:\Users\mouda\appDemo\platforms\android\cordova\lib\check_reqs.js:222:19
    at _fulfilled (C:\Users\mouda\appDemo\platforms\android\cordova\node_modules\q\q.js:834:54)
    at self.promiseDispatch.done (C:\Users\mouda\appDemo\platforms\android\cordova\node_modules\q\q.js:863:30)
    at Promise.promise.promiseDispatch (C:\Users\mouda\appDemo\platforms\android\cordova\node_modules\q\q.js:796:13)
    at C:\Users\mouda\appDemo\platforms\android\cordova\node_modules\q\q.js:857:14
    at runSingle (C:\Users\mouda\appDemo\platforms\android\cordova\node_modules\q\q.js:137:13)
    at flush (C:\Users\mouda\appDemo\platforms\android\cordova\node_modules\q\q.js:125:13)
    at _combinedTickCallback (internal/process/next_tick.js:67:7)
    at process._tickCallback (internal/process/next_tick.js:98:9)
    Error: Failed to find ‘ANDROID_HOME’ environment variable. Try setting setting it manually.
    Failed to find ‘android’ command in your ‘PATH’. Try update your ‘PATH’ to include path to valid SDK directory.

    • Bonsoir,

      je suis content que ça a été utile.

      pour ton erreur, il parait que l’environnement de dev d’android n’a pas été correctement installé sur ta machine ( il manque la variable ANDROID_HOME)

      assure toi que c’est bien installé voici un guide sympa https://spring.io/guides/gs/android/

      Bon courage.

      • Mohamed

        Merci pour ta réponse. Exactement. J’ai pu configurer ANDROID_HOME et le problème est réglé.

      • Mohamed

        Bonjour!

        C’est encore moi hein!

        J’ai cette erreur, est-ce que ça te dit quelque chose:

        C:\Users\mouda\appDemo>cordova plugin add ionic-plugin-keyboard
        Installing « ionic-plugin-keyboard » for android
        ANDROID_HOME=C:\Users\mouda\AppData\Local\Android\sdk
        JAVA_HOME=C:\Program Files (x86)\Java\jdk1.8.0_111
        Subproject Path: CordovaLib
        Starting a new Gradle Daemon for this build (subsequent builds will be faster).

        FAILURE: Build failed with an exception.

        * What went wrong:
        Unable to start the daemon process.
        This problem might be caused by incorrect configuration of the daemon.
        For example, an unrecognized jvm option is used.
        Please refer to the user guide chapter on the daemon at https://docs.gradle.org/2.14.1/userguide/gradle_daemon.html
        Please read the following process output to find out more:
        ———————–
        Error occurred during initialization of VM
        Could not reserve enough space for 2097152KB object heap

        * Try:
        Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output.
        Failed to install ‘ionic-plugin-keyboard’:Error: cmd: Command failed with exit code 1
        at ChildProcess.whenDone (C:\Users\mouda\appDemo\platforms\android\cordova\node_modules\cordova-common\src\superspawn.js:169:23)
        at emitTwo (events.js:106:13)
        at ChildProcess.emit (events.js:191:7)
        at maybeClose (internal/child_process.js:877:16)
        at Process.ChildProcess._handle.onexit (internal/child_process.js:226:5)
        Error: cmd: Command failed with exit code 1

        C:\Users\mouda\appDemo>

        Et pourtant dans Android Studio, dans Settings, je suis allé configurer –stacktrace et j’ai augmenté aussi la taille au maximum…Bref, j’ai toujours la même erreur.

        • Salut,

          je vois ici une erreur de heap space insuffisant, oriente tes recherches sur comment augmenter le heap space pour ton application,

          voici un indice sur un problème simmilaire : http://stackoverflow.com/questions/30346229/ionic-android-build-not-enough-memory-to-start-jvm

          bon courage

          • Mohamed

            Bonjour,

            J’ai fait toutes les installations sans erreur. À la fin, quand je lance l’une des commandes suivantes (puisque c’est les deux que j’ai):
            Dans le navigateur
            ionic serve

            Dans l’émulateur android
            ionic emulate android

            ça n’a pas marché. Je me dis ou bien j’ai mal nommé les fichiers? ou bien les fichiers doivent être dans le même répertoire? Bref, voici les repertoires que j’ai utilisé ainsi que les noms:

            C:\Users\mouda\appDemo\js:
            – app.js
            – controllers.js
            – services.js

            C:\Users\mouda\appDemo\templates:
            -form.html
            -index.html
            -list.html

            Ou bien j’ai mal nommé? ou bien mal positionné?

            Merci pour ton soutien.

            Momo.

  • Mohamed

    Bonjour!

    C’est bon!!!Waoh!!!Ça a marché super bien. Merci Marwane .

    C’est très bien.

  • sanae

    Bonjour

    ça marche très bien quand je teste avec ionic serve sur chrome. Par contre une fois l’application exporté en .apk et testé sur mobile rien ne marche…

    est ce que qlq un a une idee svp

  • mokai

    Bonjour Merci bien pour ce tuto qui marche a merveille .

    puis je avoir un super tuto aussi sur SQLite avec ionic 2 cella me fera vraiment du bien

    merci

  • deyris

    Tres bien detaillé. Merci beaucoup pour ce tuto. ça marche propre!!!

    SVP, J’aimerais exporter les notes enregistrées dans l’application vers un fichier excel ou word. je suis tombé sur le plugin  »ng-csv » dans mes petites fouilles. mais je ne sais pas trop comment m’y prendre. Si quelqu’un a une idée ?
    Que ce soit par rapport à ce plugin ou à un autre moyen d’exporter.
    Merci d’avance.

  • deyris

    please help!!

  • Krychylskyy

    Merci!!!!

  • Krychylskyy

    Bonjour,
    merci pout tuto!
    J’ai une question. Je ne peux pas ajouter plusieurs VALUES
    essayant
    ‘INSERT INTO T_NOTE (title, content) VALUES(?, ?, ?, ? ….n?)’, [note.title, note.content, note.content_2, note.content_3, ….note.content_n)

    mais ca ne marche pas.

    HELP ME s’il vous plaite!!
    Merci

  • yohanan

    très instructif comme article , merci !

  • tenemos

    Bonjour.
    je travail avec ionic 2 svp je veu savoir si ce sont les même lignes de code pour la base de donnée ? svp je suis coinse à ce niveau

  • Caroline

    Bonjour,

    Merci beaucoup pour votre tutoriel qui fonctionne très bien.
    Cependant, j’ai déployé votre application sur Android & Iphone, sur Android elle fonctionne très bien mais sur Iphone il semble qu’il y aurait un problème lors de l’ajout de la note.
    Pourquoi j’ai ce problème ?
    Comment puis-je le résoudre ?

    Merci d’avance !
    Caroline

  • Seb

    Merci pour cette article très clair et concis

  • Saabid

    salut les amis, svp j’utilise inonic2 alors comment realiser cet tuto avec ionic2. jai essayer ùais sa marche pas car les contenus de ionic2 sont different de celles de ionic1.. merci

    • Bonjour,

      la réponse est dans la question, ce tuto est fait pour IONIC Version 1, Ionic 2 est basé sur Angular 4, du coup c’est normal que ça ne marche pas.

      • Saabid

        ok merci, j’ai reussir a fair avec ionic1. ce tres gentil

      • Saabid

        chef svp j’ai un souci encore, j’ai rencontré le meme probleme qu’un ami a mensionné en dessus de commentaires mais malgré j’ai pas reussit. le problem ce lorsque je teste sur un navigateur sa marche bien mais sur un telephone sa ne marche pas, si je clique sur sauvegarder rien ne change. Aider moi

  • Saabid

    tres cool j’ai reussi a 100/100

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *