Cours Programmation Mobile Ionic IM2AG

Alexandre DEMEURE - Sylvain DEDIEU

         s.dedieu@criteo.com

Sylvain DEDIEU

Software development engineer –
#
ui-foundation 🛸

         s.dedieu@criteo.com

         s.dedieu@criteo.com

             alexandre.demeure@univ-grenoble-alpes.fr

Alexandre DEMEURE

Enseignant Chercheur #IIHM #UGA

Agenda

1

2

3

4

Pré-requis

Présentation

Démarrage

Projet

1

2

3

4

Pré-requis

Présentation

Démarrage

Projet

Pré-requis

Installation du JDK

Installation du JDK

export JAVA_HOME=/chemin/vers/java
export PATH=$JAVA_HOME/bin:$PATH

Vérifier que l'installation du JDK c'est bien passée

your/current/path % java -version

java version "11.x.x.x" YYYY-MM-DD LTS
Java(TM) SE Runtime Environment (build 11.x.x.x+x-LTS-x)
Java HotSpot(TM) 64-Bit Server VM (build 11.x.x.x+x-LTS-x, mixed mode, sharing)

Installation du SDK

Téléchargez Android Studio et installez-le.

SDK - Système Linux

export ANDROID_SDK_ROOT=/chemin/vers/Android/Sdk/
export PATH=${PATH}:${ANDROID_SDK_ROOT}/tools
export PATH=${PATH}:${ANDROID_SDK_ROOT}/platform-tools

SDK - Système MacOS

Pour les possesseurs de Mac, installez XCode depuis l'App Store.

Installation de Git

Installez Git si vous ne l'avez pas déjà.

your/current/path % git --version

git version x.xx.x (Apple Git-136)

Installation de NodeJS

Installez Node.js si vous ne l'avez pas déjà (préférez la version LTS).

Installation de NodeJS

export PATH=/chemin/vers/node/bin:$PATH

Vérifier que l'installation de NodeJS s'est bien déroulée

your/current/path % node --version
v22.xx.x
your/current/path % npm --version
10.xx.x

Installation de Ionic

Surtout ne pas faire du sudo sur Linux

npm install -g @ionic/cli
your/current/path % ionic --version
7.x.x

Installation de Angular CLI

Surtout ne pas faire du sudo sur Linux

npm install -g @angular/cli
your/current/path % ng version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI       : 21.0.3
Node.js           : 22.13.0
Package Manager   : npm 10.9.2
Operating System  : darwin arm64

Update de la version Angular

{
  "name": "kahoot",
  "version": "0.0.1",
  "author": "Ionic Framework",
  ...
  "private": true,
  "dependencies": {
    "@angular/animations": "^20.0.0",
    "@angular/common": "^20.0.0",
    "@angular/compiler": "^20.0.0",
    "@angular/core": "^20.0.0",
    "@angular/forms": "^20.0.0",
    "@angular/platform-browser": "^20.0.0",
    "@angular/platform-browser-dynamic": "^20.0.0",
    "@angular/router": "^20.0.0",
    ...
  },
  "devDependencies": {
    "@angular-eslint/builder": "^20.0.0",
    "@angular-eslint/eslint-plugin": "^20.0.0",
    "@angular-eslint/eslint-plugin-template": "^20.0.0",
    "@angular-eslint/schematics": "^20.0.0",
    "@angular-eslint/template-parser": "^20.0.0",
    "@angular/build": "^20.0.0",
    "@angular/cli": "^20.0.0",
    "@angular/compiler-cli": "^20.0.0",
    "@angular/language-service": "^20.0.0",
    ...
    "typescript": "~5.9.0"
  },
  "description": "An Ionic project"
}
{
  "name": "kahoot",
  "version": "0.0.1",
  "author": "Ionic Framework",
  ...
  "private": true,
  "dependencies": {
    "@angular/animations": "^21.0.6",
    "@angular/common": "^21.0.6",
    "@angular/compiler": "^21.0.6",
    "@angular/core": "^21.0.6",
    "@angular/forms": "^21.0.6",
    "@angular/platform-browser": "^21.0.6",
    "@angular/platform-browser-dynamic": "^21.0.6",
    "@angular/router": "^21.0.6",
    ...
  },
  "devDependencies": {
    "@angular-eslint/builder": "^21.0.6",
    "@angular-eslint/eslint-plugin": "^21.0.6",
    "@angular-eslint/eslint-plugin-template": "^21.0.6",
    "@angular-eslint/schematics": "^21.0.6",
    "@angular-eslint/template-parser": "^21.0.6",
    "@angular/build": "^21.0.6",
    "@angular/cli": "^21.0.6",
    "@angular/compiler-cli": "^21.0.6",
    "@angular/language-service": "^21.0.6",
    ...
    "typescript": "~5.9.0"
  },
  "description": "An Ionic project"
}
ng update @angular/cli @angular/core

1

2

3

4

Pré-requis

Présentation

Démarrage

Projet

Présentation

Qu'est ce qu'un Framework ?

Ensemble de fonctions pouvant être appelées à l’intérieur de votre code.

Libraries

Votre code est lu, transformé par le framework.

Framework

Ensemble d’outils et librairies facilitant la création et la construction d’applications.

SDK

Qu'est ce qu'un Framework ?

  1. Qu'est-ce que c'est ?
    Un Framework de développement d’applications mobiles.
     

  2. A quoi ça sert ?

    A construire des IPA, APK et PWA publiables sur les stores Android, IOS ou sur un serveur WEB.
     
  3. Comment ?

    En fournissant un ensemble d’outils permettant de travailler vite et bien.

     

Écosystème Ionic

SDK

Plateformes

Package manager

Langages Natifs

Components ready to use

Frameworks de développements

Documentation

1

2

3

4

Pré-requis

Présentation

Démarrage

Projet

Démarrage

Nouvelle application

ionic start <nom_de_son_application> blank --type=angular

Arborescence du projet

Points d'entrées de l'application Web

Fichier de configuration de l'application Mobile

Démarrer son application sur un serveur WEB local

ionic serve

Ajout de la Plateforme Android - Etape 1

ionic build
npm install @capacitor/android
npx cap add android

Ajout de la Plateforme Android - Etape 2

npx cap open android

Ajout de la Plateforme Android - Etape 3

  1. Installez un appareil via le AVD Manager


     
  2. Lancez l’application

 

Ajout de la Plateforme IOS - Etape 1

npm install @capacitor/ios
npx cap add ios

Ajout de la Plateforme IOS - Etape 2

npx cap open ios

Ajout de la Plateforme IOS - Etape 3

  1. Installez un appareil via le Device Manager


     
  2. Lancez l’application

 

1

2

3

4

Pré-requis

Présentation

Démarrage

Projet

Projet

Exercice 1 - Spec

Projet Ionic blank

Départ

Une application de Quiz qui permet de :

  • Créer plusieurs Quiz
  • Avoir plusieurs Questions par Quiz
  • Un Quiz possède :
    • un id
    • un label
    • une liste de Questions
  • Une Question possède :
    • un id
    • un label
    • un index
    • une liste de Choix
  • Un Choice possède :
    • ​un id
    • un label

Arrivée

Exercice 1 - Tip

Définition des interfaces

  • Créez en premier les interfaces pour les objets métier Quiz Question et Choice.
    Astuce: utilisez la méthode generate de la CLI pour aller plus vite


     
  • Renseignez les attributs des interfaces
  • id

  • title

  • description

  • questions

Quiz

  • id

  • text

  • choices

  • correctChoiceId

Question

ionic g interface models/<nom_de_mon_interface>
// eg: ionic g interface models/quiz
  • id

  • text

Choice

Exercice 1 - Tip

Définition du service

Créez un service de gestion des Quiz, de leurs Questions et Choix.
Astuce: utiliser la méthode generate de la CLI pour aller plus vite
 




Le service possède une variable privée qui correspond à un tableau (array) de toutes les Questions existantes.

Le service possède les méthodes:

ionic g service services/<nom_de_mon_service>
// eg: ionic g service services/quiz
getAll(): Promise<Quiz[]>;
get(quizId: string): Promise<Quiz>;
addQuiz(quiz: Quiz): Promise<Quiz>;
deleteQuiz(quizId: string): Promise<void>;
updateQuiz(updatedQuiz: Quiz): Promise<Quiz>;

Exercice 1 - Tip

Réalisation de la HomePage

 

  • Elle possède aussi un bouton d’ouverture de modale, qui contient un formulaire pour créer un nouveau Quiz et l’ajouter à la liste existante.
     
  • Chaque ion-card de Quiz est cliquable et permet d'accéder à un écran de détail d'un Quiz.
    Documentation utile: https://angular.dev/guide/routing

Exercice 1 - Tip

Réalisation de la Modale de création d'un Topic

Exercice 1 - Tip

Réalisation de la page de détail d'un Quiz

  • Un clic sur un objet Quiz de la HomePage permet d'accéder à la page de détails de cet objet Quiz.

  • La page doit afficher toutes les Questions sous forme d’ion-item.
    Documentation utile: https://ionicframework.com/docs/api/list https://ionicframework.com/docs/api/item
     

  • Elle possède aussi un bouton d'édition qui provoque l’ouverture de modale, qui contient un formulaire pour modifier le Quiz existant.

Exercice 1 - Pour aller plus loin - Test

npm install --save-dev cypress @testing-library/cypress
npx cypress open
  • Installation de Cypress

Exercice 1 - Pour aller plus loin - Test

  • Créer un fichier tsconfig.json dans "/cypress"
{
  "extends": "../tsconfig.json",
  "include": [
    "../node_modules/cypress",
    "**/*.cy.ts"
  ],
  "compilerOptions": {
    "noEmit": false,
    "sourceMap": false,
    "types": [
      "cypress", 
      "@testing-library/cypress"
    ]
  }
}
  • Editer le fichier cypress.config.ts
import { defineConfig } from "cypress";

export default defineConfig({
  viewportHeight: 760,
  viewportWidth: 360,
  e2e: {
    baseUrl: 'http://localhost:8100'
  },
});
  • Editer dans cypress/support/commands.ts
import '@testing-library/cypress/add-commands'

Exercice 1 - Pour aller plus loin - Test

  • Créer une nouvelle spec "add-a-new-quiz.cy.ts"
  • Ecrire son premier test

Exercice 1 - Suite

  • Ajout d'une méthode de suppression de Quiz

HomePage

Cours Asynchrone

Exercice 2 - Observables

Le quizService doit retourner uniquement des Observables

Les méthodes de:

  • Récupération des Quiz
  • Récupération d'un Quiz

doivent à présent retourner des Observables.

getAll(): Observable<Quiz[]>;
get(quizId: string): Observable<Quiz>;

Pour cela, changez la variable topics en BehaviorSubject et utilisez les méthodes asObservable() (pour la récupération) et next() (pour la modification).

Corrigé

Exercice 3 - Firebase - Créer son projet 1/5

Exercice 3 - Firebase - Créer son projet 2/5

Exercice 3 - Firebase - Créer son projet 2/5 (Google Analytics optionel)

Exercice 3 - Firebase - Créer son projet 3/5 (Google Analytics optionel)

Exercice 3 - Firebase - Créer son projet 4/5

Exercice 3 - Firebase - Créer son projet 5/5

Exercice 3 - Firebase - Créer sa base Firestore (1/4)

Exercice 3 - Firebase - Créer sa base Firestore (2/4)

Exercice 3 - Firebase - Créer sa base Firestore (3/4)

Exercice 3 - Firebase - Créer sa base Firestore (4/4)

Exercice 3 - Firestore - Mocker un Quiz (1/4)

Exercice 3 - Firestore - Mocker un Quiz (2/4)

Exercice 3 - Firestore - Mocker un Quiz (3/4)

Exercice 3 - Firestore - Mocker un Quiz (3/4) - Ne pas ajouter les questions dans le document !

Exercice 3 - Firestore - Mocker un Quiz (4/4)

Exercice 3 - Firestore - Mocker une Question (1/3)

Exercice 3 - Firestore - Mocker une Question (2/3)

Exercice 3 - Firestore - Mocker une Question (3/3)

Exercice 3 - Firestore - Récupérer les données

Affichage des données crées dans l'application

npm i @angular/fire --legacy-peer-deps
  • Configurer l'accès à la firebase en renseignant ses fichiers environments

Exercice 3 - Firestore - Récupérer les données

Affichage des données crées dans l'application

export const environment = {
  production: false,
  firebaseConfig: {
      apiKey: "<API_KEY>",
      authDomain: "<AUTH_DOMAIN>",
      projectId: "<PROJECT_ID>",
      storageBucket: "<STORAGE_BUCKET>",
      messagingSenderId: "<MESSAGING_SENDER_ID>",
      appId: "<APP_ID>",
      measurementId: "<MEASUREMENT_ID>"
  }
};
  • Configurer l'accès à la firebase en renseignant ses fichiers environments

Exercice 3 - Firestore - Récupérer les données

Ne pas commiter d'informations sensibles

git rm --cached src/environments/environment.ts
// do the same for production if needed

Exercice 3 - Firestore - Récupérer les données

Provide Firestore

bootstrapApplication(AppComponent, {
  providers: [
    ...
    provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
    provideFirestore(() => getFirestore()),
  ],
});

Exercice 3 - Firestore - Récupérer les données

Affichage des données crées dans l'application

Continuer en utilisant la documentation de l'API

import { Firestore, collection, collectionData, doc, docData } from '@angular/fire/firestore';

export class UserProfileService {
    private firestore: Firestore = inject(Firestore); // inject Cloud Firestore
  
    getAll() {
      // get a reference to the user-profile collection
      const userProfileCollection = collection(this.firestore, 'users');

      // get documents (data) from the collection using collectionData
      return collectionData(userProfileCollection, {idField: 'id'}) as Observable<UserProfile[]>;
    }
  
    getById(id: string) {
      // get a reference to the user-profile doc
      const userProfileDoc = doc(this.firestore, `users/${id}`);

      // get document (data) from the doc using docData
      return docData(userProfileDoc, {idField: 'id'}) as Observable<UserProfile>;
    }
}

Exercice 3 - Firestore - Post / Update /Delete

import { Firestore, addDoc, setDoc, deleteDoc } from '@angular/fire/firestore';

export class UserProfileService {
    private firestore: Firestore = inject(Firestore); // inject Cloud Firestore
  
    add(user: User) {
      // get a reference to the user-profile collection
      const userProfileCollection = collection(this.firestore, 'users');

      // add document from the collection using addDoc
      return addDoc(userProfileCollection, user);
    }
  
    edit(user: User) {
      // get a reference to the user-profile doc
      const userProfileDoc = doc(this.firestore, `users/${id}`);

      // add document from the collection using addDoc
      return setDoc(userProfileCollection, user);
    }
  
    delete(user: User) {
      // get a reference to the user-profile doc
      const userProfileDoc = doc(this.firestore, `users/${id}`);

      // delete document from the doc using deleteDoc
      return deleteDoc(userProfileDoc);
    }
}

Exercice 3.5 - Firestore - Tests

Installation de firebase emulator

# install firebase CLI
npm i -g firebase-tools

# login to firebase account
firebase login

# init frebase conf / link to existing project
firebase init
 ◉ Firestore: Configure security rules and indexes files for Firestore
 ◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
 ◉ Emulators: Set up local emulators for Firebase products

? Please select an option: Use an existing project
? Select a default Firebase project for this directory: (Use arrow keys)
❯ m2pgi-topic-manager (m2pgi-topic-manager) 

# validate all unitl

? What do you want to use as your public directory? www
? Configure as a single-page app (rewrite all urls to /index.html)? No
? Set up automatic builds and deploys with GitHub? No
✔  Wrote www/404.html
? File www/index.html already exists. Overwrite? No

=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm
 your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to 
proceed)
 ◉ Authentication Emulator
 ◉ Firestore Emulator
 ◉ Hosting Emulator

Exercice 4 - Fireauth - Configuration (1/4)

Exercice 4 - Fireauth - Configuration (2/4)

Exercice 4 - Fireauth - Configuration (3/4)

Exercice 4 - Fireauth - Configuration (4/4)

Exercice 4 - Fireauth - Implémentation (1/3)

  • Créer un AuthService en utilisant la CLI


    Le service possède des méthodes pour:

  • Enregistrer un nouvel utilisateur (createUser)
  • Connecter un utilisateur existant (signIn)
  • Déconnecter un utilisateur existant (signOut)
  • Savoir si quelqu'un est connecté ou non
ionic g service services/<nom_de_mon_service>
// eg: ionic g service services/auth
createUser(
  email: string, 
  password: string
): Promise<UserCredential>;

signIn(
  email: string, 
  password: string
): Promise<UserCredential>;

signOut(): Promise<void>;

isConnected(): User;

Exercice 4 - FireAuth - Implémentation (2/3)

  • Créer deux pages LoginPage & RegisterPage en utilisant la CLI

 

  • Créer un lien de Router pour aller à la page register depuis la page login (pas encore de compte ?)

 

  • Implémenter les formulaires
ionic g page pages/<nom_de_ma_page>
// eg: ionic g page services/login

Réalisation des pages formulaires register & login

Exercice 4 - FireAuth - Implémentation (3/3)

Ajouter un Guard pour éviter qu'un utilisateur non-connecté n'accède aux pages home et quiz-details

Ajout d'un Router Guard

Exercice 4.5 - FireAuth - Pour aller plus loin

Ajout de la confirmation de l'email et la récupération d'un mot de passe oublié

Exercice 5 - Topic Ownership & Sharing

Ajout de la fonctionnalité de propriété et partage d'un Topic

  • Un Quiz appartient à son "Owner", c'est-à-dire le User qui a créé le Topic.
     
  • Le Owner peut ajouter d'autres Users à son Quiz soit en tant que "Reader", soit en tant que "Editor".
     
  • Un User ne voit que les Quiz où il est soit Owner, soit Editor soit Reader.
     
  • Un User Reader ne peut ni éditer ni ajouter de Questions.
     
  • Un User Owner ou Editor peut éditer et ajouter des Questions.

Exercice 5 - Topic Ownership & Sharing

Ecrire ses Security Rules Firebase

IM2AG - Cours Programmation Mobile Ionic

By Dedieu Sylvain

IM2AG - Cours Programmation Mobile Ionic

  • 1,057