Spencer Pope
Spencer Pope

Reputation: 475

improve angular performance when using filters

I have a bunch of angular filters which I am using to sort out my data, however, my app seems to load pretty slowly. I am wondering if there is a better way for me to structure my code in order to improve performance.

js

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

myApplication.controller("Catalog", function ($scope) {

$scope.books = books;

$scope.showInfo = false;

})

.filter('mydate', function() {
return function(input) {
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1; //January is 0, so always add + 1
var yyyy = today.getFullYear();
if (dd < 10) {
  dd = '0' + dd
}
if (mm < 10) {
  mm = '0' + mm
}
today = mm + '/' + dd;
return (input == today)
}
})

.filter('past', function() {
return function(input) {
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1; //January is 0, so always add + 1
var yyyy = today.getFullYear();
if (dd < 10) {
  dd = '0' + dd
}
if (mm < 10) {
  mm = '0' + mm
}
today = mm + '/' + dd;
return (input)
}
})

.filter('January', function() {
return function(input) {
return input.slice(0,2) === '01';
}
})

.filter('February', function() {
return function(input) {
return input.slice(0,2) === '02';
}
})

.filter('March', function() {
return function(input) {
return input.slice(0,2) === '03';
}
})

.filter('April', function() {
return function(input) {
return input.slice(0,2) === '04';
}
}) 

.filter('May', function() {
return function(input) {
return input.slice(0,2) === '05';
}
})

.filter('June', function() {
return function(input) {
return input.slice(0,2) === '06';
}
})

.filter('July', function() {
return function(input) {
return input.slice(0,2) === '07';
}
})

.filter('August', function() {
return function(input) {
return input.slice(0,2) === '08';
}
})

.filter('September', function() {
return function(input) {
return input.slice(0,2) === '09';
}
})

.filter('October', function() {
return function(input) {
return input.slice(0,2) === '10';
}
})

.filter('November', function() {
return function(input) {
return input.slice(0,2) === '11';
}
})

.filter('December', function() {
return function(input) {
return input.slice(0,2) === '12';
}
});

html

 <ul>
  <li class="pane1">
    <div class="bg" ng-repeat="book in books" >
       <div ng-show=" book.doc.date | mydate" >
          <div class="date">{{book.doc.date}}</div>
          <div class="title">{{book.doc.title}}</div>
          <div class="quote">{{book.doc.quote}}</div>
          <div class="attribution">-{{book.doc.attribution}}</div>
          <div class="textt">{{book.doc.text}}</div>
      <div style="height:10px"></div>      
       </div>
      </div>  
  </li>
  <li class="pane2">
      <div class="january" ng-click="showJan = !showJan">
        <div class="titletext">January</div>
      </div>
      <div class="bg"  ng-repeat="book in books ">
        <div ng-show="showJan">
          <div style="padding-top:10px; border-bottom:2px solid #ededed;" ng-click="showInfo = !showInfo" ng-show=" book.doc.date |January">
            <div id="circleJan"><div class="day">{{book.doc.day}}</div></div>
            <div class="title">{{book.doc.title}}</div>
            <div class="quote"  ng-show="showInfo">{{book.doc.quote}}       </div>
            <div class="attribution" ng-show="showInfo">-{{book.doc.attribution}}</div>
            <div class="textt" ng-show="showInfo">{{book.doc.text}}</div>    
            <div style="height:10px"></div>    
          </div>
        </div>
      </div>
      <div class="february" ng-click="showFeb = !showFeb">
        <div class="titletext">February</div>
      </div>
      <div class="bg" ng-repeat="book in books ">
        <div ng-show="showFeb">
          <div style="padding-top:10px; border-bottom:2px solid #ededed;" ng-click="showInfo = !showInfo" ng-show=" book.doc.date |February">
            <div id="circleFeb"><div class="day">{{book.doc.day}}</div></div>
            <div class="title">{{book.doc.title}}</div>
            <div class="quote"  ng-show="showInfo">{{book.doc.quote}}</div>
            <div class="attribution" ng-show="showInfo">-{{book.doc.attribution}}</div>
            <div class="textt" ng-show="showInfo">{{book.doc.text}}</div>    
            <div style="height:10px"></div>    
          </div>
        </div>
      </div>
      <div class="march" ng-click="showMarch = !showMarch">
        <div class="titletext">March</div>
      </div>
      <div class="bg" ng-repeat="book in books ">
        <div ng-show="showMarch">
          <div style="padding-top:10px; border-bottom:2px solid #ededed;" ng-click="showInfo = !showInfo" ng-show=" book.doc.date |March">
            <div id="circleMarch"><div class="day">{{book.doc.day}}</div></div>
            <div class="title">{{book.doc.title}}</div>
            <div class="quote"  ng-show="showInfo">{{book.doc.quote}}</div>
            <div class="attribution" ng-show="showInfo">-{{book.doc.attribution}}</div>
            <div class="textt" ng-show="showInfo">{{book.doc.text}}</div>    
            <div style="height:10px"></div>    
          </div>
        </div>
      </div>

Upvotes: 1

Views: 445

Answers (3)

vittore
vittore

Reputation: 17579

There are whole bunch of issues with your code in terms of performance best practices.

  1. Always use track by part of ng-repeat. That the first thing to do when you have performance issue

  2. Avoid filters all together on big lists. Create another property that will hold filtered array.

  3. Turn off debug information ($compileProvider.debugInfoEnabled(false);).

  4. Use ng-if rather than ng-show and ng-hide as they actually remove nodes from DOM with corresponding bindings

  5. @JDTLH9 already mentioned one-time-bindings. Use them when appropriate

And something unrelated to angular - always try to think of proper data structures for the problem you are solving. If you have a list of months and you have list of books that you need to spread across values in first array, you can create a hash map (simple object in js) with keys to be month name or code and value be filtered array. This way you are not travesting your array 12 times. See @cubbuk answer for an easy way of doing that with underscore/lodash.

Another thing you might want to consider with your example is pre-populating month value for each book, once you've got it from server. This way you can have one ng-repeat with simple filter:

 <div ng-repeat='book in books track by book.id | month: selectedMonth' >
 ...

And tab buttons just changing value of $scope.selectedMonth

UPDATE: there is also cool library that helps operating big arrays of data that you want slice and dice in memory: http://square.github.io/crossfilter/

There is also module for angular: https://github.com/Wildhoney/ngCrossfilter

But the thing is, unless you actually have several hundreds thousands records in your array, you should not have any issue with just fixing issues mentioned above. However, should you find yourself in need of searching in huge arrays (100k+ items) you really need to use advanced in memory indexes, and crossfilter is a huge help.

Upvotes: 1

JDTLH9
JDTLH9

Reputation: 1775

If using 'angularjs' 1.3 or higher, try the single-bind functionality for a significant performance boost on your read-only 'ng-repeat's.

Read about this here

Change you code to use a double colon before the array:

<div class="bg" ng-repeat="book in ::books ">

Upvotes: 0

cubbuk
cubbuk

Reputation: 7920

You can use already existing date filter of AngularJS if you need date formatting. Other than as far as I see you are trying to split your records into months. Instead of using filters in this case grouping your data by months in your controller would be easier. So each group would contain records of a specific month and you can list these arrays in your view rather than creating filters for each month. Here is a script for grouping your records by month using libraries underscore and moment

$scope.booksPerMonth = _.group($scope.books, function(book){
 return moment(book.doc.date).month();
});
//$scope.booksPerMonth[0] contain books of January you can use ng-repeat to traverse months, then another ng-repeat to traverse books of the month.

Upvotes: 0

Related Questions