2
votes

EDIT: DEMO OF THE NON-WORKING CODE

I'm having issues with using the async pipe on an observable. It works in one app but not the other.

The working one can be found here: https://stackblitz.com/edit/angular-ry1dev

The non-working code:

products.component.ts:

export class ProductsComponent implements OnInit {
  products$: Observable<Product[]>;

  //NOTE: this service is coming from Akita state management
  constructor( private productService: ProductService ) {}

  ngOnInit() {
    this.products$ = this.productService.getProducts();
  }
}

products.service.ts (Akita state management):

@Injectable({ providedIn: 'root' })
export class ProductService {
  constructor( private prodStore: ProductStore, private prodService: ProductsService ) {}

  getProducts() {
    return this.prodService.getProducts().pipe(
      tap( result => {
        let dataArr = [];
        for (let obj in result) {
          dataArr.push(result[obj]);
        }

        this.prodStore.add(dataArr)
      })
    )
  }
}

products.service.ts (http called by prodService above):

getProducts() {
  return this.http.get<Product[]>(`${this.API}/products`);
}

products.component.html:

<mat-accordion *ngIf="products$" class='product-accordion'>
  <mat-expansion-panel *ngFor="let product of products$ | async">
    <mat-expansion-panel-header>
      SKU: {{ product.sku }}
    </mat-expansion-panel-header>

    <p>${{ product.price }}</p>
    <p>{{ product.description }}</p>

  </mat-expansion-panel>
</mat-accordion>

The above app's store and query components are the same as the stackblitz.

I should be getting a list of products like the list of todos in the stackblitz but it's erroring in the HTML with:

ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

Why is it working on stackblitz but not in my app?

EDIT: DEMO OF THE NON-WORKING CODE

2
The problem is that the types don't match and you're using objects where arrays are expected. To help you find those kind of error at compile time and not at runtime you should add explicit return types to your functions, e.g. getProducts(): Observable<Product[]>. - frido

2 Answers

1
votes

Could you check what's the actual response from your back-end? Your front-end's assumption is to fetch an array but seems like your back-end is responding with something like this:

{
  "something": {},
  "something": {}
}

the response should be an array instead, like this:

[
  "something": {},
  "something": {}
]

EDIT: If we can not change the backend behavior. Then we need to update our getProducts method to return what our async products binding requires.

getProducts() {
    return this.prodService.getProducts().pipe(
      map( result => {
        let dataArr = [];
        for (let obj in result) {
          dataArr.push(result[obj]);
        }

        this.prodStore.add(dataArr);
        return dataArr;
      })
    )
  }
0
votes

The issue is that the following code returns an object:

getProducts() {
  return products;
}

And ngFor supports only iterable objects like arrays.

If you want to go the Akita path, you should fetch the products, update the store and query it in the component:

this.products$ = this.productsQuery.selectAll();