0

I have an angular service which contains two function to return an array of objects. The first returns the entire array, and the second a filtered array based on an ID. When I use the first function returning all the objects, data-binding works automatically and my front end updates as soon as I push a new object to the array. However when I use the function that returns a filtered list using underscore, my frontend doesn't update automatically.

I've done some research and the only thing similar to this that I have seen is in relation to async requests and using promises, but I'm not sure if this is appropriate in this case as I'm only using in service objects currently.

Service

angular.module('RankingsApp')
.service('results', function() {    
    var uid = 2;
    var results = [
    {
        "id":1,
        "fencer":1,
        "competition":1,
        "placing":1,
        "points":50
    }
    ];

    this.getResults = function()
    {
        return results;
    }

    this.getResultsForCompetition = function (_id) 
    {
        var resultsForCompetition = _.filter(results, function(x){ return x.competition == _id});

        return resultsForCompetition;
    };

    this.insertResult = function (result) {
        result.id = uid++;
        results.push(result);
    };



});

Controller

angular.module('RankingsApp')
.controller('CompetitionCtrl', function ($scope, competitions,fencers,results, $routeParams) {

    $scope.getResults = function()
    {
        return results.getResultsForCompetition($routeParams.competitionID);
    }

    $scope.competition = competitions.getCompetition($routeParams.competitionID);
    $scope.fencers = fencers.getFencers();  
    $scope.compResults = results.getResultsForCompetition($routeParams.competitionID);



    function getNextPlacing()
    {
        return $scope.compResults.length + 1;
    }

    $scope.getFencerFromResult = function(result)
    {
        return fencers.getFencer(result.fencer);        
    }

    $scope.getCompFromResult = function(result)
    {
        return competitions.getCompetition(result.competition);     
    }



    $scope.addNewResult = function(fencer)
    {
        var result = { "fencer": fencer.id,  "competition":$routeParams.competitionID,  "placing": getNextPlacing(), "points":50 };
        results.insertResult(result);       
        $scope.selectedFencer = null;

    }       
});

View

<table style="width: 100%">
                    <thead>
                        <tr>
                            <th>Placeing</th>
                            <th>Fencer</th>
                            <th>Comp</th>                           
                            <th>Points</th>                         
                            <th>Edit</th>                           
                        </tr>
                    </thead>
                    <tbody>
                        <tr ng-repeat='result in compResults'>
                            <td>{{result.placing}}</td>
                            <td>{{getFencerFromResult(result).firstname}} {{getFencerFromResult(result).lastname}}</td>
                            <td>{{getCompFromResult(result).shortName}}</td>
                            <td>{{result.points}}</td>                                              
                            <td><a>Edit</a></td>                            
                        </tr>                       
                    </tbody>
                </table>
Scott
  • 2,969
  • 8
  • 23
  • 23

2 Answers2

1

It's because your method (with _.filter()) returns another array than what your view in the frontend was bind to (as binding is done by reference in case of an Array or an Object).

To solve this, you may place filtering logic in views and use ng-repeat.

If it's not an option, you should directly modify the results variable in the service by using pop()/push() methods.

Update:

<tr ng-repeat='result in compResults'>

should be

<tr ng-repeat='result in compResults | filter{ competition: _id }'>

where

$scope.compResults = results.getResults();

and

$scope._id = $routeParams.competitionID;

found here

Community
  • 1
  • 1
Mironor
  • 1,157
  • 10
  • 25
  • I've updated my post with the view, when you say filter in the view, could you explain what you mean? Do you mean use the ng-repeat to make the call to the service? – Scott Nov 02 '14 at 15:40
  • Would it not be wasteful to return all the results? For example what if there was 1000+ or 2000+ in the array down the line? – Scott Nov 02 '14 at 15:50
  • Well, sure, if there was 10 000 elements that would need to be filtered in the array, there would be a need for an optimization. But it would be another question. If your service has a need for such optimization, the angularjs bindings are the first thing you would need to get rid of and start to render things directly (with pagination). – Mironor Nov 02 '14 at 15:54
  • And you don't return all results, you return a pointer to the array, not the whole structure – Mironor Nov 02 '14 at 15:56
0

Using the advice posted by @Mironor I was able to come up with the following solution which solves my issue. By changing the ng-repeat to call the function directly the list updates itself when I push a new value to the service.

View

<tr ng-repeat='result in getResultsForCompetition(competitionID)'>
Scott
  • 2,969
  • 8
  • 23
  • 23
  • 1
    Bare in mind that during digest cycle angularJs will re-evaluate getResultsForCompetition(competitionID) to render new or delete removed models. I doubt that this is faster than the filter included in ng-repeat. – Mironor Nov 02 '14 at 16:32