How to create random/shuffle filter without infinite digest loop

I would want to achieve the following:

  • To be able to show image from array in random order.

To solve this, I decided to create a filter :

var app = angular.module('app');
app. filter('randomizer', randomizer);


 function randomizer() {

        return function (collection, defaultValue) {

            var result = defaultValue;

            if (!!collection) {
                if (Array.isArray(collection) && collection.length === 1) {
                    result = collection[0] || defaultValue;
                } else if (!Array.isArray(collection)) {
                    result = collection || defaultValue;
                } else {
                    // randomize here please
                    var idx = parseInt(((Math.random()) * collection.length));
                    result = collection[idx] || defaultValue;
                }
            }

            return result;
        }
    }

Now in template, I am calling as:

<div class="masonry-pin masonry-brick" ng-repeat="p in vm.list | orderBy: 'updatedAt':true">

     <img ng-src="{{vm.baseUrl + 'documents/view/' + ( p.documents | randomizer:{id: 'notfound'}:p.id).id }}">

</div>

However, I am getting this error:

Uncaught Error: [NG-Modular Error] [$rootScope:infdig] 10 $digest() iterations reached. Aborting! Watchers fired in the last 5 iterations: [[{"msg":"fn: expressionInputsWatch","newVal":"http://localhost:3002/documents/view/158","oldVal":"http://localhost:3002/documents/view/159"},{"msg":"fn:

After doing some research, I found that it is do with value changes, but where I am changing my values.

Now I can understand that some how it might be the scope is changing. However, what I have noticed that even if I have a simple filter like this: {{p.documents | console}}

and the console filter just takes an array and print in the console.

Now when I have 10 objects in the list, It is calling filter 30 times.

Here is how the console filter is, but for 10 items it is calling 66 times.

Why ??

app.filter('console', pipeConsole);

function pipeConsole() {


        return function (value, o) {
            print(value);
            print(o);
            print(count);

        }

        function print(o) {
            if (!!o) {
                console.log(o);
            }
            else if (o === null) {
                console.warn(o);
            }
        }
    };

Here I am not even returning different values... (if I go with the logic explained here - Angular: infinite digest loop in filter)

Even if it is not a filter,then also it is causing this problem

Now that I have created a service function, and I am having this problem.

 <img ng-src="{{vm.baseUrl + 'documents/view/' + vm.random( p.documents , {id: 'notfound'}).id }}">


vm.random = function (a, s) {

            return utility.randomizer(a, s);
        };

So what is the solution??

Answers:

Answer

To fix the infinite digest, you need to wrap your filter in a memoize function. The best way to solve your problem is to install knuth-shuffle and lodash from npm, then use whatever module system you like to create the filter. This example is with CommonJS/browserify.

var memoize = require('lodash/function/memoize');
var shuffle = require('knuth-shuffle');

app.filter('shuffle', function() {
  return memoize(shuffle);
});

When creating filters this way, you may have a problem if an empty value is passed to the shuffle function. In that case, just add a check:

app.filter('shuffle', function() {
  return memoize(function(input) {
    if (input === undefined) { return; }
    return shuffle.apply(null, arguments);
  });
});

You can learn more about the infinite digest problem from this answer.

To re-shuffle the list, you can pass an arbitrary $scope property to the filter, then change that property when you want to re-shuffle. Either incrementing a number or using Math.random() are a good way to do this. This works because the result is cached according to the arguments passed, so passing an otherwise useless argument produces a new result.

myList | shuffle:whatever
$scope.whatever = 0;
$scope.reshuffle = function() {
  ++$scope.whatever;
  // OR
  $scope.whatever = Math.random();
};

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.