Ответ 1
Когда у вас есть событие click, область $update обновляется, без события вам нужно будет использовать $apply
$scope.$apply(function () {
$scope.current = track;
});
AngularJS noob здесь, на моем пути к Angular Просветлению:)
Здесь ситуация:
Я реализовал сервис "AudioPlayer" внутри моего модуля "приложение" и зарегистрировался так:
app.service('AudioPlayer', function($rootScope) {
// ...
this.next = function () {
// loads the next track in the playlist
this.loadTrack(playlist[++playIndex]);
};
this.loadTrack = function(track) {
// ... loads the track and plays it
// broadcast 'trackLoaded' event when done
$rootScope.$broadcast('trackLoaded', track);
};
}
и здесь контроллер приемника (в основном для логики интерфейса/представления)
app.controller('PlayerCtrl', function PlayerCtrl($scope, AudioPlayer) {
// AudioPlayer broadcasts the event when the track is loaded
$scope.$on('trackLoaded', function(event, track) {
// assign the loaded track as the 'current'
$scope.current = track;
});
$scope.next = function() {
AudioPlayer.next();
};
}
в моих представлениях Показываю текущую информацию о треке, например:
<div ng-controller="PlayerCtrl">
<button ng-click="next()"></button>
// ...
<p id="info">{{current.title}} by {{current.author}}</p>
</div>
метод next() определен в PlayerCtrl, и он просто вызывает тот же метод в службе AudioPlayer.
Проблема
Это прекрасно работает при ручном взаимодействии (т.е. когда я нажимаю на кнопку next()) - поток следующий:
Однако, когда метод next() вызывается из AudioService в фоновом режиме (т.е. когда дорожка завершена), все шаги от 1 до 5 выполняются, но представление не получает уведомления изменения в текущем объекте PlayerCtrl.
Я ясно вижу, что новый объект дорожки назначается в PlayerCtrl, но он как будто представление не получает уведомления об изменении. Я noob, и я не уверен, что это поможет, но я попытался добавить выражение $watch в PlayerCtrl
$scope.$watch('current', function(newVal, oldVal) {
console.log('Current changed');
})
который печатается только во время "ручных" взаимодействий...
Опять же, как я уже сказал, если я добавлю console.log(текущий) в $on listener так:
$scope.$on('trackLoaded', function(event, track) {
$scope.current = track;
console.log($scope.current);
});
это печатает правильно все время.
Что я делаю неправильно?
(ps Я использую AudioJS для аудиоплеера HTML5, но я не думаю, что это тот, кто виноват здесь...)
Когда у вас есть событие click, область $update обновляется, без события вам нужно будет использовать $apply
$scope.$apply(function () {
$scope.current = track;
});
Как не заглядывать в внутренности дайджеста, самый простой способ - использовать $timeout
:
$timeout(function () {
$scope.current = track;
}, 0);
Обратный вызов выполняется всегда в хорошей среде.
EDIT: Фактически, функция, которая должна быть завернута в фазу применения,
this.loadTrack = function(track) {
// ... loads the track and plays it
// broadcast 'trackLoaded' event when done
$timeout(function() { $rootScope.$broadcast('trackLoaded', track); });
};
В противном случае трансляция будет пропущена.
~~~~~~
На самом деле альтернатива может быть лучше (по крайней мере, с семантической точки зрения), и она будет работать одинаково внутри или вне цикла дайджеста:
$scope.$evalAsync(function (scope) {
scope.current = track;
});
$scope.$apply
: вам не нужно знать, находитесь ли вы в цикле дайджеста.$timeout
: вам не нужен тайм-аут, и вы получаете более простой синтаксис без дополнительного параметра 0
.// apply changes
$scope.current = track;
try {
if (!$scope.$$phase) {
$scope.$apply($scope.current);
}
} catch (err) {
console.log(err);
}
Пробовал все, он работал у меня с $rootScope.$applyAsync(function() {});