简体   繁体   中英

Reactive Form , check if username exists

I have a problem in Ionic/Firebase with a value of validator in a reactive form. In particular I have this 2 function below that check if a username in a realtime database of firebase exists. The 2 functions return a Promise boolean:

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)
      }         
  }

Then, I have I class in which I define a FormGroup and validator:

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'))
      })
  };

The problem is that validUsername, from the console, is always null regardless of the value of isPresent, also when isPresent is false. How can I fix this?

You were close, but you've mixed different syntaxes in your attempts to fix the problem which has lead to confusion.

One other thing that is leading you into trouble, is confusing the two different types of DataSnapshot .

  • For a direct reference (eg database().ref("path/to/data") ), you can use exists() and val() to get information about that location's data.
  • For a queried reference (eg database().ref("path/to/group").orderByChild("name").equalTo("Tim's Group") ), the data is returned as a list where you can use numChildren() to get the number of matching results, hasChildren() to see if there are any results (similar to exists() ) and you can iterate through the results using 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());
}

However, I do not recommend searching /users for just usernames as it means that your user's data is world-readable and it's also inefficient. Instead you should create an index in your database that contains only usernames.

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

If you secure this using these Realtime Database Security Rules , you can also make use of the following simple functions.

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

To check if a username is available:

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

To claim a username (if the write itself fails, assume the username is taken):

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM