AngularJS: $scope.$watch не обновляет значение, полученное из $resource в пользовательской директиве

у меня проблема с пользовательскими директивами, которые сводят меня с ума. Я пытаюсь создать следующую пользовательскую (атрибутивную) директиву:

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

эта директива "типа атрибута" (называемая seatMap) пытается показать список идентификаторов мест (e.g, для театра), который я получу с сервера через $resource service (см. код ниже) в div (элемент).

я использую его с этим простым частичным 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>

и это контроллер, который загрузка области:

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

где "места" - это простой сервис, использующий $resources для извлечения JSON с сервера

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

app.js (asientos_libres.html-это частичный, который я использовал):

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

проблема, хотя я создал "сферу.$watch "в функции link директивы, чтобы область могла проверить, изменился ли атрибут "seats" для обновления списка идентификаторов, он не работает в данный момент $scope.места меняются в контроллер (когда мы вызываем "запрос").

как вы можете видеть в коде, я попытался использовать $ timeout для задержки запуска "updateSeatInfo", но я боюсь, что это не самое умное решение...

Я также пытался не делать запрос JSON, но использовать жестко закодированный словарь в $scope.места и это работает, так что, похоже, это вопрос синхронизации.

Примечание: updateSeatInfo это просто тестовая функция, саму функцию я использую немного больше сложный.

есть идеи, как с этим справиться?

большое спасибо заранее!

Edit 1: добавлено приложение.js, где я использую маршрутизатор для вызова SeatsCtrl, спасибо Supr за совет. Тем не менее, у меня все та же проблема.

Edit 2: Решено!(?) ОК! Кажется, я нашел решение, которое может быть не лучшим, но оно работает правильно! :) Насколько я мог видеть, http://docs.angularjs.org/api/ng.$тайм-аут, мы можем использовать $тайм-аут (фантик за помощью setTimeout) С! Это здорово, потому что мы не искусственно задерживаем выполнение нашего кода внутри $timeout, но мы делаем директиву не запускать его, пока асинхронный запрос не закончится.

надеюсь, что он будет работать и для запросов длительного ожидания...

если кто-то знает лучший способ исправить это, пожалуйста, скажи!

4 ответов


проблема в том, что watch сравнивает ссылку вместо объекта по умолчанию. Добавьте, true до конца, чтобы вместо этого сравнить значение.

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

у меня тоже эта проблема. Это было связано с тем, что моя переменная не определена "неопределенным" в рамках. Часы, похоже, не работают с неопределенными переменными. В конце концов, это очевидно.

Я сначала пытался использовать watch для запуска, когда моя переменная будет эффективно установлена контроллером. Пример:

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

Я решил это, инициализировав мою переменную пустым объектом перед вызовом службы:

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

в этом случае смотреть было чтобы обнаружить изменение переменной.


Я не вижу, что вы используете SeatsCtrl-контроллера в любом месте? Как он используется? И вы проверили, что он активирован, и что запрос фактически выполнен?

самый быстрый способ проверить, используется ли SeatsCtrl-просто добавить console.log('SeatsCtrl actived!'); внутри него. Если нет, то добавьте ng-controller="SeatsCtrl" до div.

вы также можете поставить часы и журнал на сиденья непосредственно внутри контроллера, чтобы убедиться, что это не проблема с областью.


У меня тоже есть эта проблема. Я думаю, что это проблема в угловой. Есть билет на GitHub в "говядина вверх $ фьючерсы на ресурсы" что, вероятно, решит эту проблему, так как вам действительно нужен доступ к объекту promise для ресурса, который у вас есть.

или наблюдатели могут подождать, пока не будет выполнено обещание.

тем временем, немного более элегантный способ исправить это, который не требует тайм-аута, - переназначить свойство scope, которое отслеживается из обратного вызова успеха на ресурсе$. Это заставит наблюдателя выстрелить снова в соответствующее время.

тайм-аут без задержки просто помещает оценку отложенной функции в конец текущего стека - в вашем случае ваш ресурс был разрешен к тому времени, но это может быть проблемой, если сервер имеет дело с понедельниками.

пример:

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