can't get JSON data with ng-for - json

I have JSON containing Stores[]--->Products[]--->ProductDetails--->ProductTags[] for the begining I want to see all the data
so I coded:
service
export class StoreService {
//private myurl: string ="apidata/products.json"
constructor(private _http: Http) {}
getProducts(): Observable < any > {
let apiUrl = './assets/products.json';
return this._http.get(apiUrl)
.pipe(map((response: Response) => {
const data = response.json();
return data;
}));
}
store component:
export class StoreComponent implements OnInit {
products = [];
constructor(private _storeService: StoreService) {}
ngOnInit() {
this._storeService.getProducts()
.subscribe(data => {
this.products = data;
console.log(data)
});
}
}
html
<h2>store</h2>
<ul>
<li *ngFor="let p of products">{{p}}</li>
</ul>
the error I get:
Error trying to diff '[object Object]'. Only arrays and iterables are
allowed

An error which you are getting suggests that your data is either an object / map instead of an array.
If you want to use *ngFor in your template, your data needs to be an array instead of an object.
Although angular team introduce a pipe called "keyvalue" in version 6.1. So you can take advantage of that and iterate over an object inside your angular template.
For example, assume that your object looks like below
const user = {
name: 'xyz',
age: 30,
gender: 'male'
};
So in your template file, you can iterate over this object like this
<ul>
<li *ngFor="let data of user | keyvalue">
Key : {{data.key}} and Value : {{data.value}}
</li>
</ul>
Please refer this link for more info about this pipe

You generally get this error when you pass an Object to *ngFor as an input. As the error states, *ngFor only works with Iteratable Data Structures, which in our case will be an array of products.
Make sure that in here: this.products = data;, data is actually an array.
UPDATE:
As it's quite obvious from this screenshot of the console log:
data is an object and not an array. PriceFilter, Stores and GenderFilter on that object are arrays. If you want to show stores, you can do:
export class StoreComponent implements OnInit {
stores = [];
constructor(private _storeService: StoreService) {}
ngOnInit() {
this._storeService.getProducts()
.subscribe(data => {
this.stores = data.Stores;
console.log(data)
});
}
}
And in template
<h2>store</h2>
<ul>
<li *ngFor="let store of stores">
<h4>Products:</h4>
<ul>
<li *ngFor="let product of store.Products">
<p>Product Id: {{ product.ProductId }}</p>
<p>Product Title: {{ product.ProductTitle }}</p>
...
</li>
</ul>
</li>
</ul>

The main purpose of this error is that your data (products) is returning an object {} - ngFor requires an array [] to iterate. Either you have to check your HTTP Response or fix your backend API.
And merely for your example you can do this this.products = data.Stores; (if it is stores is what you're looking for to iterate on).
UPDATE
According to your link in comments you have to change your code to :
this.products = data.Stores;
And then :
<div *ngFor="let p of products">
<ul>
<li *ngFor="let item of p">
<-- HERE you take whatever you want from your product object --!>
</li>
</ul>
</div>

Related

Why can't I display the list of products in the client although I have them in the database and in the Network tab in the console

so basically I want to display products in the Client. Im using Angular 12 and for backend .NET 5. I'm getting products from products controller and I can't see them, however I can see "welcome" title moreover I have products in the Database. In network tab I can see list of products and I don't have any errors. What I'm doing wrong? Any suggestions? I did some coding:
export class ListsComponent implements OnInit {
products: any[];
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.http.get('https://localhost:6001/api/products').subscribe((response:any) => {
this.products = response.data;
}, error => {
console.log(error);
});
}
}
And my HTML code
<div class="container" style="margin-top: 140px">
<h1>welcome</h1> <!-- I can see it -->
<ul>
<li class="list-unstyled" *ngFor="let product of products"><!-- I cant see any of this -->
{{product.name}}
</li>
</ul>
this.http.get('https://localhost:6001/api/products').subscribe((response:any) => {
this.products = response;
}, error => {
console.log(error);
});
It seems like in your backend you return something named data and you think you need to access it in the front , you http response is an array , you don't need to use response.data .

How to list objects in an object in html?

I'm writing app in Angular and Node.js. I have an object (order) that has a list of objects (items) that also contain objects (list of product id). I want to display them all in an html file. Please help me.
html file:
<div *ngIf="order">
<div *ngFor="let item of order.items"> // <- it does not work
<a>{{order.items}}</a> // <--
</div>
</div>
ts file:
export class AdminOrderItemComponent implements OnInit {
order: Order;
orderId;
constructor(private orderService: OrderService, private route: ActivatedRoute) { }
ngOnInit() {
this.route.paramMap
.subscribe(params => {
this.orderId = [params.get('id')];
this.getOrderById();
});
}
getOrderById() {
this.orderService.getOrderById(this.orderId).subscribe(
res => {
this.order = res;
},
err => console.log(err)
);
}
}
order interface:
export interface Order {
_id: any;
shipping: string;
userData: {};
sum: number;
items: {};
}
The ngFor iterates over the items of a collection. If you take a look at your model, you will realize that items is an object ({}), not an array ([]).
Your best bet is transforming the object received from your node.js backend to match your needs or (preferably I think) make your node.js model treat items as a collection as well which seems more appropriate.
Answer from #kg99:
Key: {{item.key}} and Value: {{item.value}}

Loop over Observable

In my frontend part of application use a method
this.knowledgeMan.getUserAllowedCases(Item.ItemNumber)
which returns Observable. On my backend part, this method returns a List<String>.
My question is: how do I get to loop over the elements of this list of Strings?
If you have an observable you have to subscribe to it to get the actual value. Within subscribtion its up to you, here you can map or loop over your values.
this.knowledgeMan.getUserAllowedCases(Item.ItemNumber).subscribe(allowedCases => {
allowedCases.map(allowedCase => {
// your code here
});
});
If you are using this List<String> observable to show on the HTML part you can use a combination of async and *ngFor to get the desired result.
//in your html for example
<ul>
<li *ngFor="let item of (data$ | async)"> {{ item }} </li>
</ul>
//in your component
//usual angular stuff
export class MyComponent implements OnInit {
data$: Observable<String[]>;
constructor(private knowledgeMan: YourServiceInterface){}
ngOnInit() {
data$ = this.knowledgeMan.getUserAllowedCases(Item.ItemNumber);
}
}
If you are just doing this to compute some value you can do this following.
this.knowledgeMan.getUserAllowedCases(Item.ItemNumber).pipe(
flatMap(),
map(item => //do something with item here)
).subscribe();
If as you say, getUserAllowedCases returns string[] then you can do this:
this.knowledgeMan.getUserAllowedCases(Item.ItemNumber).subscribe(x => {
// assuming x is string[]
for (const item of x) {
// use item
}
});

find object of particular id in json in html tamplate

I'm writing the page where I render posts.
in my controller I have:
export class PostsComponent implements OnInit {
posts$: Object;
users$: Object;
constructor(private data: DataService) { }
ngOnInit() {
this.data.getPosts().subscribe(
data => this.posts$ = data
)
this.data.getUsers().subscribe(
data => this.users$ = data
)
}
}
and in the template I iterate through the posts to render them:
<h1>Posts</h1>
<ul>
<li *ngFor='let post of posts$'>
<h3>{{ post.title }}</h3>
<h5>created by:</h5>
<p>{{ post.body }}</p>
</li>
</ul>
after 'created by: ' I would like to add {{user$.username}} where user$ should be a user of post.userId from users$ (already initialized in the controller). Methods getUsers and getPosts work fine and fetch the data from https://jsonplaceholder.typicode.com. I've got also getUser(id) method written and I thought I could use that one. It should be then in the controller:
this.data.getUser(id).subscribe(
data => this.users$ = data
)
but I don't know where to get the id from as I iterate through posts in the template. Could you help me with that?
You can make a function getUser(id) in your component and let it return the user.
In your html
{{ getUser(post.userId) }}.
Or
this.data.getPosts().subscribe(
data => {
for(let i = 0; i < data.length; i++) {
data[i].username = this.getUser(data[i].userId);
}
this.posts$ = data;
})

How do i pass data received from angular to html front-end?

this is my angular service:
#Injectable()
export class ApiService {
private get_msg_url: string = "http://localhost:3000/getmessages";
constructor(private http:HttpClient) { }
getMessage(): Observable<IPosts> {
return this.http.post<IPosts>(this.get_msg_url,{"data":"data1"});
}
}
this is my message.component.ts :
export class MessagesComponent implements OnInit {
data = [];
iposts: IPosts[];
constructor(private apiSerivce: ApiService) {
}
getMessage(): void {
this.apiSerivce.getMessage()
.subscribe(data1 => this.data.push(data1));
}
ngOnInit(): void {
this.getMessage();
console.log(this.data);
}
}
this is my posts.ts:
export interface IPosts {
timestamp : number;
message_content : string;
message_link :string;
tags : string[] ;
}
this is messsage.component.html:
<p>Json data {{data}} </p>
whenever the code is run i can see the required data in console of chrome but there is no data shown on page.
the data received on console is of form:
[]
0:
message: Array(3)
0: {timestamp: 1522072833748,
tags: Array(2), _id: "5aacb7cc0281b558debacf26",
message_link:"String"
}}
the problem is, that you try to print an array, instead of the elements.
<p>Json data {{data}} </p>
change this to something like this:
<p *ngFor="let element of data; index as i">
Json data #{{i}}: {{element]]
</p>
As I can see, data is an array. you can display the raw content of an array with the pipe json : {{data | json}}
However I am not sur what you want to do with a json displayed in your html.
The problem is your output data is an array, not an element.
Try something like this,
<ul *ngFor="let element of data">
<li>{{element}}</li>
</ul>