Search code examples
angularfirebasegoogle-cloud-firestoreangular-routerangular-resolver

Angular Router Event does not ended


I have a problem when rendering my "Product Edit Page" when I click a specific item in my list.

I have a method from a service that retrieves a specific data from firebase firestore.

product.service.ts

/**
* Get specific product
*/
getProduct(guid: string): Observable<IProduct> {
return new Observable((observer) => {
      this.firebaseDb.collection('products')
        .doc('XNRkodfbiRoKc2DYPA3o')
        .onSnapshot((querySnapshot) => {

          let product: IProduct;
          product = <IProduct>querySnapshot.data();
          product.guid = guid;

          observer.next(product);

        }, (error) => console.log(error.message));
    });
}

The above method will be called in my Resolver as shown below.

product-resolver.service.ts

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<IProduct> {
const id = route.params['id'];

return this.productService.getProduct(id)
              .pipe(
                map(product => {
                  if (product) {
                    return product;
                  }
                  console.log(`Product was not found: ${id}`);
                  this.router.navigate(['/products']);
                  return null;
                }),
                catchError(error => {
                  console.log(`Retrieval error: ${error}`);
                  return of(null);
                })
              );
}

product-routing.module.ts

  {
    path: 'products/:id/edit',
    component: ProductEditComponent,
    canActivate: [AuthGuardService],
    resolve: { product: ProductResolver }
  }

The problem now is, whenever I click the edit button from my list, nothing happens. This supposed to be redirected to my edit product page and fill all the data from the textbox using this.route.snapshot.data['product'].

I debug the route by passing enableTracing: true to my routing and it seems it was stuck in Router Event: ResolveStart. Can someone enlighten me why this is the behavior?


Solution

  • The router waits for the observable to complete. Ensure that it is completed after the first value is emitted by using take(1) or first(). E.g.:

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<IProduct> {
        return this.productService.getProduct(route.paramMap.get('id')).pipe(
          take(1),
          map(product => {
            if (!product) {
              // noinspection JSIgnoredPromiseFromCall
              this.router.navigate(['/404']);
              return null;
            } else {
              return product;
            }
          })
        );
      }
    

    P.S. you can cleanup your getProduct method like this:

    getProduct(guid: string): Observable<IProduct> {
        return from(this.firebaseDb.collection('products').doc('XNRkodfbiRoKc2DYPA3o').onSnapshot()).pipe(
            map((snapshot:any) => {
                const product = <IProduct>snapshot.data();
                product.guid = guid;
                return product;
            }),
        ); 
    }