反应形式,检查用户名是否存在

Reactive Form , check if username exists

我在 Ionic/Firebase 中遇到一个问题,验证器的值是反应式的。特别是我在下面有这 2 个函数,用于检查 firebase 实时数据库中的用户名是否存在。 2 个函数 return 一个 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)
      }         
  }

然后,我 class 在其中定义了一个 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.required,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()获取匹配的数量结果,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 用户名,因为这意味着您的用户数据是全球可读的,而且效率很低。相反,您应该在数据库中创建一个仅包含用户名的索引。

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

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

{
  "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());
}

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

static async claimUsername(fc: FormControl) {
  const user = firebase.auth().currentUser;
  if (!user) {
    throw new Error("You need to login first!")
  }
 
  return firebase.database().ref()
    .child("usernames")
    .child(fc.value)
    .set(user.uid);
}