0
votes

I am trying to use the Async pipe to display my list. I continually get the error invalid pipe argument [object Object] for pipe 'AsyncPipe'

I have used the snapshot changes instead of the value changes since I need both the list and the key. I have mapped the observable to the interface and then subscribe to it in the admin-product component. The list does not show up at all, but when I remove the async on the Html the list shows up.

/////////PRODUCT SERVICE.ts///////////

getAll() {
    return this.db.list('/products')
        .snapshotChanges()
        .pipe(
            map(changes =>
                changes.map(c => {
                    const data = c.payload.val() as Product;
                    const id = c.payload.key;
                    return { id, ...data };
                })
            )
        );
}


////// Product Interface///////

export interface Product {
    title: string;
    price: number;
    category: string;
    imageUrl: string;
}



////Admin-Products Component//////

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ProductService} from './../../product.service';
import { Subscription } from 'rxjs/Subscription';
import {Product } from './../../models/product';

@Component({
  selector: 'app-admin-products',
  templateUrl: './admin-products.component.html',
  styleUrls: ['./admin-products.component.css']
})
export class AdminProductsComponent implements OnInit, OnDestroy {
products: Product[];
filteredProducts: any[];
subscription: Subscription;

constructor(private productService: ProductService) {
  this.subscription = this.productService.getAll()
  .subscribe(products => this.filteredProducts = this.products = products)

}

filter(query: string) {
  this.filteredProducts = (query) ?
  this.products.filter(p => p.title.toLowerCase().includes(query.toLowerCase())) :
  this.products;
}

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }


//// Admin-Products.component.html /////


<p>
  <a routerLink='/admin/products/new' class='btn btn-primary'>New Product</a>
</p>
<p>
  <input
  #query
  (keyup)='filter(query.value)'
  type="text" class="form-control" placeholder="Search...">
</p>

<table class="table">
  <thead>
    <tr>
      <th>Title</th>
      <th>Price</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor='let p of filteredProducts | async '>
      <td>{{p?.title}}</td>
      <td>{{p?.price}}</td>
      <td></td>
      <td>
        <a [routerLink]='["/admin/products/", p?.$key]'>edit</a>
      </td>
    </tr>
  </tbody>
</table>
1
filteredProducts is an array, not an observable or a promise. So there's no need for any async pipe here. - JB Nizet
I'm still new to programming and trying to understand. If I do not use the async pipe then I lose the ability to have that constant refresh. I used the async pipe before in this project and it worked; however, when I added the input on the HTML and instituted the filter method then the async pipe broke. I am trying to understand why that happened, and how I can fix this code to be able to use the async pipe so that I can have that real-time update. - CTay_612
Is p?.$key an observable inside an object? I believe this is where the issue is. - Ben Racicot
Honestly, I have no idea if that is right or not. I know in older versions we could use $key to access the key of the firebase database, but that changed and now we use the snapshotChanges method and then map it. I am not sure what that changes to with the new implementation. - CTay_612
If you want to use async you must expose the observable itself in your component, and not subscribe to it, since that's what the async pipe does. - JB Nizet

1 Answers

0
votes

In my case, I was both subscribing to the observable and using the Async pipe. Here is where I corrected the code to make it work.

  ////Old Code//// 


     <tr *ngFor='let p of filteredProducts | async '>
          <td>{{p?.title}}</td>
          <td>{{p?.price}}</td>
          <td></td>
          <td>
            <a [routerLink]='["/admin/products/", p?.$key]'>edit</a>
          </td>


    //// New Code //// 

    <tr *ngFor='let p of filteredProducts'>
          <td>{{p?.title}}</td>
          <td>{{p?.price}}</td>
          <td></td>
          <td>
            <a [routerLink]='["/admin/products/", p.id]'>edit</a>