Dans ce tuto Ionic 4, nous allons voir comment créer un bouton like dans des publications avec Firebase. Pour commencer le tuto, vous devez avoir les deux choses suivantes dans votre application:
- Ionic connecté à un projet Firebase
- une méthode de connexion valide
Si vous n’avez pas ces deux éléments dans votre application Ionic, veuillez suivre les tutoriels suivants: associer Firebase à votre application Ionic et intégrer Facebook Login.
Comment publier du contenu avec un bouton like dans Ionic ?
Pour commencer, nous devons créer des publications à l’intérieur desquelles nous allons ajouter nos boutons like. Voilà les trois étapes que nous allons suivre:
- Créer le formulaire
- Créer la fonction pour envoyer le contenu
- Réinitialiser le formulaire à zéro
On commence par créer le formulaire, qui consiste simplement en champ input texte comme ceci:
<ion-item> <ion-label>Texte:</ion-label> <ion-input type="text" [(ngModel)]="postData.text"></ion-input> </ion-item>
Ensuite, à la fin de notre formulaire, nous aurons besoin d’un bouton associé à une fonction pour envoyer nos données dans Firebase. Vous pouvez créer le bouton suivant associé à la fonction publish():
<ion-button expand="block" (click)="publish()">Publier</ion-button>
Finalement pour proposer un formulaire plus complet, nous proposer un champ image URL pour partager des photos dans nos publication. Nous ajoutons également une en-tête à notre formulaire:
<ion-item> <ion-label><b>Publiez quelque chose</b></ion-label> </ion-item> <ion-item> <ion-label>Texte:</ion-label> <ion-input type="text" [(ngModel)]="postData.text"></ion-input> </ion-item> <ion-item> <ion-label>Url de l'image:</ion-label> <ion-input type="text" [(ngModel)]="postData.imgUrl"></ion-input> </ion-item> <ion-button expand="block" (click)="publish()">Publier</ion-button>
Passons maintenant à la partie TypeScript dans laquelle nous allons créer la fonction et réinitialiser notre formulaire. Vous devez importer les plugins AngularFire dans votre page, comme indiqué dans le tuto Firebase dans Ionic:
import { AngularFireDatabase } from '@angular/fire/database'; import { AngularFireAuth } from '@angular/fire/auth';
Vous devez également déclarer ces plugins dans votre constructor() pour pouvoir les utiliser dans le reste de votre page:
constructor( public afDB: AngularFireDatabase, public afAuth: AngularFireAuth ) {}
On déclare également l’objet qui va contenir les valeurs de nos champs inputs, on appelle cet objet postData et on lui donne deux champ, text et imgUrl:
postData = { text: '', imgUrl: '' };
On arrive ensuite à la fonction publish(), qui pour commencer va simplement consister à ajouter un item à votre table Posts/. On utilise pour cela le plugin AngularFireDatabase, la sous-fonction list() pour faire appel à notre table Posts, puis à la fonction push() pour ajouter un élément à cette liste:
publish() { this.afDB.list('Posts/').push({ text: this.postData.text, imgUrl: this.postData.imgUrl, likesNb: 0 }); }
Nous donnons à ce nouvelle élément de la liste Posts, les valeurs des champs de notre formulaire, stockées dans l’objet this.postData. On initialise également le nombre de likes de notre publication à zéro.
Une fois les données envoyées dans Firebase, il faut réinitialiser le formulaire, pour cela rien de plus simple:
this.postData = { text: '', imgUrl: '' };
Votre fonction publish() doit donc ressembler à ça à la fin:
publish() { this.afDB.list('Posts/').push({ text: this.postData.text, imgUrl: this.postData.imgUrl, likesNb: 0 }); this.postData = { text: '', imgUrl: '' }; }
Comment afficher les publications pour y intégrer un bouton like ?
Nous allons maintenant récupérer et afficher le contenu de ces publications dans notre page. Pour cela nous allons suivre les étapes suivantes:
- Créer la fonction qui récupère les données
- Créer le contenu HTML de base
- Ajouter une boucle ng-For dans le HTML
Pour commencer nous devons créer une variable, ici il s’agira d’un tableau, pour contenir et stocker les informations de nos publications. Nous appelons cette variable posts:
posts: any;
Puis nous créons la fonction getPosts() qui va parcourir la table Firebase de nos posts et récupérer ses informations:
getPosts() { }
Voyons maintenant le code de notre fonction getPosts() étape par étape. On commence par utiliser le plugin AngularFire et son module Database pour récupérer les informations de notre liste Posts/:
this.afDB.list('Posts/').snapshotChanges(['child_added']).subscribe(actions => { }
En fait, on « s’abonne » (subscribe en anglais) au contenu de cette table Posts/, ce qui signifie qu’à chaque fois qu’un élément sera ajouté à cette liste, le code à l’intérieur des accolades s’exécutera.
C’est pour cette raison que l’on doit réinitialiser la variables posts avant de parcourir la tables Posts/:
this.posts = [];
On maintenant pouvoir réellement parcourir notre table, en utilisant la fonction forEach() et l’objet actions qui nous est renvoyé par la fonction subscribe().
De cette manière on est capable d’accéder aux informations de chaque élément de notre liste. On renvoie l’objet action qui contient les informations d’un élément et on les affiche avec la syntaxe suivante:
actions.forEach(action => { console.log(action.payload.exportVal().text); }
Nous sommes donc capable de récupérer les champs texte et image de nos publications, seul la syntaxe pour récupérer la clé de notre post est différente (action.key):
this.posts.push({ key: action.key, text: action.payload.exportVal().text, imgUrl: action.payload.exportVal().imgUrl, likesNb: action.payload.exportVal().likesNb });
Globalement, la fonction getPosts() ressemble donc à ceci:
getPosts() { this.afDB.list('Posts/').snapshotChanges(['child_added']).subscribe(actions => { this.posts = []; actions.forEach(action => { console.log(action.payload.exportVal().text); this.posts.push({ key: action.key, text: action.payload.exportVal().text, imgUrl: action.payload.exportVal().imgUrl, likesNb: action.payload.exportVal().likesNb }); }); }); }
Bien entendu, il faut exécuter cette fonction quelque part dans notre code comme ceci:
this.getPosts();
Personnellement, j’exécute cette fonction dans mon constructor() après avoir vérifié l’état de connexion de l’utilisateur lors du chargement de la page. Si vous n’êtes pas familier à l’état de connexion de l’utilisateur, consultez le tuto Facebook Login.
constructor( public afDB: AngularFireDatabase, public afAuth: AngularFireAuth ) { this.afAuth.authState.subscribe(auth => { if (!auth) { console.log('non connecté'); this.connected = false; } else { console.log('connecté: ' + auth.uid); this.connected = true; this.userId = auth.uid; this.getPosts(); } }); }
Nous pouvons désormais passer à la partie HTML, dans laquelle nous allons d’abord créer une publication statique, puis parcourir notre variable posts.
On commencer en créant une simple image qui prendre toute la largeur de notre page:
<img src="../../assets/orange.png">
Ensuite, on ajoute un ion-item avec juste du texte à l’intérieur:
<ion-item> <ion-label>Texte de la publication</ion-label> </ion-item>
On créé ensuite un ion-badge, c’est-à-dire un texte entouré d’une certaine couleur, ici en rouge et avec par défaut la valeur 4. C’est dans ce badge que l’on affichera le nombre de likes de la publication:
<ion-badge color="danger">4</ion-badge>
Enfin on ajoute notre icône like, plus précisément un cœur vide (heart-empty en anglais) que l’on positionne à la fin de notre item avec l’attribut slot = « end »:
<ion-icon slot="end" name="heart-empty"></ion-icon>
Voilà donc le code globale pour toute une publication:
<img src="../../assets/orange.png"> <ion-item> <ion-label>Texte de la publication</ion-label> <ion-badge color="danger">4</ion-badge> <ion-icon slot="end" name="heart-empty"></ion-icon> </ion-item>
L’idée maintenant est de proposer un affichage dynamique de nos publications. Pour cela nous allons créer une boucle *ngFor et parcourir notre tableau posts:
*ngFor="let post of posts"
On peut intégrer cette boucle *ngFor dans une boîte <div> HTML pour afficher chaque élément de notre tableau dans l’objet post, ici par exemple le texte de chaque publication:
<div *ngFor="let post of posts"> {{ post.text }} </div>
Nous pouvons finalement intégrer les balises HTML de notre publication et proposer un contenu dynamique avec chaque champ de notre objet post:
<div *ngFor="let post of posts"> <img [src]="post.imgUrl"> <ion-item> <ion-label>{{ post.text }}</ion-label> <ion-icon slot="end" name="heart-empty"></ion-icon> <ion-badge color="danger">{{ post.likesNb }}</ion-badge> </ion-item> </div>
Comment compter le nombre de likes d’une publication Ionic ?
On souhaite maintenant pouvoir créer réellement le bouton like, et surtout commencer par compter le nombre de likes pour une publication. Pour cela nous allons suivre les étapes suivantes:
- Associer la fonction addLike() à l’icône cœur
- Transmettre les informations de la publication
- Créer la fonction addLike()
On commence par reprendre notre icône cœur, qui fera office de bouton Ionic. On lui associe simplement la fonction addLike() avec l’attribut (click):
<ion-icon (click)="addLike(post)" slot="end" name="heart-empty"></ion-icon>
On lui transmet également comme paramètre notre objet post qui contient toutes les informations relatives à une publication en particulier. Cela nous servira à modifier son champ likesNb et à le mettre à jour dans notre publication.
<div *ngFor="let post of posts"> <img [src]="post.imgUrl"> <ion-item> <ion-label>{{ post.text }}</ion-label> <ion-icon (click)="addLike(post)" slot="end" name="heart-empty"></ion-icon> <ion-badge color="danger">{{ post.likesNb }}</ion-badge> </ion-item> </div>
Mais la réelle utilité de transmettre les information de la publication, est de pouvoir récupérer son identifiant Firebase (key) et mettre à jour ses informations dans la base.
On déclare donc la fonction addLike() capable de prendre en compte un paramètre de tout type, dans notre cas un objet.
addLike(post: any) { }
La première chose à faire dans notre fonction est d’ajouter l’identifiant de l’utilisateur qui a liké la publication dans Firebase. Nous allons ajouter un nouvelle élément dans la table de notre publication avec le code suivant:
addLike(post: any) { this.afDB.object('Posts/' + post.key + '/' + this.userId).set({ liked: true }); }
On utilise la fonction object() et set() de angulareFireDatabase pour pouvoir ajouter un item et surtout pour pouvoir vérifier sa présence par la suite.
Le résultat sera donc le suivant dans Firebase:
Maintenant qu’on a ajouté l’identifiant de l’utilisateur dans la publication Firebase, il nous faut mettre à jour le compteur de likes. On commence par reprendre le nombre de likes de notre publication et lui ajouté +1 avec la syntaxe suivante:
post.likesNb++;
Ensuite on met à jour la valeur du nombre de likes de la publication dans Firebase avec la fonction update():
this.afDB.object('Posts/' + post.key).update({ likesNb: post.likesNb });
Voilà donc notre fonction addLike() une fois mise à jour:
addLike(post: any) { post.likesNb++; this.afDB.object('Posts/' + post.key + '/' + this.userId).set({ liked: true }); this.afDB.object('Posts/' + post.key).update({ likesNb: post.likesNb }); }
Désormais votre nombre de likes devrait se mettre à jour à chaque fois que vous cliquez sur le bouton like.
Néanmoins, l’apparence de l’icône ne change pas, et un utilisateur peut mettre autant de likes qu’il le souhaite, nous allons régler tout ça dans la prochaine partie.
Comment modifier l’apparence du bouton quand on a déjà liké ?
Il nous reste maintenant à proposer un affiche dynamique du bouton like, lorsqu’on clique dessus il doit passer de vide à plein et inversement.
On doit également faire en sorte lors du chargement de la page que si on a déjà la publication le bouton apparaisse en rouge et plein. Suivons pour cela les étapes suivantes:
- Vérifier si l’utilisateur a déjà cliqué sur le bouton like
- Créer une deuxième bouton like plein et rouge
- Afficher l’un ou l’autre selon le cas
Regardons pour commencer noter fonction getPosts() qui nous permet de récupérer les informations de nos publications ainsi que leur nombre de likes.
getPosts() { this.afDB.list('Posts/').snapshotChanges(['child_added']).subscribe(actions => { this.posts = []; actions.forEach(action => { this.posts.push({ key: action.key, text: action.payload.exportVal().text, imgUrl: action.payload.exportVal().imgUrl, likesNb: action.payload.exportVal().likesNb }); }); }); }
Puisque lorsque nous mettons un like à un publication, notre identifiant est stocké dans les informations Firebase de celle-ci. Nous sommes donc capable de vérifier sa présence dans Firebase avec la variable suivante:
this.userId
On pourrait par exemple lui demander d’afficher le champ liked pour voir si on a aimé ou pas la publication.
console.log('like:' + action.payload.exportVal()[this.userId].liked);
Le problème c’est que si on n’a pas encore liké la publication, notre identifiant n’est pas du tout stocké dans la table de notre publication Firebase. On doit donc vérifier sa présence avec la condition suivante:
if (!action.payload.exportVal()[this.userId]) {}
Donc dans le cas on l’on ne trouve pas l’identifiant de l’utilisateur dans la table, on peut lui affecter le champ liked: false :
if (!action.payload.exportVal()[this.userId]) { this.posts.push({ key: action.key, text: action.payload.exportVal().text, imgUrl: action.payload.exportVal().imgUrl, likesNb: action.payload.exportVal().likesNb, liked: false }); }
Sinon, dans le cas contraire où l’identifiant est présent, on lui affecte le champ liked: true. Cela nous servira à afficher les deux types de boutons plein et vide.
Voilà le code de la fonction getPosts() finale:
getPosts() { this.afDB.list('Posts/').snapshotChanges(['child_added']).subscribe(actions => { this.posts = []; actions.forEach(action => { console.log('like:' + action.payload.exportVal()[this.userId].liked); if (!action.payload.exportVal()[this.userId]) { this.posts.push({ key: action.key, text: action.payload.exportVal().text, imgUrl: action.payload.exportVal().imgUrl, likesNb: action.payload.exportVal().likesNb, liked: false }); } else { this.posts.push({ key: action.key, text: action.payload.exportVal().text, imgUrl: action.payload.exportVal().imgUrl, likesNb: action.payload.exportVal().likesNb, liked: true }); } }); }); }
Nous pouvons désormais proposer les deux types de boutons, simplement en copiant le premier et en modifier l’attribut name et color du suivant.
Puisqu’on souhaite avoir un bouton cœur plein et rouge, on crée les deux boutons suivants:
<ion-icon (click)="addLike(post)" slot="end" name="heart-empty"></ion-icon> <ion-icon (click)="addLike(post)" slot="end" color="danger" name="heart"></ion-icon>
En revanche il nous faut maintenant distinguer les deux cas et n’afficher qu’un seul des deux à la fois. Pour cela on définir une condition *ngIf avec comme vérification la valeur du champ liked de notre publication:
*ngIf="!post.liked"
Nous devons également créer une deuxième fonction dans le cas où on aime déjà la publication et qu’il faudra le like de celle-ci quand on clique sur le cœur.
removeLike()
Voilà donc nos deux boutons avec les deux traités séparément et leur icône résepctive:
<ion-icon *ngIf="!post.liked" (click)="addLike(post)" slot="end" name="heart-empty"></ion-icon> <ion-icon *ngIf="post.liked" (click)="removeLike(post)" slot="end" color="danger" name="heart"></ion-icon>
Revenons rapidement sur notre première fonction addLike() car nous devons lui ajouter une ligne de code. Nous devons indiquer à notre publication Ionic que l’utilisateur vient de modifier son choix et donc aime maintenant la publication.
post.liked = true;
Voilà donc la fonction addLike() finale:
addLike(post: any) { post.likesNb++; post.liked = true; this.afDB.object('Posts/' + post.key + '/' + this.userId).set({ liked: true }); this.afDB.object('Posts/' + post.key).update({ likesNb: post.likesNb }); }
Nous également terminer par créer la fonction removeLike() pour supprimer notre like en l’indiquer à Firebase et à notre publication Ionic.
On commence par enlever -1 à notre compteur de like puis à mettre à jour cette nouvelle valeur dans la table Firebase de notre publication.
removeLike(post: any) { post.likesNb--; post.liked = false; this.afDB.list('Posts/' + post.key).remove(this.userId); this.afDB.object('Posts/' + post.key).update({ likesNb: post.likesNb }); }
Puis on modifie la valeur de notre like à false, puisque maintenant l’utilisateur n’aime plus cette publication.
On finit par supprimer son identifiant dans la table Firebase de la publication pour que lorsqu’on recharge la page, la donnée soit bien mise à jour.
Voilà pour l’affichage dynamique de notre bouton like, vous n’avez plus qu’à tester son fonctionnement maintenant.
Comment personnaliser l’icône du bouton like ?
Nous concluons ce tutoriel par une partie un peu plus amusante où nous allons modifier les types de nos icônes like. Nous allons voir les étapes suivantes pour reproduire les boutons like de Facebook:
- Modifier les icônes de nos deux boutons
- Modifier la couleur du bouton « j’aime déjà »
On commence par se rendre sur le site https://ionicons.com/ pour choisir les icônes de nos boutons likes. Tapez dans la barre de recherche « thumbs« .
On prendre pour nos deux boutons l’icône thumbs-up dont nous modifierons juste la couleur:
<ion-icon name="thumbs-up"></ion-icon>
Voilà le code du premier bouton like, de couleur grise:
<ion-icon *ngIf="!post.liked" (click)="addLike(post)" slot="end" name="thumbs-up"></ion-icon>
Puis le code du bouton une fois qu’on a aimé, de couleur bleu primaire:
<ion-icon *ngIf="post.liked" (click)="removeLike(post)" slot="end" color="primary" name="thumbs-up"></ion-icon>
On peut également modifier la couleur du badge qui compte les likes:
<ion-badge color="primary">{{ post.likesNb }}</ion-badge>
Voilà donc le code HTML final de votre publication Ionic de style Facebook:
<div *ngFor="let post of posts"> <img [src]="post.imgUrl"> <ion-item> <ion-label>{{ post.text }}</ion-label> <ion-icon *ngIf="!post.liked" (click)="addLike(post)" slot="end" name="thumbs-up"></ion-icon> <ion-icon *ngIf="post.liked" (click)="removeLike(post)" slot="end" color="primary" name="thumbs-up"></ion-icon> <ion-badge color="primary">{{ post.likesNb }}</ion-badge> </ion-item> </div>
Dans ma page, j’ai également modifié la couleur de mon header pour harmoniser le thème de la page en bleu:
<ion-toolbar color="primary">
Voilà pour ce tutoriel Ionic où nous avons créée des boutons like. Vous pouvez télécharger le code source de cette page en cliquant sur ce lien ou sur l’image ci-dessous: