How to use dayjs relative time - momentjs

Hi guys I have this weird problem when using Dayjs relative time
Previously I use momentjs
var date = "2021-02-26 16:04:15";
moment(date).fromNow();
The relative time will always increase as expected
20 minutes ago, then increase to 21 minutes ago, then increase to 22 minutes ago
However when I use dayjs, the relative time is not increase. It works on page load, but never increase
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
var date = "2021-02-26 16:04:15";
dayjs(date).fromNow();
Output
20 minutes ago, and never increase. Just static
Is there any configuration that I miss or dayjs doesnt provide auto update on relative time?

After my research, I believe Day Js does not have auto increment relative time, as compare to MomentJS
So I just need to use setTimeout to auto emit event every 60 seconds contains the latest timestamp.
And all place that need relative time will be notify to update the state
Example in Vue JS
app.js
created() {
let self = this;
this.timer = setInterval(function () { self.$eventBus.$emit("updateTimeLabel", new Date()); }, 60000);
},
beforeDestroy() {
clearInterval(this.timer)
}
TimeLabel.vue
props: {
timestamp: {
type: String,
required: true,
},
},
data() {
return {
from_now: null,
};
},
created() {
this.from_now = this.$fromNow(this.timestamp);
// every one minute, app.js will emit event to update all TimeLabel.vue relative time
this.$eventBus.$on("updateTimeLabel", (datetime) => {
this.from_now = this.$fromNow(this.timestamp);
});
},
methods: {
$fromNow(timestamp) {
return dayjs.utc(timestamp.substring(0, 23)).local().fromNow();
},
}

Related

Polymer property not updating when set from Javascript

I have made this simple property (Polymer 2.x):
static get properties() {
return {
bpm: {
type: Number,
value: () => {
return 0
},
observer: "_bpm"
}
}
}
I tried to update it using this.bpm = 60; in a function called when clicking a button. If I output the value using console.log(this.bpm); it displays the correct value, but my heading <h2 id="bpm">[[bpm]]</h2> is not updated and the observer is not called.
When bpm is set using something like <paper-slider value="{{bpm}}"></paper-slider> it works.
What am I doing wrong? Thank you for your help!
It will be easier for the community to know that this question was answered into the comments of the requests.
Initial problem : Binding value not updated because bpm property was set from a function outside of the element.
Correction : Here a working JSFiddle (to use in chrome) used to demonstrate how to use the binding.
I also faced similar issue due to setting the property from a different function. Putting it here for reference.
My code:
Polymer({
is: 'test-test',
properties: {
min: {
type: Number,
value: -1,
observer: '_minChangedd'
}
},
_minChangedd: function (val) {
console.log(val);
},
ready: function () {
setInterval(function () {
this.min = this.min + 1;
}, 500);
},
});
Problem:
The setInterval function had its own this and so the expression this.min actually refers to min of setInterval.
Using arrow functions resolved the issue, by replacing the call with setInterval(() => {...});

METEOR - Show loader and run methods at specific time

I wanna build a loader circle what goes from 1 to 100% and in the meantime to run some methods.
loader circle
The scenario is:
load the page and start counting.
When the counter is at 50% pause counting and run the first method and when I have the result to start counting from where I was left.
count until 90% and run the second method.
I was trying something with Meteor.setInterval on onCreated but I'm not sure if it's the right method to do this.
Can someone give me some ideas about how to approach this?
Thanks!
There are several ways you can do this depending on your specific needs and you might even want to use one of the many Reactive Timer packages that are out there.
Here is one working example that only uses the Meteor API (no packages). Note, I did not actually incorporate the loader circle animation since it wasn't specifically part of the question.
Template definition
<template name="loader">
<h1>Loading...({{loadingPercentage}})</h1>
</template>
Template logic
Template.loader.onCreated(function() {
// used to update loader circle and triggering method invocation
this.elapsedTime = new ReactiveVar(0);
// handle for the timer
var timerHandle;
// starts a timer that runs every second that can be paused or stopped later
const startTimer = () => {
timerHandle = Meteor.setInterval(() => {
this.elapsedTime.set(this.elapsedTime.get() + 1);
}, 1000);
};
// pauses/stops the timer
const pauseTimer = () => {
Meteor.clearInterval(timerHandle);
};
// let's first start our timer
startTimer();
// runs every second and triggers methods as needed
this.autorun(() => {
const elapsedTime = this.elapsedTime.get();
// call methods based upon how much time has elapsed
if (elapsedTime === 5) {
pauseTimer();
// call method and restart the timer once it completes
Meteor.call('methodOne', function(error, result) {
// do stuff with the result
startTimer();
});
} else if (elapsedTime === 9) {
pauseTimer();
// call method and restart the timer once it completes
Meteor.call('methodOne', function(error, result) {
// do stuff with the result
// DO NOT RESTART TIMER!
});
}
});
});
Template.loader.helpers({
// helper used to show elapsed time on the page
loadingPercentage: function() {
return Template.instance().elapsedTime.get();
},
});
Let me know if you have any questions.
This is what i was trying to do:
Template.onboarding.onCreated(function(){
var instance = this;
instance.progress = new ReactiveVar(0);
instance.autorun(function(){
var loader = {
maxPercent: 100,
minPercent: instance.progress.get(),
start: function(){
var self = this;
this.interval = Meteor.setInterval(function(){
self.minPercent += 1;
if(self.minPercent >= self.maxPercent) {
loader.pause();
}
if( self.minPercent == 25) {
loader.pause();
Meteor.call('runMethodOne', (err,res)=>{
if (!err) loader.resume();
});
}
if(self.minPercent == 75) {
loader.pause();
Meteor.call('runMethodTwo', (err,res) =>{
if(res) loader.resume();
});
}
}
});
}
instance.progress.set(self.minPercent)
},50);
},
pause: function(){
Meteor.clearInterval(this.interval);
delete this.interval;
},
resume: function(){
if(!this.interval) this.start();
}
};
loader.start();
}
});
});

Material Design Lite - Programatically Open and Close Toast

I would like to open and close MDL toast rather than use the timeout property as indicated in the MDL usage guide. The reason is that I want the toast to remain while geolocation is occuring, which sometimes takes 10+ seconds and other times happens in 1 second.
Any idea how this could be done?
A q&d solution i found, invoke cleanup_ method on the sb object.
With this solution i can show the sb, click action handler to hide it, then re trigger the action to show it without any problem.
var snackbar = form.querySelector("[class*='snackbar']");
if (snackbar) {
var data = {
message: 'Wrong username or password',
timeout: 20000,
actionHandler: function(ev){
// snackbar.classList.remove("mdl-snackbar--active")
snackbar.MaterialSnackbar.cleanup_()
},
actionText: 'Ok'
};
snackbar.MaterialSnackbar.showSnackbar(data);
}
As cleanup_ is not part of the public api, i guess it worth to enclose this with some small checks to avoid a disaster.
snackbar.MaterialSnackbar.cleanup_
&& snackbar.MaterialSnackbar.cleanup_()
!snackbar.MaterialSnackbar.cleanup_
&& snackbar.classList.remove("mdl-snackbar--active")
Got it working as so: I basically set a 30 second timeout on the toast assuming my geolocation and georesults (GeoFire) will take no more than 30 seconds.
I get the length of the returned array of map markers and multiply that by the javascript timeout events. I finally remove mdl-snackbar--active which hides the toast. So, basically - it works.
UPDATED
The above actually had a major problem in that additional toasts would not display until that long timeout completed. I could not figure out how to apply the clearTimeout() method to fix it so I found a solution that works - trigger the toast up and down by just toggling the mdl-snackbar--active class - no timer setting necessary.
So to call toast as normal using this code, simply tools.toast('hello world',error,3000). To programatically open and close toast call tools.toastUp('hey') and tools.toastDown(), respectively. So, you might call tools.toastDown after a promise resolves or something...
var config = (function() {
return {
timeout: 50, //in milliseconds
radius: 96, //in kilometers
};
})();
var tools = (function() {
return {
toast: function(msg,obj,timeout){
var snackbarContainer = document.querySelector('#toast'); //toast div
if(!obj){obj = ''}
if(!timeout){timeout = 2750}
data = {
message: msg + obj,
timeout: timeout
};
snackbarContainer.MaterialSnackbar.showSnackbar(data);
},
toastUp: function(msg){
var toast = document.querySelector('#toast');
var snackbarText = document.querySelector('.mdl-snackbar__text');
snackbarText.innerHTML = msg;
toast.classList.add("mdl-snackbar--active");
},
toastDown: function(count) {
setTimeout(function () {
var toast = document.getElementById("toast");
toast.classList.remove("mdl-snackbar--active");
}, config.timeout * count);
},
};
})();
In case you want to fire tools.toastDown after a timeout loop, you can do:
function drop(filteredMeetings) {
tools.clearMarkers(true);
for (var i = 0; i < filteredMeetings.length; i++) {
//drop toast once markers all dropped
if(i === filteredMeetings.length - 1) {
tools.toastDown(i);
}
tools.addMarkerWithTimeout(filteredMeetings[i], i * config.timeout);
}
}

data binding in react + signalR

I have a strategy question.
I want to change data in my website using signalR and display changed data using react. My question would be: How to perform data binding between signalR and react?
My first clue is the following:
signalR:
chat.client.addMessage = function (name, message) {
chatHistory.push({ Author: name, Text: message }); //here I change global variable chatHistory
};
react:
var CommentList = React.createClass({some class here});
var CommentBox = React.createClass({
componentRefresh: function () {
this.setState({ data: chatHistory });
},
getInitialState: function () {
return { data: chatHistory };
},
componentDidMount: function () {
this.componentRefresh();
setInterval(this.componentRefresh, this.props.interval);
},
render: function () {
return (
React.DOM.div(null,
CommentList({ data: this.state.data })
)
);
}
});
React.renderComponent(
CommentBox({ interval: 2000 }),
document.getElementById('content')
);
in react commentBox component I feed global chatHistory and ask for a new value every 2 seconds.
Is there more elegant way of doing it?
and how to avoid redrawing of CommentBox if chatHistory variable wasn't changed?
Your approach of maintaining state in CommentBox is fine. As your component base grows, it might become complicated to maintain self-updating components though. I recommend investigating the Flux architecture the React team designed and their Todo MVC Flux example in particular.
You could implement shouldComponentUpdate to prevent React from re-rendering the CommentBox if you know state hasn't changed. Also, you should keep a reference to the interval so you can clear it when the CommentBox is unmounted otherwise it will go on polling after the component is removed.
var CommentBox = React.createClass({
...
componentDidMount: function() {
this.componentRefresh();
this._interval = setInterval(this.componentRefresh, this.props.interval);
},
componentWillUnmount: function() {
clearInterval(this._interval);
this._interval = null;
},
shouldComponentUpdate: function(nextProps, nextState) {
// Do a deep comparison of `chatHistory`. For example, use
// Underscore's `isEqual` function.
return !_.isEqual(this.state.chatHistory, nextState.chatHistory);
},
...
});

Is it possible to display a UI transition to reflect changes in a collection with meteorjs?

I would like to display a pulse transition when my collection change.
In my html file, I have that:
<template name="menuItemTag">
<div class="app-menu-item-tag ui label">{{ count }}</div>
</template>
In my js file, I expose the count variable for my template like that:
Template.menuItemTag.count = function() {
return MyCollection.find().count();
};
With that the count change in the ui when the collection is updated.
Now, I would like to display a pulse transition on my div label each time the count value change.
I tried to use the cursor.observe
Template.menuItemTag.rendered = function() {
MyCollection.find().observe({
added: function (id, user) {
$('.app-menu-item-tag:nth(0)').transition('pulse');
},
removed: function () {
$('.app-menu-item-tag:nth(0)').transition('pulse');
}
});
};
Unfortunately, it is call too many times when the template is rendered the first time.
If initialy I have 40 items in my collection, the transition is played 40 times...
Is there a clean way for playing a ui transition on changes and avoid the collection initialisation?
Try this:
Template.menuItemTag.count = function() {
return Session.get('count');
};
Template.menuItemTag.rendered = function() {
this.countComputation = Deps.autorun(function() {
Session.set('count', MyCollection.find().count());
$('.app-menu-item-tag:nth(0)').transition('pulse');
});
};
Template.menuItemTag.destroyed = function() {
this.countComputation.stop();
};
Thanks sbking for your answer, I still have a problem on initialization of the collection.
I propose below to defer the first animation util the collection will be completely filled:
Template.menuItemTag.count = function() {
return Session.get('count');
};
Template.menuItemTag.rendered = function() {
var that = this;
this.countComputation = Deps.autorun(function() {
Session.set('count', MyCollection.find().count());
// Cancel playing UI transition. The collection is not completely initialized
if (that.handleTimeout) {
Meteor.clearTimeout(that.handleTimeout);
}
// Play the UI transition without the timeout if the collection is initialized
if (that.noNeedTimeoutAnymore) {
$('.app-menu-item-tag:nth(0)').transition('pulse');
}
// Tentative to play the UI transition during the collection feeding
else {
that.handleTimeout = Meteor.setTimeout(function() {
$('.app-menu-item-tag:nth(0)').transition('pulse');
// At this point we know that the collection is totaly initialized
// then we can remove the timeout on animation for the future update
that.noNeedTimeoutAnymore = true;
}, 1500); // You can adjust the delay if needed
}
});
};
Template.menuItemTag.destroyed = function() {
this.countComputation.stop();
};

Resources