Monday, 1 February 2021

AngularJS ng-repeat is slow

It is not like it is slow on rendering many entries. The problem is that whenever the $scope.data got updated, it adds the new item first at the end of the element, then reduce it as it match the new $scope.data.

For example:

<div class="list" ng-repeat="entry in data">
    <h3></h3>
</div> 

This script is updating the $scope.data:

$scope.load = function() {
    $scope.data = getDataFromDB();
}

Lets say I have 5 entries inside $scope.data. The entries are:

[
    {
        id: 1,
        title: 1
    },
    {
        id: 2,
        title: 2
    },
    ......
]

When the $scope.data already has those entries then got reloaded ($scope.data = getDataFromDB(); being called), the DOM element for about 0.1s - 0.2s has 10 elements (duplicate elements), then after 0.1s - 0.2s it is reduced to 5.

So the problem is that there is delay about 0.1s - 0.2s when updating the ng-repeat DOM. This looks really bad when I implement live search. Whenever it updates from the database, the ng-repeat DOM element got added up every time for a brief millisecond.

How can I make the rendering instant?


EDITED

I will paste all my code here:

The controller:

$scope.search = function (table) {
    $scope.currentPage = 1;
    $scope.endOfPage = false;
    $scope.viewModels = [];
    $scope.loadViewModels($scope.orderBy, table);
}

$scope.loadViewModels = function (orderBy, table, cb) {
    if (!$scope.endOfPage) {
        let searchKey = $scope.page.searchString;
        let skip = ($scope.currentPage - 1) * $scope.itemsPerPage;
        let searchClause = '';

        if (searchKey && searchKey.length > 0) {
            let searchArr = [];
            $($scope.vmKeys).each((i, key) => {
                searchArr.push(key + ` LIKE '%` + searchKey + `%'`);
            });
            searchClause = `WHERE ` + searchArr.join(' OR ');
        }

        let sc = `SELECT * FROM ` + table + ` ` + searchClause + ` ` + orderBy +
            ` LIMIT ` + skip + `, ` + $scope.itemsPerPage;
        sqlite.query(sc, rows => {
            $scope.$apply(function () {
                var data = [];
                let loadedCount = 0;
                if (rows != null) {
                    $scope.currentPage += 1;
                    loadedCount = rows.length;
                    if (rows.length < $scope.itemsPerPage)
                        $scope.endOfPage = true

                    for (var i = 0; i < rows.length; i++) {
                        let item = rows.item(i);
                        let returnObject = {};
                        $($scope.vmKeys).each((i, key) => {
                            returnObject[key] = item[key];
                        });
                        data.push(returnObject);
                    }
                    $scope.viewModels = $scope.viewModels.concat(data);
                } 
                else
                    $scope.endOfPage = true;

                if (cb)
                    cb(loadedCount);
            })
        });
    }
}

The view:

<div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
    <div class="row note-list" ng-if="showList">
        <h3>Notes</h3>
        <input ng-model="page.searchString" id="search"
               ng-keyup="search('notes')" type="text" class="form-control"
               placeholder="Search Notes" style="margin-bottom:10px">
        <div class="col-12 note-list-item"
             ng-repeat="data in viewModels track by data.id"
             ng-click="edit(data.id)"
             ontouchstart="touchStart()" ontouchend="touchEnd()"
             ontouchmove="touchMove()">
            <p ng-class="deleteMode ? 'note-list-title w-80' : 'note-list-title'"
               ng-bind-html="data.title"></p>
            <p ng-class="deleteMode ? 'note-list-date w-80' : 'note-list-date'"></p>
            <div ng-if="deleteMode" class="note-list-delete ease-in" ng-click="delete($event, data.id)">
                <span class="btn fa fa-trash"></span>
            </div>
        </div>
        <div ng-if="!deleteMode" ng-click="new()" class="add-btn btn btn-primary ease-in">
            <span class="fa fa-plus"></span>
        </div>
    </div>
    <div ng-if="!showList" class="ease-in">
        <div>
            <div ng-click="back()" class="btn btn-primary"><span class="fa fa-arrow-left"></span></div>
            <div ng-disabled="!isDataChanged" ng-click="save()" class="btn btn-primary" style="float:right">
                <span class="fa fa-check"></span>
            </div>
        </div>
        <div contenteditable="true" class="note-title"
             ng-bind-html="selected.title" id="title">
        </div>
        <div contenteditable="true" class="note-container" ng-bind-html="selected.note" id="note"></div>
    </div>
</div>

<script src="../js/pages/note.js"></script>

Calling it from:

$scope.loadViewModels($scope.orderBy, 'notes');

The sqlite query:

query: function (query, cb) {
    db.transaction(function (tx) {
        tx.executeSql(query, [], function (tx, res) {
            return cb(res.rows, null);
        });
    }, function (error) {
        return cb(null, error.message);
    }, function () {
        //console.log('query ok');
    });
},

It is apache cordova framework, so it uses webview in Android emulator.


My Code Structure

<html ng-app="app" ng-controller="pageController">
    <head>....</head>
    <body>
        ....
        <div id="pageContent" class="root-page" ng-controller="noteController" ng-cloak>
            ....
        </div>
    </body>
</html>

So there is controller inside controller. The parent is pageController and the child is noteController. Is a structure like this slowing the ng-repeat directives?

Btw using track by is not helping. There is still delay when rendering it. Also I can modify the entries as well, so when an entry was updated, it should be updated in the list as well.


NOTE

After thorough investigation there is something weird. Usually ng-repeat item has hash key in it. In my case ng-repeat items do not have it. Is it the cause of the problem?



from AngularJS ng-repeat is slow

No comments:

Post a Comment