Обновление значений времени назад в 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>

Я надеюсь, что это работает для вас.

Другие вопросы по тегам