Search code examples
angularrxjsangular2-formbuilder

angular: trouble with an asynchronous form validator that uses results from multiple observables


I am using the angular forms validation to attempt to check if a given value is already in use in a set of data.

The app I am building is an online auction platform so I have a structure where 'Items' belong to 'Auctions' and each item within a given auction should have a unique "Lot Number." It is also important that the user be able to assign the lot number manually, rather than it being an auto-assigned index.

When a seller is inputing information about a a new Item I want to perform a check on my database (Firebase) to determine in a given lot number has already been used in the given auction. I am trying to perform this check as an asynchronous angular form validator.

The code I have is this:

Note: this.data is the name of a service where I keep functions which hit the database. They return observables.

uniqueLotNumberValidator(control: FormControl){
    return this.data.getItemsByAuction_snapshot(this.auctionId).map((itemsSnapList) => { 
      let itemObservableArray = [];
      itemsSnapList.forEach((snap) => {
        itemObservableArray.push(this.data.getItemLotNumberById(snap.key));
      })
      forkJoin(itemObservableArray).subscribe(lotNumbers => {
        let i = lotNumbers.length;
        while (i--) {
          if (lotNumbers[i] == control.value) {
            console.log('already in use');
              return { dubNumber: true };
          }
        }
        console.log('lot number is unique');
        return null;
      });
    });
   }

To walk through what I am trying to do:

First, data.getItemsByAuction_snapshot returns an observable which provides a list of item keys that belong to the auction.

Then I populate an array with an observable (that returns the lot number) for each of the keys so that I can do a forkJoin and get an array back of each lot number in use in the auction.

With the result of that forkjoin I now have an array of all of the lot numbers in use and I loop through them to compare each to the value of the user input.

If the user's provided value matches one of the values already in use I want the validator to fail. If however it is unique the validator should read as valid.

This is not behaving as I would hope. I suspect my error lies in my not fully grasping observables. Any help understanding the error here would be appreciated.


Solution

  • The solution I found was to return a new promise and place my code inside it:

      uniqueLotNumberValidator(control: FormControl){
        return new Promise((resolve, reject) => {
          this.data.getItemsByAuction_snapshot(this.auctionId).subscribe((itemsSnapList) => { 
            let itemObservableArray = [];
            itemsSnapList.forEach((snap) => {
              itemObservableArray.push(this.data.getItemLotNumberById(snap.key));
            });
           forkJoin(itemObservableArray).subscribe(lotNumbers => {
              let i = lotNumbers.length;
              while (i--) {
                if (lotNumbers[i] == control.value) {
                  resolve({ dubNumber: true });
                }
              }
              resolve(null);
            });
          });
        });
       }
    

    It works as expected.