user2897069
user2897069

Reputation: 1

Prevent digest of parent scopes in Angular

I am investigating Angular as a potential framework to use for an upcoming project. The test app I am making consists of an unordered list of which list items can be added to via an add link. Each list item contains a number of checkboxes. The number of checkboxes for a particular list item can be increased or decreased with plus and minus links next to each list item. Observe:

enter image description here

Hopefully that makes sense. Each checkbox has an ng-model directive binding the value of the checkbox to a property in an object. When the application is in the state above clicking any of the checkboxes fires six checks (one for each checkbox) -- the entire $scope of the root controller is checked for changes. Ideally, only the $scope of the relevant list item would be checked for changes. How can I accomplish this? I've attached my test code for reference. I've tried adding ng-click="$event.stopPropagation()" to the input node as well as to the li node but this appears to increase (double) the number of checks in the digest.

HTML:

<div ng-app ng-controller="App">
  <ul>
    <li ng-repeat="line in lines" ng-controller="LineController">
      <input type="checkbox" ng-repeat="box in line.boxes" ng-model="box.on" />
      <a ng-show="line.boxes.length > 1" ng-click="removeBox()">-</a>
      <a ng-click="addBox()">+</a>
    </li>
  </ul>
  <a ng-click="addLine()">Add</a>
</div>

JavaScript:

function App($scope) {
  $scope.lines = [];
  $scope.addLine = function () {
    $scope.lines.push({
      boxes: []
    });
  };
}

function LineController($scope) {
  $scope.addBox = function () {
    var box = {};
    Object.defineProperty(box, 'on', {
      enmerable: true,
      get: function () {
        console.log('Get!');
        return this._on;
      },
      set: function (on) {
        this._on = on;
      }
    });
    $scope.line.boxes.push(box);
  };

  $scope.removeBox = function () {
    $scope.line.boxes.pop();
  };
}

Upvotes: 0

Views: 661

Answers (2)

Maxim Shoustin
Maxim Shoustin

Reputation: 77904

What about to use $watch. We can invoke watch only for specific row. That means if you have 4x4 matrix (4 rows , 4 columns) on any checkbox state change we call watch 4 times

var webApp = angular.module('myModule', []);

webApp.controller('App', function ($scope) {
    $scope.lines = [];

    $scope.addLine = function () {

        console.log("addLine");

        $scope.lines.push({
            boxes: []
        });

    };
});

webApp.controller('LineController', function ($scope) {
    $scope.addBox = function () {
        var box = {};
       /* Object.defineProperty(box, 'on', {
            enmerable: true,
            get: function () {
                console.log('Get!');
                return this._on;
            },
            set: function (on) {
                this._on = on;
            }
        });*/
        $scope.line.boxes.push(box);



        $scope.$watch(function () {
            return $scope.line.boxes;
        },
        function (newValue, oldValue) {

            if(newValue == oldValue) return;
             console.log('Get new checkbox!');

        }, true);

    };

    $scope.removeBox = function () {
        $scope.line.boxes.pop();
    };
}); 

Demo Fiddle

Upvotes: 1

mortalapeman
mortalapeman

Reputation: 1425

If your concern is that AnguarJS dirty checking is going to be too slow for your needs, your question really need to be "is AngularJS going to be to slow to build X?" If X is a 3D game with lots of constant rendering then the answer is probably yes, AngularJS is not what you want. If X is "a scalable business/consumer oriented single page application", then the dirty checking algorithm is not going to be your bottle neck.

This SO answer has a good explanation of how data binding works and talks a bit about performance concerns.

Upvotes: 1

Related Questions