1
votes

Actually I stuck in this problem.

I've created a custom async validator to check if a username ist taken or not. This is where I bind it:

 'name': ['',[Validators.required, Validators.minLength(4), Validators.minLength(4)], this.asyncName.bind(this)],

And this is my Validator:

asyncName(control: FormControl) {
    this.names$ = this.af.database.list('/usernames')
     .map (name => {
            name.filter(name => {
                if (name.name.toLowerCase() === control.value.toLowerCase()) {
                     console.log("Username taken");  
                     return {
                        valid: false
                    }; 
                } else {
                    console.log("Username available");
                    return {
                        valid: true
                    };
                }
            })
        });             

    return this.names$;
};

So this validator works. If the user name is taken it logs "Username taken" else "Username available".

My Problem: The return statement is not working. When I subscribe to the state of the form it would be still "Pending" and not "Valid" or "Invalid". Angular2 thinks the Validation is still running.

Does anyone know how to create valid return or resolve statement based on observables?

Maybe to subscribe by my self and do something with the resolve() function. But all my attempts were not working.

Thanks in advance!!!

2

2 Answers

3
votes

You can try .first() to ensure the observable closes after the first event:

asyncName(control: FormControl) {
    this.names$ = this.af.database.list('/usernames').first()
    .map(...

Not sure if this is the actual problem.

0
votes

I had similar problem. In my case, I had categories list and I wanted to check if a category name is unique or not. This is my validator:

export class CategoryFormComponent {
  constructor(
    private firebase: AngularFireDatabase
  ) {}
  
   categoryExists(control: AbstractControl) {
    return  this.firebase.database
        .ref(`categories/${this.uid}`) // user's uid
        .orderByChild('name')
        .equalTo(control.value)
        .once('value')
        .then((snapshot) => {
          return snapshot.val() ? { categoryExists: true } : null;
      });
  }
}

As you can see you can make search on database (more performant) and then use once which returns Promise.