Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue momentjs update relative time in real time

How I can do update relative time from momentjs in real time for user?

I have computed:

computed: {
  ago() {
     return moment().fromNow();
    },
}

And when I use in component this:

<span class="text-muted pr-2" v-text="ago"></span>

I get static text: a few seconds ago, how update this text without page reloading? I want see: a minute ago, a two minutes ago e.t.c..

How I can do this in real time for user?

like image 751
Dumitru Avatar asked Jan 20 '19 19:01

Dumitru


2 Answers

Since moment().fromNow() isn't reactive so you will not see any change,to deal with we fix an old time property which should be initialized in the created hook this.oldTime = new Date();, and set a time interval with 1s, based on the old time property we call moment(this.old).fromNow(); to update our property ago.

// ignore the following two lines, they just disable warnings in "Run code snippet"
Vue.config.devtools = false;
Vue.config.productionTip = false;

new Vue({
  el: '#app',

  data() {
    return {
      ago: '',
      oldTime: '',
    interval:null
    }
  },
 destroyed(){
   clearInterval(this.interval)
   },
  created() {
    this.oldTime = new Date();
    this.interval=setInterval(() => {
      this.ago = moment(this.oldTime).fromNow();
    }, 1000)
  }
});
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>

<div id="app" class="container">

  <span class="text-muted pr-2" >
  {{ago}}
  </span>
</div>

Based on the comment of @Badgy :

How would you handle it for a v-for where you show it in the UI via a function? I thought about attaching it to the message object on created and update all message objects every x seconds but not sure if its the best way

to fit to this situation we should create a time interval in which we update the ago property of each message :

// ignore the following two lines, they just disable warnings in "Run code snippet"
Vue.config.devtools = false;
Vue.config.productionTip = false;

new Vue({
  el: '#app',

  data() {
    return {

      messages: [{
          content: 'Hello !',
          time: '2019-09-10 00:08'
        },
        {
          content: 'Hello again!',
          time: '2019-09-10 00:10'
        }
      ],
  interval:null
    }
  },
  computed: {
    msgs() {
      return messages

    }
  },
       destroyed(){
   clearInterval(this.interval)
   },
  created() {

    this.interval=setInterval(() => {

      this.messages = this.messages.map(m => {
        m.ago = moment(m.time).fromNow();
        return m;
      })
    }, 1000)
  }
});
.primary{
color:blue
}
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>

<div id="app" class="container">

  <ul>
    <li v-for="m in messages">{{m.content}} <span class="primary">{{m.ago}}</span></li>
  </ul>
</div>
like image 75
Boussadjra Brahim Avatar answered Nov 06 '22 23:11

Boussadjra Brahim


I solve this using a global "ticker" in my Vuex store. It starts incrementing itself on load every 30 seconds. Descriptive messages of the time compare to this ticker instead of to other implementations of "now" or whatever. The advantage of this technique over something like every UI element iterating its own datetime ticker every second is that you don't have reactive elements in Vue all refreshing themselves on a regular basis all willy nilly. In my application, the one global "pseudo-now" ticker iterates every 30 seconds and all elements which provide descriptive accounts of the time all refresh at once in one frame. This benefits performance.

snippet from a relevant Vuex store:

import Vue from 'vue'

const TICKER_INTERVAL = 30000; // milliseconds

let tickerPointer = null;

const data = {
  state: {
    now: new Date,
  },

  mutations: {
    refreshTicker(state) {
      state.now = new Date;
    },
  },

  actions: {
    startTicker({commit}) {
      if (tickerPointer === null) {
        tickerPointer = setInterval(() => {
          commit('refreshTicker');
        }, TICKER_INTERVAL);
      }
    },

    haltTicker({state}) {
      clearInterval(tickerPointer);
      state.now = null;
      tickerPointer = null;
    },

"StartTicker" is dispatched once by the top of my application upon load.

like image 8
SirLamer Avatar answered Nov 06 '22 23:11

SirLamer