响应式表单,检查用户名是否存在

问题描述

我在 Ionic/Firebase 中遇到了一个问题,验证器的值以反应形式存在。特别是我下面有这 2 个函数,用于检查 firebase 实时数据库中的用户名是否存在。这两个函数返回一个 Promise 布尔值:

export class UsernameValidator {
  static async getSnapshot(fc: FormControl){
    let isPresent:boolean = false;
    await firebase.database().ref().child("users").orderByChild("username")
    .equalTo(fc.value)
    .once("value",snapshot => {          
    }).then((data)=> {
      if(data.exists())
        isPresent = true;
      else
        isPresent = false;
    });
    console.log(isPresent);
    return isPresent; 
  }

  static async validUsername(fc: FormControl){
    try{
      let present:boolean =await UsernameValidator.getSnapshot(fc)
      if(present==true)
      return  (null);         
   else{
      return ({validUsername: true}); 
    } 
      }catch(e){
        console.log(e)
      }         
  }

然后,我在类中定义了一个 FormGroup 和验证器:

constructor(private route: ActivatedRoute,private router: Router,public pfService: ProfileService,public fb: FormBuilder,public authService: AuthenticationService) 
  {
    this.id = this.authService.userData.uid;
    //Underscore and dot can't be next to each other (e.g user_.name).
    //Underscore or dot can't be used multiple times in a row (e.g user__name / user..name).
    this.validPattern = "^(?=.{6,20}$)(?!.*[_.]{2})[a-z0-9._]+$"; 
    this.validPatternName = "^[a-z]{3,10}$";
    this.userForm = fb.group({
      txtUsername:  ["",[Validators.required,Validators.pattern(this.validPattern),UsernameValidator.validUsername]],txtName:     ["",Validators.pattern(this.validPatternName)]],});
    this.userForm .valueChanges.subscribe(()=> {
      console.log(this.userForm.getError('validUsername'))
      })
  };

问题在于,无论 isPresent 的值如何,控制台中的 validUsername 始终为 null,即使 isPresent 为 false。我该如何解决这个问题?

解决方法

您很接近,但是您在尝试解决导致混淆的问题时混合了不同的语法。

让您陷入困境的另一件事是混淆了两种不同类型的 DataSnapshot

  • 对于直接引用(例如 database().ref("path/to/data")),您可以使用 exists()val() 来获取有关该位置数据的信息。
  • 对于查询引用(例如database().ref("path/to/group").orderByChild("name").equalTo("Tim's Group")),数据作为列表返回,您可以在其中使用numChildren()获取匹配结果的数量,{{1 }} 以查看是否有任何结果(类似于 hasChildren()),您可以使用 exists() 遍历结果。
forEach()

但是,我不建议仅搜索 static async isUsernameTaken(fc: FormControl) { // renamed from getSnapshot() return firebase.database().ref() // note the return here .child("users") .orderByChild("username") .equalTo(fc.value) .once("value") .then((querySnapshot) => querySnapshot.hasChildren()); } 以获取用户名,因为这意味着您的用户数据是全球可读的,而且效率也很低。相反,您应该在数据库中创建一个仅包含用户名的索引。

/users

如果您使用这些 Realtime Database Security Rules 来保护它,您还可以使用以下简单功能。

"/usernames": {
  "bob": "userId1","frank": "userId2","c00lguy": "userId3"
}

要检查用户名是否可用:

{
  "usernames": {
    "$username": {
      // world readable
      ".read": true,// must be logged in to edit,you can only claim free usernames OR delete owned usernames
      ".write": "auth != null && (!newData.exists() || auth.uid == newData.val()) && (!data.exists() || data.val() == auth.uid)",// strings only
      ".validate": "newData.isString()",}
  }
}

声明用户名(如果写入失败,假设用户名已被占用):

static async isUsernameTaken(fc: FormControl) {
  return firebase.database().ref()
    .child("usernames")
    .child(fc.value)
    .once("value")
    .then((dataSnapshot) => dataSnapshot.exists());
}