2012-06-21 12 views
24

Tengo un problema con las directivas personalizadas que me está volviendo loco. estoy tratando de crear el siguiente encargo (atributo) Directiva:

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

Esta directiva "de tipo atributo" (llamado Mapa de asientos) está tratando de mostrar una lista de identificadores de seguridad (por ejemplo, para un teatro), que Lo buscaré desde el servidor a través del servicio $ resource (ver el código a continuación) en un div (elemento).

lo estoy usando con esta simple html parcial:

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

y este es el controlador que está cargando al alcance:

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

Donde "Asientos" es un servicio sencillo usando $ recursos en busca de un JSON desde el servidor

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

app.js (asientos_libres.html parcial es el que he b een el uso):

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

El problema es, a pesar de que he creado un "alcance $ reloj." en la función de enlace de la Directiva por lo que el alcance puede comprobar si "asientos" atributo ha cambiado para actualizar el lista de identificadores, no está funcionando en este momento $ scope.seats está cambiando en el controlador (cuando llamamos "consulta").

Como se puede ver en el código, he hecho un intento usando $ tiempo de espera para retrasar el lanzamiento de "updateSeatInfo", pero me temo que no es la solución más inteligente, con mucho, ...

También probé para no hacer una solicitud JSON, pero use un diccionario codificado en $ scope.seats y funciona, por lo que parece que es una cuestión de sincronía.

Nota: UpdateSeatInfo es solo una función de prueba, la función real que usaré es un poco más compleja.

¿Alguna idea sobre cómo lidiar con eso?

¡Muchas gracias de antemano!

Edit 1: App.js agregada, donde estoy usando enrutador para llamar a SeatsCtrl, gracias a Supr por el consejo. Sin embargo, sigo teniendo el mismo problema.

Editar 2: Resuelto! (?) Ok! Parece que encontré una solución, que puede no ser la mejor, ¡pero funciona correctamente! :) Por lo que pude ver aquí http://docs.angularjs.org/api/ng.$timeout, podemos usar $ timeout (un contenedor sobre setTimeout) sin demora! Esto es genial porque no estamos retrasando artificialmente la ejecución de nuestro código dentro de $ timeout, pero estamos haciendo que la directiva no lo ejecute hasta que la solicitud asíncrona haya finalizado.

esperanza de que va a trabajar para las solicitudes largo de espera, también ...

Si alguien sabe una mejor manera de solucionarlo, por favor diga!

Respuesta

57

La cuestión es que reloj compara la referencia en lugar del objeto de forma predeterminada. Agregue, fiel al final, para que compare el valor en su lugar.

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

Una palabra de advertencia: las comparaciones profundas como esta tienen un costo de rendimiento. En su mayor parte es un problema cuando se observa una colección de objetos en lugar de un solo objeto, pero vale la pena tenerlo en cuenta. –

0

¿No puedo ver que está utilizando el controlador SeatsCtrl en cualquier lugar?¿Cómo se usa? ¿Y ha verificado que está activado y que la consulta se realiza realmente?

La forma más rápida de comprobar si SeatsCtrl está en uso es simplemente agregar un console.log('SeatsCtrl actived!'); dentro de él. Si no es así, agregue ng-controller="SeatsCtrl" al div.

También puede poner un reloj y registro en los asientos directamente dentro del controlador solo para asegurarse de que no sea un problema con el alcance.

+0

Bueno, realmente uso SeatsCtrl a través del enrutador en app.js, siento no contarlo antes. He editado la publicación para que quede más clara. – Sheniff

+0

Ya veo. Sin embargo, ¿verificó que el controlador se ejecuta y que la consulta está devolviendo los asientos como se esperaba? Como dije, agregar un reloj dentro de SeatsCtrl al menos revelará si la consulta se ejecuta como debería o no, en cuyo caso probablemente sea un problema con el alcance. – Supr

+0

verificado. La consulta se está ejecutando ** pero ** solo si uso un tiempo de espera excedido. Es el mismo problema que está pasando en la directiva. Parece que es completamente una cuestión de sincronía para obtener datos a través de Ajax (a través de $ respuesta) ... El alcance está bien, por cierto. – Sheniff

0

Tengo este problema también. Creo que es un problema en Angular. Hay un boleto en GitHub al "beef up $resource futures" que probablemente abordaría este problema, ya que lo que realmente necesita es acceso al objeto de promesa para el recurso que tiene.

O bien, los observadores podrían esperar a disparar hasta que se resuelva la promesa.

Mientras tanto, una forma un poco más elegante de arreglar esto que no requiere un tiempo de espera es reasignar la propiedad del alcance que se está viendo desde la devolución de llamada correcta en $ recurso. Esto hará que el observador dispare de nuevo en el momento apropiado.

Un tiempo de espera sin demora es solo poner la evaluación de la función diferida al final de la pila actual; en su caso, su recurso se resolvió para entonces, pero esto podría ser un problema si el servidor está teniendo un caso de los lunes.

Ejemplo:

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

Tuve este problema también. Fue debido al hecho de que mi variable al principio no estaba definida como "indefinida" en el alcance. Watch parece no funcionar en variables indefinidas. Parece obvio después de todo.

Estaba tratando por primera vez de usar el reloj para activar cuando mi variable sería configurada efectivamente por el controlador. Ejemplo:

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

Lo resuelto por la inicialización de mi variable, con un objeto vacío antes de llamar al servicio:

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

En ese caso, el reloj fue capaz de detectar el cambio en la variable.

Cuestiones relacionadas