I am writing a (very) simple datepicker control in vue.js, using moment.js for formatting and mutating the date.
The problem I'm having is that even though the date instance in the component is modified when I click either button, the display does not update.
I've tried changing this to a simple integer instead of a date instance, and that works as expected (the DOM is updated properly)
Here is the source code that I have for this:
App.js
Vue.component("datePicker", {
props: ["value"],
data: function() {
return { selectedDate: moment(this.value) };
},
template: "<div><button v-on:click='decrement'><</button>{{formattedSelectedDate}}<button v-on:click='increment'>></button></div>",
methods: {
increment: function () {
this.selectedDate.add(1, "days");
this.$emit('input', this.selectedDate);
},
decrement: function () {
this.selectedDate.subtract(1, "days");
this.$emit('input', this.selectedDate);
}
},
computed: {
formattedSelectedDate: function() {
return this.selectedDate.format("YYYY-MM-DD");
}
}
});
var PointTracker = new Vue({
el: "#PointTracker",
data: {
selectedDate: moment(),
todoItems: {}
}
});
Index.html
<html>
<head>
<meta charset="utf-8">
<title>Point Tracker</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="PointTracker">
<date-picker v-model="selectedDate">
</div>
<script src="node_modules/moment/moment.js"></script>
<script src="node_modules/vue/dist/vue.js"></script>
<script src="node_modules/vue-resource/dist/vue-resource.js"></script>
<script src="app.js"></script>
</body>
</html>
Overview. In a Vue application, the DOM update is always asynchronous. This means that after we've made changes to the value of Vue, the Vue queue takes some time to update and render the changes.
Virtual DOM in Vue is a JavaScript object that represents the Document Object Model (DOM). The application updates the Virtual DOM instead of the DOM directly. So, it minimizes the updating cost of the real DOM as it is computationally expensive.
We can use moment. js in our components by calling it in methods. to add the moment method that returns the return result of the moment function. Then we call it in our template to show today's date.
If a ref attribute is added to an HTML element in your Vue template, you'll then be able to reference that element or even a child element in your Vue instance. You can also access the DOM element directly; it is a read-only attribute and returns an object.
You have to change the reference of selectedDate
, as it is returned from moment
functions, they are always of same reference, so vue watchers are not triggered for these.
You have to make following changes to change the reference:
methods: {
increment: function () {
this.selectedDate = moment(this.selectedDate).add(1, "days")
},
decrement: function () {
this.selectedDate = moment(this.selectedDate).subtract(1, "days")
}
},
Working fiddle: http://jsfiddle.net/mimani/pLcfyrvy/1/
Following is implementation of add/subtract from moment library:
function addSubtract (duration, input, value, direction) {
var other = createDuration(input, value);
duration._milliseconds += direction * other._milliseconds;
duration._days += direction * other._days;
duration._months += direction * other._months;
return duration._bubble();
}
// supports only 2.0-style add(1, 's') or add(duration)
export function add (input, value) {
return addSubtract(this, input, value, 1);
}
// supports only 2.0-style subtract(1, 's') or subtract(duration)
export function subtract (input, value) {
return addSubtract(this, input, value, -1);
}
It returns the same object so the reference is same for the date object.
It happens because moment.js is a isn't immutable
, what it means is when you call add/subtract function on a moment object, it returns the same object, with after changing the properties.
There are some caveats on reactivity of vue and as Object.observe is obsolete now, it can not track if an javascript object has changed internally, unless you clone the object and create a new object which is needed in your case.
There are other workarounds also for this including using frozen-moment library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With