OakBehringer
OakBehringer

Reputation: 55

AngularJS - Design pattern for returning a subset of data from a service... and binding to the set?

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

Upvotes: 2

Views: 743

Answers (2)

Michael Kang
Michael Kang

Reputation: 52867

You need to reapply the fiters because the bound arrays have not changed when you call addBoy() or addOther().

One way around this is to call the filter after each call:

function applyFilter(){
        $scope.showSome = MrData.fetchForType('boy');
        $scope.showAll = MrData.fetchAll();
}

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

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

I would show how it could be done with angular filters but it seems someone already has.

Upvotes: 0

Matt Way
Matt Way

Reputation: 33179

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

Upvotes: 1

Related Questions