Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does vue.js not update the dom with datepicker using moment.js

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'>&lt;</button>{{formattedSelectedDate}}<button v-on:click='increment'>&gt;</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>
like image 648
Patrick Avatar asked Dec 04 '16 14:12

Patrick


People also ask

How do I update Vue DOM?

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.

What updates the virtual Dom in Vue?

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.

How do I use moment JS in Vue component?

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.

Can I use DOM in Vue?

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.


1 Answers

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.


Why It happened

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.

like image 147
Saurabh Avatar answered Nov 15 '22 12:11

Saurabh