Having worked on various AngularJS applications in the past, I was quite surprised by some built-in functionalities that are not a part of Angular 4. One of them was the groupBy filter, which I’ve used in multiple occasions in the ng-repeat of my AngularJs applications.
Custom pipes in Angular 4
It turns out that in Angular 4 (and Angular 2) you have to create your own custom GroupBy pipe in order to group objects from an array inside your *ngFor directive. Luckily there are a lot of examples of custom pipes on the internet and creating one for grouping an array of objects wasn’t so difficult.
Here is the custom pipe that I used in various places of my application:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'groupBy'})
export class GroupByPipe implements PipeTransform {
transform(collection: Array, property: string): Array {
// prevents the application from breaking if the array of objects doesn't exist yet
if(!collection) {
return null;
}
const groupedCollection = collection.reduce((previous, current)=> {
if(!previous[current[property]]) {
previous[current[property]] = [current];
} else {
previous[current[property]].push(current);
}
return previous;
}, {});
// this will return an array of objects, each object containing a group of objects
return Object.keys(groupedCollection).map(key => ({ key, value: groupedCollection[key] }));
}
}
Usage
To give you an example of how this pipe will work let’s use this example array of objects:
var myArray = [
{
name: "Apple",
color: "Green"
},
{
name: "Banana",
color: "Yellow"
},
{
name: "Grape",
color: "Green"
},
{
name: "Melon",
color: "Yellow"
},
{
name: "Orange",
color: "Orange"
}
];
In our *ngFor directive we can now use the custom pipe to group our array of objects by colour:
<ul>
<li *ngFor="let object of myArray | groupBy:'color'"></li>
</ul>
The result of this is that the pipe will return an array of objects that will look the following way:
[
{
key: "Green",
value: [
{
name: "Apple",
color: "Green"
},
{
name: "Grape",
color: "Green"
}
]
},
{
key: "Yellow",
value: [
{
name: "Banana",
color: "Yellow"
},
{
name: "Melon",
color: "Yellow"
}
]
},
{
key: "Orange",
value: [
{
name: "Orange",
color: "Orange"
}
}
];
Now we can easily display the key of our grouped objects {{ object.key }} or use one more *ngFor directive to loop through object.value and display the separate objects.
10 Comments
collection.reduce is not a function error
Thank you that was exactly what I needed
it is giving error :
geeneric type Array required,
it is working properly but i need to do nested group, how can i achieve
This works for only one nested object, such as Property.SubProp
In this scenario: item.property.subproperty :
your call it is like this:*ngFor=”let item of myCollection| groupBy: ‘property.subproperty’ ”
And the code changes are:
import { Pipe, PipeTransform } from ‘@angular/core’;
@Pipe({name: ‘groupBy’})
export class GroupByPipe implements PipeTransform {
transform(collection: any, property: string): any {
// prevents the application from breaking if the array of objects doesn’t exist yet
if (!collection) {
return null;
}
const groupedCollection = collection.reduce((previous, current) => {
const currentValue = this.getNavigationPropertyValue(property, current);
if (!previous[currentValue.key]) {
previous[currentValue.key] = [currentValue.value];
} else {
previous[currentValue.key].push(currentValue.value);
}
return previous;
}, {});
// this will return an array of objects, each object containing a group of objects
return Object.keys(groupedCollection).map(key => ({ key, value: groupedCollection[key] }));
}
getNavigationPropertyValue(property: string, current: any): any {
if (property.indexOf(".") <= 0) {
return property;
}
const tempArray = property.split(".");
property = tempArray[0];
const subProperty = tempArray[1];
if (current == null) {
return property;
}
return { value: current, key: current[property][subProperty] };
}
}
When I use with infinitive scroll, after scroll event, I use ” myArray.push(…eventData) ” . But it not working. How to solved it ? Thanks!
Thanks ! Working like a charm !
it is giving error :
geeneric type Array required,
Will this solution work for grouping on multiple columns?
Thank You.