AngularJS - Design template to return a subset of data from a service & hellip; and bind to the whole?

advertisements

I have a service that manages a set of data. The service is polite enough to provide options to return a subset of said data based on (whatever logic, in this example it's simply going to look for a specific data attribute value). The service will return an array of matches. In my view, I want to bind to this set of matches. However, because the service returns a new array object each time the filter function is called, that doesn't work. My view is bound to the previously returned array object.

Try this fiddle:

http://jsfiddle.net/FdWeK/1/

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

app.factory('MrData', function() {
    var allData = [
        {name: 'Adam', type: 'boy'},
        {name: 'Kassidy', type: 'girl'},
        {name: 'Justin', type: 'boy'},
        {name: 'Chloe', type: 'cat'},
        {name: 'D The P', type: 'dog'},
    ];

    return {
        add: function(thing) {
            allData.push(thing);
        },
        fetchAll: function() {
            return allData;
        },
        fetchForType: function(type) {
            var some = [];

            for (var i = 0; i < allData.length; i++) {
                if (allData[i].type == type)
                    some.push(allData[i]);
            }

            return some;
        }
    }
});

app.controller('SomeCtrl', function($scope, MrData) {
    $scope.showSome = MrData.fetchForType('boy');
    $scope.showAll = MrData.fetchAll();

    $scope.addBoy = function() {
        MrData.add({name: 'TED!', type: 'boy'});
    }

    $scope.addOther = function() {
        MrData.add({name: 'Other', type: 'Other'});
    }
});

and the view:

<div ng-app="app">
    <div ng-controller="SomeCtrl">
        <button ng-click="addBoy()">Add Boy</button>
        <button ng-click="addOther()">Add Other</button>

        <h2>Boys</h2>
        <ol>
            <li ng-repeat="thing in showSome">
                {{ thing.type }}
                {{ thing.name }}
            </li>
        </ol>

        <h2>All</h2>
        <ol>
            <li ng-repeat="thing in showAll">
                {{ thing.type }}
                {{ thing.name }}
            </li>
        </ol>
    </div>
</div>

You can see that the list of boys is not updated when you click Add Boy. And I understand why- but I don't understand how to fix this! Must be a simple design pattern or feature that I just don't know about, or can't figure out on my own.

Thanks you in advance, Adam


You obviously understand what is broken, that your view is bound to two different lists. However, the problem is that you are filtering the model, when you really should be filtering the view. This way you always stay bound to a single list, and the view manages how that list is presented to the user.

What you should be using is a filter https://docs.angularjs.org/api/ng/filter/filter

For example, this simple ng-repeat should work:

<li ng-repeat="thing in showAll | filter:{type: 'boy'}">

Also updated fiddle