Cannot use *ngFor for a two-dimensional array in Angular - json

I saw many similar questions but the solutions there didn't work for me. I want to display orders in Angular.
I receive clients' orders in Json format from Spring. Like this:
[
{
"id": 1,
"orderProducts": [],
"numberOfProducts": 0
},
{
"id": 2,
"orderProducts": [
{
"quantity": 4,
"product": {
"id": 1,
"brand": "apple",
"name": "iphone11",
"categoryId": 1
}
}
],
"numberOfProducts": 1
},
{
"id": 3,
"orderProducts": [
{
"quantity": 9,
"product": {
"id": 1,
"brand": "apple",
"name": "iphone11",
"categoryId": 1
}
},
{
"quantity": 6,
"product": {
"id": 2,
"brand": "xiaomi",
"name": "Note10",
"categoryId": 1
}
},
{
"quantity": 1,
"product": {
"id": 6,
"brand": "cccccccccccccccc",
"name": "cccccccccccccc",
"categoryId": 1
}
}
],
"numberOfProducts": 3
},
{
"id": 4,
"orderProducts": [
{
"quantity": 5,
"product": {
"id": 1,
"brand": "apple",
"name": "iphone11",
"categoryId": 1
}
}
],
"numberOfProducts": 1
}
]
So i created a class in Angular to accept it.
db-orders.ts
export class DbOrders {
id: number;
orders: ProductOrder[];
numberOfProducts: number;
constructor(orders: ProductOrder[]){
this.orders = orders;
}
}
product-order.ts
export class ProductOrder {
product: Product;
quantity: number;
constructor(product: Product, quantity: number){
this.product = product;
this.quantity = quantity;
}
}
product.ts
export class Product {
id: number;
brand: string;
name: string;
category: ProductCategory;
constructor(){}
}
Here is a service class.
order.service.ts
export class OrderService {
private orderUrl: string = 'http://localhost:8080/orders';
constructor(private http: HttpClient) { }
saveOrder(order: ProductOrders) {
console.log("Hello")
return this.http.post<ProductOrders>(this.orderUrl, order);
}
public findOrders(): Observable<DbOrders[]> {
return this.http.get<DbOrders[]>(this.orderUrl);
}
}
order-list.component.ts
export class OrderListComponent implements OnInit {
receivedOrders: DbOrders[] = [];
constructor(private orderService: OrderService) { }
ngOnInit(): void {
this.orderService.findOrders().subscribe(
data =>{
this.receivedOrders = data;
}
);
}
}
order-list.component.html
<div *ngFor="let receivedOrder of receivedOrders">
<p>Number of Products in the order: {{receivedOrder.numberOfProducts}}</p>
<div *ngFor="let order of receivedOrder.orders">
<h1>Product name: {{ order.product.name }}</h1>
</div>
</div>
In this case, only the number of products is displayed,nothing else:
I tried to add a toArray method:
<div *ngFor="let order of toArray(receivedOrder.orders)">
ngOnInit(): void {
this.orderService.findOrders().subscribe(
data =>{
this.receivedOrders = data;
}
);
}
toArray(orders: object) {
return Object.keys(orders).map(key => orders[key])
}
Doesn't work.
Also i tried to add indexes.
<div *ngFor="let receivedOrder of receivedOrders; let i=index"">
<p>Number of Products in the order: {{receivedOrder.numberOfProducts}}</p>
<div *ngFor="let order of receivedOrder.orders; let j=index"">
<h1>Product name: {{ order.product.name }}</h1>
</div>
</div>
Doesn't work either.
What is my mistake? Thank you!

You seem to have a few problems. Firstly, your data structure does not match your classes.
E.g. your ProductOrder object should have an array of OrderProduct objects as that is what your data has.
Your constructor for ProductOrder never gets called because you are no instantiating the object from the class.
In simple terms, change your html to this for it to work:
<div *ngFor="let receivedOrder of data; let i=index">
<p>Number of Products in the order: {{receivedOrder.numberOfProducts}}</p>
<div *ngFor=" let order of receivedOrder.orderProducts; let j=index">
<h1>Product name: {{ order.product.name }}</h1>
</div>
</div>
You can also find a demo on StackBlitz.

Related

Angular 13 - How to get pager details from the JSON response

I work on Angular 13 and I face an issue in that I can't retrieve pager data from the JSON.
The items array returned success but I can't return pager details.
So how to do it?
{
"items": [
{
"id": 3,
"itemNameER": "قلم",
"itemNameEN": "pen",
"description": "1"
},
{
"id": 4,
"itemNameER": "قلم",
"itemNameEN": "pencil",
"description": null
},
{
"id": 5,
"itemNameER": "قلم",
"itemNameEN": "pen2",
"description": null
},
{
"id": 8,
"itemNameER": "car",
"itemNameEN": "car",
"description": "1"
},
{
"id": 9,
"itemNameER": "mobile",
"itemNameEN": "mobile",
"description": "1"
}
],
"pager": {
"numberOfPages": 2,
"currentPage": 1,
"totalRecords": 6
}
}
What I had try is:
items?:ItemsData[];
export interface ItemsData {
id:number;
itemNameER:string;
itemNameEN:string;
description:string;
}
retrieveAllItems(pageNumber: number = 0): void {
this.erpservice.getAll(pageNumber)
.subscribe(
data => {
this.items=data.items;
console.log(data);
},
error => {
console.log(error);
});
}
How to extract pager data from JSON for the numberOfPages, currentPage and totalRecords?
Updated post
This is the information for the getAll return type.
So how to get pager data details?
export interface DataWrapper {
items: ItemsData[];
}
getAll(pageNumber: number): Observable<DataWrapper> {
let params = new HttpParams();
if (pageNumber)
params = params.append('pageNumber', pageNumber);
let httpOptions = {
params: params
};
return this.http.get<DataWrapper>(baseUrl,httpOptions);
}
What I had try is:
pager: any;
this.pager = data.pager;
But I get an error:
Property 'pager' does not exist on type 'DataWrapper'.ts(2339)
<ul>
<li *ngFor="let item of items | paginate: { currentPage:pager.currentPage }; let i = index">
{{ pager.numberOfPages * (pager.currentPage - 1) + i }}
</li>
</ul>
The error message is quite clear. The DataWrapper interface doesn't have a pager property.
You need to:
Add the pager property into DataWrapper interface.
Define the IPager interface.
export interface DataWrapper {
items: ItemsData[];
pager: IPager;
}
export interface IPager {
numberOfPages: number;
currentPage: number;
totalRecords: number;
}

filtering by pipe: Template parse Error Angular4

its my first time with "pipe" so I think I missed few things:
I have a JSON file which contain data of products. the products can be sort by "ProductTags" - JSON with the tag they can be filter by. also, I have JSON Which contain details about the filter:
"PriceFilter": [
{
"TagId": 20,
"Type": "Budget",
"Value": 5,
"Values": null,
"DisplayText": "$5",
"Order": null
},
{
"TagId": 21,
"Type": "Budget",
"Value": 10,
"Values": null,
"DisplayText": "$10",
"Order": null
}]
product:
"Products": [
{
"ProductId": 206419,
"ProductTitle": "Mom is Fabulous Fruit Box - Good",
"ProductTags": [ 20, 2, 3, 4 ]
}]
I need to order the products using the tags in this way: price
store. html
<table>
<tr *ngFor="let P of PriceFilter | filter : term | orderBy: 'Price'">
<td>{{PriceFilter.DisplayText}}</td>
</tr>
</table>
store component:
stores=[];
products=[];
PriceFilter = [];
GenderFilter =[];
filtered=[];
constructor(private _storeService:StoreService) { }
ngOnInit() {
this._storeService.getProducts()
.subscribe(data =>{
this.products = data.Stores.Products;
this.stores=data.Stores;
this.PriceFilter = data.PriceFilter;
this.GenderFilter = data.GenderFilter;
console.log(data.PriceFilter)
console.log(data.GenderFilter)
console.log(data.Stores)
});
}
filter pipe:
transform(items: any[], term): any {
console.log('term', term);
return term
? items.filter(item => item.ProductTags.indexOf(term) !== -1)
: items;
}
orderBy pipe:
export class OrderbyPipe implements PipeTransform {
transform(items: any[], orderBy: string): any {
console.log('orderdBy', orderBy);
return items.sort((a, b) => {return b[orderBy] - a[orderBy]});
}

Drop down populated from json data

I want my drop down to display 2017 and 2018 from my data. 2017 and 2018 repeats a lot throughout my json data file. But I want all the 2017 data to appear when selected and all the 2018 data to be displayed when selected. Currently it shows all data and the drop down is over populated.
I was told to try this but haven't managed to get it to work:
import {DatePipe} from '#angular/common';
.
.
volumes: Volumes[];
years: [] = [];
groupedVolumes : any;
constructor(private volumeService: VolumeService, private router: Router, private datePipe: DatePipe) {
}
ngOnInit(){
this.volumeService.getVolumes().subscribe(volumes => {
this.volumes = volumes;
for(let volume of volumes){
if(this.years.indexOf(datePipe.formatDate(volume.month, 'yyyy')) === -1)
this.years.push(datePipe.formatDate(volume.month, 'yyyy'));
}
this.groupedVolumes = this.group(this.volumes);
this.dataOk = true;
}
}
Html:
<div class="row justify-content-center">
<div class="col-4s">
<p>Financial Year:</p>
</div>
<div class="col-4s">
<select>
<option *ngFor="let year of years">{{ year }}</option>
</select>
</div>
</div>
Json File:
json file:
[
{
"id": 1,
"month": "2017-03-01"
}
{
"id": 2,
"month": "2017-04-01"
}
{
"id": 3,
"month": "2017-05-01"
}
{
"id": 4,
"month": "2017-06-01"
}
{
"id": 5,
"month": "2017-07-01"
}
{
"id": 6,
"month": "2017-08-01"
}
{
"id": 7,
"month": "2017-09-01"
}
{
"id": 8,
"month": "2017-10-01"
}
{
"id": 9,
"month": "2017-11-01"
}]
The problem with this is the DatePipe. It only has a transform function rather than formatDate.
Also it doesn't like years: [] = []
ngOnInit() {
this.years = this.volumeService.getVolumes().subscribe(volumes => {
this.volumes = volumes;
this.volumes.forEach(volume => {
if(!this.years || !(this.years.some(year => year.includes(volume.month.split('-')[0])))) {
this.years.push(volume.month.split('-')[0])
}
});
this.groupedVolumes = this.group(this.volumes);
this.dataOk = true;
}

best JSON Structure and getData() in Service using Interfaces & Models for Nested JSON

I have tried to find solutions that I can understand with my knowledge level. But without success. I hope that others can benefit from this question.
Currently I am trying to understand how I provide data from different nested JSON files via interfaces and models. For my example, let's say I plan to have 3 JSON files in the future. (items.json, characters.json, monsters.json)
First, I'd like to find out what a clever JSON structure looks like. (There are many example tutorials, but I did not find any for such a special case as mine)
Wich one is smarter?
First variant:
{
"wearable": {
"torso": [
{
"name": "Schlachtrobe",
"defense": 7,
"price": 22
}
],
"head": [
{
"name": "stirnband",
"defense": 2,
"price": 50
}
],
"wrist": [
// etc...
]
},
"weapons": [
// etc...
],
"consumables": [
{
"name": "Bonbon",
"effect": "Heilt um 100 TP"
},
{
"name": "Schokolade",
"effect": "Heilt um 250 TP"
}
]
}
Second variant:
[
{
"id": 1,
"category": "wearable",
"type": "torso",
"name": "Schlachtrobe",
"defense": 7,
"price": 22
},
{
"id": 2,
"category": "wearable",
"type": "head",
"name": "stirnband",
"defense": 2,
"price": 50
},
{
"id": 3,
"category": "consumables",
"name": "Bonbon",
"effect": "Heilt um 100 TP"
},
{
"id": 4,
"category": "consumables",
"name": "Schokolade",
"effect": "Heilt um 250 TP"
}
]
The second variant makes it easier to attach items (for example in the future monsters.ts) to other items. In this case, e.g. just attach the id numbers. But then each item must use the same interface structure ... Then, for example, the "Bonbon" of the category "consumable" always has an empty number field called "defense" And this is just a similiar example. There will certainly be many more empty fields.
And what is about the interfaces and the Models? I try to solve this like this:
items.interface.ts
export interface ItemsInterface {
}
export interface ArmorsInterface {
name: string;
defense: number;
price: number;
}
export interface ConsumablesInterface {
name: string;
price: number;
effect: string;
}
item.model.ts
export class ItemsModel implements ItemsInterface {
}
export class ArmorsModel implements ArmorsInterface {
name: string;
defense: number;
price: number;
constructor(obj?: any) {
this.name = (obj && obj.name) ? obj.name : '';
this.defense = (obj && obj.defense) ? obj.defense : '';
this.price = (obj && obj.price) ? obj.price : '';
}
}
export class ConsumablesModel implements ConsumablesInterface {
name: string;
price: number;
effect: string;
constructor(obj?: any) {
this.name = (obj && obj.name) ? obj.name : '';
this.price = (obj && obj.price) ? obj.price : '';
this.effect = (obj && obj.effect) ? obj.effect : '';
}
}
These could then be used in the service like this:
#Injectable()
export class ItemsService {
private items;
private _armors: BehaviorSubject<ArmorsInterface> = new BehaviorSubject(new ArmorsModel());
private _consumables: BehaviorSubject<ConsumablesInterface> = new BehaviorSubject(new ConsumablesModel());
constructor(private http: HttpClient) {
this.loadItems();
}
get getArmors() {
return this._armors.asObservable();
}
get getConsumables() {
return this._consumables.asObservable();
}
loadItems() {
this.http.get(`./assets/data/items.json`).subscribe((items: ItemsInterface) => {
this.items = items;
this._armors.next(new ArmorsModel(items));
this._consumables.next(new ConsumablesModel(items));
});
}
}
There are certainly many mistakes in the service. I am still a beginner. ;)
And in the end how you could use the service:
export class SiteHomeComponent implements OnInit {
public armors: ArmorsInterface;
constructor(private iS: ItemsService) {
}
ngOnInit() {
// Returns the Data and write it in data
this.iS.getArmors.subscribe((data) => {
this.armors = data;
});
}
}
What can be done better? Or what would you do differently? I am grateful for everything that helps me to recognize my mistakes and to learn how to do it right. Thank you for your attention. :)

Populating Class[] with JSON in TypeScript

As part of my model I have this class in TypeScript:
module App.Model {
export class Unit {
id: number;
participantId: number;
name: string;
isProp: boolean;
}
}
In the controller, I need a a hash with the id as key:
module App.Controllers {
export class MyController {
public units: App.Model.Unit[];
populateDemoData() {
this.units = {
"1": { "id": 1, "participantId": 1, "name": "Testname", "isProp": true },
"2": { "id": 2, "participantId": 1, "name": "Another name", "isProp": false }
};
}
}
}
However, compiling the controller, I get the following error message:
Error 2 Cannot convert '{ }; [n: number]: App.Model.Unit; }' to ' }; [n: number]: App.Model.Unit; }' is missing property 'concat' from type 'App.Model.Unit[]'.
What am I doing wrong? And why is TypeScript asking for a concat property?
You defined units as an Array object, but assigned it a literal object. Just to clarify, a hash (a literal object) is not an array.
If all the IDs are an integer you can still use the array but it would be like this instead:
populateDemoData() {
this.units = [];
this.units[1] = { "id": 1, "participantId": 1, "name": "Testname", "isProp": true };
this.units[2] = { "id": 2, "participantId": 1, "name": "Another name", "isProp": false };
}
Edit:
Ok, you have to define a hash table to do that, but you also need to make App.Model.Unit an interface that matches your JSON objects.
module App.Model {
export interface Unit {
id: number;
participantId: number;
name: string;
isProp: boolean;
}
export interface UnitHashTable {
[id: string]: Unit;
}
}
module App.Controllers {
export class MyController {
public units: App.Model.UnitHashTable;
populateDemoData() {
this.units = {
"1": { "id": 1, "participantId": 1, "name": "Testname", "isProp": true },
"2": { "id": 2, "participantId": 1, "name": "Another name", "isProp": false }
};
}
}
}