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