Я подозреваю, что ответ «вы не можете» - но, возможно, вы можете показать мне лучший подход.Как я могу наблюдать за изменениями свойств Угловой службы?
Рассмотрите эту очень простую игру. Вы нажимаете на кнопку, и она отслеживает, сколько раз вы нажимали. В конце концов, однако, это будет намного сложнее - поэтому у меня есть услуга Player
, которая содержит значение timesClicked
.
angular.module('ClickGame', [])
angular.module('ClickGame').factory('Player', function() {
var svc = {};
svc.timesClicked = 0;
return svc;
});
angular.module('ClickGame').controller('MainController', [ '$scope', 'Player', function($scope, Player) {
// In practice, I wouldn't necessarily expose Player directly on the
// scope like this, but it makes for simpler demo code.
$scope.Player = Player;
$scope.click = function() {
Player.timesClicked++;
};
} ]);
HTML, довольно очевидны:
<html ng-app="ClickGame">
<body ng-controller="MainController">
<p><button type="button" ng-click="click()">Click me!</button></p>
<p>You have clicked {{ Player.timesClicked }} times.</p>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="demo.js"></script>
</body>
</html>
До сих пор, так хорошо. Здесь я сталкиваюсь с проблемами: я хотел бы добавить систему достижений, которая будет награждать значки игроку, когда они достигнут определенных целей (например, нажав кнопку пять раз). Это кажется, что она принадлежит в своем компоненте, поэтому я создаю вторую услугу под названием Achievements
, который впрыскивает Player
:
app.factory('Achievements', [ 'Player', function(Player) {
// I can't do this, because neither Achievements nor Player have a scope.
$scope.$watch('Player.timesClicked', function(newValue, oldValue) {
if (newValue >= 5) {
console.log('ACHIEVEMENT UNLOCKED: Clicked 5 Times');
// TODO: unwatch this model, to conserve resources and prevent
// the achievement from being awarded more than once
}
});
} ]);
Посмотреть проблему?
подходы Я рассмотрел и отклонил:
Использование
$interval
опрашиватьPlayer.timesClicked
периодически. Это, очевидно, ужасно по целому ряду причин.Сделать
Achievements
директивой вместо службы, поскольку директивы имеют объем. Тем не менее, это потребовало бы, чтобы я поместил тег<achievement>
в свой index.html, как своего рода метод боковой двери для создания экземпляра этой области и сохранения ее в течение всего жизненного цикла приложения. Я рассматривал этот подход несколько раз, но это похоже на неправильное использование директив. Там должен быть более чистый путь.У
Player
$broadcast
события на корневой сферы, когдаtimesClicked
увеличивается на единицу, и слушать это событие вAchievements
. Мне это не нравится. По мере того, как игра будет усложняться, в итоге получится много шума передачи. Во всяком случае, это старое «просто сделать это глобальное» решение - эффективное, но обычно это не очень хорошая идея в конечном итоге :)В Достижениях создайте новую специальную область с помощью
$rootScope.$new()
, прикрепите к ней игрока, а затем$watch
это. (Или я мог бы создать новую область вPlayer
, а затем выставить эту область как свойствоsvc
.) Честно говоря, я не уверен, что это даже сработает - но когда я нахожу себя таким, как это, я подозреваю, что мне нужно возвращать назад и реорганизовывать что-то.
Есть ли другой подход, который я рассматриваю? Есть ли способ рефакторинга моего приложения, так что timesClicked
хранится в месте, которое проще $watch
(но все же легко делиться между различными частями моего приложения)?
Извините, если мне не хватает чего-то сверх очевидного; Я смотрел на угловом коде весь день, и я, вероятно, нужен перерыв :)
Update: Следует отметить, что регистрируя $watch
изнутри MainController
не будет работать.Хотя я сохранил этот демонстрационный код простым, фактическое приложение является SPA, использующим ngRoute, поэтому MainController будет выгружен, если игрок перейдет на другой маршрут. Но достижения следует все же контролировать и присуждать.
завод и обслуживание не поддерживает $ scope –
@SheraliTurdiyev, но он поддерживает $ rootScope. Как бы то ни было, использование $ watch сильно зависит от производительности, особенно когда у вас их много. Я думаю, что он ищет шаблон наблюдателя, см .: http://stackoverflow.com/questions/12576798/how-to-watch-service-variables/17558885#17558885](http://stackoverflow.com/questions/12576798/how-to-watch-service-variables/17558885 # 17558885) – iH8
Похоже, что первым аргументом $ watch также может быть функция (docs здесь, хотя я не могу ссылаться на определенную часть, поэтому ctrl + f для «смотреть» и посмотреть таблицу параметров: https://docs.angularjs.org/api/ng/type/$rootScope.Scope). вы пытались использовать $ rootScope в Достижении и смотрели 'function() {return Player.timesClicked}' вместо «Player.links»? – martiansnoop