Bonjour à tous, dans ce tuto Ionic 4 nous allons voir comment créer une application calendrier avec Firebase et le plugin Ionic Calendar.

Nous allons afficher une calendrier complet dans notre application Ionic, et permettre d’afficher et d’ajouter des évènements en passant par Firebase.

Cela nous donnera une application dynamique, connectée à internet et nous offre un exemple avancé de ce que l’on peut faire en associant Firebase à d’autres fonctionnalités.

IONIC CALENDAR: Comment créer une app Ionic 4 Calendrier ?

Pour utiliser le plugin Ionic Calendar et créer une application calendrier, nous allons suivre les étapes suivantes:

  1. Installer le plugin Ionic Calendar
  2. Afficher un calendrier dans Ionic 4
  3. Ajouter un évènement dans Ionic Calendar
  4. Stocker les évènements dans Firebase
  5. Ouvrir un modal pour les détails de l’événement

Comment installer le plugin Ionic Calendar ?

Pour commencer, nous allons voir les étapes pour installer le plugin Ionic Calendar dans notre application Ionic 4.

Je vous invite en premier lieu à consulter la documentation officielle de ce plugin en parallèle: https://github.com/twinssbc/Ionic2-Calendar

Également, vous pouvez consulter les démonstration de ce plugin aux adresses suivantes:

C’est partie pour l’installation du plugin, avec tout simplement la commande suivante à entrer dans votre terminal (avec sudo sur Mac):

npm install ionic2-calendar --save

Une fois l’installation terminée, vous pouvez relancer votre application Ionic dans votre navigateur avec la commande:

ionic serve

Pour ce plugin, contrairement à certains nous n’avons pas besoin de le déclarer de manière globale dans notre application.

La seule obligation est de le déclarer dans le fichier module de la page dans laquelle nous souhaitons afficher notre calendrier.

Dans mon cas il s’agit de la page Home et du fichier:

src/app/home/home.module.ts

Importer le module de notre calendrier avec la ligne de code suivante:

import { NgCalendarModule  } from 'ionic2-calendar';

Puis déclarer ce module dans le tableau imports de votre fichier:

@NgModule({
  imports: [
    NgCalendarModule,
    CommonModule,
    FormsModule,
    ...

Maintenant que nous avons installé et déclaré le plugin Ionic Calendar, nous pouvons passer à l’affichage d’un calendrier dans notre application.

Comment afficher un calendrier dans Ionic 4 ?

Le plugin Ionic Calendar nous fait quasiment tout le travail finalement, après avoir installé le plugin nous avons simplement à utiliser la balise <calendar>.

Voilà par exemple la première version de notre calendrier avec les paramètres et attribut que nous allons détailler:

<calendar id="myCalendar"
  locale="fr-FR"
  calendarMode="month" 
  startHour="6"
  endHour="20"
  step="30"
  startingDayWeek="1"
  [currentDate]="currentDate"
  (onTitleChanged)="onViewTitleChanged($event)">
</calendar>

Comme vous pouvez le voir, nous commençons par donner un identifiant à notre calendrier ainsi qu’un ensemble d’attribut.

Certain de ces attributs font appel à des variables que nous allons déclarer dans notre fichier Typescript, les autres sont standards.

Rendez-vous donc dans le fichier suivant pour continuer la configuration de notre calendrier:

src/app/home/home.page.ts

On commence par importer le plugin ViewChild pour identifier notre calendrier avec son identifiant ainsi que le composant de notre calendrier CalendarComponent:

import { Component, ViewChild } from '@angular/core';
import { CalendarComponent } from 'ionic2-calendar/calendar';

Ensuite nous passons à la déclaration des variables essentielles au fonctionnement de notre calendrier.

La première est tout simplement la date actuelle pour positionner notre calendrier à cette date précise:

currentDate = new Date();

La deuxième variable est une simple chaîne de caractère pour stocker le mois en cours sélectionner, que nous afficherons dans le header de notre page:

currentMonth: string;

Vous pouvez rapidement modifier le titre de votre page de la manière suivante:

<ion-title>
  {{ currentMonth }}
</ion-title>

Enfin on déclare notre calendrier dans notre fichier Typescript pour pouvoir le manipuler et par lui suite, lui affecter des évènements par exemple:

@ViewChild(CalendarComponent, {static: false}) myCalendar: CalendarComponent;

La dernière chose à faire dans cette page est de déclarer la fonction onViewTitleChanged() qui est préconçu avec le calendrier pour renvoyer le mois en cours.

Déclarez donc cette fonction pour affecter à notre variable currentMonth, le nouveau mois en cours d’affichage:

onViewTitleChanged(title: string) {
  this.currentMonth = title;
}

Pour finir la configuration de notre calendrier, et traiter notamment l’affichage de celui-ci en français rendez-vous à nouveau dans le fichier module de votre page en cours:

src/app/home/home.module.ts

Puis importer et déclarer la langue locale de votre application pour afficher notre calendrier en français:

import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
registerLocaleData(localeFr, 'fr');

La toute dernière touche consiste à modifier la couleur de votre calendrier et celle du jour sélectionné, ainsi que les jours comportant des évènements.

Pour cela rendez-vous dans le fichier CSS suivant (s’applique à toute l’application):

src/global.scss

Et ajouter les lignes de codes suivantes, en modifiant selon votre souhait les couleurs des cases de votre calendrier:

.monthview-primary-with-event { background-color: #FF5722!important; }
.monthview-selected { background-color: #FF9800 !important; }

Voilà la configuration de votre calendrier est terminée, il devrait s’afficher en français dans votre page d’accueil désormais.

Comment ajouter un évènement avec Ionic Calendar ?

Passons maintenant à l’une des fonctionnalités les plus utiles des calendriers: la création et consultation d’évènement.

Jusqu’à présent nous n’avons fait qu’afficher le calendrier dans notre application Ionic, nous allons désormais interagir avec lui.

Pour ajouter un évènement à notre calendrier, nous allons afficher un formulaire à la place de celui-ci.

Ainsi pour afficher ou masquer le formulaire, nous créons un bouton flottant associé à la fonction showHideForm():

<ion-fab vertical="bottom" horizontal="end" slot="fixed">
  <ion-fab-button (click)="showHideForm()">
    <ion-icon *ngIf="!showAddEvent" name="add"></ion-icon>
    <ion-icon *ngIf="showAddEvent" name="close"></ion-icon>
  </ion-fab-button>
</ion-fab>

La subtilité de l’affichage ou non de notre formulaire réside simplement dans la variable booléenne suivante:

showAddEvent: boolean;

De cette manière, on affiche ou cache notre formulaire en fonction de la valeur de showAddEvent, qui peut valoir true ou false.

On ajoute à notre formulaire la condition *ngIf=”showAddEvent” comme ceci pour ne l’afficher que si la variable vaut true:

<div *ngIf="showAddEvent" class="ion-padding">
  <ion-item>
    <ion-label><b>Titre:</b></ion-label>
    <ion-input type="text" placeholder="Titre" [(ngModel)]="newEvent.title"></ion-input>
  </ion-item>
  <ion-item>
    <ion-label><b>Description:</b></ion-label>
    <ion-input type="text" placeholder="Description" [(ngModel)]="newEvent.description"></ion-input>
  </ion-item>
  <ion-item>
    <ion-label><b>Image:</b></ion-label>
    <ion-input type="text" placeholder="URL de l'image" [(ngModel)]="newEvent.imageURL"></ion-input>
  </ion-item>
  <ion-item>
    <ion-label><b>Début</b></ion-label>
    <ion-datetime doneText="OK" cancelText="Annuler" i18n displayFormat="DD/MM/YYYY HH:mm" pickerFormat="MMM D HH:mm" [(ngModel)]="newEvent.startTime" [min]="minDate"></ion-datetime>
  </ion-item>
  <ion-item>
    <ion-label><b>Fin</b></ion-label>
    <ion-datetime doneText="OK" cancelText="Annuler" displayFormat="DD/MM/YYYY HH:mm" pickerFormat="MMM D HH:mm" [(ngModel)]="newEvent.endTime" [min]="minDate"></ion-datetime>
  </ion-item>
  <ion-button color="warning" expand="block" (click)="addEvent()">Ajouter</ion-button>
</div>

Comme par défaut lorsque je déclare une variable booléenne elle vaut false, le formulaire sera caché au lancement de la page, et je n’ai qu’à utiliser la ligne de code suivante pour afficher ou cacher mon formulaire:

this.showAddEvent = !this.showAddEvent;

Et enfin vous l’avez sans doute remarqué, nous associons notre formulaire à un objet pour contenir ces champs, la variable newEvent que nous déclarons dans notre page:

newEvent = {
  title: '',
  description: '',
  imageURL: '',
  startTime: '',
  endTime: ''
};

Je peux ainsi terminer ma fonction showHideForm() que nous avons associé à notre bouton flottant, pour cacher ou afficher notre formulaire à chaque clique.

Également, à chaque ouverture ou fermeture du formulaire, nous réinitialisons tous ses champs comme ceci:

showHideForm() {
  this.showAddEvent = !this.showAddEvent;
  this.newEvent = {
    title: '',
    description: '',
    imageURL: '',
    startTime: new Date().toISOString(),
    endTime: new Date().toISOString()
  };
}

Maintenant que nous sommes capables d’afficher et de cacher notre formulaire, il nous faut terminer la configuration de notre formulaire.

Nous avons encore besoins de quelques variables, notamment minDate pour indiquer la date minimum (actuelle) que l’utilisateur peut sélectionner pour la date du nouvel évènement.

minDate = new Date().toISOString();

En plus de celle-ci, nous créons une variable et plus précisément un tableau pour stocker tous les évènements que nous aurons créé pour notre calendrier:

allEvents = [];

Nous pouvons alors passer à la fonction addEvent() pour créer et ajouter notre événement à notre tableau.

Le principe est assez simple, on ajoute ce nouvel évènement dans le tableau allEvents, puis on demande à notre calendrier de recharger ses évènements, enfin on cache notre formulaire:

addEvent() {
  this.allEvents.push({
    title: this.newEvent.title,
    startTime:  new Date(this.newEvent.startTime),
    endTime: new Date(this.newEvent.endTime),
    description: this.newEvent.description,
    imageURL: this.newEvent.imageURL
  });
  this.showHideForm();
}

En ce qui concerne notre calendrier, nous devons aussi le mettre à jour, lui ajouter la condition *ngIf”!showAddEvent” pour ne l’afficher que si la variable vaut false.cher que si la variable vaut false.

Et enfin ajouter l’attribut eventSource associé à la variable allEvents pour indiquer à notre calendrier que les évènements de ce calendrier sont stockés dans la variable allEvents:

<calendar *ngIf="!showAddEvent" id="myCal"
    [locale]="calendar.locale"
    [eventSource]="allEvents" 
    [calendarMode]="calendar.mode" 
    [currentDate]="calendar.currentDate"
    (onTitleChanged)="onViewTitleChanged($event)"
    (onTimeSelected)="onTimeSelected($event)" 
    startHour="6"
    endHour="20"
    step="30"
    startingDayWeek="1">
</calendar>

J’ai également ajouter la fonction onTimeSelected() pour que la date du formulaire ce mette à jour avec la date et le jour actuellement sélectionnée dans le calendrier:

onTimeSelected(ev: any) {
  const selected = new Date(ev.selectedTime);
  this.newEvent.startTime = selected.toISOString();
  selected.setHours(selected.getHours() + 1);
  this.newEvent.endTime = (selected.toISOString());
}

Voilà, nous sommes maintenant capables d’ajouter des évènements à notre calendrier!

Le problème c’est que ces évènements ne sont pas stockés, il suffit de rafraichir la page pour perdre toutes nos informations.

C’est pour cette raison que nous allons faire appel à Firebase.

Comment stocker les évènements dans Firebase ?

Pour cette partie, vous avez besoin d’une application Ionic associée à un projet Firebase valide.

Je vous invite donc à suivre mon tuto Ionic 4 sur l’utilisation d’AngularFire: https://drissas.com/associer-firebase-application-ionic-4/

Si votre application est déjà configurée avec AngulareFire, vous pouvez passer à la partie suivante.

Commencez par vous rendre dans le fichier suivant:

src/app/home/home.page.ts

Puis à importer le module Database du plugin AngulareFire, qui nous permettre d’envoyer et de récupérer des inforamations de notre base de données:

import { AngularFireDatabase } from '@angular/fire/database';

Puis déclarer ce plugin dans votre constructor() pour pouvoir l’utiliser dans le reste de votre page:

constructor(
    public afDB: AngularFireDatabase
  ) {}

Une fois l’importation et la déclaration de votre plugin terminée, la création de la fonction d’envoi dans Firebase est très simple.

On modifie la fonction addEvent(), pour qu’elle ajoute les informations de notre évènement à Firebase, en utilisant la sous-fonction push() issue du plugin AngulareFireDatabase:

addEvent() {
  this.afDB.list('Events').push({
    title: this.newEvent.title,
    imageURL: this.newEvent.imageURL,
    startTime:  this.newEvent.startTime,
    endTime: this.newEvent.endTime,
    description: this.newEvent.description
  });
  this.showHideForm();
}

À chaque fois que nous ajoutons un évènement, il est stocké dans la table ‘Events’ de notre base de données Firebase, puis nous refermons le formulaire.

Il nous faut maintenant récupérer ces évènements depuis Firebase et les affecter à notre calendrier.

Je créé donc la première version de ma fonction loadEvents() qui comme son nom l’indique va charger nos évènements dans le calendrier.

Pour commencer, je demande à cette fonction de faire une boucle dans ma table Events et de m’afficher pour chaque évènement rencontré son titre.

loadEvent() {
  this.afDB.list('Events').snapshotChanges(['child_added']).subscribe(actions => {
    actions.forEach(action => {
    	console.log('Titre: ' + action.payload.exportVal().title);
    });
  });
}

Ensuite je n’ai plus qu’à effectuer les même étapes que dans la fonction addEvent précédente, en ajoutant à ma variable allEvents chaque évènement rencontré dans la base:

loadEvent() {
  this.afDB.list('Events').snapshotChanges(['child_added']).subscribe(actions => {
    this.allEvents = [];
    actions.forEach(action => {
      console.log('action: ' + action.payload.exportVal().title);
      this.allEvents.push({
        title: action.payload.exportVal().title,
        startTime:  new Date(action.payload.exportVal().startTime),
        endTime: new Date(action.payload.exportVal().endTime),
        description: action.payload.exportVal().description,
        imageURL: action.payload.exportVal().imageURL
      });
      this.myCal.loadEvents();
    });
  });
}

De cette manière, à chaque fois qu’un évènement sera ah-jouté dans la base de données Firebase, ce code s’exécutera et renouvellera tous les évènements.

La dernière chose à faire est d’exécuter cette fonction au chargement de notre page Home:

constructor(
  public afDB: AngularFireDatabase
) {
  this.loadEvent();
}

Les évènements sont maintenant stockés de manière dynamique dans la base de données Firebase, nous pouvons ainsi recharger notre page sans perdre les évènements.

Comment afficher les détails d’un évènement dans un modal ?

La dernière partie de ce tuto Ionic 4 consiste à ouvrir le détail d’un évènement en particulier.

Pour cela nous allons faire en sorte d’ouvrir une page modale pour afficher les informations de l’évènement sélectionné.

On commence à ajouter la fonction suivante pour repérer lorsqu’on clique sur un évènement en particulier:

<calendar
  (onEventSelected)="onEventSelected($event)">
</calendar>

Votre balise ionic calendar doit ressembler globalement à ça:

<calendar *ngIf="!showAddEvent" id="myCal"
    [locale]="calendar.locale"
    [eventSource]="allEvents" 
    [calendarMode]="calendar.mode" 
    [currentDate]="calendar.currentDate"
    (onEventSelected)="onEventSelected($event)"
    (onTitleChanged)="onViewTitleChanged($event)"
    (onTimeSelected)="onTimeSelected($event)" 
    startHour="6"
    endHour="20"
    step="30"
    startingDayWeek="1">
</calendar>

Nous pouvons maintenant afficher les informations détaillées de l’évènement sélectionné dans la console, lorsque l’on clique dessus:

async onEventSelected(event: any) {
  console.log('Event: ' + JSON.stringify(event));
}

Nous souhaitons maintenant ouvrir une nouvelle page à chaque clique sur un évènement. On commence par créer cette page Event avec la commande suivante:

ionic g page Event

Puis in importe cette page dans le fichier suivant pour l’afficher sous forme de modal par la suite:

src/app/app.module.ts

Importez la page module de votre page Event comme ceci:

import { EventPageModule } from './event/event.module';

Puis déclarez là dans votre tableau imports[]:

imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    AngularFireModule.initializeApp(firebaseConfig),
    AngularFireDatabaseModule,
    AngularFireAuthModule,
    AngularFireStorageModule,
    EventPageModule
  ],

De cette manière, nous allons pouvoir ouvrir la page Event en tant que modale. Rendez-vous ensuite dans le fichier TypeScript de votre page Home:

src/app/home/home.page.ts

Puis importez votre nouvelle page Event ainsi que le plugin ModalController pour créer une page modale:

import { ModalController } from '@ionic/angular';
import { EventPage } from '../event/event.page';

Déclarez ensuite le nouveau plugin dans le constructor() pour l’utiliser dans les fonctions de votre page:

constructor(
  public modalController: ModalController,
  public afDB: AngularFireDatabase
) {
  this.loadEvent();
}
async onEventSelected(event: any) {
}

Nous pouvons finalement mettre à jour la fonction onEventSelected() qui s’exécute à chaque clique sur un évènement, et ouvrir la page Event en modale à chaque clique:

async onEventSelected(event: any) {
  console.log('Event: ' + JSON.stringify(event));
  const modal = await this.modalController.create({
    component: EventPage,
    componentProps: event
  });
  return await modal.present();
}

Vous remarquez que nous transmettons toutes les informations de notre évènements dans l’objet event qui nous ai renvoyé par la fonction onEventSelected().

Passons maintenant à l’affichage des informations de notre évènement dans la page Event et son HTML:

src/app/event/event.page.html

Nous commençons par modifier le header de notre page, pour afficher avec la variable title le titre de notre évènement ainsi qu’un bouton pour fermer le modal:

<ion-header>
  <ion-toolbar color="warning">
    <ion-title>{{ title }}</ion-title>
    <ion-buttons slot="end">
      <ion-button color="light" (click)="close()">
        <ion-icon slot="icon-only" name="close"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

Ensuite nous passons au contenu de notre page, qui contiendra en premier lieu l’image de notre évènement, avec une nouvelle variable que nous allons déclarer imageURL en tant que source:

<ion-content>
  <img [src]="imageURL">
 
</ion-content>

Le reste de notre contenu représente la description suivie de la date de début et de fin de notre évènement, affiché dans des ion-item:

<ion-content>
  <img [src]="imageURL">
  
  <ion-item>
    <ion-label text-wrap>{{ decsription }}</ion-label>
  </ion-item>
  <ion-item>
    <ion-label><b>Début:</b> {{ start }}</ion-label>
  </ion-item>
  <ion-item>
    <ion-label><b>Fin:</b> {{ end }}</ion-label>
  </ion-item>

</ion-content>

La partie contenu est maintenant terminée, nous devons passer au Typescript pour rendre cette affichage dynamique et fonctionnel, ouvrez donc le fichier suivant:

src/app/event/event.page.ts

Et commencez par importez les plugin nécessaires pour fermer notre modal (ModalController) et pour récupérer les informations transmises de l’évènement (NavParams):

import { ModalController } from '@ionic/angular';
import { NavParams } from '@ionic/angular';

Nous importons également le plugin formatDate d’Angular pour afficher les dates de notre évènement en français par la suite:

import { formatDate } from '@angular/common';

Ensuite nous déclarons les différentes variables pour stocker les informations de notre évènement, et les afficher par la suite dans notre HTML:

export class EventPage {
  title: string;
  imageURL: string;
  decsription: string;
  start: string;
  end: string;

Vous pouvez également déclarer les deux premiers plugins dans votre constructor():

constructor(
  public modalController: ModalController,
  public navParams: NavParams
) {}

Pour récupérer les informations de notre évènements, nous faisons appel au plugin NavParams et à sa fonction get(), dans laquelle vous devez indiquer le nom du champ à récupérer, par exemple le titre avec ‘title’:

this.title = navParams.get('title');

En ce qui concerne les dates, on les adapte au format français avec le plugin et la fonction formatDate(), à laquelle on donne le paramètre de la lange ‘fr-FR’:

this.start = formatDate(navParams.get('startTime'), 'medium', 'fr-FR');

Le résultat finale de notre récupération des informations de l’évènement est le suivant:

constructor(
  public modalController: ModalController,
  public navParams: NavParams
) {
  this.title = navParams.get('title');
  this.imageURL = navParams.get('imageURL');
  this.decsription = navParams.get('description');
  this.start = formatDate(navParams.get('startTime'), 'medium', 'fr-FR');
  this.end = formatDate(navParams.get('endTime'), 'medium', 'fr-FR');
}

il ne nous reste plus qu’à ajouter la fonction close() pour fermer le modal avec le plugin correspondant:

close() {
  this.modalController.dismiss();
}