How to iterate through JSON nested objects with ngFor - javascript

I have a GET method that returns the following JSON on route localhost:3000/documents
[{
"id": "5b48bffc644fca001419769c",
"names": [{
"name": "bob"
},
{
"name": "stan"
}
],
"cities": [{
"city": "London"
},
{
"city": "Madrid"
}
]
}]
I want to concatenate all the names and cities and display them inside HTML tags with Angular.
<p> id </p>
<p> concatenated names here </>
<p> concatenated cities here </>
Is it possible to access documents and concatenate the array values using ngFor?
I have the following component:
import {Component, OnInit} from '#angular/core';
import {DocumentService} from '../services/document.service';
import {Document} from './document.model';
#Component({
selector: 'app-document',
templateUrl: './document.component.html',
styleUrls: ['./document.component.css']
})
export class DocumentComponent implements OnInit {
documents: Document[];
constructor(private documentService: DocumentService) {
}
ngOnInit() {
this.getDocuments();
}
getDocuments(): void {
this.documentService.getDocuments()
.subscribe(documents => this.documents = documents);
}
}
And the following service:
import {Injectable} from '#angular/core';
import {HttpClient, HttpHeaders} from '#angular/common/http';
import {Document} from '../document/document.model';
#Injectable({providedIn: 'root'})
export class DocumentService {
private urlDocuments = 'localhost:3000/documents';
constructor(private http: HttpClient) {
}
getDocuments() {
return this.http.get<Document[]>(this.urlDocuments);
}
}
My document model is:
export class Document {
public _id: string;
public names: [{ name: string }];
public cities: [{ city: string }];
constructor(_id: string, names: [{ name: string }],
cities: [{ city: string }]]) {
this._id = _id;
this.cities=cities;
this.names= name;
}
}

I have the solution, but you need to modify your object.
You have to override toString method for cities and names in your model:
test= [{
"id": "5b48bffc644fca001419769c",
"names": [{
"name": "bob",
toString: function(){return this.name;}
},
{
"name": "stan",
toString: function(){return this.name;}
}
],
"cities": [{
"city": "London",
toString: function(){return this.city;}
},
{
"city": "Madrid",
toString: function(){return this.city;}
}
]
}];
HTML section will be look like:
<div *ngFor="let t of test">
<p> {{t.id}}</p>
<p> {{t.names.join(",")}}</p>
<p> {{t.cities.join(",")}} </p>
</div>
Output:
5b48bffc644fca001419769c
bob,stan
London,Madrid

Assume your documents data is getting no problem from server, then try this HTML code below:
<div *ngFor="let d of documents">
<p>Id: {{d.id}}</p>
<p>Names: <span *ngFor="let dd of d.names">{{dd.name}},</span></p>
<p>Cities: <span *ngFor="let dd of d.cities">{{dd.city}},</span></p>
</div>

You just need to use an *ngFor in order to iterate over the documents and then two *ngFors for iterating over the names and the cities like this (StackBlitz Demo):
ts:
documents = [{
"id": "5b48bffc644fca001419769c",
"names": [{"name": "bob"},{"name": "stan"}],
"cities": [{"city": "London"},{"city": "Madrid"}]
},{
"id": "5b48bffc644fca001419769cde",
"names": [{"name": "Jon"},{"name": "Doe"}],
"cities": [{"city": "Barcelona"},{"city": "Saragoza"}]
}
];
html:
<div *ngFor="let doc of documents; let last = last"> <!-- iterate over all documents, let last = last is optional -->
<p>Id: {{doc.id}}</p>
<!-- iterate over all names (n) for every document (doc) -->
<p>Names: <span *ngFor="let n of doc.names; last as lastName">{{n.name}}{{lastName ? '': ','}} </span></p>
<!-- iterate over all cities (c) for every document (doc) -->
<p>Cities: <span *ngFor="let c of doc.cities; last as lastCity">{{c.city}}{{lastCity ? '': ','}} </span></p>
<!-- optional , this will add a separator between documents-->
<hr *ngIf="!last"/>
</div>
Output:

Related

Why are my data model values undefined after data is stoed in Angular?

I'm building a notes app, fetching data from a local api that I made.
Thing is, when I load data into my NoteModel in my component, values under the second "level" of models are undefined.
The data comes as a JSON obviously, that I want to save into my Model:
[
{
"id": 1,
"note": [
{
"id": 1,
"title": "Test1",
"description": "Testing testing",
"completed": false
},
{
"id": 2,
"title": "Test2",
"description": "Testing testing",
"completed": false
},
{
"id": 3,
"title": "Test3",
"description": "Testing testing",
"completed": false
}
]
}
]
This is my NoteModel
import { NoteContent } from "./note-content-model"
export interface NoteModel {
Id: number;
NoteContent: NoteContent[];
}
And this is my NoteContent model:
export interface NoteContent {
Id: number;
Title: string;
Description: string;
Completed: boolean;
}
As I understand it, it should load everything into both models when I save the JSON into the model object, as I try in my home.component.ts, that should load my list of components
import { Component, OnInit } from '#angular/core';
import { NoteService } from '../services/note.service';
import { NoteModel } from '../note-model';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
notes?: NoteModel;
constructor(private noteService: NoteService) { }
ngOnInit(): void {
this.noteService.getNotes()
.subscribe(response => {
if (response.length >= 0) {
this.notes = response[0];
console.log(this.notes.NoteContent)
}
});
}
}
Then I try to get the stored data in my html component using ngFor but I dont get anything.
Your interfaces have properties that are named in PascalCase.
The received JSON has properties that are named in cammelCase.
Equally important is that your received JSON has a property name note, but in your NoteModel interface you named the property NoteContent.
Replace your interfaces with the following and your code should work.
export interface NoteModel {
id: number;
note: NoteContent[];
}
export interface NoteContent {
id: number;
title: string;
description: string;
completed: boolean;
}
Javascript is case sensitive. 'title' and 'Title' are different things. You need to unify your model and the json

I am trying to put a list of countries in a .js file and export it for reuse. Getting errors

I am building a react/material-ui site and want to create a countries.js file that has the following in it (as an example)
export default const countryList = [
{
"label": "United States",
"value": "US"
},
{
"label": "Afghanistan",
"value": "AF"
},
...
]
I want to be able to import this as follows:
import countryList from '/json/countries.js'
where countryList will be the array that I can map like countryList.map...
I keep getting a parsing error, no matter how I define the function. What am I doing wrong?
UPDATE: After an answer from Prachi below, I tried to export/import like this:
export const Countries = [
{
"label": "United States",
"value": "US"
},
{
"label": "Afghanistan",
"value": "AF"
}
]
export default Countries;
Then I import the file as follows. My intellisense tells me I have the correct path because I can see 'json' folder name in my intellisense list when typing:
import Countries from '../../../json/Countries';
I still receive the following error:
Module not found: Can't resolve '../../../json/Countries' in 'C:\projects\DoxaProject\DoxaUI\DoxaUI\ClientApp\src\pages\patientpage\forms'
Any help on solving this would be greatly appreciated!
The default export can only take an expression; const and let statements are not allowed.
For example:-
const MyComponent = () => {
return <div class="foo">HI</div>;
};
export default MyComponent;
You should write your code as:
export const countryList = [
{
label: "United States",
value: "US"
},
{
label: "Afghanistan",
value: "AF"
}
];
export default countryList;
And import it as:
import countryList from "./country";

Render Object Array Properties In a Single Line

I have an object like below. I need to render all the subcategoryList names to an Angular HTML template as single line. I use this kind of solution print array in a single line: someObject.languages.join(' , '). What type of method can I use for this? I can't use *ngFor because it will render separate line for each object item.
{
firstName: " Hello",
lastName: "World",
category: [{
catId: "1",
catName: "SomeCat"
subcategoriesList: [{
subcatId: "5",
subcatName: "SubSomeCat"
},
{
subcatId: "6",
subcatName: "SubSomeCat1"
}
]
}, {
catId: "2",
catName: "SomeCat2"
subcategoriesList: [{
subcatId: "9",
subcatName: "SubSomeCat"
},
{
subcatId: "10",
subcatName: "SubSomeCat1"
}
]
}]
}
Either you can change the structure to a more flattened way. Or you can use span or inline div's.
<span *ngFor="let category of data.category">
<span *ngFor="let subcategory of category.subcategoriesList">
{{subcategory.subCatName}}
</span>
</span>
From what I understood, you want all sub-catgeory names as single string in HTML. You can use angular pipe to transform the data before displaying it on UI.
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'categoryNames'
})
export class CategoryNamesPipe implements PipeTransform {
public transform(obj: unknown): any {
return category
.map(list =>
list.subcategoriesList.map(subCatgeory => subCatgeory.name).join(', ')
)
.join(', ');
}
}
HTML:
<span> {{fullCatgeoryObj | categoryNames}}</span>
As Arcteezy mentioned it can be done with a hack, By default DIV render as display:block and SPAN as display:inline.
----------------------------USING SPAN--------------------------
<br>
<span *ngFor="let category of myData.category">
<span *ngFor="let subcategory of category.subcategoriesList">
{{subcategory.subcatName}}
</span>
</span>
<br>
----------------------------USING DIV------------------
<BR>
<div *ngFor="let category of myData.category" style="float:left">
<div *ngFor="let subcategory of category.subcategoriesList" style="float:left">
{{subcategory.subcatName}}
</div>
</div>
STACKBLITZ DEMO

Nested json which need to be display in tree structure format dynamically with the help of angular

I am seeking the below result with nested JSON. I am not able to retrive/call keys and values in template dynamically. Refrence link is attached for detail veiw
Angular Code
let checklist = {
"CS": [
{
"id": "1",
"name": "A"
},
{
"id": "2",
"name": "B"
},
{
"id": "3",
"name": "C"
}
],
"Comment": [
{
"id": "1",
"name": "D"
},
{
"id": "2",
"name": "E"
},
{
"id": "3",
"name": "F"
}
]
}
<div *ngFor="let item of Checklist | Key">
{{key}}
<div>{{item}}</div>
</div>
Desired Result
Use keyvalue pipe to loop over Objects within template, Use some css to modify the display but a code like below will serve your need.
<div *ngFor="let item of checklist | keyvalue">
<div>
{{item.key}}
</div>
<div>
<p *ngFor="let eachValue of item.value">
{{eachValue.name}}
</p>
</div>
</div>
https://stackblitz.com/edit/angular-jgwk8n?file=src%2Fapp%2Fapp.component.html
Edit
For angular version < 6, keyvalue pipe doesn't exist. You can create your own custom pipe, maybe like:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'customKeyvalue',
pure: true // keyvalue pipe is actually impure, which means this value would be false
})
export class CustomKeyvaluePipe implements PipeTransform {
transform(inputOb: any): any {
let returnVal = [];
for (let eachKey in inputOb) {
returnVal.push({key: eachKey, value: inputOb[eachKey]})
}
return returnVal
}
}
Now in case your Object changes dynamically without changing its original reference then you would have to make the above pipe as impure (pure: false). This has a downside of being triggered in every change detection.
https://stackblitz.com/edit/angular-jgwk8n?file=src%2Fapp%2Fapp.component.html
You can use Object.keys to get the keys of the object.
This is your component:
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
public checklist: any;
public ObjectKeys = Object.keys;
name = 'Angular';
ngOnInit() {
this.checklist = {
"CS": [
{
"id": "1",
"name": "A"
},
{
"id": "2",
"name": "B"
},
{
"id": "3",
"name": "C"
}
],
"Comment": [
{
"id": "1",
"name": "D"
},
{
"id": "2",
"name": "E"
},
{
"id": "3",
"name": "F"
}
]
};
}
}
This is the HTML:
<table border="1">
<tbody>
<tr *ngFor="let key of ObjectKeys(checklist)">
<td colspan="2" style="border-right-style: solid; border-width: 1px;">
{{ key }}
</td>
<td>
<div *ngFor = "let entry of checklist[key]">
{{ entry.name }}
</div>
</td>
</tr>
</tbody>
</table>
This is the result:
You can add CSS to make it look better, but you get the gist now :)
This is the StackBlitz link which you can edit.
The trick here is to use the display: inline-block and vertical-align: top.
It's similar to xyz example.
<div *ngFor="let item of checklist | keyvalue" >
<div style="display:inline-block;">
{{item.key}}
</div>
<div style="display:inline-block;vertical-align: top;">
<div *ngFor="let eachValue of item.value" >
{{eachValue.name}}
</div>
</div>
</div>
Example: https://angular-hzuexu.stackblitz.io

JSON array from local file to Typescript array in Angular 5

I am using Angular 5 to develop a web app. I have JSON file which looks like this:
[
{
"id": 0,
"title": "Some title"
},
{
"id": 1,
"title": "Some title"
},
...
]
This file is stored locally. I also have an interface:
export interface Book {
id: number;
title: string;
}
The questions are how do I:
Fetch the data from the file?
Create an array of type Book[] from this data?
You could load the file with import:
import data from 'path/to/data.json';
And assign that to an array of type Book[]:
#Component({...})
class Foo {
books: Book[] = data;
}
demo
Also add a wildcard module definition in src/typings.d.ts to tell the TypeScript compiler how to import *.json:
declare module "*.json" {
const value: any;
export default value;
}
This answer could help someone, although it pertains to Typescript 4.(Angular 5 supports Typescript 2 ).
The sample JSON file content (data.json):
[
{
"Gender": "Male",
"HeightCm": 171,
"WeightKg": 96
},
{
"Gender": "Male",
"HeightCm": 161,
"WeightKg": 85
}
]
Appropriate tsconfig.json entry:
{
"compilerOptions": {
"esModuleInterop": true
"resolveJsonModule": true
}
}
The code that reads the JSON content into an interface Person:
import data from './data.json';
interface Person{
Gender: string,
HeightCm: number,
WeightKg: number
}
const personArray:Person[] = data as Person[];

Categories

Resources