Authentification Firebase et Angular avec Auth0: Partie 2 –
Cet article a été publié à l'origine sur le blog Auth0.com et est republié ici avec sa permission.
Dans cette série de didacticiels en deux parties, nous apprendrons comment créer une application qui sécurise une extrémité arrière de nœud et une extrémité avant angulaire avec authentification Auth0 . Notre serveur et notre application authentifieront également une base de données Cloud Firestore Firebase avec des jetons personnalisés afin que les utilisateurs puissent laisser des commentaires en temps réel de manière sécurisée après s'être connectés avec Auth0. Le code de l'application angulaire peut être trouvé sur le repo GitHub angulaire-firebase et l'API Node se trouve dans le repo firebase-auth0-nodeserver .
La première partie de notre tutorial, Authentification Firebase et Angular avec Auth0: Part 1 couvert:
- intro et configuration pour Auth0 et Firebase
- implémentant une API Node sécurisée qui frappe les jetons Firebase personnalisés et fournit des données pour notre application [19659006] Architecture d'application angulaire avec modules et chargement différé
- Authentification angulaire avec Auth0 avec service et route guard
- Composants angulaires partagés et service API.
Authentification de Firebase et Angular avec Auth0: Partie 2
Partie 2 de notre tutoriel couvrira:
- Affichage des chiens: Async et NgIfElse
- Détails du chien avec les paramètres de l'itinéraire
- Commentaire Catégorie du modèle
- Firebase Firestore Cloud et les règles [19659006] Commentaires Composant
- Formulaire de commentaire Component
- Temps réel Commentaires
- Conclusion
ceci:
Reprenons là où nous nous étions arrêtés à la fin de Authentification Firebase et Angular avec Auth0: Partie 1 .
Affichage des chiens: Async et NgIfElse
Implémentons la page d'accueil de notre application – la liste des chiens. Nous avons créé l'échafaudage pour ce composant lorsque nous avons configuré l'architecture de notre application Angular
Remarque importante: Assurez-vous que votre API Node.js est en cours d'exécution. Si vous avez besoin d'un rappel sur l'API, reportez-vous à Comment authentifier Firebase et Angular avec Auth0: Partie 1 – API nœud .
Dogs Component Class
Ouvrez le composant dogs. ts
fichier de classe maintenant et implémenter ce code:
// src / app / dogs / dogs / dogs.component.ts
import {Component, OnInit} à partir de '@ angular / core';
importer {Title} à partir de '@ angular / platform-browser';
importer {ApiService} à partir de '../../core/api.service';
importer {Dog} à partir de './../../core/dog';
import {Observable} à partir de 'rxjs / Observable';
importer {tap, catchError} à partir de 'rxjs / operators';
@Composant({
sélecteur: 'app-dogs',
templateUrl: './dogs.component.html'
})
export classe DogsComponent implémente OnInit {
pageTitle = 'Chiens populaires';
dogsList $: Observable ;
chargement = vrai;
erreur: booléen;
constructeur(
titre privé: Titre,
api privée: ApiService
) {
this.dogsList $ = api.getDogs $ (). pipe (
appuyez sur (val => this._onNext (val)),
catchError ((err, caught) => this._onError (err, catch))
)
}
ngOnInit () {
this.title.setTitle (this.pageTitle);
}
private _onNext (val: Dog []) {
this.loading = faux;
}
private _onError (err, caught): Observable {
this.loading = faux;
this.error = true;
return Observable.throw ('Une erreur est survenue lors de l'extraction des données de chiens.');
}
}
Après nos importations, nous allons mettre en place quelques propriétés locales:
-
pageTitle
: pour mettre nos pages
et -
dogsList $
: l'observable retourné par notre requête HTTP API pour récupérer les données de listage des chiens -
loading
: pour afficher une icône de chargement alors que la requête API est en cours -
error
: pour afficher une erreur si quelque chose se passe mal récupérant des données de l'API.
Nous allons utiliser le tuyau asynchrone déclaratif pour répondre à dogsList $
observable retourné par notre API GET
demande. Avec le pipe async, nous n'avons pas besoin de vous abonner ou de vous désinscrire dans notre classe DogsComponent
: le processus d'abonnement sera géré automatiquement! Nous devons simplement mettre en place notre observable.
Nous rendrons Titre
et ApiService
disponibles à notre classe en les transmettant au constructeur, puis nous établirons notre dogsList $
observable. Nous utiliserons les opérateurs RxJS tap
(précédemment connu sous le nom d'opérateur do
) et catchError
pour appeler les fonctions du gestionnaire. L'opérateur tap
exécute les effets secondaires mais n'affecte pas les données émises, il est donc idéal pour la définition d'autres propriétés. La fonction _onNext ()
définira en chargeant
à false
(puisque les données ont été émises avec succès). La fonction _onError ()
définira en chargeant
et l'erreur
correctement et lancera une erreur. Comme mentionné précédemment, nous n'avons pas besoin de s'abonner ou se désinscrire de dogsList $
observable parce que le tuyau asynchrone (que nous ajouterons dans le modèle)
Lors de l'initialisation de notre composant, nous allons utiliser ngOnInit ()
pour espionner le hook de cycle de vie OnInit à définir le document
. [19659036] Voilà pour notre classe de composants Chiens!
Modèle de composants pour chiens
Passons au modèle à dogs.component.html
:
{{pageTitle}}
Ces ont été les 10 races de chiens les plus populaires aux États-Unis en 2016 classés par l'American Kennel Club (AKC).
# {{dog.rank}}: {{dog.breed}}
Il y a deux choses dans ce modèle que nous allons examiner de plus près:
...
...
...
Ce code fait des choses très utiles de manière déclarative. Explorons
Tout d'abord, nous avons un élément
avec une variable de référence de modèle ( #noDogs
). L'élément
n'est jamais rendu directement. Il est destiné à être utilisé avec des directives structurelles (telles que NgIf). Dans ce cas, nous avons créé une vue incorporée avec
qui contient à la fois les composants de chargement et d'erreur. Chacun de ces composants sera rendu basé sur une condition. La vue incorporée noDogs
elle-même ne sera restituée que si on lui demande de le faire.
Alors, comment (et quand) devons-nous dire cette vue?
La suivante <div * ngIf = ". ..
est en fait un NgIfElse utilisant le préfixe asterisk comme sucre syntaxique .Nous utilisons aussi le tuyau async avec notre dogsList $
observable et réglage une variable pour que nous puissions référencer les valeurs émises dans notre template ( comme dogsList
) Si quelque chose ne va pas avec les dogsList $
observable, nous avons un else noDogs
] instruction qui indique au modèle de rendre la vue
.Ceci serait vrai avant que les données aient été extraites avec succès de l'API, ou si une erreur a été levée par l'observable.
Si dogsList $ | async
a émis avec succès une valeur, la div va afficher et nous pouvons itérer sur notre valeur dogsList
(qui devrait être un tableau de Dog
s, comme spécifié dans notre classe de composants) utilisant la directive structurelle NgForOf ( * ngFor
) pour afficher les informations de chaque chien.
Comme vous pouvez le voir dans le HTML restant, chaque chien sera affiché avec une image, un rang, une race, et un lien vers leur page de détail individuelle, que nous créerons ensuite.
Voir le composant Chiens dans le navigateur en accédant à la page d'accueil de votre application à l'adresse http: // localhost: 4200 . L'application Angular devrait demander à l'API d'aller chercher la liste des chiens et de les afficher!
Note: Nous avons aussi inclus le composant
. Puisque nous avons généré ce composant mais n'avons pas encore implémenté sa fonctionnalité, il devrait apparaître dans l'interface utilisateur sous la forme "Comments works!"
Pour tester la gestion des erreurs, vous pouvez arrêter le serveur API ( Ctrl + c
dans l'invite de commande ou le terminal du serveur). Ensuite, essayez de recharger la page. Le composant d'erreur doit s'afficher puisque l'API ne peut pas être atteinte, et nous devrions voir les erreurs appropriées dans la console du navigateur:

Détails du chien avec paramètres de route
] Ensuite, nous allons implémenter notre composant Dog. Ce composant routé sert de page de détails pour chaque chien. Nous avons déjà mis en place notre architecture de module Dog avec routage et chargement paresseux dans la première partie de ce tutoriel.
Rappel: Vous pouvez vous souvenir de la partie 1 que la page des détails du chien est protégée par la AuthGuard
garde de route . Cela signifie que le visiteur doit être authentifié pour accéder à la page. En outre, l'appel API nécessite un jeton d'accès pour renvoyer des données.
Dog Component Class
Ouvrez le fichier de classe dog.component.ts
et ajoutez:
// src / app / chien / chien / dog.component.ts
importer {Component, OnInit, OnDestroy} à partir de '@ angular / core';
importer {ActivatedRoute} à partir de '@ angular / router';
importer {Title} à partir de '@ angular / platform-browser';
importer {ApiService} à partir de '../../core/api.service';
importer {DogDetail} à partir de './../../core/dog-detail';
import {Abonnement} à partir de 'rxjs / Subscription';
import {Observable} à partir de 'rxjs / Observable';
importer {tap, catchError} à partir de 'rxjs / operators';
@Composant({
sélecteur: 'app-dog',
templateUrl: './dog.component.html',
styles: [`
.dog-photo {
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: cover;
min-height: 250px;
width: 100%;
}
`]
})
export class DogComponent implémente OnInit, OnDestroy {
paramSub: Abonnement;
chien $: Observable ;
chargement = vrai;
erreur: booléen;
constructeur(
route privée: ActivatedRoute,
api privée: ApiService,
titre privé: Titre
) {}
ngOnInit () {
this.paramSub = this.route.params
.souscrire(
params => {
this.dog $ = this.api.getDogByRank $ (params.rank) .pipe (
appuyez sur (val => this._onNext (val)),
catchError ((err, caught) => this._onError (err, catch))
)
}
)
}
private _onNext (val: DogDetail) {
this.loading = faux;
}
private _onError (err, caught): Observable {
this.loading = faux;
this.error = true;
return Observable.throw ('Une erreur est survenue lors de la récupération des données détaillées de ce chien.');
}
getPageTitle (dog: DogDetail): string {
const pageTitle = `# $ {dog.rank}: $ {dog.breed}`;
this.title.setTitle (pageTitre);
return pageTitre;
}
getImgStyle (url: string) {
retourne `url ($ {url})`;
}
ngOnDestroy () {
this.paramSub.unsubscribe ();
}
}
Ce composant est très similaire à notre composant de liste Dogs avec juste quelques différences clés.
Nous allons importer les dépendances nécessaires et utiliser en privé le service ApiService
et Title
dans notre classe.
Le composant Détails du chien repose sur un paramètre d'itinéraire pour déterminer quel chien nous devons extraire des données. Le paramètre route correspond au rang du chien désiré dans la liste des dix chiens les plus populaires, comme:
# URL pour chien # 2:
http: // localhost: 4200 / chien / 2
Pour accéder à ce paramètre dans la classe component, nous devons importer l'interface ActivatedRoute la transmettre au constructeur, et souscrire aux paramètres de la route activée
observable .
Nous pouvons ensuite passer le paramètre rank
à notre méthode de service API getDogByRank $ ()
. Nous devrions aussi nous désabonner des paramètres d'itinéraire observables lorsque le composant est détruit . Notre chien $
observable peut utiliser tap
et catchError
des gestionnaires similaires à notre liste de chiens.
Nous aurons aussi besoin de quelques méthodes pour aider notre
La méthode getPageTitle ()
utilise les données API pour générer un titre de page qui inclut le rang et la race du chien.
La méthode getImgStyle ()
utilise l'API données pour renvoyer une valeur CSS d'arrière-plan.
Modèle de composant de chien
Utilisons maintenant ces méthodes dans notre modèle dog.component.html
:
{{getPageTitle (chien)}}
- Groupe: {{dog.group}}
- Personnalité: {{dog.personality}}
- Niveau d'énergie: {{dog.energy}}
Dans l'ensemble, ce modèle ressemble et fonctionne de la même façon que notre modèle de composant listing Dogs, sauf que nous ne faisons pas d'itération sur un tableau. Au lieu de cela, nous montrons des informations pour un seul chien, et le titre de la page est généré dynamiquement au lieu de statique. Nous utiliserons les données du chien émis par
de l'observable (de dog $ async comme chien
) pour afficher les détails à l'aide des classes Bootstrap CSS . Le composant devrait ressembler à ceci dans le navigateur quand fini:

Pour arriver à la page de détail d'un chien, un utilisateur non authentifié sera invité par le AuthGuard
pour se connecter en premier. Une fois authentifiés, ils seront redirigés vers la page de détails demandée. Essayez-le!
Maintenant que nos pages de listes de chiens et de détails sont terminées, il est temps de travailler sur l'ajout de commentaires en temps réel!
La première chose que nous ferons est d'établir la forme de nos commentaires. initialiser de nouvelles instances de commentaire. Implémentons la classe comment.ts
dans notre application Angular:
// src / app / comments / comment.ts
classe d'exportation Commentaire {
constructeur(
utilisateur public: chaîne,
uid public: chaîne,
image publique: chaîne,
texte public: chaîne,
horodatage public: numéro
) {}
// Solution de contournement car Firestore n'accepte pas les instances de classe
// comme données lors de l'ajout de documents; doit déballer l'instance pour enregistrer.
// Voir: https://github.com/firebase/firebase-js-sdk/issues/311
public get getObj (): object {
résultat de const = {};
Object.keys (this) .map (key => résultat [key] = this [key]);
résultat de retour;
}
}
Contrairement aux modèles Dog
et DogDetail
notre modèle Comment
est un class pas une interface . Nous finirons par initialiser Commentaire
instances dans notre composant de formulaire de commentaire, et pour ce faire, une classe est nécessaire. En outre, Firestore n'accepte que les objets JS classiques lors de l'ajout de documents à une collection. Nous devons donc ajouter une méthode à notre classe qui déplie l'instance vers un objet. En revanche, une interface ne fournit qu'une description d'un objet. Cela suffisait pour Dog
et DogDetail
mais ne suffirait pas pour Commenter
.
Une fois rendu, nous voulons que les commentaires ressemblent à ceci: [19659036] 
Comme vous pouvez le voir, chaque commentaire a un nom d'utilisateur, une image, un texte de commentaire, et une date et l'heure. Les commentaires ont également besoin d'un identifiant unique, fourni dans les données sous la forme uid
. Cet identifiant unique garantit aux utilisateurs un accès approprié pour supprimer leurs propres commentaires, mais pas les commentaires laissés par d'autres personnes.
Maintenant que nous avons une idée de ce à quoi un commentaire devrait ressembler, allons configurer nos règles Firebase Firestore.
Firebase Cloud Firestore et ses règles
Nous utiliserons la base de données Cloud Firestore de Firebase pour stocker les commentaires de notre application. Cloud Firestore est une base de données NoSQL, flexible, évolutive et hébergée dans le cloud qui fournit des fonctionnalités en temps réel. Au moment de la rédaction, Firestore est en version bêta, mais c'est la base de données recommandée pour toutes les nouvelles applications mobiles et Web. Vous pouvez en savoir plus sur en choisissant entre Base de données en temps réel (RTDB) et Cloud Firestore ici .
Rappel: Si vous avez besoin d'un rafraîchissement rapide sur le produit Firebase, relisez Comment authentifier Firebase et Angulaire avec Auth0 - Partie 1: Firebase et Auth0 .
Firestore organise des données comme documents dans collections . Ce modèle de données devrait vous être familier si vous avez de l'expérience avec les bases de données NoSQL axées sur les documents comme MongoDB . Sélectionnez maintenant Cloud Firestore comme base de données maintenant
- Connectez-vous au projet Firebase que vous avez créé dans la partie 1 de ce tutoriel
- Cliquez sur Database dans le menu latéral.
- Dans la liste déroulante située à côté de l'en-tête de la base de données, sélectionnez Cloud Firestore .
Ajouter une collection et Premier document
L'onglet Data sera affiché par défaut. base de données n'a actuellement rien dedans. Ajoutons notre collection et un document pour que nous puissions interroger notre base de données dans Angular et que quelque chose soit retourné.
Cliquez sur + Ajouter Collection . Nommez votre collection commentaires
puis cliquez sur le bouton Suivant . Vous serez invité à ajouter votre premier document

Dans le champ Document id cliquez sur Auto-ID . Cela va automatiquement remplir un identifiant pour vous. Ensuite, ajoutez les champs que nous avons créés précédemment dans le modèle comment.ts
avec les types appropriés et quelques données d'espace réservé. Nous avons seulement besoin de ce document de départ jusqu'à ce que nous sachions que notre liste se comporte correctement dans notre application Angular, puis nous pouvons le supprimer en utilisant la console Firebase et entrer des commentaires correctement en utilisant un formulaire dans le front end.
avoir un formulaire construit encore, les données de la graine seront utiles. Une fois que vous avez entré les champs et les types corrects, vous pouvez remplir les valeurs comme bon vous semble. Voici une suggestion:
utilisateur : Test User
Uid : abc-123
photo : https://cdn.auth0.com/avatars/tu.png
text : Ceci est un commentaire de test de la console Firebase.
horodatage : 1514584235257
Note: Un commentaire avec une valeur inventée uid
ne sera valide pour aucun utilisateur authentifié réel une fois que nous aurons établi des règles de sécurité Firebase. Le document de départ devra être supprimé en utilisant la console Firebase si nous voulons l'enlever plus tard. Nous ne pourrons pas le supprimer en utilisant les méthodes SDK dans l'application Angular, comme vous le verrez dans les règles ci-dessous.
Une fois que vous avez entré le commentaire de votre faux utilisateur, cliquez sur le bouton Enregistrer . La nouvelle collection et le nouveau document doivent apparaître dans la base de données. Cela fournit des données que nous pouvons interroger dans notre application Angular
Firebase Rules
Ensuite, nous allons configurer la sécurité de notre base de données Firestore. Passez maintenant à l'onglet Rules
Les règles de sécurité Firebase fournissent la validation back-end et la validation . Dans l'API du nœud de notre application, nous avons vérifié que les utilisateurs étaient autorisés à accéder aux points de terminaison à l'aide du middleware d'authentification Auth0 et JWT . Nous avons déjà configuré l'authentification Firebase dans notre application API et Angular, et nous utiliserons la fonction de règles pour autoriser les permissions sur la base de données.
Une règle est une expression évaluée pour déterminer si une requête est autorisée pour effectuer une action souhaitée. - Référence aux règles de sécurité de Cloud Firestore
Ajoutez le code suivant dans l'éditeur de règles de base de données Firebase. Nous y reviendrons plus en détail ci-dessous.
// Règles de base de données Firebase pour Cloud Firestore
service cloud.firestore {
match / databases / {base de données} / documents {
match / comments / {document = **} {
autoriser read: si vrai;
Autoriser create: if request.auth! = null
&& request.auth.uid == request.resource.data.uid
&& request.resource.data.text est une chaîne
&& request.resource.data.text.size () <= 200;
autoriser la suppression: if request.auth! = null
&& request.auth.uid == resource.data.uid;
}
}
}
Firestore a des méthodes de demande de règle : a lu
et a écrit
. La lecture comprend obtenir les opérations
et list
. L'écriture inclut les opérations create
update
et delete
. Nous allons implémenter lire
créer
et supprimer
règles.
Note: Nous n'allons pas ajouter de fonction d'édition de commentaires dans notre application, donc mise à jour
n'est pas incluse. Toutefois, n'hésitez pas à ajouter une règle update
si vous souhaitez ajouter cette fonctionnalité par vous-même!
Les règles sont exécutées lorsqu'une requête d'utilisateur correspond
à un document chemin. Les chemins peuvent être entièrement nommés, ou ils peuvent utiliser des caractères génériques. Nos règles s'appliquent à tous les documents de la collection comments
que nous avons créée
Nous voulons tout le monde pouvoir lire commentaires, aussi bien anonymes qu'authentifiés . Par conséquent, la condition pour autoriser lire
est simplement si elle est vraie
.
Nous voulons seulement utilisateurs authentifiés pour pouvoir créer nouveau commentaires. Nous allons vérifier que l'utilisateur est connecté et que les données sauvegardées possèdent une propriété uid
qui correspond à l'authentification de l'utilisateur uid
() request.auth.uid
dans les règles Firebase). En outre, nous pouvons faire un peu de validation sur le terrain ici. Nous allons vérifier que les données de la requête ont une propriété text
qui est une chaîne de 200 caractères ou moins (nous ajouterons aussi cette validation dans notre application Angular sous peu).
Enfin, nous voulons seulement Les utilisateurs peuvent supprimer leurs propres commentaires . Nous pouvons autoriser la suppression
si l'UID de l'utilisateur authentifié correspond à la propriété uid
du commentaire existant en utilisant resource.data.uid
.
Remarque: Vous pouvez en savoir plus sur la requête et ressources mots-clés dans les documents Firebase
Maintenant que notre base de données est prête, il est temps de retourner à notre application Angular et de mettre en œuvre des commentaires en temps réel! La première chose que nous allons faire est d'afficher les commentaires. Nous voulons que les commentaires soient mis à jour de façon asynchrone en temps réel, explorons comment faire avec notre base de données Cloud Firestore et le angularfire2 SDK .
Nous avons déjà créé l'architecture de notre module Commentaires, commençons par construire sur notre comments.component.ts
:
// src / app / comments / comments / comments.component.ts
import {Component} à partir de '@ angular / core';
importer {AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument} à partir de 'angularfire2 / firestore';
import {Observable} à partir de 'rxjs / Observable';
importer {map, catchError} depuis 'rxjs / operators';
importer {Commentaire} à partir de './../comment';
importer {AuthService} à partir de '../../auth/auth.service';
@Composant({
selector: 'app-comments',
templateUrl: './comments.component.html',
styleUrls: ['./comments.component.css']
})
export class CommentsComponent {
private _commentsCollection: AngularFirestoreCollection ;
commentaires $: Observable ;
chargement = vrai;
erreur: booléen;
constructeur(
privé afs: AngularFirestore,
autorisation publique: AuthService
) {
// Recevez les 15 derniers commentaires de Firestore, classés par date et heure
this._commentsCollection = afs.collection (
'commentaires',
ref => ref.orderBy ('horodatage'). limite (15)
)
// Mettre en place observable des commentaires
this.comments $ = this._commentsCollection.snapshotChanges ()
.tuyau(
map (res => this._onNext (res)),
catchError ((err, caught) => this._onError (err, catch))
)
}
private _onNext (res) {
this.loading = faux;
this.error = false;
// Ajouter Firestore ID aux commentaires
// L'ID est nécessaire pour supprimer des commentaires spécifiques
return res.map (action => {
const data = action.payload.doc.data () comme commentaire;
const id = action.payload.doc.id;
return {id, ... données};
});
}
private _onError (err, caught): Observable {
this.loading = faux;
this.error = true;
return Observable.throw ('Une erreur s'est produite lors de la récupération des commentaires.');
}
onPostComment (commentaire: commentaire) {
// Défaites l'occurrence de Comment à un objet pour Firestore
// Voir https://github.com/firebase/firebase-js-sdk/issues/311
const commentObj = comment.getObj;
this._commentsCollection.add (commentObj);
}
canDeleteComment (uid: string): booléen {
if (! this.auth.loggedInFirebase ||! this.auth.userProfile) {
return false;
}
return uid === this.auth.userProfile.sub;
}
deleteComment (id: string) {
// Supprimer le commentaire avec l'invite de confirmation en premier
if (window.confirm ('Êtes-vous sûr de vouloir supprimer votre commentaire?')) {
const thisDoc: AngularFirestoreDocument = this.afs.doc (`comments / $ {id}`);
thisDoc.delete ();
}
}
}
Nous allons d'abord importer les dépendances angularfire2 nécessaires pour utiliser Firestore, les collections et les documents. Nous avons aussi besoin de Observable
carte
et catchError
de RxJS, notre modèle Comment
et AuthService
. [19659036] Nous allons déclarer les membres ensuite. Le privé _commentsCollection
est une collection Firestore contenant des objets ayant la forme de Comment
. Le commentaires $
observable est un flux avec des valeurs qui prennent la forme de tableaux de Comment
s. Ensuite, nous avons nos propriétés de chargement
et erreur
.
Après avoir passé AngularFirestore
et AuthService
à la fonction constructeur, nous devons récupère nos données de collecte auprès de Cloud Firestore. Nous allons utiliser la méthode angularfire2 collection ()
pour ce faire, en spécifiant Comment
comme type, en passant le nom de notre collection ( commentaires
) , ordonnant les résultats par horodatage
et limitant aux 15 derniers commentaires
Ensuite, nous allons créer nos commentaires $
observables en utilisant le _commentsCollection
. Nous utiliserons les opérateurs map ()
et catchError ()
RxJS pour gérer les données et les erreurs émises.
Dans notre gestionnaire privé _onNext ()
nous 'set chargement
et erreur
à faux
. Nous ajouterons également l'ID de document Firestore à chaque élément des tableaux émis par le flux commentaires $
. Nous avons besoin de ces identifiants pour permettre aux utilisateurs de supprimer des commentaires individuels. Afin d'ajouter l'ID aux valeurs émises, nous utiliserons la méthode snapshotChanges ()
pour accéder aux métadonnées . Nous pouvons alors mapper (19459037) document id
s dans les données renvoyées en utilisant l'opérateur de propagation .
Note: Vous remarquerez peut-être que nous n'avons pas mis erreur
à faux
dans la méthode de succès dans nos chiens ou chiens observables, mais nous le faisons ici. Le flux de commentaires émet une valeur à chaque fois qu'un utilisateur de ajoute un commentaire en temps réel. Par conséquent, nous devrons peut-être réinitialiser le statut d'erreur de manière asynchrone en réponse.
Le gestionnaire privé _onError ()
doit être très familier avec nos autres composants. Il définit chargement
et erreur
propriétés et génère une erreur.
La méthode onPostComment ()
sera exécutée lorsque l'utilisateur soumet un commentaire en utilisant le formulaire de commentaire composant (que nous allons construire prochainement). La charge utile onPostComment ()
contiendra une instance Comment
contenant les données de commentaire de l'utilisateur, qui doivent ensuite être dépliées sur un objet normal pour être enregistrées dans Firestore. Nous allons enregistrer l'objet commentaire non déroulé à l'aide de la méthode angular firestore add ()
.
La méthode canDeleteComment ()
vérifie si l'utilisateur actuel est le propriétaire d'un commentaire donné. S'ils ont créé le commentaire, ils peuvent également le supprimer. Cette méthode vérifie que la propriété userProfile.sub
de l'utilisateur connecté correspond à l'identificateur du commentaire
.
La méthode deleteComment ()
est exécutée lorsque l'utilisateur clique sur l'icône pour supprimer un commentaire. Cette méthode ouvre une boîte de dialogue de confirmation qui confirme l'action et, si elle est confirmée, utilise l'argument id
pour supprimer le document de commentaire correct de la collection Firestore. (C'est pourquoi nous avons besoin d'ajouter le document id
s à nos données lorsque nous avons mappé les valeurs émises par nos commentaires $
observables.)
Note: Rappelons que nos règles Firestore empêchent également utilisateurs de supprimer des commentaires qu'ils n'ont pas créés. Nous devrions toujours nous assurer que les droits d'accès sont appliqués le les deux front-end et back-end pour une sécurité adéquate.
Maintenant, mettons nos fonctionnalités de classe pour fonctionner dans l'interface utilisateur. Ouvrez le fichier comments.component.html
et ajoutez:
Nous utiliserons principalement des classes Bootstrap pour styliser nos commentaires, avec un peu de CSS personnalisé que nous ajouterons ensuite. Notre modèle de commentaires, comme nos modèles de chien et de composant chien, a un
et utilise le tuyau asynchrone avec NgIfElse pour afficher l'interface utilisateur appropriée
La liste des commentaires devrait afficher l'image du commentaire
(l'utilisateur avatar de son auteur), le nom de l'utilisateur
et l'horodatage
formaté avec le [DatePipe] . Nous passerons l'uid du commentaire
à la méthode canDeleteComment ()
pour déterminer si un lien de suppression doit être affiché. We’ll then display the comment text
using property binding to innerHTML
.
Finally, we’ll create elements to show the comment form or a message instructing users to log in if they wish to leave a comment.
Note: Our
will use event binding to emit an event called postComment
when a user submits a comment. The CommentsComponent
class listens for that event and handles it with the onPostComment()
method that we created, using the $event
payload to save the submitted comment to the Firestore database. We’ll hook up the (postComment)
event when we create the form in the next section.
Finally, open the comments.component.css
file and let’s add a few styles to our comments list:
/* src/app/comments/comments/comments.component.css */
.avatar {
affichage: inline-block;
height: 30px;
}
.comment-text {
background: #eee;
position: relative;
}
.comment-text::before {
border-bottom: 10px solid #eee;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
content: '';
display: block;
height: 1px;
position: absolute;
top: -10px; left: 9px;
width: 1px;
}
Now that we have a listing of comments that updates in realtime, we need to be able to add new comments in our front end.
Open the comment-form.component.ts
file and let’s get started:
// src/app/comments/comment-form/comment-form.component.ts
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Comment } from './../../comment';
import { AuthService } from '../../../auth/auth.service';
@Composant({
selector: 'app-comment-form',
templateUrl: './comment-form.component.html'
})
export class CommentFormComponent implements OnInit {
@Output() postComment = new EventEmitter();
commentForm: Comment;
constructor(private auth: AuthService) { }
ngOnInit() {
this._newComment();
}
private _newComment() {
this.commentForm = new Comment(
this.auth.userProfile.name,
this.auth.userProfile.sub,
this.auth.userProfile.picture,
'',
null);
}
onSubmit() {
this.commentForm.timestamp = new Date().getTime();
this.postComment.emit(this.commentForm);
this._newComment();
}
}
As mentioned earlier, we will need to emit an event from this component to the parent CommentsComponent
which sends the new comment to Firestore. The CommentFormComponent
is responsible for constructing the Comment
instance with the appropriate information gathered from the authenticated user and their form input and sending that data to the parent. In order to emit the postComment
event, we’ll import Output
and EventEmitter
. We’ll also need our Comment
class and AuthService
to get user data.
Our comment form component’s members include an Output decorator (postComment
) that is an EventEmitter with type of Comment
and commentForm
which will be an instance of Comment
to store form data.
In our ngOnInit()
method, we’ll create a new Comment
instance with the private _newComment()
method. This method sets the local commentForm
property to a new instance of Comment
with the authenticated user’s name
sub
and picture
. The comment text
is an empty string and the timestamp
is set to null
(it will be added when the form is submitted).
The onSubmit()
method will be executed when the comment form is submitted in the template. This method adds the timestamp
and emits the postComment
event with the commentForm
data as its payload. It also calls the _newComment()
method to reset the comment form.
Open the comment-form.component.html
file and add this code:
The comment form template is quite simple. The form’s only field is a text input, since all other comment data (like name, picture, UID, etc.) is added dynamically in the class. We’ll use a simple template-driven form to implement our comment form.
element listens for an (ngOnSubmit)
event, which we’ll handle with our onSubmit()
method. We’ll also add a template reference variable called #tplForm
and set it to ngForm
. This way, we can access the form’s properties in the template itself.The
element should have an [(ngModel)]
that binds to commentForm.text
. This is the property we want to update when a user types in the form field. Recall that we set up our Firestore rules to accept comment text 200 characters or less, so we’ll add this maxlength
to our front end, along with a required
attribute so that users cannot submit empty comments.
Finally, the
to submit the form should be [disabled]
if the form is not valid. We can reference the valid
property using the tplForm
reference variable we added to the
Source link
Commentaires
S'il vous plaît connectez-vous pour laisser un commentaire.