Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS : $scope.$watch is not updating value fetched from $resource on custom directive

Tags:

I'm having an issue with custom directives that's driving me crazy. I'm trying to create the following custom (attribute) directive:

angular.module('componentes', [])     .directive("seatMap", function (){         return {             restrict: 'A',             link: function(scope, element, attrs, controller){                  function updateSeatInfo(scope, element){                     var txt = "";                     for (var i in scope.seats)                         txt = txt + scope.seats[i].id + " ";                     $(element).text("seat ids: "+txt);                 }                  /*                      // This is working, but it's kind of dirty...                     $timeout(function(){updateSeatInfo(scope,element);}, 1000);                 */                  scope.$watch('seats', function(newval, oldval){                     console.log(newval, oldval);                     updateSeatInfo(scope,element);                 });             }         }     }); 

This "attribute-type" directive (called seatMap) is trying to show a list of seat ids (e.g, for a theatre) which I'll fetch from the server via $resource service (see code below) into a div (element).

I'm using it with this simple partial html:

<div>     <!-- This is actually working -->     <ul>         <li ng-repeat="seat in seats">{{seat.id}}</li>     </ul>      <!-- This is not... -->     <div style="border: 1px dotted black" seat-map></div> </div> 

And this is the controller which is loading the scope:

function SeatsCtrl($scope, Seats) {     $scope.sessionId = "12345";     $scope.zoneId = "A";     $scope.seats = Seats.query({sessionId: $scope.sessionId, zoneId: $scope.zoneId});     $scope.max_seats = 4; } 

Where "Seats" is a simple service using $resources to fetch a JSON from the server

angular.module('myApp.services', ['ngResource'])     .factory('Seats', function($resource){         return $resource('json/seats-:sessionId-:zoneId.json', {}, {});     }) ; 

app.js (asientos_libres.html is the partial I've been using):

angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives', 'componentes']).   config(['$routeProvider', function($routeProvider) {     $routeProvider.when('/view1', {templateUrl: 'partials/asientos_libres.html', controller: SeatsCtrl});     $routeProvider.otherwise({redirectTo: '/view1'});   }]); 

The problem is, even though I set up a "scope.$watch" in the link function of the directive so that the scope can check whether "seats" attribute has changed to update the list of ids, it isn't working at the moment $scope.seats is changing in the controller (when we call "query").

As you might see in the code, I made a try using $timeout to delay the launch of "updateSeatInfo", but I'm afraid it's not the smartest solution by far...

I also tried to not to make a JSON request, but use a hard-coded dictionary in $scope.seats and it works, so it seems it's a matter of synchrony.

Note: The updateSeatInfo is just a test function, the actual function I'll use is a bit more complex.

Any idea about how to cope with it?

Thank you a lot beforehand!

Edit 1: Added app.js, where I'm using router to call SeatsCtrl, thanks to Supr for the advice. However, I'm still having the same issue.

Edit 2: Solved!(?) Ok! It seems I found a solution, which may not be the best, but it's working properly! :) As far as I could see here http://docs.angularjs.org/api/ng.$timeout, we can use $timeout (a wrapper over setTimeout) with no delay! This is great because we aren't artificially delaying the execution of our code inside $timeout, but we're making the directive not to run it until the asynchronous request has finished.

Hope it will work for long-wait requests, too...

If someone knows a better way to fix it, please tell!

like image 632
Sheniff Avatar asked Jun 21 '12 10:06

Sheniff


1 Answers

The issue is that watch compares the reference instead of the object by default. Add ,true to the end to have it compare the value instead.

scope.$watch('seats', function(newval, oldval){                 console.log(newval, oldval);                 updateSeatInfo(scope,element);             }, true); 
like image 78
Bryan Mundie Avatar answered Oct 08 '22 23:10

Bryan Mundie