Angular.js - 向指令发出周期性事件
Angular.js - emit periodic event to directive
我有一个指令,它将日期和时间转换为更好的 - 人类阅读 - 形式,如 "now"、"one minute ago"、..
我想定期更新它,所以翻译成 "now" 的日期将在一分钟后更新。
解决此问题的一种方法是在指令中创建 $interval
,以便它在 60'000 毫秒后继续更新,但对于使用此指令的具有大量日期的页面来说,这可能是瓶颈..
第二个想法是制作服务,它将向 $rootScope
广播事件心跳,指令将绑定侦听器并更新其时间。但这也意味着我将每 N 秒通过广播事件遍历应用程序中的所有范围..
所以问题是.. 有没有什么办法,如何在 angular(更纯粹的解决方案更好)中使服务仅将事件触发到一个指令?
类似于:
$directive("timeAgo").$emit("hearthbeat", new Date().getTime())
您可以尝试使用过滤器而不是指令。据我所知,AngularJS 每个 $digest 圈都会更新过滤器。因此,您根本不需要使用计时器。但我不确定我是否理解过滤器如何正确工作。
至于服务,您可以尝试使用$rootScope.$emit
方法代替$rootScope.$broadcast
方法。文档说 $emit
向上发送事件,而 $broadcast
向下发送事件。因此,对于前一个事件,事件只能在 $rootScope
上访问,并且不会传播到其子节点。您可以在官方文档或例如 in this question.
中找到有关这些方法的更多信息
我认为,也可以使用常规回调代替事件。您可以尝试在服务中添加一个像 addTickEvenListener
这样的方法,并在每个计时器滴答时只调用已注册的回调。我不确定,但在指令销毁时删除事件侦听器可能会有点问题。可能这个问题可以通过听取范围的 $destroy
事件来解决,就像解释的那样 here.
我创建了一个plunker来解释一些粗略的想法:
http://plnkr.co/edit/d8vE0HCLbemgiPNuvl47?p=preview
It is not complete yet since elements are not moved to different queues after threshold is met.
而不是依赖 $rootScope
,您可以创建一个包罗万象的包装器指令,您将在所有要使用智能日期的范围内使用一次,就像这样...
app.directive('smartDateMaster', SmartDateMasterDirective);
function SmartDateMasterDirective() {
return {
restrict: 'AE',
scope: true,
controller: 'SmartDateController'
};
}
app.controller('SmartDateController', ['$interval', '$scope', SmartDateCntl]);
function SmartDateCntl($interval, $scope) {
// one interval is set, and the event is broadcast from this scope
$interval(function() {
$scope.$broadcast('sdPulse', new Date());
}, 10000); // 10 seconds, adjust to taste
}
...然后在任何您想显示这些智能的相对日期之一的地方,使用侦听事件并相应更新的指令:
app.directive('smartDate', SmartDateDirective);
function SmartDateDirective() {
return {
restrict: 'A',
template: '<span>{{ dateText }}</span>',
scope: {
srcDate: '=smartDate' /* a reference to a JS time object */
},
link: function($scope, elem, attrs) {
$scope.$on('sdPulse', function(evt, currentDate) {
var difference = currentDate.getTime() - $scope.srcDate.getTime();
// some logic to determine what the date text should be based on difference
// then set it in scope for display
$scope.dateText = 'whatever you decided';
});
}
};
}
HTML 中的实现看起来像这样,您使用 Smart Date Master 指令包装所有出现的 Smart Dates。在任何需要智能日期的地方,使用 smart-date
属性以及指向父范围中日期的可分配表达式。
<body>
<!-- some outer content unconcerned with dates -->
<smart-date-master>
<!-- some inner content with dates spread throughout -->
<div>
Jack commented on your status <span smart-date="jackCommentDate"></span>.
</div>
<div>
Jill poked you <span smart-date="jillPokeDate"></span>.
</div>
<div>
John sent you a message <span smart-date="johnMsgDate"></span>
</div>
</smart-date-master>
</body>
这应该仍然是一个高性能的解决方案,您不会做任何非标准或极低效率的事情。
我有一个指令,它将日期和时间转换为更好的 - 人类阅读 - 形式,如 "now"、"one minute ago"、..
我想定期更新它,所以翻译成 "now" 的日期将在一分钟后更新。
解决此问题的一种方法是在指令中创建 $interval
,以便它在 60'000 毫秒后继续更新,但对于使用此指令的具有大量日期的页面来说,这可能是瓶颈..
第二个想法是制作服务,它将向 $rootScope
广播事件心跳,指令将绑定侦听器并更新其时间。但这也意味着我将每 N 秒通过广播事件遍历应用程序中的所有范围..
所以问题是.. 有没有什么办法,如何在 angular(更纯粹的解决方案更好)中使服务仅将事件触发到一个指令?
类似于:
$directive("timeAgo").$emit("hearthbeat", new Date().getTime())
您可以尝试使用过滤器而不是指令。据我所知,AngularJS 每个 $digest 圈都会更新过滤器。因此,您根本不需要使用计时器。但我不确定我是否理解过滤器如何正确工作。
至于服务,您可以尝试使用$rootScope.$emit
方法代替$rootScope.$broadcast
方法。文档说 $emit
向上发送事件,而 $broadcast
向下发送事件。因此,对于前一个事件,事件只能在 $rootScope
上访问,并且不会传播到其子节点。您可以在官方文档或例如 in this question.
我认为,也可以使用常规回调代替事件。您可以尝试在服务中添加一个像 addTickEvenListener
这样的方法,并在每个计时器滴答时只调用已注册的回调。我不确定,但在指令销毁时删除事件侦听器可能会有点问题。可能这个问题可以通过听取范围的 $destroy
事件来解决,就像解释的那样 here.
我创建了一个plunker来解释一些粗略的想法: http://plnkr.co/edit/d8vE0HCLbemgiPNuvl47?p=preview
It is not complete yet since elements are not moved to different queues after threshold is met.
而不是依赖 $rootScope
,您可以创建一个包罗万象的包装器指令,您将在所有要使用智能日期的范围内使用一次,就像这样...
app.directive('smartDateMaster', SmartDateMasterDirective);
function SmartDateMasterDirective() {
return {
restrict: 'AE',
scope: true,
controller: 'SmartDateController'
};
}
app.controller('SmartDateController', ['$interval', '$scope', SmartDateCntl]);
function SmartDateCntl($interval, $scope) {
// one interval is set, and the event is broadcast from this scope
$interval(function() {
$scope.$broadcast('sdPulse', new Date());
}, 10000); // 10 seconds, adjust to taste
}
...然后在任何您想显示这些智能的相对日期之一的地方,使用侦听事件并相应更新的指令:
app.directive('smartDate', SmartDateDirective);
function SmartDateDirective() {
return {
restrict: 'A',
template: '<span>{{ dateText }}</span>',
scope: {
srcDate: '=smartDate' /* a reference to a JS time object */
},
link: function($scope, elem, attrs) {
$scope.$on('sdPulse', function(evt, currentDate) {
var difference = currentDate.getTime() - $scope.srcDate.getTime();
// some logic to determine what the date text should be based on difference
// then set it in scope for display
$scope.dateText = 'whatever you decided';
});
}
};
}
HTML 中的实现看起来像这样,您使用 Smart Date Master 指令包装所有出现的 Smart Dates。在任何需要智能日期的地方,使用 smart-date
属性以及指向父范围中日期的可分配表达式。
<body>
<!-- some outer content unconcerned with dates -->
<smart-date-master>
<!-- some inner content with dates spread throughout -->
<div>
Jack commented on your status <span smart-date="jackCommentDate"></span>.
</div>
<div>
Jill poked you <span smart-date="jillPokeDate"></span>.
</div>
<div>
John sent you a message <span smart-date="johnMsgDate"></span>
</div>
</smart-date-master>
</body>
这应该仍然是一个高性能的解决方案,您不会做任何非标准或极低效率的事情。