AngularJS: $champ d'application.$watch ne met pas à jour la valeur extraite de $resource sur la directive personnalisée

J'ai un problème avec les directives personnalisées qui me rend fou. J'essaie de créer la directive personnalisée (attribut) suivante:

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);
                });
            }
        }
    });

Cette directive "attribute-type" (appelée seatMap) essaie d'afficher une liste d'identifiants de siège (par exemple, pour un théâtre) que je vais récupérer du serveur via $resource service (voir le code ci-dessous) dans un div (élément).

Je l'utilise avec ce simple html partiel:

<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>

Et c'est le contrôleur qui charge le portée géographique:

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

Où "Seats" est un service simple utilisant $resources pour récupérer un JSON à partir du serveur

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

App.js (asientos_libres.html est le partiel que j'ai utilisé):

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'});
  }]);

Le problème est, même si j'ai mis en place un "scope".$ watch "dans la fonction link de la directive afin que la portée puisse vérifier si l'attribut "seats"a changé pour mettre à jour la liste des identifiants, cela ne fonctionne pas pour le moment $scope.seats change dans le contrôleur (quand nous appelons "requête").

Comme vous pouvez le voir dans le code, j'ai essayé d'utiliser $timeout pour retarder le lancement de "updateSeatInfo", mais je crains que ce ne soit pas la solution la plus intelligente de loin...

J'ai également essayé de ne pas faire de requête JSON, mais d'utiliser un dictionnaire codé en dur dans $scope.sièges et ça marche, donc il semble que c'est une question de synchronisation.

Note: updateSeatInfo est juste une fonction de test, la fonction réelle que je vais utiliser est un peu plus complexe.

Une idée sur la façon de faire face avec elle?

Merci d'avance!

Modifier 1: application ajoutée.js, où j'utilise le routeur pour appeler SeatsCtrl, merci à Supr pour les conseils. Cependant, je suis toujours le même problème.

Edit 2: Résolu!(?) Ok! Il semble que j'ai trouvé une solution, qui n'est peut-être pas la meilleure, mais cela fonctionne correctement! :) Pour autant que je puisse voir ici http://docs.angularjs.org/api/ng. $ timeout , nous pouvons utiliser $timeout (un wrapper sur setTimeout) sans délai! C'est génial parce que nous ne retardons pas artificiellement l'exécution de notre code dans $timeout, mais nous faisons la directive de ne pas l'exécuter tant que la requête asynchrone n'est pas terminée.

J'espère que cela fonctionnera aussi pour les demandes de longue attente...

Si quelqu'un connaît une meilleure façon de le réparer, veuillez le dire!

24
demandé sur John Slegers 2012-06-21 14:04:48

4 réponses

Le problème est que watch compare la référence au lieu de l'objet par défaut. Ajouter à la fin de l'avoir comparer la valeur à la place.

scope.$watch('seats', function(newval, oldval){
                console.log(newval, oldval);
                updateSeatInfo(scope,element);
            }, true);
57
répondu Bryan Mundie 2012-10-19 00:25:16

J'ai eu ce problème aussi. C'était dû au fait que ma variable n'était d'abord pas définie "undefined" dans la portée. Watch semble ne pas fonctionner sur des variables indéfinies. Semble évident après tout.

J'essayais d'abord d'utiliser watch pour déclencher quand ma variable serait effectivement définie par le contrôleur. Exemple:

myApp.controller('Tree', function($scope, Tree) {

Tree.get({},
function(data) { // SUCCESS
    console.log("call api Tree.get succeed");
    $scope.treeData = data;
},

function(data) { // FAILURE
    console.log("call api Tree.get failed");
    $scope.treeData = {};
});
});

Je l'ai résolu en initialisant ma variable avec un objet vide avant d'appeler le service:

myApp.controller('Tree', function($scope, Tree) {

$scope.treeData = {};      // HERE

Tree.get({},
function(data) { // SUCCESS
    console.log("call api Tree.get succeed");
    $scope.treeData = data;
},

function(data) { // FAILURE
    console.log("call api Tree.get failed");
    $scope.treeData = {};
});
});

Dans ce cas, watch a pu détecter le changement dans variable.

4
répondu t4ncr3d3 2012-12-29 13:47:45

Je ne vois pas que vous utilisez le SeatsCtrl-controller n'importe où? Comment est-il utilisé? Et avez-vous vérifié qu'il est activé, et que la requête est réellement effectuée?

Le moyen le plus rapide de vérifier si SeatsCtrl est utilisé est d'ajouter simplement un console.log('SeatsCtrl actived!'); à l'intérieur. Si ce n'est pas le cas, ajoutez ng-controller="SeatsCtrl" au div.

Vous pouvez également mettre une montre et un journal sur les sièges directement à l'intérieur du contrôleur juste pour vous assurer que ce n'est pas un problème avec la portée.

0
répondu Supr 2012-06-21 11:19:14

J'ai aussi ce problème. Je pense que c'est un problème dans Angulaire. Il y a un ticket sur GitHub pour "beef up $resource futures" qui résoudrait probablement ce problème, car ce dont vous avez vraiment besoin est l'accès à l'objet promise pour la ressource que vous avez.

Ou, les observateurs pourraient attendre de tirer jusqu'à ce que la promesse soit résolue.

En attendant, un moyen un peu plus élégant de résoudre ce problème qui ne nécessite pas de délai d'attente est de réaffecter la propriété scope à partir de laquelle elle est surveillée le rappel de succès sur la ressource$. Cela provoquera le feu de l'observateur à nouveau au moment approprié.

Un délai d'attente sans délai ne fait que mettre l'évaluation de la fonction différée à la fin de la pile actuelle - dans votre cas, votre ressource a été résolue d'ici là, mais cela pourrait être un problème si le serveur a un cas des lundis.

Exemple:

$scope.myAttr = MyResource.fetch(
  function (resource) { $scope.myAttr = new MyResource(angular.copy(resource)); }
);
0
répondu Joe Martinez 2012-08-31 18:06:38