The other day, I wanted to output a list of values in AngularJS using ngRepeat; however, I wanted that list to be grouped into sublists. Out of the box, ngRepeat doesn't have any group-by control, like ColdFusion does. But, nesting ngRepeats is fairly straightforward if you take on the responsibility of creating the groups yourself.
In the following demo, I have members of the cast from Arrested Development. This list can be grouped by gender and hair color. In order to create the grouping, we have to maintain a separate list of groups in parallel with the original list of cast members. Then, as the requirements of the grouping change, we have to manually rebuild the groups using the original list.
NOTE: Some of this code would be much easier with a library like LoDash.js.
<!doctype html>
<html ng-app="Demo" ng-controller="DemoController">
<head>
<meta charset="utf-8" />
<title>
Grouping Nested ngRepeat Lists In AngularJS
</title>
</head>
<body>
<h1>
Grouping Nested ngRepeat Lists In AngularJS
</h1>
<p>
Group by:
<a ng-click="groupBy( 'gender' )">Gender</a> -
<a ng-click="groupBy( 'hair' )">Hair</a>
</p>
<!-- BEGIN: Outer ngRepeat. -->
<div ng-repeat="group in groups">
<h2>
{{ group.label }}
</h2>
<ul>
<!-- BEGIN: Inner ngRepeat. -->
<li ng-repeat="friend in group.friends">
{{ friend.name }}
</li>
<!-- END: Inner ngRepeat. -->
</ul>
</div>
<!-- END: Outer ngRepeat. -->
<!-- Load jQuery and AngularJS from the CDN. -->
<script
type="text/javascript"
src="//code.jquery.com/jquery-1.9.1.min.js">
</script>
<script
type="text/javascript"
src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js">
</script>
<!-- Load the app module and its classes. -->
<script type="text/javascript">
// Define our AngularJS application module.
var demo = angular.module( "Demo", [] );
// -------------------------------------------------- //
// -------------------------------------------------- //
// I am the main controller for the application.
demo.controller(
"DemoController",
function( $scope, $timeout ) {
// -- Define Controller Methods. ------------ //
// I sort the given collection on the given property.
function sortOn( collection, name ) {
collection.sort(
function( a, b ) {
if ( a[ name ] <= b[ name ] ) {
return( -1 );
}
return( 1 );
}
);
}
// -- Define Scope Methods. ----------------- //
// I group the friends list on the given property.
$scope.groupBy = function( attribute ) {
// First, reset the groups.
$scope.groups = [];
// Now, sort the collection of friend on the
// grouping-property. This just makes it easier
// to split the collection.
sortOn( $scope.friends, attribute );
// I determine which group we are currently in.
var groupValue = "_INVALID_GROUP_VALUE_";
// As we loop over each friend, add it to the
// current group - we'll create a NEW group every
// time we come across a new attribute value.
for ( var i = 0 ; i < $scope.friends.length ; i++ ) {
var friend = $scope.friends[ i ];
// Should we create a new group?
if ( friend[ attribute ] !== groupValue ) {
var group = {
label: friend[ attribute ],
friends: []
};
groupValue = group.label;
$scope.groups.push( group );
}
// Add the friend to the currently active
// grouping.
group.friends.push( friend );
}
};
// -- Define Scope Variables. --------------- //
// I am the raw collection of friends.
$scope.friends = [
{
name: "Michael",
gender: "Male",
hair: "Brunette"
},
{
name: "George Michael",
gender: "Male",
hair: "Brunette"
},
{
name: "Gob",
gender: "Male",
hair: "Brunette"
},
{
name: "Tobias",
gender: "Male",
hair: "Black"
},
{
name: "Lindsay",
gender: "Female",
hair: "Blonde"
},
{
name: "Maeby",
gender: "Female",
hair: "Black"
}
];
// I am the grouped collection. Each one of these
// will contain a sub-collection of friends.
$scope.groups = [];
}
);
</script>
</body>
</html>
As you can see, in order to group the list, I create a secondary, "groups" list. Each item in this list contains a sub-collection of the original friends collection. Then, we simply use ngRepeat to output one list within the other.
I've seen a lot of AngularJS code that seeks to do similar things using filters or inline method calls (ie. method calls located directly within the HTML templates). This approach works well for testing and small interfaces; but, even Misko Hevery - creator of AngularJS - states that filters should only be used with trivial examples. As such, manually grouping your data within your Controller is definitely the way to go.
Want to use code from this post? Check out the license.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4