如何在Angular Firestore中检查文档唯一值?解决注册重复问题
解决Firestore注册重复数据与唯一性校验问题
核心问题分析
- 重复点击提交按钮会多次触发
add()方法,生成重复文档 - 注册流程未前置校验用户名、邮箱的唯一性
- 流程顺序错误:先写入Firestore数据再执行Auth注册,可能导致Auth失败但Firestore已存在无效数据
修复方案
1. 增加提交状态锁,拦截重复点击
在组件中添加状态变量,提交时锁定按钮,流程结束后解锁,从交互层面防止重复提交。
2. 前置唯一性校验
通过Firestore查询分别检查用户名、邮箱是否已存在,只有两者都未被使用时才继续注册流程。
3. 调整流程顺序
正确流程:表单验证 → 唯一性校验 → Auth注册 → 写入Firestore数据,避免Auth失败后产生无效数据。
修改后的完整代码
import { Component } from '@angular/core'; import { AngularFirestore } from '@angular/fire/compat/firestore'; import { AuthService } from './auth.service'; import { ToastrService } from 'ngx-toastr'; import { Router } from '@angular/router'; @Component({ selector: 'app-signup', templateUrl: './signup.component.html', styleUrls: ['./signup.component.css'] }) export class SignupComponent { signUpForm: any; // 假设已定义表单实例 isSubmitting = false; // 新增提交状态锁 constructor( private afs: AngularFirestore, private authService: AuthService, private toast: ToastrService, private router: Router ) {} async submit() { // 表单无效或正在提交时直接返回 if (!this.signUpForm.valid || this.isSubmitting) { return; } this.isSubmitting = true; const { name: username, email, password } = this.signUpForm.value; try { // 检查用户名是否已存在 const usernameQuery = await this.afs.collection('user', ref => ref.where('username', '==', username) ).get().toPromise(); if (!usernameQuery.empty) { this.toast.error('该用户名已被使用'); this.isSubmitting = false; return; } // 检查邮箱是否已存在 const emailQuery = await this.afs.collection('user', ref => ref.where('email', '==', email) ).get().toPromise(); if (!emailQuery.empty) { this.toast.error('该邮箱已被注册'); this.isSubmitting = false; return; } // 执行Auth注册 await this.authService.signUp(username, email, password).pipe( this.toast.observe({ success: '注册成功!', loading: '注册中...', error: ({ message }) => `${message}` }) ).toPromise(); // 写入Firestore用户数据(用Auth的UID作为文档ID,确保唯一性) const currentUser = await this.authService.getCurrentUser(); // 假设authService提供此方法 if (currentUser) { const userData = { bio: "not set", completedList: [], dislikedList: [], email: email, isBanned: false, likedList: [], planList: [], username: username }; await this.afs.collection('user').doc(currentUser.uid).set(userData); } // 发送验证邮件并跳转登录页 this.authService.sendEmail(); await this.authService.logout(); this.router.navigate(['/auth']); } catch (error) { this.toast.error('注册失败,请稍后重试'); console.error(error); } finally { this.isSubmitting = false; // 无论成败,解锁提交状态 } } }
额外优化建议
- 在Firestore控制台为
username和email字段创建唯一索引,从数据库层面强制唯一性,避免并发场景下的竞态问题 - 模板中给注册按钮添加禁用状态:
<button (click)="submit()" [disabled]="isSubmitting || !signUpForm.valid">注册</button> - 使用Auth用户的UID作为Firestore文档ID,既保证唯一性,也方便后续通过UID快速查询用户数据
内容的提问来源于stack exchange,提问作者Pat Rose
相关产品推荐
相关产品推荐

