4

I'm having real trouble converting a JSON array to collection of objects in Angular. I've not used Angular for some time, please forgive me if any of my terminology is incorrect.

I have the following products.json file in my project:

[
  {
    "id": 1,
    "name": "Pen",
    "description": "The finest blue pen.",
    "relatedProducts": [2]
  },
  {
    "id": 2,
    "name": "A4 Paper",
    "description": "A4 sized printer paper.",
    "relatedProducts": [1]
  }
]

This product.ts file:

export class Product {
    Id: number;
    Name: string;
    Description: string;
    RelatedProducts: Array<number>;
}

My app.component.ts file:

import * as ProductsJson from '../../json-data/products.json';
import { Component, OnInit } from '@angular/core';
import { Product } from 'src/app/models/product';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  productsJson: any = ProductsJson;
  products: Array<Product>;

  ngOnInit() {
    this.products = <Product[]>this.productsJson;
    console.log(this.products);
  }
}

And finally my app.component.html:

<ul>
  <li *ngFor="let p of products">
    {{p.Id}} {{p.Name}}
  </li>
</ul>

enter image description here

My console log shows the JSON data but it seems as though I'm making some error trying to convert it to a list of Product objects. This is also preventing me from successfully using ngFor in my view as the console error shows, so nothing shows on the page. How can I perform this conversion correctly so the loop works and this data is shown on the view?

8 Answers 8

2

you can use class-transformer which helps you to convert plain javascript objects to real es6 classes. With this real instances you can also use class methods.

import { Component, OnInit } from "@angular/core";
import ProductsJson from "./products.json";
import {plainToClass} from "class-transformer";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  productsJson: any = ProductsJson;
  products: Array<Product>;
  constructor() {}
  ngOnInit() {
    //this.products = <Product[]>this.productsJson;
    this.products = plainToClass(Product, this.productsJson as []);
    console.log(this.products);
  }
}
export class Product {
  id: number;
  name: string;
  description: string;
  relatedProducts: Array<number>;

  getId(){
    return this.id + "_ID";
  }
}

template:

<ul>
    <li *ngFor="let p of products">
        {{p.getId()}} {{p.id}} {{p.name}}
    </li>
</ul>
Sign up to request clarification or add additional context in comments.

2 Comments

This is the sort of thing I'm looking for. I notice if I was to change a property name, e.g. name: string; to fooName: string; that the console still logs the property as "name". Is there something missing to do some kind of mapping?
@Citrus maybe this helps you: github.com/typestack/….
2

look at stackblitz

Let me know still you have an issue..

thanks

Comments

2
**- Use interface instead of class for Product.**

      export interface Product {
        id: number;
        name: string;
        description: string;
        relatedProducts: number[];
    }

**- If you need a class, then specify the parameterised constructor to assign variable values.**

  export class Product {
    id: number;
    name: string;
    description: string;
    relatedProducts: number[];

  constructor(product) {
     this.id = product.id;
    this.name: product.name;
    this.description: product.description;
    this.relatedProducts: product.relatedProducts;
   }
}


**and then inside the component you can use** 

this.products = new Product(this.productsJson); // if Product is a class

OR

this.products = this.productsJson; // if Product is an interface

Comments

0

Your model should look like below,

export interface Product {
    id: number;
    name: string;
    description: string;
    relatedProducts: number[];
}

Change the products as

 products: Product[];

and then,

this.products = this.productsJson;

3 Comments

The outcome is the same with this code, the error persists and nothing is displayed in the view.
Thanks, but the same problem persists.
can you produce a stackblitz
0

Adding to Kushal Shah answer this also works.

import { Component, OnInit } from "@angular/core";
import ProductsJson from "./products.json";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {

  products = <Product[]>ProductsJson;;

  constructor() {}

  ngOnInit() {
    console.log(this.products);
  }

}

export class Product {
  id: number;
  name: string;
  description: string;
  relatedProducts: Array<number>;
}

Comments

0

step by step:

Component.ts

The first, you have unuseless variables you have reduce keeping just one 'products' and initializing just like that the following code.

In ngOnInit() you must to load the array initialized before. The way to do that is casting with type any cause importing from local json generates another data model, you have to access to the property 'default' and that's all.

import * as ProductsJson from '../../json-data/products.json';
import { Component, OnInit } from '@angular/core';
import { Product } from 'src/app/models/product';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  products: Array<Product> = new Array<Product>();

  ngOnInit() {
    this.products = (ProductsJson as any).default;
    console.log(this.products);
  }

}

Component.html

Another point to change is properties naming you have 'Id' & 'Name' instead of 'id' and 'name'.

<ul>
   <li *ngFor="let p of products">
    {{p.id}} {{p.name}}
   </li>
</ul>

Entities

Product Class have to change the properties naming without Capital letters to have a correspondence with the Json Data or change Json data model to the Class anyway.

If I were you I will change also Product, using interface instead Class

export interface Product {
  id: number;
  name: string;
  description: string;
  relatedProducts: Array<number>;
}

Comments

0

UPDATE 1 [ You can go declarative way of rxjs too ]..

Use of operator of rxjs to create new stream of observable from your json file.

import {of} from 'rxjs'

productsJson = of <Product []>(ProductsJson);

export interface Product{
      id: number;
      name: string;
      description: string;
      relatedProducts: Array<number>;
}

and then in your template you can simply use async pipe ...

<ul>
  <li *ngFor="let p of productsJson | async">
    {{p.id}} {{p.name}}
  </li>
</ul>

Update 2 [ You can simply use JSON.parse() and JSON.stringify() ]..

 productsJson: any = JSON.parse(JSON.stringify(ProductsJson));

and then use this in template like this...

<div *ngFor = "let item of productsJson">
    <span> {{item.id }} </span>
</div>

Comments

0
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
   selector: 'app-hello-world',
   templateUrl: './hello-world.component.html',
   styleUrls: ['./hello-world.component.css'],
})
export class HelloWorldComponent implements OnInit {
   jsonString: string = '[{"title":"test"},{"title":"test2"}]';
   jsonObj: Array<object>;
   jsonObj2: Array<object> = [{ title: 'test' }, { title: 'test2' }];
  constructor() {
     this.jsonObj = JSON.parse(this.jsonString);
     this.jsonString = JSON.stringify(this.jsonObj2);
  }

  ngOnInit() {}
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.