Comment créer un chat dans Ionic avec Firebase ? Comment créer une application de messagerie très simple avec Ionic, Facebook Login et Firebase ?

Nous allons voir différentes étapes pour créer cette application chat:

  • Les différentes méthodes de connexion pour Firebase
  • Tester et récupérer l’ID de l’utilisateur
  • Envoyer un message
  • Afficher les messages
  • Diviser les messages perso et des autres

1 – Méthode de connexion

Pour commencer il nous faut une méthode de connexion pour identifier nos utilisateurs. Heureusement Firebase en propose une dizaine dans sa gestion de l’authentification.

Vous pouvez par exemple choisir l’authentification mail et mot de passe, téléphone ou bien par Google.

Les méthodes de connexion de Firebase
Les méthodes de connexion de Firebase

Pour des raisons des simplicité, j’ai choisi de reprendre l’authentification par Facebook qui est selon moi la plus simple et la plus efficace à prendre en main.

L’avantage de Facebook Login est que nous récupérons le nom complet de l’utilisateur et sa photo de profil une fois connecté.

Pour cette raison, vous devez avoir suivi le tutoriel sur la connexion avec Facebook: https://drissas.com/facebook-login-ionic/

Prenez soin d’ajouter un bouton de déconnexion dans votre page Ionic, vous permettre à vos utilisateurs de se déconnecter en cas de besoin.

Voici la fonction de déconnexion logout():

logout() {
	this.afAuth.auth.signOut();
	this.connected = false;
}

Ainsi que le bouton HTML de déconnexion:

<ion-buttons slot="end" *ngIf="connected">
  <ion-button color="dark" (click)="logout()">
    <ion-icon slot="icon-only" name="log-out"></ion-icon>
  </ion-button>
</ion-buttons>

Vous pouvez placer ce bouton à la suite de votre <ion-title> dans le <ion-header> de votre page.

<ion-header>
  <ion-toolbar>
    <ion-title>
      Chat
    </ion-title>
    <ion-buttons slot="end" *ngIf="connected">
      <ion-button color="dark" (click)="logout()">
        <ion-icon slot="icon-only" name="log-out"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

2 – Tester la connexion

Passons maintenant à la deuxième étape, dans laquelle nous testons l’état de connexion de l’utilisateur. Nous créons pour commencer deux variables:

  • connected: pour stocker l’état de connexion (true ou false)
  • userId: pour stocker l’identifiant unique de l’utilisateur
connected = false;
userId: string;

Ensuite, nous créons un script nous tester l’état de connexion de l’utilisateur avec le plugin AngularFireAuth.

Pour cela nous exécutons la sous-fonction authState qui comme son nom l’indique renvoie l’état de connexion de l’utilisateur, et en cas de connexion nous renvoie ses informations.

Ces informations sont renvoyées dans l’objet auth, qui s’il est vide signifie que l’utilisateur n’est pas connecté, sinon que l’utilisateur est bien connecté et que l’on peut lire ses informations comme son identifiant avec auth.uid:

this.afAuth.authState.subscribe(auth => {
	if (!auth) {
		console.log('non connecté');
	} else {
		console.log('UserId: ' + auth.uid);
		this.connected = true;
		this.userId = auth.uid;
		this.getMessages();
	}
});

Si l’utilisateur est bien connecté, on affiche dans la console son identifiant, on le stocke dans la variable userId, on indique que le l’utilisateur est bien connecté avec la variable connected = true.

Enfin on exécute la fonction getMessages() et getCurrentUserData() que nous définirons par la suite.

3 – Envoyer un message

Passons à l’une des partie les plus intéressante de l’application chat avec Ionic. Nous allons créer un formulaire d’envoi de message en bas de notre page.

Pour commencer nous utilisons la balise <ion-footer> qui positionnera son contenu en bas de l’écran, y compris si le clavier de l’utilisateur est ouvert.

Nous ajoutons la condition *ngIf= »connected » pour n’afficher ce formulaire que dans le cas où l’utilisateur est connecté:

<ion-content>[...]</ion-content>

<ion-footer *ngIf="connected">
 
</ion-footer>

Ensuite nous utilisons les balise <ion-grid>, <ion-row> et <ion-col> pour créer une apparence de formulaire de messagerie.

La première colonne qui contient notre <ion-input> (là où l’utilisateur entre son message) prend une largeur de 10/12 tandis que la deuxième qui contient le bouton d’envoi a une largeur de 2/12:

<ion-footer *ngIf="connected">
  <ion-grid>
    <ion-row>
      <ion-col size="10">
        <ion-input type="text" placeholder="Entrez votre message"></ion-input>
      </ion-col>
      <ion-col size="2">
        <ion-button shape="full">
          <ion-icon name="paper-plane"></ion-icon>
        </ion-button>
      </ion-col>
    </ion-row>
  </ion-grid>
</ion-footer>

Enfin pour que notre application ressemble à une véritable application chat nous lui donnons quelques propriétés CSS. Ajoutez d’abord les classes « input-col » et « send-col » à vos deux colonnes HTML:

<ion-footer *ngIf="connected">
  <ion-grid>
    <ion-row>
      <ion-col size="10" class="input-col">
        <ion-input  type="text" placeholder="Entrez votre message"></ion-input>
      </ion-col>
      <ion-col size="2" class="send-col">
        <ion-button shape="full">
          <ion-icon name="paper-plane"></ion-icon>
        </ion-button>
      </ion-col>
    </ion-row>
  </ion-grid>
</ion-footer>

Voilà ensuite le CSS à ajouter à votre page, surtout pour modifier l’apparence du bouton d’envoi:

ion-grid {
	padding: 0px!important;
	border-top: solid 1px #d2cdcd;
}
.input-col {
	padding-left: 10px;
}
.send-col {
	padding: 0px!important;
}
.send-col ion-button {
	height: 100%!important;
	margin: 0px!important;
	border-radius: 0px !important;
}
.send-col button {
	border-radius: 0px !important;
}
.send-col ion-icon {
	font-size: 20px!important;
}

Concernant les derniers détails de notre formulaire, nous devons le rendre dynamique et l’associer à notre fichier TypeScript.

Ajouter [(ngModel)]= »messageText » à votre <ion-input> pour l’associer à la variable messageText:

<ion-input autocomplete="true" spellcheck="true" [(ngModel)]="messageText" type="text" placeholder="Entrez votre message" name="message"></ion-input>

Puis associer votre bouton HTML à la fonction sendMessage():

<ion-button shape="full" (click)="sendMessage()">
  <ion-icon name="paper-plane"></ion-icon>
</ion-button>

Votre formulaire et <ion-footer> doit maintenant ressembler à ça:

<ion-footer *ngIf="connected">
  <ion-grid>
    <ion-row>
      <ion-col size="10" class="input-col">
        <ion-input autocomplete="true" spellcheck="true" [(ngModel)]="messageText" type="text" placeholder="Entrez votre message" name="message"></ion-input>
      </ion-col>
      <ion-col size="2" class="send-col">
        <ion-button shape="full" (click)="sendMessage()">
          <ion-icon name="paper-plane"></ion-icon>
        </ion-button>
      </ion-col>
    </ion-row>
  </ion-grid>
</ion-footer>

Concluons sur la partie TypeScript de notre formulaire qui vous allez le voir est très simple.

Commençons pas déclarer une variable dans notre page, que nous appelons messageText est qui nous servira à conserver la valeur du message de l’utilisateur:

messageText: any;

Créons ensuite la fonction pour envoyer le message sur Firebase.

Pour cela rien de plus simple, on utilise le plugin AngularFireDatabase ainsi que la sous fonction « list » avec comme paramètre l’adresse de notre liste (‘Messages/) et comme deuxième fonction « push » pour lui ajouter du contenu:

sendMessage() {
	this.afDB.list('Messages/').push({
		userId: this.userId,
		text: this.messageText,
		date: new Date().toISOString()
	});
	this.messageText = '';
}

À la fin de notre fonction, nous réinitialisons la valeur de notre variable messageText qui vide instantanément notre champ message du formulaire.

4 – Afficher les messages

Continuons avec l’affichage des messages de nos utilisateurs dans notre page. Nous allons d’abord traiter la partie TypeScript pour récupérer les messages puis la partie HTML pour les afficher.

Pour commencer, nous créons une variable de type tableau pour contenir tous les messages de la base de données Firebase:

public messages: any = [];

Ensuite nous créons la fonction pour parcourir notre tables Messages:

getMessages() {

}

D’abord nous utilisons le plugin AngularFireDatabase, ainsi que la sous fonction list, qui va nous permettre d’indiquer l’adresse exacte de notre base.

La sous fonction snapshotChanges avec comme paramètre ([‘child_added’]) nous permet de nous « abonner » à notre table et d’exécuter ce script à chaque ajout de données à notre table:

getMessages() {
	this.afDB.list('Messages/').snapshotChanges(['child_added'])
	.subscribe(actions => {
	
	});
}

Finalement on ajoute le paramètre suivant à notre requête dans la fonction list, pour lister les éléments (orderByChild) par leur date:

getMessages() {
	this.afDB.list('Messages/', ref => ref.orderByChild('date')).snapshotChanges(['child_added'])
	.subscribe(actions => {
		
	});
}

Ce que nous allons maintenant ajouter à l’intérieur de cette accolade va s’exécuter à chaque fois qu’un message est ajouté à notre table « Messages ».

On commence par initialiser la variables messages sous forme de tableau vide:

this.messages = [];

Puis nous parcourons l’objet renvoyé par la fonction AngularFireDatabase:

actions.forEach(action => {
  console.log('Message: ' + action.payload.exportVal().text);
});

À chaque message rencontré, on peut lui demander d’afficher son contenu texte, la date à laquelle il a été posté et l’identifiant de l’utilisateur.

Plutôt que de simplement les afficher, nous les ajoutons directement à notre variable messages qui contiendra à la fin tous les messages existant:

getMessages() {
	this.afDB.list('Messages/', ref => ref.orderByChild('date')).snapshotChanges(['child_added'])
	.subscribe(actions => {
		this.messages = [];
		actions.forEach(action => {
			this.messages.push({
				userId: action.payload.exportVal().userId,
				text: action.payload.exportVal().text,
				date: action.payload.exportVal().date
			});
		});
	});
}

La fonction getMessages() est désormais terminée et nous pouvons passer à la partie HTML.

Tout d’abord nous créons une boîte <div> pour contenir l’ensemble de nos messages. Nous lui ajoutons la condition*ngIf= »connected » pour n’afficher cette partie que si l’utilisateur est bien connecté.

<div class="stream" *ngIf="connected">

</div>

Ensuite nous ajoutons le contenu de cette boîte, qui consiste en une liste de <ion-item> qui contienne chacun le texte d’un message.

Pour cela nous effectuons une boucle pour parcourir tous les messages avec la propriété ngFor= »let message of messages »:

<div class="stream" *ngIf="connected">
	<ion-item lines="none" *ngFor="let message of messages">
		<p>{{ message.text }}</p>
	</ion-item>
</div>

Les messages de votre base de données devraient maintenant apparaître dans votre page. Vous pouvez également déjà tester l’actualisation des messages en direct en ajouter de nouveaux messages.

5 – Filtrer les messages

Pour conclure ce tutoriel, nous allons travailler sur une des fonctionnalités essentielles des chats et autres messagerie. Il s’agit de différencier ses propres messages de ceux des autres utilisateurs.

Étant donné que pour chaque message nous disposons de l’identifiant de d’utilisateur et que nous connaissant celui de l’utilisateur connecté nous pouvons comparer les deux.

Nous disposons donc de deux cas:

  • *ngIf= »message.userId != userId »: C’est le message d’un autre utilisateur
  • *ngIf= »message.userId == userId »: C’est l’un de mes messages

Nous créons donc deux contenus différents sous forme de badge (voir la documentation Ionic) qui vont prendre une couleur différente et se positionner en début ou en fin de ligne.

Si c’est un message d’un autre utilisateur:

<ion-badge text-wrap slot="start" color="light" *ngIf="message.userId != userId">{{ message.text }}</ion-badge>

Si c’est un message de l’utilisateur connecté:

<ion-badge text-wrap slot="end" color="primary" *ngIf="message.userId == userId">{{ message.text }}</ion-badge>

Voilà donc le résultat final de notre contenu HTML pour afficher tous les messages de la base de données et de notre chat:

<div class="stream" *ngIf="connected">
	<ion-item lines="none" *ngFor="let message of messages">
		<ion-badge text-wrap slot="start" color="light" *ngIf="message.userId != userId">{{ message.text }}</ion-badge>
		<ion-badge text-wrap slot="end" color="primary" *ngIf="message.userId == userId">{{ message.text }}</ion-badge>
	</ion-item>
</div>

Pour conclure, vous pouvez ajouter les propriétés CSS suivantes à vos messages:

.stream ion-item {
    min-height: fit-content !important;
    height: 51px;
}
ion-badge {
    padding: 10px!important;
    text-align: left;
    max-width: 80%;
}