0

I'm attempting to map a JSON object to an observable Interface that I've configured in my angular app, my hope is that once I have it mapped I can use it as an input to loop through an ngFor.

Unfortunately I don't believe I'm configuring either my service correctly, or possible the service call.

I get the json object returned as a single object but the ngFor does not properly loop through the results returned, any assistance in pointing out what I may be overlooking would be greatly appreciated.

// Interface I am trying to access

export interface IProduct {
   name: string;
   description: string;
   price: number;
   category: string;
   image: string;
}

// Service I am attempting to call

private productList = new BehaviorSubject<IProduct[]|null>(null);
productListChanges$ = this.productList.asObservable();
constructor(private http: HttpClient) { }

getProds(): Observable<IProduct[]> {
     this.productList.next(null);
     return this.http.get<IProduct[]> 
                      ('http://localhost:4200/assets/data/products.json')
     .pipe(
        tap(data => this.productList.next(data)),
     );
 }

// Call for the service

productsList: IProduct[] = [];

this.productService.getProds().subscribe((response: any) => {
  this.productsList = response.products[0] as IProduct[];
  console.log(this.productsList);
});

// Attempt to use ngFor with obtained object

<app-product *ngFor="let product of productsList" [prod]="product" ></app-product>

// Console log from the service call outputs the following

logOutput from service call

bruno_cw
  • 994
  • 1
  • 8
  • 22
Edward H
  • 19
  • 8

3 Answers3

0

Try with async when looping with Observables.

<app-product *ngFor="let product of productsList | async" [prod]="product" ></app-product>
Neeraj Kumar
  • 436
  • 1
  • 4
  • 13
  • Tried implementing the async, but the only difference is the console log outputs an 'Object' and the ngFor still doesn't run properly – Edward H Feb 26 '19 at 18:42
0

I see you're using HTTP to serve JSON data thats readily avialable as data coming from a static file existing in your assets folder. I would avoid HTTP calls to serve these. I've refactored your code to both serve static data, and also support remote data query using the same service method. It should also help out during unit testing as async testing is a nightmare.

// Changed the Interface to a class
export class Product {
   name: string;
   description: string;
   price: number;
   category: string;
   image: string;
}

// Create a sample-data.ts file
export const ProductListData: Array<Product> = [
    {
       name: 'ProductName',
       description: 'Description',
       price: '9.99', // Maybe should be a number type and not string
       category: 'Test Category',
       image: 'your url for testing'
    }
]

// In service... 
import { of } from 'rxjs';
import { ProductListData } from './sample-data';
useSampleData = false;

getProducts(): Observable<Array<Product>> {
    if (useSampleData) {
        // Use this is your want to serve static JSON
        return of(ProductListData); // of() turns your readily avialable object into an obeservable promise
    } else {
    // Get from actual HTTP as async
        return this.http.get<Array<Product>>('http://localhost:4200/api/your-endpoint-url');
    }
 }
 
 
// In you Component...
public productList: Array<Product> = [];

this.productService.getProducts().subscribe(
    productList => {
      this.productsList = productList;
    },
    err => {
        console.log('Error: ', err);
    }
);

Your template wouldn't need changing.

A. Figueroa
  • 215
  • 1
  • 8
  • I tried making all the indicated changes but it seems as though the ngFor still isn't building properly... not sure what I'm missing here... it's like the productsList is an Array of objects that the ngFor just isn't appropriately reading? – Edward H Feb 26 '19 at 21:52
  • Can you share any new errors? Can you put a console.log on the component call to your service, so we can confirm that the objects arrive? – A. Figueroa Feb 27 '19 at 14:32
0

As your console output indicates, productsList is an Object, but ngFor expects an Array.

If you can change the data, it should be easily doable to change it to an Array ([...]) instead of an Object ( {...}).

Otherwise you have several options to convert the structure in code to an Array. For example, when using Object.values() you can convert your current structure to an Array. Alternatively, you can also use the KeyValuePipe available since Angular 6.1. Also answered here https://stackoverflow.com/a/51491848/9999800

f.loris
  • 911
  • 1
  • 13
  • 25
  • This answer actually did it for me, SUCH an oversight, adjusted the products.json itself to be [{},{},{}] instead of { 1: {}, 2:{}, 3:{} }. THANK you so much for your input – Edward H Feb 26 '19 at 21:57