0

I am working with Angular 15 and I am trying to use the routing resolver handler to prefetch products before loading a product-list component. This product-list component simply displays the products. I am very new to stackoverflow so I'll try my best to explain my problem. I have 4 main actors:

ProductListComponent app-routing module ProductRouteGuard ProductService

The app-routing-module is my router, routing to the ProductListComponent, when the url "/products" is requested but right before loading this component, my router calls the ProductRouteGuard resolver to prefetch products before loading my ProductListComponent

My router :

{
path: 'products', component: ProductListComponent, resolve: {products: ProductRouteGuardService}
}

And then my ProductRouteGuard resolver asks my ProductService to get the products list:

@Injectable()
export class ProductRouteGuardService{
  constructor(private productService : ProductService) { }
  resolve(){
    return this.productService.getProducts().pipe(
        map(product=> product)
      );
  }

My ProductService :

@Injectable()
export class ProductService{
  products = [...]; // array of products

  getProducts(){
      
    let products$ = from(this.products); // observable of products to simulate an api response
    return products$;
  
  }

Eventually my ProductListComponent gets the data by asking the router resolver :

export class ProductListComponent implements OnInit{
  products : any; // my component local array to display products
  constructor(private route : ActivatedRoute){}
  ngOnInit(): void {
    this.products = [this.route.snapshot.data['products']];
    console.log(this.products)
  }
}

So when I run this code, I can see that only my first product is logged in the console meaning, in my opinion, the resolver is only listening the first value published by my observable. I know that in my Observable subscriber function, I could just do something like :

observer.next(this.products);

But I really want the products all to be fetched one by one, and the resolver waiting for them to be all fetched. My question is : How can I make the router resolver to listen to all the published values coming from my observable ?

idash
  • 29
  • 6

1 Answers1

1

Try the of([...]) operator instead of the from([...]) in your simulation inside the service.

More info: 'of' vs 'from' operator

It is important to note the difference between of and from when passing an array-like structure (including strings):

Observable.of([1, 2, 3]).subscribe(x => console.log(x)); would print the whole array at once.

On the other hand, Observable.from([1, 2, 3]).subscribe(x => console.log(x)); prints the elements 1 by 1.

For strings the behaviour is the same, but at character level.

If you want to resolve multiple values you can use rxjs operators to put them together and resolve once:

@Injectable()
export class ProductService{
  products = [...]; // array of products
  categories = [...]; // array of categories
  getProducts(){
    return of(this.products);   
  }
  getCategories(){
    return of(this.categories);   
  }
}

@Injectable()
export class ProductRouteGuardService{
  constructor(private productService : ProductService) { }
  resolve(){
    return forkJoin([
      this.productService.getProducts()
        .pipe(map(products => products)),             
      this.productService.getCategories()
       .pipe(map(categories => categories))]);
  }
}
Don
  • 366
  • 1
  • 10
  • thank you for your help Don. The thing is I really want the values of my array to be published one by one. – idash Apr 19 '23 at 11:16
  • No problem. But why would they come from your api one by one and not as a list alltogether? – Don Apr 19 '23 at 11:19
  • 1
    I don't think resolvers are meant to work with an event stream, but rather resolve once. You can read more here: https://angular-schule.github.io/website-articles/blog/2019-07-resolvers/README.html Also, fetching your items inside the resolver is not "best practice", resolvers are meant for configurations. You want the user to instantly land on your page, with your component loading its data in the initializing process. Its better for web-performance too. – Don Apr 19 '23 at 11:24
  • let's say I need 3 different data from 3 different apis, and I need this three data to be fetched before displaying my component – idash Apr 19 '23 at 11:30
  • I see what you mean for best practices, it was just a practice exercice for me. You are right about quick page loading but I saw on very famous online courses that resolver is also very useful to stream and pass data out to the component – idash Apr 19 '23 at 11:33
  • 1
    I extended my answer to fit your needs. when you want to have multiple api calls inside the resolver you can join the observables and resolve them once both are completed. Be aware i changed the from to of operator, since api calls also only have one result in this case. – Don Apr 19 '23 at 11:51
  • If you think it was the right answer to your question you can mark it as accepted :) – Don Apr 19 '23 at 15:13