Sunday, March 12, 2017

How to generate properties of array with some default value while doing ng repeat?

Leave a Comment

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

Can anybody please help me with this?

6 Answers

Answers 1

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 TOTOAL_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 < TOTOAL_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 >= TOTOAL_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> 

Answers 2

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>

Answers 3

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>

Answers 4

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

Answers 5

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.

Answers 6

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.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment