2

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

JD Carr
  • 219
  • 3
  • 17
  • 1
    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`. – frido May 02 '19 at 08:28

2 Answers2

1

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;
      })
    )
  }
talhature
  • 2,246
  • 1
  • 14
  • 28
  • My backend data comes in weird, which is why I push it to a dataArr before saving it to the store (though this might be where it has the problem?) JSON response: `{ '6be315a0-d661-4dae-ba91-903e3efeef24': { id: '6be315a0-d661-4dae-ba91-903e3efeef24', sku: '333344', description: 'skdhfskdjf', price: '123' }, '748d3d1f-7d55-4492-b39d-0f37479848ba': { id: '748d3d1f-7d55-4492-b39d-0f37479848ba', sku: '222233', description: 'sdjshfdkjshf', price: '12' } } ` – JD Carr May 01 '19 at 20:29
  • But the thing I am saving to the store looks like this: `[ { id: '6be315a0-d661-4dae-ba91-903e3efeef24', sku: '333344', description: 'skdhfskdjf', price: '123' }, { id: '748d3d1f-7d55-4492-b39d-0f37479848ba', sku: '222233', description: 'sdjshfdkjshf', price: '12' } ]` – JD Carr May 01 '19 at 20:31
  • My states are also interesting. Here is the state of the thing that's working: [Todo State](https://i.imgur.com/sPfluSS.png) And here the state of the things that isn't: [Product State](https://i.imgur.com/1Xi8Qn7.png) The two states look identical aside from the IDs.... I wonder if that's it? I wonder if the IDs have to be enumerated rather than a UUID? – JD Carr May 01 '19 at 20:35
  • Then your case requires a map operation instead of tapping. Im editing my answer according to that. – talhature May 02 '19 at 05:27
0

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();
Bazinga
  • 10,716
  • 6
  • 38
  • 63