Le setup pour produire une application native

Ce qui permet de produire une application native, c’est l’intégration de Capacitor à votre projet. Celle-ci est réalisée de facto lorsque vous créez votre projet Ionic.

Évidemment, comme vous souhaitez produire une application native, il faut la compiler pour chaque plateforme (iOS, android) sur laquelle vous souhaitez l’utiliser. Avant de faire cela, il faut réaliser l’opération suivante, qui vise à créer le répertoire www qui sera utilisé pour compiler en natif :

ionic build

Setups spécifiques aux plateformes :

Si vous souhaitez produire des applications Android, il faut installer le package Android de Capacitor et indiquer à votre projet que vous allez l’utiliser :

npm install @capacitor/android
npx cap add android

Si vous souhaitez produire des applications iOS, il faut installer le package ios de Capacitor et indiquer à votre projet que vous allez l’utiliser :

npm install @capacitor/ios
npx cap add ios

Attention

Pour compiler pour iOS, vous avez besoin d’être sur un OS Apple et d’avoir à disposition XCode.

Production de l'application native

Essentiellement, la production de l’application native se fait en 3 étapes :

  1. Compiler l’application web en exécutant la commande bash :

    ionic build
    
  2. Synchroniser avec l’appli native en exécutant la commande bash :

    npx cap copy
    
  3. Ouvrir Android studio ou XCode :

    npx cap open android
    

    Attention

    Pour pouvoir utiliser cette commande, il faut préciser à la ligne de commande le « path » d’android-studio :

    export CAPACITOR_ANDROID_STUDIO_PATH=`which android-studio`
    

    Pour Xcode, il faut utiliser plutôt :

    npx cap open ios
    

    Le résultat obtenu avec Android studio :

    Le rendu de l'application sur un smartphone

Création d'une Progressive Web Application (PWA)

Pour transformer votre application en PWA, il faut installer le package suivant :

ng add @angular/pwa

Cela va créer, notamment, deux fichiers : un web manifest (le manifest du PWA) et un fichier de configuration pour ngsw (qui va gérer le cache du service worker).

Les fichiers après avoir installé @angular/pwa

Notez que l’on a toujours le répertoire www et c’est dans celui-ci que l’on va produire notre PWA. Pour cela, il faut utiliser la commande suivante :

ionic build --prod -- --base-href "/xmobile_cours/maPWA/"

Ici, /xmobile_cours/maPWA/ est l’URL à partir de laquelle on accède à la PWA à partir d’un navigateur, comme indiqué ci-dessous :

La PWA dans un navigateur

Notez également dans la commande ionic build que, juste après le –prod, il y a un . Celui-ci sert à indiquer que ce qui suit sont des paramètres à passer à Angular plutôt qu’à Ionic.

Attention

Dans le –base-href, il est important de mettre un / à la fin de l’URL.

Dans l’image ci-dessus, on a ouvert les developer tools et, contrairement à d’habitude, ce qui nous intéresse n’est pas la console ou le network mais plutôt l’onglet application. On y voit qu’un service worker est associé à notre page, ce qui prouve bien qu’il s’agit d’une PWA.

Installation d'une PWA

Pour permettre d’installer aisément la PWA, on va créer un bouton d’installation. Le HTML est classique : on crée un bouton standard et on associe à son événement click une callback :

home.page.html
<ion-header [translucent]="true">
  <ion-toolbar>
    @if(promptEvent) {
      <ion-buttons slot="start">
        <ion-button (click)="onInstall()">Installer</ion-button>
      </ion-buttons>
    }
    <ion-title>Blank</ion-title>
    <ion-buttons slot="end">
      <ion-button (click)="openMenu($event)">
        <ion-icon slot="icon-only"
                  name="add-circle"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <p><a routerLink="/cours">vers la page cours</a></p>
</ion-content>

Notez le @if(promptEvent) : l’idée est d’avoir dans le TypeScript un attribut qui permet de déterminer si l’on a installé ou non l’application. Pour cela, ce TypeScript va exploiter l’événement beforeinstallprompt. Quand on démarre notre application, cet événement est émis si et seulement si on n’a pas encore installé l’application. Dans ce cas, on va s’arranger pour que promptEvent ne soit pas null. Ainsi, le bouton d’installation sera visible. Lorsque l’application a été installée, l’événement beforeinstallprompt ne sera pas émis et notre bouton d’installation ne sera pas visible.

home.page.ts
import { Component, OnInit } from '@angular/core';
import {
  IonHeader, IonToolbar, IonTitle, IonContent,
  IonList, IonItem,
  IonButtons, IonMenuButton, IonButton, IonIcon, PopoverController
} from '@ionic/angular/standalone';
import {MonServiceService, MyInfo} from "../mon-service.service";
import {RouterLink} from "@angular/router";
import {DropdownMenuComponent} from "../dropdown-menu/dropdown-menu.component";
import {addIcons} from "ionicons";
import {addCircle} from "ionicons/icons";
import {CommonModule} from "@angular/common";

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
  standalone: true,
  imports: [
    IonHeader, IonToolbar, IonTitle, IonContent,
    IonItem, IonList,
    RouterLink, IonButtons, IonMenuButton, IonButton, IonIcon,
    CommonModule
  ],
})
export class HomePage implements OnInit {
  info!: MyInfo;

  // un événement qui sera initialisé dans le constructeur si l'application n'a pas
  // été installée
  promptEvent: any = null;

  constructor(private mon_service: MonServiceService,
              private popover: PopoverController) {
    // l'événement "beforeinstallprompt" est émis chaque fois que l'on charge la
    // page et que l'application n'a pas encore été installée. S'il est émis, on
    // met à jour promptEvent
    window.addEventListener(
      'beforeinstallprompt', event => {
        this.promptEvent = event;
      });
  }

  async onInstall() {
    if (this.promptEvent !== null) {
      this.promptEvent.prompt();
      const { outcome } = await this.promptEvent.userChoice;
      if (outcome === 'accepted') {
        this.promptEvent = null;
      }
    }
  }

  async ngOnInit() {
    this.mon_service.getInfos().subscribe(res => {this.info = res;});

    addIcons({addCircle});
  }

  openMenu(myevent: MouseEvent) : void {
    // on crée le popup contenant le code du menu
    this.popover.create({
      component: DropdownMenuComponent, // on précise quoi inclure
      showBackdrop: true,
      cssClass: 'my-menu-class', // on peut préciser un css
      event: myevent,            // l'événement clic souris
      componentProps: { // ici, on indique les propriétés que l'on souhaite
        myprop: 'xxxx'  // initialiser dans le composant DropdownMenuComponent
      }
    }).then((popoverElement) => {
      popoverElement.present(); // on affiche le menu
      popoverElement.onDidDismiss().then((res) => {
        console.log(res);
      });
    });
  }
}

Avec le code ci-dessus, lorsque l’application n’est pas installée, on obtient l’affichage suivant :

Le bouton d'installation de la PWA

Lorsque l’on clique sur le bouton :

Lorsque l'on clique sur le bouton d'installation de la PWA

Après avoir cliqué sur install, l’application est installée. On peut la voir ici en allant sur l’URL chrome://apps :

L'application installée dans apps

Si l’on clique sur l’icône, cela démarre notre application. On peut voir tout en haut de la fenêtre qu’il s’agit bien d’une application et non plus simplement d’une page web. On remarque, d’ailleurs, qu’on ne peut plus saisir d’URL.

L'application installée dans apps

Exercice 1 : Forum - Le bouton d'ajout de nouveaux topics   

Rajoutez en haut de la page des topics un bouton permettant de rajouter de nouveaux topics. Reliez-le à une « alerte », similaire à celle que vous aviez construite pour le composant de login. Pour l’instant, dans l’alerte, affichez juste un texte « bidon » et faites en sorte qu’il y ait 2 boutons en bas de l’alerte: « cancel » et « créer ». Testez que votre alerte s’affiche bien.

Exercice 2 : Forum - Les rôles des boutons « cancel » et « créer »   

  1. Dans votre TypeScript, la méthode onDidDismiss() de votre alerte vous renvoie une Promise qui, une fois résolue, contiendra des informations, notamment, sur lequel des deux boutons « cancel » ou « créer » a fermé l’alerte. Pour visualiser ces informations, après le alert.present(), tapez l’instruction :

    alert.onDidDismiss().then((data) => {console.log(data);});
    

    Regardez ce que cela produit dans la console quand vous fermez votre alerte en appuyant sur le bouton « cancel », puis en cliquant sur le bouton « créer ».

  2. Il serait utile pour la suite que le rôle du bouton « créer » soit « créer » plutôt que undefined. Pour cela, dans le tableau buttons du alertController.create, au lieu de spécifier uniquement les noms des boutons, vous allez indiquer plus d’informations. Chaque bouton sera ainsi défini par un objet Javascript :

    { text: 'nom du bouton', role: 'nom du bouton' }
    

    Regardez maintenant ce que cela donne dans la console quand vous cliquez sur les boutons « cancel » et « créer ».

Exercice 3 : Forum - Le nom du nouveau topic   

Comme vous l’avez vu précédemment, vous avez créé votre alerte en passant au alertController.create un objet avec un champ message. Pour ajouter une balise <input> plutôt qu’un texte, il suffit de remplacer ce champ par un champ :

inputs: [
  {
    name: "l'attribut name de la balise <input>",
    type: "l'attribut text de la balise <input>",
    placeholder: "l'attribut placeholder de la balise <input>"
  }
]

Ajoutez donc votre balise <input> et testez ce que cela donne. Notamment, regardez dans la console comment récupérer ce que vous avez tapé dans l'input (cf. la méthode onDidDismiss).

Exercice 4 : Forum - La fin de l'alerte   

Maintenant, vous savez quel bouton a été utilisé pour fermer l’alerte ainsi que le texte qui a été saisi dans l'input de l’alerte. Créez une nouvelle méthode prenant en paramètre ce texte et qui va le transmettre au backend afin de créer le nouveau topic. Pour cela, inspirez-vous de ce que vous aviez écrit dans votre forum Angular dans le composant de dialogue du « create topic », ainsi que dans le composant des topics (mise à jour de la liste des topics à afficher).

Testez bien que votre base de données est bien mise à jour si le sujet n’existe pas déjà. Lorsque le sujet existe, affichez une nouvelle alerte indiquant que ce sujet existe déjà. Si, dans page des topics, vous affichez ces derniers, vérifiez que votre nouveau sujet apparaît bien.

Exercice 5 : Forum - Le back button de la mort qui tue   

Maintenant que votre page des topics est correcte, comptez le nombre de topics qu’elle contient ou regardez combien il y en a pour ce cours dans phpMyAdmin. Puis appuyez sur le back button. Si vous n’avez pas de back button (ce qui peut arriver si vous avez rechargé manuellement votre page dans votre navigateur), retournez à la page de login, sélectionnez un cours, ajoutez un nouveau topic, et recomptez le nombre de topics dans votre page, puis appuyez sur le back button.

Observez le nombre de topics indiqué dans le cours que vous venez de modifier. Il est fort probable qu’il soit incorrect. Dans ce cas, cela vient du fait que, dans la page des cours, vous avez téléchargé la liste des cours dans la méthode ngOnInit, comme en Angular. Malheureusement, lorsque l’on clique sur le back button, la méthode ngOnInit n’est pas réexécutée (cf. la séance 5). Pour pallier cela, placez le code de téléchargement de la liste des cours non pas dans la méthode ngOnInit mais plutôt dans la méthode ionViewWillEnter, comme vu dans la séance 5. Retestez.

 
© C.G. 2007 - 2025