Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

AngularJS ng-repeat Alternative Approach

3.08/5 (6 votes)
11 Jul 2016CPOL3 min read 47.7K  
How to replace the ng-repeat with Custom Directive

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.

HTML
<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.

JavaScript
// Assuming heavy value is retrieved from the server. Here only sample values are given
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; //Assign to Angular $scope for processing.

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.

JavaScript
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:

JavaScript
$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.

JavaScript
$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
JavaScript
var userDirectives = angular.module([]);

userDirectives.directive('DOMElementFound', function () {
    return {
        replace: true,
        link: function ($scope, $elem, attrs) {
                  //Post processing operations
        }
    }
});

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:

JavaScript
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('');
                });

                
                //If IE is your primary browser, 
                //innerHTML is recommended to increase the performance
                $elem.context.innerHTML = tableRow;
                //If IE is not your primary browser, 
                //just appending the content to the element is enough .
                //$elem.append(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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)