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

后端 未结 3 913
礼貌的吻别
礼貌的吻别 2020-12-11 06:07

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

相关标签:
3条回答
  • 2020-12-11 06:31

    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.

    0 讨论(0)
  • 2020-12-11 06:34

    The issue is that the reference to the selectedDate never changes. It is the same object even if you are calling methods on it.
    One approach here would be to store the date in a different format. As a date object, as a number etc. Then when you are making a change on it you can generate a new instance of that data.

    Here is an example:

    var datePicker = Vue.component("datePicker", {
        data: function() {
            return { selectedDate: moment().toDate() }
        },
        template: `<div>
            <button v-on:click='decrement'>&lt;</button>
              {{formattedSelectedDate}}
            <button v-on:click='increment'>&gt;</button>
            {{selectedDate}}
          </div>`,
        methods: {
            increment: function () {
                var currentDate = moment(this.selectedDate);
                currentDate.add(1, "days");
                this.selectedDate = currentDate.toDate();
            },
            decrement: function () {
                var currentDate = moment(this.selectedDate);
                currentDate.subtract(1, "Days");
                this.selectedDate = currentDate.toDate();
            }
        },
        computed: {
            formattedSelectedDate: function() {
                return moment(this.selectedDate).format("YYYY-MM-DD");
            }
        }
    });
    
    var PointTracker = new Vue({
        el: "#PointTracker",
        data: {
            date: 0
        },
        components: {
            datePicker
        }
    });
    <div id="PointTracker">
      <date-picker />
    </div>
    
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.js"></script>

    0 讨论(0)
  • 2020-12-11 06:40

    This is because moment and vue both rely on defining objects' getters and setters, which results in one overriding another in your case. (So changing the reference works because vm.selectedDate.set is overridden while vm.set is not)

    From moment's doc:

    Moment.js uses overloaded getters and setters.

    From vue's doc:

    Vue will recursively convert its properties into getter/setters to make it “reactive”. The object must be plain: native objects such as browser API objects and prototype properties are ignored. A rule of thumb is that data should just be data - it is not recommended to observe objects with its own stateful behavior.`

    A quick validation: I added console.log(this.selectedDate.get) to the fiddle saurabh created, the logged function is stringGet in moment.js, I then checked vm.foo's get in my project, it's proxyGetter which is a wrapper for reactiveGetter, both in vue.runtime.common.js.

    0 讨论(0)
提交回复
热议问题