Search code examples
javascriptjquerylaravelvue.jsvue-resource

Vue DevTools updating correctly but not browser window


I'm having a strange issue where the value found in Vue DevTools is correct. It's declared in my data as expected. The first time I click on "Edit" an item, the correct value shows up in my browser window as well.

However, if I click on "Edit" an item that has a different quantity, the same value shows up again even if it is incorrect (it should be prepopulating from the database).

Then, if I click back on the first "Edit" item again that value will get updated with the previous value!

The craziest part is that while my browser window is not showing the correct value, the correct result is showing up in Vue DevTools at all times! The circled item in the image below is the UUID for the "Quantity" of 100, which is the correct value. Yet 700 is showing up (the previous Edit item's value). Anybody ever had this happen before and know what gives?

Browser not matching (correct) value in Vue DevTools

Here's some snippets of relevant code (it's from a Vue component using vue-resource, and this is taking place in a bootstrap modal in a Laravel project):

Vue JS

data() {
        return {
            selected_options: {},
            attributes: [],
        }
    },

methods: {

    editLineItem: function (line_item) {
            this.getProductOptionsWithAttributes(line_item.product_id);
            this.getPrepopulatedOptionsForLineItem(line_item.id);
    },

    getProductOptionsWithAttributes: function (product_id) {
            var local_this = this;
            var url = '/api/v1/products/' + product_id + '/options';
            this.$http.get(url).then(function (response) {
                local_this.attributes.$set(0, response.data);
            }, function (response) {
                // error handling
            });
        },

    getPrepopulatedOptionsForLineItem: function (id) {
            var local_this = this;
            var url = '/api/v1/line_items/' + id + '/options';
            this.$http.get(url).then(function (response) {
                Object.keys(response.data).forEach(function (key) {
                    Vue.set(local_this.selected_options, key, response.data[key]);
                });
            }, function (response) {
                //@TODO Implement error handling.
            });
        },
    }

HTML

<div v-for="(key, attribute) in attributes[0]" class="col-md-12 selectbox_spacing">
   <label for="option_{{$index}}">{{key}}</label><br/>
   <select class="chosen-select form-control" v-model="selected_options[key]" v-chosen="selected_options[key]" id="option_{{$index}}">
       <option v-for="option in attribute" value="{{option.id}}">{{option.name}}</option>
   </select>
   </div>
<button v-on:click="editLineItem(line_item)">

Main.js vue-directive:

Vue.directive('chosen', {
    twoWay: true, // note the two-way binding
    bind: function () {
    $(this.el)
        .change(function(ev) {
            // two-way set
            //this.set(this.el.value);

            var i, len, option, ref;
            var values = [];
            ref = this.el.selectedOptions;

            if(this.el.multiple){
                for (i = 0, len = ref.length; i < len; i++) {
                    option = ref[i];
                    values.push(option.value)
                }
                this.set(values);

            } else {
                this.set(ref[0].value);
            }

        }.bind(this));
    },
    update: function(nv, ov) {
        // note that we have to notify chosen about update
        $(this.el).trigger("chosen:updated");
    }
});

var vm = new Vue({
    el      : '#wrapper',

    components: {
        LineItemComponent
    }
});

Script in edit.blade.php file:

<script>
    $(document).ready(function() {
        $('#lineItemModal').on('shown.bs.modal', function () {
                $('.chosen-select', this).chosen('destroy').chosen();
        });
}
</script>

Solution

  • by default, custom directives have a priority of 1000. v-model has a priority of 800 meaning it's evaluated after v-chosen when the template is compiled.

    My Assumption is now: this is also affecting the update.

    What I mean by that: I think $(this.el).trigger("chosen:updated"); in the v-chosen update method is called before v-model did refresh the selected attribute on the list of <option> elements - and that's where chosen checks for the new selected value.

    Long story short: try this:

    Vue.directive('chosen', {
        priority: 700, // Priority lower than v-model
        twoWay: true, // note the two-way binding
        bind: function () {
        ....