Introduction
This is about alternative approach to ng-repeat to handle heavy data binding with better page performance. This article will provide insights of how to replace particular ng-repeat
with particular data.
When to Avoid ng-repeat
ng-repeat
is good, if the data is less. But if heavy data is to be displayed from a collection, it is good to have the Custom Directive approach.
Especially if the data is going to be static/read only, definitely the ng-repeat
should be avoided.
Expressions in ng-repeat & $watch
Each expression will create a $watch
in Angular.
To handle 2 way binding, Angular will keep on watching all the scope values continuously. In other words, whenever one of the $scope
values is changed, it will check all the $watches
in the application.
In this ng-repeat
, if the row contains 15 columns with binding expression. If the data rows are 1000+, then $watch
will become 15000. So performance is affecting primarily.
Alternative to ng-repeat for single instance
If the content is to be static, if we don't require 2 way binding, need to go for one time binding syntax. {{::value}}
If the angularJS version of the application is below 1.3, the one time binding syntax will not support. Best alternative approach is Custom Directive. In other words, the static data could be formed with Angular features with simple steps as mentioned below.
Implementation
Here, we will create Unordered List and will bind the contents as List Contents of Students
data dynamically.
As a first step, just create UL
tag only as container.
Where List Contents will be displayed?
We will render the List
contents(<LI>
) dynamically. Place a DIV
and provide the unique attribute name "repeater-alternative-forstudentdata
"- which will be used in rendering flow.
<div>
<ul>
<div repeater-alternative-forstudentdata></div>
</ul>
</div>
What is the source for that List?
Assign collection data to be displayed in the Angular's $scope
.
var studentsList =
[
{
FirstName: "Raj,
LastName : "B",
Country : "India",
BirthDate: "01/01/1990"
},
{
FirstName: "Kumar,
LastName : "S",
Country : "India",
BirthDate: "01/01/1990"
},
..................
..................
..................
..................
];
$scope.collectionObject = studentsList;
Where is the actual List Content?
Our aim is to repeat the collection object and will display the list. So form the similar logic - loop the collection and form the string
as we desire to display.
var tableRow = "";
angular.forEach($scope.collectionObject, function (item) {
tableRow = tableRow + ['<li>',
'<div class="col-md-1">' + item.FirstName + '</div> ',
'<div class="col-md-1 ">' + item.LastName + '</div> ',
'<div class="col-md-1 ">' + item.Country+ '</div> ',
'<div class="col-md-1 ">' + item.Id + '</div> ',
'<div class="col-md-1 ">' +
$filter('date')(item.BirthDate, 'dd-MMM-yyyy') + '</div> ',
'</li>'].join('');
});
But Where to Place this List Forming logic?
Once the value of collectionObject
is available(assigned), we need to form prepare the table and need to display.
But How Do We Know - WHEN the value is assigned/available to the CollectionObject?
Angular is watching all the $scope
variable changes. whenever its value is changed, it is listener -$watch
is fired. So we will place the logic in that's $scope
variable's $watch
.
For this scope variable, whenever its value is changed, it is firing the following function:
$scope.$watch($scope.object, function (oldValue, newValue) {
})
So we will place the logic in that $scope
variable's $watch
. In other words, when we assign the value, Angular processes the value and forms the List Content.
$scope.$watch('collectionObject', function (oldValue, newValue) {
var tableRow = "";
angular.forEach($scope.collectionObject, function (item) {
tableRow = tableRow + ['<li>',
'<div class="col-md-1">' + item.FirstName + '</div> ',
'<div class="col-md-1 ">' + item.LastName + '</div> ',
'<div class="col-md-1 ">' + item.State + '</div> ',
'<div class="col-md-1 ">' + item.Id + '</div> ',
'<div class="col-md-1 ">' +
$filter('date')(item.BirthDate, 'dd-MMM-yyyy') + '</div> ',
'</li>'].join('');
});
})
But how will the Angular render this table under that HTML <div>.
repeater-alternative-forstudentdata
?
With the help of Angualr's Directive concepts, we achieve.
In brief, we instruct the Angular -
When the particular named element is found,
What to do as Post Processing Operation
var userDirectives = angular.module([]);
userDirectives.directive('DOMElementFound', function () {
return {
replace: true,
link: function ($scope, $elem, attrs) {
}
}
});
In this example, we inform Angular when the "repeater-alternative
-forstudentdata
" element is found, append these list rows(divs).
The syntax of the Angular directive is as follows:
var userDirectives = angular.module([]);
userDirectives.directive('repeaterAlternativeForstudentdata', function () {
return {
replace : true,
link: function ($scope, $elem, attrs) {
$scope.$watch('collectionObject', function (oldValue, newValue) {
var tableRow = "";
angular.forEach($scope.collectionObject, function (item) {
tableRow = tableRow + ['<li>',
'<div class="col-md-1">' + item.FirstName + '</div> ',
'<div class="col-md-1 ">' + item.LastName + '</div> ',
'<div class="col-md-1 ">' + item.State + '</div> ',
'<div class="col-md-1 ">' + item.Id + '</div> ',
'<div class="col-md-1 ">' + $filter('date')
(item.BirthDate, 'dd-MMM-yyyy') + '</div> ',
'</li>'].join('');
});
$elem.context.innerHTML = tableRow;
});
}
}
});
Output
In brief, we simulate ng-repeat
, but with static content for particular data. So output will be similar to ng-repeat
, but will render faster, since there is no 2 way binding - no 'n
' number of $watchers
.
Notes
The aim of the article is how to replace ng-repeat
with Custom Directive for particular data. So Directive is just brief. More details on Directive will be in future articles.