Обновление значений времени назад в Angularjs и Momentjs
Оригинал: у меня есть таблица, сгенерированная с помощью ng-repeat с сотнями записей, состоящих из нескольких разных временных отметок Unix. Я использую moment.js, чтобы они отображались как "19 минут назад" или как давно это было. Как бы я обновлял их каждые пять минут, например, без необходимости обновлять всю таблицу (это занимает несколько секунд и прерывает сортировку и выбор пользователя).
5 ответов
Я использую следующий фильтр. Фильтруйте значения обновлений каждые 60 секунд.
angular
.module('myApp')
.filter('timeAgo', ['$interval', function ($interval){
// trigger digest every 60 seconds
$interval(function (){}, 60000);
function fromNowFilter(time){
return moment(time).fromNow();
}
fromNowFilter.$stateful = true;
return fromNowFilter;
}]);
И в HTML
<span>{{ myObject.created | timeAgo }}</span>
Я считаю, что фильтры оцениваются в каждом цикле дайджеста, поэтому использование фильтра для отображения строк "время назад" может стать дорогостоящим с сотнями записей.
Я решил использовать архитектуру pubsub, используя, по сути , предложенный подход Эберли (главным образом потому, что мне не нужно следить за $destroy
события и отписаться вручную), но с NotificationService, а не с функцией "Канал":
.factory('NotificationService', ['$rootScope',
function($rootScope) {
// events:
var TIME_AGO_TICK = "e:timeAgo";
var timeAgoTick = function() {
$rootScope.$broadcast(TIME_AGO_TICK);
}
// every minute, publish/$broadcast a TIME_AGO_TICK event
setInterval(function() {
timeAgoTick();
$rootScope.$apply();
}, 1000 * 60);
return {
// publish
timeAgoTick: timeAgoTick,
// subscribe
onTimeAgo: function($scope, handler) {
$scope.$on(TIME_AGO_TICK, function() {
handler();
});
}
};
}])
time-ago
директива регистрирует / подписывает временную метку (post.dt
в примере HTML ниже) с NotificationService:
<span time-ago="post.dt" class="time-ago"></span>
.directive('timeAgo', ['NotificationService',
function(NotificationService) {
return {
template: '<span>{{timeAgo}}</span>',
replace: true,
link: function(scope, element, attrs) {
var updateTime = function() {
scope.timeAgo = moment(scope.$eval(attrs.timeAgo)).fromNow();
}
NotificationService.onTimeAgo(scope, updateTime); // subscribe
updateTime();
}
}
}])
Несколько комментариев:
- Я пытался быть эффективным и не иметь директивы создать новую область. Хотя директива добавляет
timeAgo
свойство в области, в моем использовании, это нормально, так как я, кажется, только используюtime-ago
директива внутри ng-repeat, поэтому я просто добавляю это свойство к дочерним областям, созданным ng-repeat. {{timeAgo}}
будет проверяться каждый цикл дайджеста, но это должно быть быстрее, чем запускать фильтр- Мне также нравится этот подход, потому что у меня нет таймера, работающего на каждой отметке времени. У меня есть один таймер, работающий в NotificationService.
- функция
timeAgoTick()
может быть сделано частным, поскольку, вероятно, только NotificationService потребуется для публикации события времени назад.
Используйте угловые $timeout
обслуживание (просто обертка вокруг setTimeout()
) обновить ваши данные. Обратите внимание на третий параметр, который указывает, что Angular должен запустить $digest
цикл, который обновит ваши привязки данных.
Вот пример: http://jsfiddle.net/jandersen/vfpDR/
(Этот пример обновляется каждую секунду, поэтому вам не нужно ждать 5 минут, чтобы увидеть его;-)
Я закончил тем, что $scope.apply() вызывался каждые пять минут с помощью setInterval, который повторно применяет фильтры, форматирующие временные метки в "x минут назад". Может быть, немного хакерский, но это работает. Я уверен, что это не оптимальное решение.
Я сделал обертку для моментов https://github.com/jcamelis/angular-moment
<p moment-interval="5000">{{"2014-05-21T14:25:00Z" | momentFromNow}}</p>
Я надеюсь, что это работает для вас.