4

Below is my array :

$scope.parent.cars1 = ["Saab", "BMW"]; // There are more than 1000 records in this array

$scope.child.Cars1 = [];

Now I am trying to assign $scope.parent.cars1 to $scope.child.Cars1 but would like to have 3 properties in my $scope.child.Cars1 array i.e name, operation and selected and by default operation property will be 1 and selected property will be true.

Code :

$scope.child = {};
if ($scope.child.Cars1 == undefined)
    $scope.child.Cars1 = [];

angular.forEach($scope.parent.Cars1, function(obj, key) {
    $scope.child.Cars1.push({
            name: obj,
            operation: 1, // this value is used to tick all radio button of clear and save
            selected: true // this value is used to check all checkbox to true by default
    });
})

Now as my records $scope.parent.Cars1 contains thousands of records, so my browser gets hang because I am using this $scope.child.Cars1 on my view to display records like below:

<tr ng-repeat="item in child.Cars1 | myfilter : searchitem">
     <td>
         <input ng-model="item.selected" type="checkbox" name="a{{$index}}" id="a{{$index}}">
     </td>
     <td>
          <div>
             <input type="radio" ng-model="item.operation" value="0" name="b{{$index }}" id="b{{$index}}">Clear
          </div>
          <div>
             <input type="radio" ng-model="item.operation" value="1" name="b{{$index }}" id="b{{$index}}">Clear and Save
          </div>
     </td>
 </tr>;

Update : I am trying to avoid the process given below, so that browser doesn't get hanged because of huge number of records:

angular.forEach($scope.parent.Cars1, function(obj, key) {
        $scope.child.Cars1.push({
                name: obj,
                operation: 1, // this value is used to tick all radio button to true by default
                selected: true // this value is used to check all checkbox to true by default
        });
});

Plunker

Using @Bear plunker this happens:

enter image description here

14
  • Would you be able to create a sample plunk please. It is a bit difficult to understand the problem. Commented Mar 2, 2017 at 15:54
  • what about assign the angular.forEach($scope.parent.Cars1, function (obj, key) not to $scope array but to a var and the do $scope.child.Cars1 = myvar; Commented Mar 2, 2017 at 15:57
  • @Alok :See my update may be it can help you to understand my concern Commented Mar 2, 2017 at 15:58
  • @federicoscamuzzi :Can you please post that in the form of answer so that i can understand it Commented Mar 2, 2017 at 15:59
  • I don't know your real problem.what the problem is performance issues or your code don't work above the code? Commented Mar 2, 2017 at 16:00

6 Answers 6

7
+50

I've update and cleanup my PLUNKER with a real 8000 records...
I have to say that if you don't use pagination o some technique that only write a certain quantity of records in the DOM, you probably have a bad perfomance.

As a advice to improve better perfomace:

  • Try to do much work as you can in local vars before of binding the array
  • Work with a local copy of the whole array and only bind a limit quantity of records in the screen. (Use javascript slice(start, end))
  • add track by $index (angular docs ng-repeat)
  • When you are using angular 1.3+, disable two-way data binding if isn't necessary (angularJs one-way data binding)

My code:

CONTROLLER JS:

var bigArray = ["Dandai", "Immātīn", "Oefatu" ...];
var itemsLocal = []; //Local copy of the items to work

var PAGE_SIZE = 250;
var TOTAL_RECORDS = bigArray.length;

var start = 0;
var end = PAGE_SIZE;

$scope.items = []; //Items to repeat in screen

$scope.loadData = loadData;
$scope.prevPage = prevPage;
$scope.nextPage = nextPage;

transformData();
loadData();

function transformData() {
    for (var i = 0; i < TOTAL_RECORDS; i++) {
      itemsLocal.push({
        name: bigArray[i],
        selected: true,
        operation: 1
      });
    }
}

function loadData() {
    $scope.items = itemsLocal.slice(start, end); //repeat only certain part of the data
}

function prevPage() {
    if (start < PAGE_SIZE) 
        return;

    start = start - PAGE_SIZE;
    end = end - PAGE_SIZE;
    loadData();
}


function nextPage() {
    if (end >= TOTAL_RECORDS) 
        return;

    start = start + PAGE_SIZE;
    end = end + PAGE_SIZE;
    loadData();
}

HTML:

<div>
    <button type="button" ng-click="prevPage()">Load Previous</button>
    <button type="button" ng-click="nextPage()">Load Next</button>
</div>

<hr/>

<table class="table">
    <thead>
      <tr>
        <th>Column 1</th>
        <th>Column 2</th>
      </tr>
    </thead>
    <tbody>
      <tr ng-repeat="item in items track by $index">
        <td>
          <input ng-model="item.selected" type="checkbox"> {{ item.name }}
        </td>
        <td>
          <div>
            <input type="radio" class="radio-custom" ng-model="item.operation" value="0">Clear
          </div>
          <div>
            <input type="radio" class="radio-custom" ng-model="item.operation" value="1">Clear and Save
          </div>
        </td>
      </tr>
    </tbody>
</table>
Sign up to request clarification or add additional context in comments.

5 Comments

Upvoted for your kind efforts towards helping me.UI will be very slow if records size increases to 7000-8000
Check this PLUNKER with 8000 elements, I think that browser response is good enough for too many record in the DOM. It's pretty obvious that is slow or laggy render 8000 rows in a table without pagination or limit. If your are looking for that, I can update my answer and do it with a limit and would be much faster.
See my updated question in which i have posted what happens after running your plunker
Which version of firefox are you using? I don't have any warnings... Anyway, I think that is better approach to load with a limit to do not render 8000 rows all time... I will update the plunker later... don't worry
I updated my PLUNKER... I've implemented custom pagination (could be changed to a infinite scroll if you want). Now you have a binding for all your inputs with a great performance... I think that's this is the best solution to work with large amounts of data.
5

You can use Array.map() method to iterate the array.

For my own testing i created the array($scope.array1000) with 1000 records and it is loaded fine without any issues(hang,more load time,etc..).

DEMO

var app = angular.module('myApp', []);

app.controller('MyCtrl',function($scope) {
  $scope.parent = { "cars1": ["Saab", "BMW"] };
  $scope.array1000 = [];
  for (var i=0; i< 1000; i++) {
    $scope.array1000.push($scope.parent.cars1[0]+''+i);
  }
  
  var res = $scope.array1000.map(function(record) {
    return {
      "name": record,
      "operation": 1,
      "selected": true
    }
  });
  
  console.log(res);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
  <div ng-repeat="item in array1000">
    {{ item }}
  </div>
</div>

6 Comments

There are 2 loops in your code along with the method :1 loop for filling $scope.array1000 array and then again on view side and map function.isnt is possible to loop on view side and generate and set this property there?Still upvoted for your kind efforts towards helping me :)
First loop is just to create the array of 1000 elements. It is just for the reference. map method is used to add the default values to properties.
Can i avoid loop for pushing records in array1000??
Yes, as i already told you that for demo i created the array of 1000 elements but in your case it will automatically come from the API. so this loop will not exist.
It is working but the only thing is that my data are already coming from API in $scope.parent and i want that data in $scope.child.Cars1 with operation with default value 1 and selected with true
|
4

your problem is not the data initializing that making the browser too slowly,the main problem is manipulate DOM element with a huge of data in a single thread,then the browser look like hanged up。so you must render a piece of data from the huge of the data at a time.you can use the interval service $interval,because I never used angularjs,this problem spent me most of the night,but I think it's worth to do.

// Instantiate the app, the 'myApp' parameter must
// match what is in ng-app
var myApp = angular.module('myApp', []);
function timeFormat(time) {
    var units = ['ms', 's', 'm'];
    var radix = [1000, 60, 60];

    for (var i = 0; i < units.length; i++) {
        if (time < radix[i]) {
            return time.toFixed(2) + units[i];
        }
        time /= radix[i];
    }
    return time + 'h';
}
function run(name, fn) {
    var start = +new Date;
    var result = fn.call(this);

    function timeElapsed() {
        var elapsed = +new Date - start;
        console.log(['Stage[' + name + ']', 'elapsed', timeFormat(elapsed)].join(' '));
    }

    if (typeof result.then == 'function') {
        result.then(timeElapsed);
    } else {
        timeElapsed();
    }
    return result;
}
myApp.controller('ToddlerCtrl', function ($scope, $timeout, $interval) {
    var cars = run('initializing data', function () {
        var DATA_SIZE=1000;
        var cars = [];
        for (var i = 0; i < DATA_SIZE; i++) {
            cars.push(i);
        }
        return cars.map(function (_, i) {
            return 'car-' + i;
        }).map(function (name) {
            return {
                name: name,
                operation: 1,
                selected: true
            };
        });
    });
    $scope.className = 'loading';
    //initialing cars for rendering
    $scope.cars = [];
    var BATCH_SIZE = 30;
    var batches = Math.floor(cars.length / BATCH_SIZE);
    var DELAY = 1;
    var push = Array.prototype.push;


    var task = run('filling data', function () {
        function fill() {
            push.apply($scope.cars, cars.splice(0, BATCH_SIZE));
            //rendering data into browser
            console.log('Remaining ' + cars.length + ' items ...');
        }

        fill();
        return $interval(fill, DELAY, batches);
    }).then(function () {
        $scope.className = 'hidden';
    });

});
ul, li, html, body { margin: 0; padding: 0; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } ul { list-style: none; } .loading { font-size: 2em; font-weight: bold; position: fixed; right: 20px; top: 20px; color: green; } .hidden { display: none; } .loading em { position: relative; display: inline-block; height: 1em; width: 50px; } .loading span { position: absolute; text-overflow: clip; -webkit-animation: loading 1s infinite linear; -o-animation: loading 1s infinite linear; animation: loading 1s infinite linear; } @keyframes loading { from { clip: rect(0, 0, 50px, 0); } to { clip: rect(0, 50px, 50px, 0); } } @-o-keyframes loading { from { clip: rect(0, 0, 50px, 0); } to { clip: rect(0, 50px, 50px, 0); } } @-webkit-keyframes loading { from { clip: rect(0, 0, 50px, 0); } to { clip: rect(0, 50px, 50px, 0); } } .loading:first-letter { font-size: 1.5em; } li { float: left; width: 21%; border: 2px solid #eee; margin: 10px 2%; padding: 5px; } .car { padding: 5px 0 10px; display: block; } .button-group { text-align: right; }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular.min.js"></script>
<main ng-app="myApp">

    <div ng-controller="ToddlerCtrl">
        <div ng-class="className" class="loading">Loading <em><span>...</span></em></div>
         <ul>
             <li ng-repeat="item in cars">
                 <label class="car">
                     <input ng-model="table.selected" type="checkbox" name="a{{$index}}" id="a{{$index}}">{{ item.name }}
                 </label>
                 <div class="button-group">
                     <label>
                         <input type="radio" ng-model="item.operation" value="0" name="b{{$index }}" id="b{{$index}}">
                         Clear
                     </label>
                     <label>
                         <input type="radio" ng-model="item.operation" value="1" name="b{{$index }}" id="c{{$index}}">
                         Clear and Save
                     </label>
                 </div>
             </li>
         </ul>
    </div>
</main>

23 Comments

Can you update my plunker with your answer and see if this works or not
I'have edited some wrong thing,you can copy the code and try it again.if it is also slowly,then the problem is not here.
Upvoted for your kind efforts towards helping me.I will test it.Thank you so much :)
you can see browser supports many browsers supported.Don't forget give me feedback,I need it,because I was never used angularjs.
clusterize.js.org has demo,but you must bind/unbind data-model base on callbacks
|
3

something like .. ?? .. hope it helps

$scope.child = {};
  if ($scope.child.Cars1 == undefined)
            $scope.child.Cars1 = [];
        var all = angular.copy($scope.parent.Cars1);
        var test = [];

  angular.forEach(all, function (obj, key) {
             test.push(
           {
               name: obj,
               operation: 1, // this value is used to tick all radio button of clear and save
               selected: true // this value is used to check all checkbox to true by default
           }
       );

$scope.child.Cars1 = test;
        })

So you don't work with $scope vars (cause are haeavy and freeze UI) ...

4 Comments

Do you think this extra loop is required to fill test array??
...?? ..what .. i just do twaht you were doing ..but without working on $scoped vars
Upvoted for your kind efforts towards helping me.I have created a plunker.
really thnx .. i'll take a look
3

Well, the plunker had a lot of errors. I am assuming you created it in a haste to showcase the sample code.

Without actually seeing your actual files with actual data, it is really hard to pin-point the problem.

Try to use a simple array.map() function. Check this out http://plnkr.co/edit/Sx3lMIBEMMj4xrOBI5vI?p=preview

A mapping function on your array that returns an object will arrange the data in the required form.

If the hanging is due to js bottlenecks, it might resolve it. Otherwise, try a simple for loop which iterates over the array. A for loop is the fastest and most efficient way to loop through an array as there is not overhead of a function call.

However, more than 1000 DOM elements themselves can cause the browser to hang. If my plunker doest help, try to show only a portion of the complete data at a time.

This can be accomplished through simple pagination, in which only a page with specific number of entries is shown at a time.

Or you can use directives like virtual repeat.

Even infinite-scroll way of buffering the DOM.

Totally depends upon the use case.

1 Comment

Records is exactly the same as you have created in your plunker and sorry i cannot do pagination
3

The cause of the slowness will most likely be the filter: ng-repeat="item in child.Cars1 | myfilter : searchitem".

The child.Cars1 array of objects will be created fairly quickly.

2 Comments

Upvoted for your kind efforts towards helping me but i am not getting that why 2 parent and child controller?
I misunderstood what you're trying to do. I've updated my answer. The cause of the freeze is most likely the filter, in my opinion

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.