Reputation: 62626
Assume I have the following object 'myarr':
[
{'name':'Mary', 'chapter':'Chapter 1'},
{'name':'Joseph', 'chapter':'Chapter 1'},
{'name':'John', 'chapter':'Chapter 2'},
{'name':'Carl', 'chapter':'Chapter 3'},
{'name':'Jacob', 'chapter':'Chapter 3'}
]
Is it possible to make it so that I can output the following:
Chapter 1
Mary
Joseph
Chapter 2
John
Chapter 3
Carl
Jacob
If so, what is a good way to accomplish this with just one list? I don't want to hardcode Chapter 1, Chapter 2, Chapter 3. I would like to infer from the data.
<div *ngFor="let name of myarr">
// two ngFor to get the chapter?
{{name}}
</div>
Upvotes: 5
Views: 7973
Reputation: 25525
Use a function to determine whether to display the chapter. Using a trackBy function in the ngFor will help performance.
component:
lastChapter = '';
myarr = [
{'name':'Mary', 'chapter':'Chapter 1'},
{'name':'Joseph', 'chapter':'Chapter 1'},
{'name':'John', 'chapter':'Chapter 2'},
{'name':'Carl', 'chapter':'Chapter 3'},
{'name':'Jacob', 'chapter':'Chapter 3'}
];
displayChapter(item): boolean {
if (item.chapter !== this.lastChapter) {
this.lastChapter = item.chapter;
return true;
} else {
return false;
}
}
trackChapter(index: number; item: any): string {
return item.name + item.chapter;
}
template:
<div *ngFor="let item of myarr; trackBy: trackChapter">
<h1 *ngIf="displayChapter(item)">{{ item.chapter }}</h1>
<p>{{ item.name }}</p>
</div>
Upvotes: -2
Reputation: 5257
Check this example here: https://stackblitz.com/edit/group-by-inangular
You need to group your items by chapter first, and that should be done in your ts file like this:
groupArr = this.myarr.reduce((r,{group})=>{
if(!r.some(o=>o.chapter==chapter)){
r.push({chapter,groupItem:this.myarr.filter(v=>v.chapter==chapter)});
}
return r;
},[]);
and then in your html do this:
<table>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
<tbody *ngFor="let item of groupArr">
<ng-container>
<tr>
<td colspan="2"><b>{{item.group}}</b></td>
</tr>
<tr *ngFor="let value of item.groupItem">
<td>{{value.name}}</td>
</tr>
</ng-container>
</tbody>
</table>
Upvotes: 1
Reputation: 2943
There is one "dirty" hack that you can make in order to achieve it. Personally I recommend you to go with the grouping of items, but here is the 100% working solution in case you want to stick to a single list.
Steps are:
TypeScript
export class AppComponent {
arr = [
{'name':'Mary', 'chapter':'Chapter 1'},
{'name':'Joseph', 'chapter':'Chapter 1'},
{'name':'John', 'chapter':'Chapter 2'},
{'name':'Carl', 'chapter':'Chapter 3'},
{'name':'Jacob', 'chapter':'Chapter 3'}
];
constructor() {
this.arr = this.arr.sort((a,b) => a.chapter > b.chapter ? 1 : -1);
}
}
HTML
<div *ngFor="let item of arr; let index = index">
<h3 *ngIf="!arr[index-1] || item.chapter !== arr[index-1].chapter">{{item.chapter}}</h3>
{{item.name}}
</div>
StackBlitz: https://stackblitz.com/edit/angular-4y6anf
Upvotes: 13