Access nested json object angular 6 - json

I'm trying to access the nested data from the HTML template, but I get undefined or I get nothing as result (empty page with no class list or student list).
The HTML template:
<div class="container">
<label *ngFor="let class of listClass | keyvalue">
<span> {{class.value.name}} </span>
</label>
<div>
<label *ngFor="let student of class.students | keyvalue">
<span>{{student.value.fullName}} </span>
</label>
</div>
</div>
This is the fonction that gets the list of class and the students in it:
getListClasseStudent(){
this.classService.getStudents().subscribe((data) => {
this.listClass = data;
});
}
The nested data:
class:
0:{
code: "Math01"
teacher:
0: {id: 17551, name "Jack"}
students:
0: {studentId: 1, fullName: "Patrick bob"}
1: {studentId: 2, fullName: "Alice Alice"}
}
1:{
code: "English01"
teacher:
0: {id: 2, name "Nicolas"}
students:
0: {studentId: 1, fullName: "Patrick bob"}
1: {studentId: 2, fullName: "Alice Alice"}
}
I want to access to the list of student of each class, is there any efficient way to do it? thanks in advance.

<div class="container">
<div *ngFor="let c of listClass ">
<label >
<span> {{c.code}} </span>
</label>
<div>
<label *ngFor="let student of c.students ">
<span>{{student.fullName}} </span>
</label>
</div>
</div>
Try this (example without your pipe)
A 'Class' object don't have a attribute 'value.name' (probably gonna be injected by your pipe '| keyvalue' ).
Second *ngFor need t be inside of first, because he need's to iterate a students array, inside each class.
I hope this helps.

create a pipe like below
import { Pipe, PipeTransform } from "#angular/core";
#Pipe({ name: 'ObjNgFor', pure: false })
export class ObjNgFor implements PipeTransform {
transform(value: Object): Array<string> { return Object.keys(value); }
}
import the above pipe in app.module.ts and use pipe in the html page like below
<div *ngFor="let key of questions | ObjNgFor" class="row">
{{ questions[key].name}}
<div *ngFor="let r of questions[key].sub_sections | ObjNgFor ; let indx=index"
class="card-body">
{{ questions[key].sub_sections[r].name }}"
</div>
This example should work

Related

How to categories items according to some specification in angular using ngFor

I am trying to list doctors with some specialization. But the code below is creating several title with item of same specialization.
Below is my html code:
<div class="row">
<div class="col-md-9" *ngFor="let doctor of doctors; let i = index">
<h3 class="header-subtitle">{{doctor.doctorSpeciality}}</h3>
<div class="doctor">
<div class="doctor-description">
<h4 class="name-title">Dr. {{ doctor.doctorName}}</h4>
</div>
</div>
<hr>
</div>
</div>
The output I am getting is like:
General Physician
doctor name1
Cardiologist
doctor name2
General Physician
doctor name3
Here, the doctor name3 of category general physician should be under heading of first header title.
This is not something that you can achieve only in html (without some directives/pipes at least).
I don't know exactly how you .ts code looks like, but the grouping you're seeking for needs to be made on the actual collection that you're using inside *ngFor. Most likely doctors is a flat array of objects, something like below, on which you can use reduce and map for computing the groups.
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
readonly doctors = [
{
doctorSpeciality: 'General Physician',
doctorName: 'doctor name 1'
},
{
doctorSpeciality: 'Cardiologist',
doctorName: 'doctor name 2'
},
{
doctorSpeciality: 'General Physician',
doctorName: 'doctor name 3'
}
];
specialityGroupedDoctors = {};
constructor() {
this.computeGroups();
}
private computeGroups(): any {
this.specialityGroupedDoctors = this.doctors.reduce(
(acc: any, doc: any) => {
if (!acc[doc.doctorSpeciality]) {
acc[doc.doctorSpeciality] = [];
}
acc[doc.doctorSpeciality].push(doc);
return acc;
}, {});
}
}
And then the html template needs to change to this:
<div class="row">
<div class="col-md-9" *ngFor="let group of specialityGroupedDoctors | keyvalue">
<h3 class="header-subtitle">{{group.key}}</h3>
<div class="doctor" *ngFor="let doctor of group.value">
<div class="doctor-description">
<h4 class="name-title">Dr. {{ doctor.doctorName}}</h4>
</div>
</div>
<hr>
</div>
</div>
Here you have a StackBlitz sandbox where you can see it working: https://stackblitz.com/edit/angular-ivy-t86wnc?file=src/app/app.component.html

Angular NgFor loop 2 arrays of objects

I want to loop through two different arrays with ngFor and display them in html.
<div class="form-check ml-5" *ngFor="let item of competencias.competencias; let i = index">
<input class="form-check-input" type="checkbox" [value]="item.id [(ngModel)]="arrayCompetencias[i].checked">
<label class="form-check-label">
<strong> {{ item.id }} </strong> : {{ item.descripcion}}
</label>
</div>
Those 2 arrays has the same length but I want to combine them as to show separate data.
First array has a list of data just to display and is fine.
Second array arrayCompetencias I want just to see if user check the checkbox or not and save it as ngModel.
When trying to get the parameter data in arrayCompetencias[i].checked it through me an error that the field is undefined, but I am initializing them before.
First Array
competencias = [{id: string, descripcion: string}]
Second Array
arrayCompetencias = [{checked: boolean, id: string}]
[(ngModel)]="arrayCompetencias[i].checked">
How can i read only this field into the array and set according User checked or not
I fixed some typos on your code and added some edits.
Try to use this example and it should work perfectly with you.
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
competencias = [{id: "1", description: "This is a description"}]
arrayCompetencias = [{checked: true, id: "1"}]
}
<div *ngFor="let item of competencias; let i = index">
<input type="checkbox"
[value]="item.id"
[checked]="arrayCompetencias[i].checked"
(change)="arrayCompetencias[i].checked = !arrayCompetencias[i].checked">
<label>
<strong> {{ item.id }} </strong> : {{ item.description}}
</label>
</div>
Nothing I have changes only a problem in your data.
You can check the code here

How to print key and value of object inside template?

how can I print the key and value of the object inside the template?
The template is 'kendo chart series item tooltip'
My HTML
<kendo-chart-series-item-tooltip>
<ng-template let-value="value" let-category="category" let-series="series" let-dataItem="dataItem">
<div *ngFor="let item of dataItem.subObject| keyvalue">
{{item | json}}<br/>
Key:{{item.key}} and Value:{{item.value}}
<br/><br/>
</div>
</ng-template>
</kendo-chart-series-item-tooltip>
My JSON:
[
{
"id": "1Period",
"subObject": [{"Alex":"10"},{"Mathew":"5"}],
},
{
"id": "2Period",
"subObject": [{"Alex":"2"},{"Mathew":"50"}]
}
]
This code doesn't work and it returns this error:
Uncaught Error: Template parse errors:
The pipe 'keyvalue' could not be found
probably you want to set it like this
{
"id": "1Period",
"subObject": [
{
"key":"Alex",
"value":"10"}
}
and your HTML file like this
<kendo-chart-series-item-tooltip>
<ng-template >
<div *ngFor="let item of dataItem.subObject">
<br/>
Key:{{item.key}} and Value:{{item.value}}
<br/><br/>
</div>
</ng-template>
</kendo-chart-series-item-tooltip>
You can define the following custom pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'forObject'
})
export class ForObjectPipe implements PipeTransform {
transform(value: object): string[] {
if (!value) {
return [];
}
return Object.keys(value);
}
}
And use it like that:
<kendo-chart-series-item-tooltip>
<ng-template let-value="value" let-category="category" let-series="series" let-dataItem="dataItem">
<div *ngFor="let key of dataItem.subObject | forObject">
{{dataItem.subObject[key] | json}}<br/>
Key:{{key}} and Value:{{dataItem.subObject[key]}}
<br/><br/>
</div>
</ng-template>
</kendo-chart-series-item-tooltip>
Check out my boilerplate if you have issues defining the pipe.

Angular filter posts using pipes

I'm trying to filter posts of a table in Angular using pipes, but I keep getting on the console this error: core.js:12689 Can't bind to 'ngForFilter' since it isn't a known property of 'a'.
I think the error is on filter: filterPost, but dont know how to fix it. Any clue?
My html code is the following.
<div class="form-group">
<label for="exampleFormControlInput1">Busca un reporte</label>
<input type="text" class="form-control" id="exampleFormControlInput1" name="filterPost" placeholder="Busca.." [(ngModel)]="filterPost">
</div>
<a *ngFor="let report of All_reports | paginate: { itemsPerPage: 10, currentPage: actualPage }; let i = index;
filter: filterPost" class="list-group-item list-group-item-action flex-column align-items-start" [routerLink]="['/reporte-admin']"
routerLinkActive="router-link-active" (click)="onSubmit(report._id)">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Reporte #: {{report._id}}</h5>
<small>{{i+1}}</small>
</div>
<p class="mb-1">Comentario: {{report.comentario}}</p>
<small class="text-muted">Nombre: {{report.nombre}} {{report.apellido}}</small>
</a>
<br>
This is my filter.pipe.ts code:
#Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(value: any, arg: any): any {
const resultPosts = [];
for(const report of value){
if(report._id.indexOf(arg) > -1){
resultPosts.push(report);
};
};
return resultPosts;
}
}
Pipe operator "|" expects some value to the left of it.
In your case, you added the pipe operator at the end after let i=index;
I believe you should do something like this
*ngFor="let report of All_reports | paginate: { itemsPerPage: 10, currentPage: actualPage } | filter: filterPost; let i = index;"
and also dont forget to add in declarations in app module

Displaying comma separated string in Angular 6

I am trying to loop through a comma separated string in Angular 6.
public getCategory(){
this.Jarwis.getCategorys().subscribe((data: Array<object>) => {
this.categorys = data;
console.log(this.categorys);
});
This is my function which have a console log as
(3) [{…}, {…}, {…}, {…}, {…}, {…}]
0: {id: 4, category_name: "Agriculture", sub_category_names: "Other Agriculture,Vineyards and Wineries,Greenhouses,Tree Farms and Orchards"}
1: {id: 5, category_name: "Automotive and Boat", sub_category_names: "Auto Repair and Service Shops,Car Dealerships,Marine/Boat Service and Dealers,Junk and Salvage Yards"}
2: {id: 13, category_name: "Beauty and Personal care", sub_category_names: "Massage,Tanning Salons,Spas,Hair Salons and Barber Shops"}
I can display category name in view page with the help of
<li *ngFor='let category of categorys'>
<div>{{ category.category_name }}</div>
</li>
But how can I display sub_category_names in different divs just like this
<div> subcategory_name1 </div>
<div> subcategory_name2 </div>
Please help
You can use a custom pipe to split the array:
#Pipe({
name: 'splitComma'
})
export class SplitCommaStringPipe implements PipeTransform {
transform(val:string):string[] {
return val.split(',');
}
}
and use it like:
<div *ngFor="let subcategory of category.sub_category_names|splitComma">
{{subcategory}}
</div>
Use following code in your html:
<li *ngFor='let category of categorys'>
<div>{{ category.category_name }}</div>
<div *ngFor="let subCategory of category.sub_category_names?.split(',')">
{{ subCategory }}
</div>
</li>
May be you can try this way by using an additional *ngFor
<li *ngFor='let category of categorys'>
<div *ngFor="let subCategory of (category.sub_category_names.split(','))">{{ subCategory }}</div>
</li>
https://stackblitz.com/edit/angular-a4s1bq
You can also split the sub-categories on return of the data. And then use *ngFor on the sub-categories. In ES6 this would look like this:
this.categories = data.map((e => {
return {
...e,
sub_category_names: e.sub_category_names.split(',')
}
}));
btw. plural of category is categories
https://stackblitz.com/edit/js-rkkyjs