Angular路由解析器为什么只监听我的observable的第一个值?

huangapple go评论61阅读模式
英文:

Why does Angular route resolver only listen to the first value of my observable?

问题

我理解了,你想要关于Angular 15中路由解析器的帮助。你想确保解析器等待所有从可观察对象中发布的值。但目前你的解析器似乎只监听第一个值。

你可以尝试在解析器中使用 forkJoin 操作符,以确保等待所有产品都被获取后才继续。以下是一种可能的实现方式:

@Injectable()
export class ProductRouteGuardService {
  constructor(private productService: ProductService) { }
  
  resolve() {
    return this.productService.getProducts().pipe(
      take(1), // Make sure the observable completes after the first emission
      toArray() // Collect all emitted values into an array
    );
  }
}

这里我们使用 take(1) 以确保在第一个值被发出后,可观察对象就会完成。然后使用 toArray() 来将所有值收集到一个数组中。

希望这对你有所帮助!如果你有任何其他问题,请随时提出。

英文:

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 :

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

{

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

<!-- end snippet -->

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

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

<!-- end snippet -->

My ProductService :

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

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

<!-- end snippet -->

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

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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[&#39;products&#39;]];
    console.log(this.products)
  }
}

<!-- end snippet -->

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 ?

答案1

得分: 1

尝试使用of([...])操作符,而不是在服务内部使用from([...])

更多信息:stackoverflow.com/questions/42704552/of-vs-from-operator

需要注意的是,在传递类似数组的结构(包括字符串)时,of 和 from 之间的区别很重要:

Observable.of([1, 2, 3]).subscribe(x => console.log(x)); 会一次性打印整个数组。

另一方面,

Observable.from([1, 2, 3]).subscribe(x => console.log(x)); 会逐个打印元素。

对于字符串,行为相同,但在字符级别上。

如果你想解析多个值,你可以使用rxjs操作符将它们组合在一起,然后一次性解析:

@Injectable()
export class ProductService{
  products = [...]; // 产品数组
  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))]);
  }
}
英文:

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

More info: https://stackoverflow.com/questions/42704552/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 =&gt; products)),             
      this.productService.getCategories()
       .pipe(map(categories =&gt; categories))]);
  }
}

huangapple
  • 本文由 发表于 2023年4月19日 19:06:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76053771.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定