1

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?

pfx
  • 20,323
  • 43
  • 37
  • 57
klaydze
  • 941
  • 14
  • 36
  • Instead of creating a new observable for a firebase query, use `fromPromise`, which is way more efficient in terms of comprehension and code quantity. –  Sep 04 '18 at 06:28
  • @trichetriche Can you possibly post some code snapshot of fromPromise related to the code above? Thanks! – klaydze Sep 06 '18 at 04:54
  • Or you could [make a quick google search](https://stackoverflow.com/questions/39319279/convert-promise-to-observable) ... –  Sep 06 '18 at 06:32

1 Answers1

4

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;
        }),
    ); 
}
DarkLeafyGreen
  • 69,338
  • 131
  • 383
  • 601
  • This works! So, in my service above, I can also call "observer.complete()" to complete the observable? Is calling complete() is necessary? Many thanks! – klaydze Sep 04 '18 at 05:57
  • @klaydze yes, you can use observable.complete() as well. However you have to handle the error case in onSnapshot, e.g. observable.error(error) – DarkLeafyGreen Sep 04 '18 at 06:05
  • I tried the updated code you provided in `getProduct` method but I'm having difficulty implementing it because `onSnapshot` should have a parameter base on the doc of firebase – klaydze Sep 06 '18 at 05:12
  • @klaydze yes, this makes only sense when guid is your actual doc id – DarkLeafyGreen Sep 06 '18 at 05:31