0

I am using Jquery Chosen along with Vue. This is my Vue directive:

Vue.component("chosen-select", {
    props: {
        value: [String, Array],
        multiple: Boolean
    },
    template: `<select :multiple="multiple"><slot></slot></select>`,
    mounted() {
        $(this.$el)
            .val(this.value)
            .chosen({ width: '100%' })
            .on("change", e => this.$emit('input', $(this.$el).val()))
    },
    watch: {
        value(val) {
            $(this.$el).val(val).trigger('chosen:updated');
        }
    },
    destroyed() {
        $(this.$el).chosen('destroy');
    }
});

And using it like this:

<chosen-select v-model="basicDetailsModel.stateID" v-validate="'required'" data-vv-as="state" :state="errors.has('stateID') ? 'invalid' : 'valid'" name="stateID">
                        <option :value="null">Please select an option</option>
                        <option v-for="(state, index) in states" :key="index" :value="state.sid">{{state.nm}}</option>
                    </chosen-select>

If the states are assigned static value it works fine as per expectation but if I fetch the states value dynamically the chosen is not updated with latest values. It stays with the initial values.

How would I fix this issue?

Edit: This one works. Do you think this is the right way?

Vue.component("chosen-select", {
    data() {
        return { observer: null }
    },
    props: {
        value: [String, Array],
        multiple: Boolean
    },
    template: `<select :multiple="multiple"><slot></slot></select>`,
    mounted() {
        // Create the observer (and what to do on changes...)
        this.observer = new MutationObserver(function (mutations) {
            $(this.$el).trigger("chosen:updated");
        }.bind(this));

        // Setup the observer
        this.observer.observe(
            $(this.$el)[0],
            { childList: true }
        );
        $(this.$el)
            .val(this.value)
            .chosen({ width: '100%' })
            .on("change", e => this.$emit('input', $(this.$el).val()))
    },
    watch: {
        value(val) {
            $(this.$el).val(val);
        }
    },
    destroyed() {
        $(this.$el).chosen('destroy');
    }
});
Tim Liberty
  • 2,099
  • 4
  • 23
  • 39

2 Answers2

1

The easiest way to fix this issue is simply not to render the select until you have options to render using v-if.

<chosen-select v-if="states && states.length > 0" v-model="basicDetailsModel.stateID" v-validate="'required'" data-vv-as="state" :state="errors.has('stateID') ? 'invalid' : 'valid'" name="stateID">

You could also play around with emitting the chosen:updated event when the component is updated.

updated(){
  $(this.$el).trigger("chosen:updated")
},

which works for multiple selects, but mysteriously not for single selects.

Bert
  • 80,741
  • 17
  • 199
  • 164
  • I initially tried that updated worked but may be inefficient as updated is called each time even v-model's attribute changed (select change occurred). I found some inspiration from https://stackoverflow.com/questions/38148297/in-vue-js-can-a-component-detect-when-the-slot-content-changes and accordingly changed my code. See my edit in question. Let me know how it is. – Tim Liberty Nov 15 '17 at 03:31
-1

I am not sure how you are fetching the states dynamically, but if you're using jQuery to get them, then I think that is your problem. Vue doesn't get notified if non-Vue things (like jQuery) change anything. Even if that's not the case, this is worth reading to see why jQuery and Vue don't get along. Can you add how you are fetching them dynamically? Also, consider using a Vue framework like Vuetify which has a pretty good select control and is totally in Vue.

xon52
  • 696
  • 4
  • 12