TonyW
TonyW

Reputation: 18875

AngularJS: help needed for understanding how model activates filter

I'm reading AngularJS book from APress, I quite like the presentation in the book, and most parts are easy to grasp. However, I find it a little bit hard to understand how the checkbox with the ng-model="showComplete" activates the filter "checkedItems". How does the model call the filter function "checkedItems"? thanks!

code in my html page:

<!DOCTYPE html>
<html>
<head>
    <title>TO DO List</title>
    <link href="bootstrap.css" rel="stylesheet" />
    <link href="bootstrap-theme.css" rel="stylesheet" />
    <script src="angular.js"></script>
    <script src="chap2.9.js"></script>
</head>
<body>
    <div ng-app="todoApp" ng-controller="ToDoCtrl">
    <div class="page-header">
        <h1>{{todo.user}}'s To Do List
            <span class="label label-default" ng-class="warningLevel()" ng-hide="incompleteCount()==0">{{incompleteCount()}}</span>

        </h1>
    </div>
    <div class="panel">
        <div class="input-group">
            <input class="form-control" ng-model="actionText" />
            <span class="input-group-btn">
                <button class="btn btn-default" ng-click="addNewItem(actionText)">Add</button>
            </span>
        </div>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>Description</th>
                    <th>Done</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="item in todo.items | checkedItems : showComplete | orderBy: 'action'">
                    <td>{{item.action}}</td>
                    <td>{{item.done}}</td>
                    <td><input type="checkbox" ng-model="item.done" /></td>
                </tr>
            </tbody>
        </table>
    <div class="checkbox-inline">
        <label><input type="checkbox" ng-model="showComplete">Show Complete</label>
    </div>
</html>

my js code:

var model = {
            user: "Adam"
        };
var todoApp = angular.module("todoApp", []);

/* ajax call to import the items to do */
todoApp.run(function($http) {
  $http.get("todo.json").success(function(data) {
    model.items = data;
  });
});


todoApp.controller("ToDoCtrl", function($scope) {
  $scope.todo = model;

  $scope.incompleteCount = function() {
    var count = 0;
    angular.forEach($scope.todo.items, function(item){
      if (!item.done) {count++}
    });
    return count;
  }

  $scope.warningLevel = function() {
    return $scope.incompleteCount() < 5 ? "label-success" : "label-warning";
  }

  $scope.addNewItem = function(actionText) {
    $scope.todo.items.push({action: actionText, done: false});
  }


});

/* filter for todoApp */
todoApp.filter("checkedItems", function() {
  return function(items, showComplete) {
    var resultArr = [];
    angular.forEach(items, function(item) {
      if (item.done == false || showComplete == true) {
        resultArr.push(item);
      }
    });
    return resultArr;
  }
});

var book = {
  author: 'John Wright',
  books: [{title: 'title1', published: '2012-02-12'},
                {title: 'title2', published: '2009-12-25'},
                {title: 'title3', published: '1999-12-23' }]
};

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

myApp.controller("MyController", function($scope) {
  $scope.content = book;
});

json content in todo.json:

[{"action" : "Buy flowers", "done" : false},
 {"action" : "Watch movie", "done" : true},
 {"action" : "Buy groceries", "done" : true},
 {"action" : "Play games", "done" : false},
 {"action" : "Party with friends", "done" : false}
]

Upvotes: 1

Views: 357

Answers (1)

V31
V31

Reputation: 7666

Hi Angular works in two way binding. You have created an object (todo) in scope and iterated it using ng-repeat. What it does is now the object todo will be in the angular digest. Any changes in the scope object will run the digest to update the bindings.

Now whenever the elements changes within the scope (selection of checkboxes) you have done a two way binding on item.done. Since the property changes it will trigger the digest and hence the list will be re-created and thus the filter will be fired again.

Your code snippet in a Fiddle to demonstrate how it is working.

I have changed the code a bit to exclude http call. And hardcoded the model instead using your json code.

Code:

var model = {
user: "Adam"
};
model.items = [{
    "action": "Buy flowers",
    "done": false
}, {
    "action": "Watch movie",
    "done": true
}, {
    "action": "Buy groceries",
    "done": true
}, {
    "action": "Play games",
    "done": false
}, {
    "action": "Party with friends",
    "done": false
}];
var todoApp = angular.module("todoApp", []);
todoApp.controller("ToDoCtrl", function ($scope) {
    $scope.todo = model;
    $scope.incompleteCount = function () {
        var count = 0;
        angular.forEach($scope.todo.items, function (item) {
            if (!item.done) {
                count++
            }
        });
        return count;
    }
    $scope.warningLevel = function () {
        return $scope.incompleteCount() < 5 ? "label-success" : "label-warning";
    }
    $scope.addNewItem = function (actionText) {
        $scope.todo.items.push({
            action: actionText,
            done: false
        });
    }
});
/* filter for todoApp */
todoApp.filter("checkedItems", function () {
    return function (items, showComplete) {
        alert('here');
        var resultArr = [];
        angular.forEach(items, function (item) {
            if (item.done == false || showComplete == true) {
                resultArr.push(item);
            }
        });
        return resultArr;
    }
});
var book = {
    author: 'John Wright',
    books: [{
        title: 'title1',
        published: '2012-02-12'
    }, {
        title: 'title2',
        published: '2009-12-25'
    }, {
        title: 'title3',
        published: '1999-12-23'
    }]
};
var myApp = angular.module('myApp', []);
myApp.controller("MyController", function ($scope) {
    $scope.content = book;
});

Upvotes: 1

Related Questions