I have a nested array and I'm using ngFor to iterate the array.
For example: I have a list of cities, under the cities I display users.
I am trying to sort all users inside the city alphabetically.
HTML
<div *ngFor="let data of locations$ | async">
{{ data.name }}
<ul *ngFor="let permission of getPermissions(data) | async">
<ng-container *ngFor="let user of permission.users">
<li>{{ user.lastName }}, {{ user.firstName }}</li>
</ng-container>
</ul>
</div>
Component:
results = of(this.res);
locations$: Observable<any>;
permissions$: Observable<any>;
constructor() {
this.locations$ = this.results.pipe(map((x) => x.locations));
this.permissions$ = this.results.pipe(
map((x) => x.permissions),
tap((data) => {
data.sort((a, b) => (a.lastName > b.lastName ? 1 : -1));
})
);
}
getPermissions(data) {
return this.permissions$.pipe(
map((permission) =>
permission.filter((x) => {
return x.entity.uuid === data.uuid;
})
)
);
}
}
Note: Need access to the other properties inside permissions array (eg: entity & name) to perform other logic in component. Therefore, cannot create a separate array for users
CodePudding user response:
If it's nested then you should sort nested array.
Instead of
tap((data) => {
data.sort((a, b) => (a.lastName > b.lastName ? 1 : -1));
})
try looping over permissins first and inside each persmission you can sort users:
tap((permissions) => {
permissions.forEach(permission =>
permission.users.sort((a, b) => a.lastName.localeCompare(b.lastName))
)
})
Note: I also used String.prototype.localeCompare() in order to not to think about case-insensitive strings
Update
Since you want to filter users within each city then I would suggest you restructure your locations$
observable like:
locations$ = this.results.pipe(map(res => {
return res.locations.map(location => {
const permissions = res.permissions.filter((x) => x.entity.uuid === location.uuid);
const users = permissions.map(p => p.users.map(user => ({...user, permission: p}))).reduce((acc, item) => acc.concat(item), []).sort((a, b) =>a.lastName.localeCompare(b.lastName))
return {
...location,
users
}
})
}));
Now you have everything in one place and you don't need to restructure a new Observable by calling getPermissions(data)
on each Angular change detection cycle.
<div *ngFor="let location of locations$ | async">
{{ location.name }}
<ul>
<li *ngFor="let user of location.users">>{{ user.lastName }}, {{ user.firstName }} <b>({{ user.permission.name}})</b></li>
</ul>
</div>