web-dev-qa-db-fra.com

AngularJS: $ scope. $ Watch ne met pas à jour la valeur extraite de $ resource selon la directive custom

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 "attribut-type" (appelée seatMap) tente d'afficher une liste d'identifiants de siège (par exemple, pour une salle) que je vais récupérer du serveur via le service $ resource (voir le code ci-dessous) dans un div (élément) .

Je l'utilise avec ce code HTML partiel simple:

<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 voici le contrôleur qui charge le 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;
}

Où "Seats" est un service simple utilisant $ resources pour extraire un JSON 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'utilise):

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 que, même si j’ai configuré un "scope. $ Watch" dans la fonction link de la directive afin que le scope puisse vérifier si l’attribut "seats" a changé pour mettre à jour la liste des identifiants, il ne fonctionne pas à la moment $ scope.seats est en train de changer dans le contrôleur (quand on appelle "query").

Comme vous le verrez peut-être dans le code, j'ai essayé d'utiliser $ timeout pour retarder le lancement de "updateSeatInfo", mais j'ai bien peur 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.seats et cela fonctionne. Il semble donc que ce soit une question de synchronisation.

Remarque: updateSeatInfo est juste une fonction de test, la fonction que je vais utiliser est un peu plus complexe.

Une idée sur la façon de faire face à cela?

Merci beaucoup d'avance!

Edit 1: Ajouté app.js, où j'utilise un routeur pour appeler SeatsCtrl, grâce à Supr pour le conseil. Cependant, j'ai 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 elle fonctionne correctement! :) Autant que je puisse voir ici http://docs.angularjs.org/api/ng.$timeout , nous pouvons utiliser $ timeout (un wrapper over setTimeout) sans délai! C’est formidable car nous ne retardons pas artificiellement l’exécution de notre code dans $ timeout, mais nous faisons en sorte que la directive ne l’exécute pas tant que la demande asynchrone n’est pas terminée.

J'espère que cela fonctionnera aussi pour les demandes qui attendent longtemps.

Si quelqu'un connaît un meilleur moyen de le réparer, dites-le-moi!

24
Sheniff

Le problème est que watch compare la référence à la place de l'objet par défaut. Ajouter, fidèle à la fin pour qu'il compare la valeur à la place.

scope.$watch('seats', function(newval, oldval){
                console.log(newval, oldval);
                updateSeatInfo(scope,element);
            }, true);
57
Bryan Mundie

J'ai eu ce problème également. Cela était dû au fait que ma variable n'était pas définie dans un premier temps dans le champ "indéfini". Watch semble ne pas fonctionner sur des variables non définies. Cela semble évident après tout.

J'essayais d'abord d'utiliser watch pour déclencher le moment où 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 la variable.

4
t4ncr3d3

J'ai aussi ce problème. Je pense que c'est un problème dans Angular. Sur GitHub, il existe un ticket pour "renforcer les ressources futures" qui permettrait probablement de résoudre ce problème, car ce dont vous avez vraiment besoin, c'est d'accéder à l'objet de promesse de la ressource que vous avez.

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

Dans l'intervalle, un moyen légèrement plus élégant de résoudre ce problème sans délai est de réaffecter la propriété de portée surveillée à partir du rappel de réussite sur la ressource $. Cela fera redémarrer l’observateur au moment opportun.

Une temporisation sans retard consiste simplement à mettre l'évaluation de la fonction différée à la fin de la pile en cours. Dans votre cas, votre ressource a dû être résolue à ce moment-là, mais cela pourrait poser problème si le serveur est confronté à une Les lundis.

Exemple:

$scope.myAttr = MyResource.fetch(
  function (resource) { $scope.myAttr = new MyResource(angular.copy(resource)); }
);
0
Joe Martinez

Je ne vois pas que vous utilisez le contrôleur SeatsCtrl- 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é consiste simplement à ajouter une console.log('SeatsCtrl actived!'); à l'intérieur. Si ce n'est pas le cas, ajoutez ng-controller="SeatsCtrl" à la div.

Vous pouvez également mettre en veille les sièges directement à l'intérieur du contrôleur pour vous assurer que le problème ne concerne pas la portée.

0
Supr