2013-08-01 8 views
11

Originale: Ho una tabella generata con ng-repeat con centinaia di voci costituite da diversi timestamp unix diversi. Sto usando moment.js per renderli visualizzati come "19 minuti fa" o comunque molto tempo fa. Come potrei avere questi aggiornamenti ogni cinque minuti, ad esempio, senza dover aggiornare l'intera tabella (che richiede alcuni secondi e interromperà l'ordinamento e le selezioni dell'utente).Aggiornamento dei valori "tempo fa" in Angularjs e Momentjs

risposta

4

Utilizzare il servizio angolare $timeout (solo un wrapper attorno a setTimeout()) per aggiornare i dati. Notare il terzo parametro che indica che Angular deve eseguire un ciclo $digest che aggiornerà i collegamenti dei dati.

Ecco un esempio: http://jsfiddle.net/jandersen/vfpDR/
(questo esempio viene aggiornato ogni secondo in modo da non dover aspettare 5 minuti per vederlo ;-)

+1

vostro collegamento a [ '$ interval'] (https://docs.angularjs.org/api/ng/service/$interval) è corretto, ma il link il testo dice "$ timeout" – jandersen

+0

Nota che angular fornisce anche [$ interval] (https://docs.angularjs.org/api/ng/service/$interval) per le funzioni di chiamata ripetuta. –

0

Ho finito per dover $ scope.apply() essere chiamato ogni cinque minuti di setInterval che riapplica i filtri formattando i timestamp in "x minuti fa". Forse un po 'hacky ma funziona. Sono certo che non è la soluzione ottimale.

13

Credo che filters are evaluated every digest cycle, quindi utilizzando un filtro per visualizzare le stringhe "time fa" potrebbe ottenere CPU costose con centinaia di voci.

ho deciso di andare con un'architettura PubSub, utilizzando essenzialmente eburley's suggerito approccio (soprattutto perché non c'è bisogno di guardare per $destroy eventi e manualmente cancellare l'iscrizione), ma con un NotificationService piuttosto che una funzione "Canale":

.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(); 
      }); 
     } 
    }; 
}]) 

a time-ago registri direttiva/sottoscrive un timestamp (post.dt nell'esempio HTML sottostante) con la 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(); 
     } 
    } 
}]) 

Alcuni commenti:

  • ho cercato di essere efficienti e non hanno la direttiva crea un nuovo ambito. Sebbene la direttiva aggiunga una proprietà timeAgo all'ambito, nel mio utilizzo, questo è ok, dal momento che mi sembra di usare solo la direttiva time-ago all'interno di una ng-repeat, quindi sto solo aggiungendo quella proprietà agli ambiti figlio creati da ng- ripetere.
  • {{timeAgo}} verrà esaminato ogni ciclo di digest, ma questo dovrebbe essere più veloce di un filtro
  • Mi piace anche questo approccio perché non ho un timer in esecuzione su ogni timestamp. Ho un timer in esecuzione nel NotificationService.
  • La funzione timeAgoTick() può essere resa privata, poiché probabilmente solo NotificationService dovrà pubblicare l'evento tempo fa.
+0

Passerò un po 'di tempo a metterlo nella mia implementazione. La mia attuale implementazione non è molto intensa per la CPU rispetto alla mia osservazione, e penso che ci sia un solo timer per l'intera pagina dal momento che quel timer chiama una domanda, ma sono certo che sarebbe meglio. Grazie. –

+0

@CorbinLewis, mi spiace di non essere stato chiaro sui timer multipli ... Stavo pensando a un'implementazione alternativa in cui qualcuno potrebbe usare $ timeout in una direttiva - ciò comporterebbe un timer separato per ogni timestamp. Sono d'accordo che la tua implementazione ha solo un timer. –

14

Io uso il seguente filtro. Il filtro aggiorna il valore ogni 60 secondi.

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; 
    }]); 

E in html

<span>{{ myObject.created | timeAgo }}</span> 
+0

questo è così bello !!! –

+0

questo è geniale !!! –

Problemi correlati