gpt4 book ai didi

javascript - 我如何$watch 更改 Angular 服务的属性?

转载 作者:行者123 更新时间:2023-12-03 08:58:30 25 4
gpt4 key购买 nike

我怀疑答案是“你不能” - 但也许你可以向我展示更好的方法。

考虑这个非常简单的游戏。您单击一个按钮,它会记录您单击的次数。但最终,它会变得更加复杂 - 所以我有一个 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 的服务。 ,注入(inject) 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() 创建一个新的临时范围。 ,将 Player 附加到它,然后 $watch那。 (或者我可以在 Player 中创建新范围,然后将该范围公开为 svc 的属性。)老实说,我不确定这是否有效 - 但当我发现自己在做这样的事情时,我怀疑我需要回溯并重构一些东西。

还有另一种我忽略的方法吗?有没有办法重构我的应用程序,以便 timesClicked存储在更容易的地方$watch (但仍然可以轻松地在应用程序的不同部分之间共享)?

抱歉,如果我遗漏了一些非常明显的东西;我一整天都在盯着 Angular 代码,我可能需要休息一下:)

更新:我应该提到注册 $watch从内部MainController行不通的。虽然我让这个演示代码保持简单,但实际应用程序是使用 ngRoute 的 SPA - 因此如果玩家导航到不同的路线,MainController 将被卸载。但成就仍应受到监督和奖励。

最佳答案

一个简单而有效的方法是使用 pub/sub 方法,但使用回调而不是事件。这是我要做的:

<html ng-app="ClickGame">
<body ng-controller="MainController">
<p><button type="button" ng-click="click()">Click me!</button></p>
<p>You have clicked {{ 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>

angular.module( 'ClickGame', [] )
.factory( 'Player', function() {
var timesClicked = 0, timesClickedChangedCallbacks = [];


return {
getTimesClicked: function() { return timesClicked; },
setTimesClicked: function(val) {
timesClicked = val;
timesClickedChangedCallbacks.forEach(function(cb) { cb(val); });
},
onTimesClickedChanged: function(cb) {
if (cb && typeof cb == 'function') {
timesClickedChangedCallbacks.push(cb);
}
}
};
})
.factory( 'Achievements', [ 'Player', function( Player ) {
Player.onTimesClickedChanged(function(val) {
if ( val == 5 ) {
console.log( 'ACHIEVEMENT UNLOCKED: Clicked 5 Times' );
}
});
} ] )
.controller( 'MainController', [ '$scope', 'Player', function( $scope, Player ) {
$scope.timesClicked = Player.getTimesClicked();

$scope.click = function() {
$scope.timesClicked++;
Player.setTimesClicked($scope.timesClicked);
};
}]);

您可能还想添加一种方法来取消订阅 Player 服务中的 timesClicked 更改。

关于javascript - 我如何$watch 更改 Angular 服务的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32388985/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com