Vuejs + Materializecss select field

11,765

Solution 1

It seems that Materialize doesn't dispatch any events so I couldn't find an elegant solution. But it does seem that the following Vuejs directive + jQuery workaround is working:

Vue.directive("select", {
    "twoWay": true,

    "bind": function () {
        $(this.el).material_select();

        var self = this;

        $(this.el).on('change', function() {
            self.set($(self.el).val());
        });
    },

    update: function (newValue, oldValue) {
        $(this.el).val(newValue);
    },

    "unbind": function () {
        $(this.el).material_select('destroy');
    }
});

And then in your HTML – bind <select> using v-select instead of v-model.

Solution 2

Vue.js 2.0

Template:

<div v-text="selected"></div>
<material-select v-bind="selected = selected || options[0].value" v-model="selected">
     <option v-for="option in options" :value="option.value" v-text="option.name"></option>
</material-select>

Component:

"use strict";

Vue.component("material-select", {
    template: '<select><slot></slot></select>',
    props: ['value'],
    watch: {
        value: function (value) {

            this.relaod(value);
        }
    },
    methods:{
      relaod : function (value) {

          var select = $(this.$el);

          select.val(value || this.value);
          select.material_select('destroy');
          select.material_select();
      }
    },
    mounted: function () {

        var vm = this;
        var select = $(this.$el);

        select
            .val(this.value)
            .on('change', function () {

                vm.$emit('input', this.value);
            });

        select.material_select();
    },
    updated: function () {

        this.relaod();
    },
    destroyed: function () {

        $(this.$el).material_select('destroy');
    }
});

Solution 3

Vue.directive('material-select', {
        bind:function(el,binding,vnode){
            $(function () {
                $(el).material_select();

            });
            var arg = binding.arg;
            if(!arg)arg="change";
            arg = "on"+arg;
            el[arg]=function() {
            	
                if (binding.expression) {
                    if (binding.expression in vnode.context.$data) {
                        vnode.context.$data[binding.expression] = el.value;

                    } else if (vnode.context[binding.expression] &&
                            vnode.context[binding.expression].length <= 1) {
                            vnode.context[binding.expression](el.value);

                    } else {
                        throw new Error('Directive v-' + binding.name + " can not take more than 1 argument");
                    }
                    

                }
                else {
                    throw new Error('Directive v-' + binding.name + " must take value");
                }
            }

        },
        unbind:function(el) {
            $(el).material_select('destroy');
        }
});


new Vue({
  el: '#exemple1',
  data:function(){
    return {
    	selected: '',
	    options:[
	        {value:"v1",text:'description 1'},
	        {value:"v2",text:'description 2'},
	        {value:"v3",text:'description 3'},
	        {value:"v4",text:'description 4'},
	        {value:"v5",text:'description 5'},
	    ]
	  }
	}
});
      
new Vue({
  el: '#exemple2',
  data:function() {
    return{
    	selected: null,
	    options:[
	        {value:"v1",text:'description 1'},
	        {value:"v2",text:'description 2'},
	        {value:"v3",text:'description 3'},
	        {value:"v4",text:'description 4'},
	        {value:"v5",text:'description 5'},
	    ]
	}
  },
  methods:{
    change:function(value){
        this.selected = value;
        alert(value);
    }
  }
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/js/materialize.min.js"></script>
<h4>vue js materialize</h4>
<h5>Exemple1</h5>
<div id="exemple1">
    <select v-material-select:change="selected" class="blue-text">
        <option value="" disabled selected ><slot>Defaut message</slot></option>
        <option v-for="option in options" :value="option.value">{{ option.text}}</option>
    </select>
</div>

<h5>Exemple2</h5>
<div id="exemple2">
    <select v-material-select:change="change" class="blue-text">
        <option  disabled selected ><slot>Choisir Votre Abonnement</slot></option>
        <option v-for="option in options" :value="option.value">{{ option.text}}</option>
    </select>
</div>

Solution 4

The top answer was nice but didn't work for Vue 2.

Here is an update of which works (probably still a little hacky). I moved the jQuery hook into update() as the bind function called too early for materialize.

Vue.directive("select", {
    "twoWay": true,

    update: function(el, binding, vnode) {
        if(!vnode.elm.dataset.vueSelectReady) {

            $(el).on('change', function() {
                vnode.context.$set(vnode.context, binding.expression, el.value);
            });

            $(el).material_select();
            vnode.elm.dataset.vueSelectReady = true
        }
    },

    unbind: function(el, binding, vnode) {
        $(el).material_select('destroy');
    }
});

HTML:

<select v-select=selected>
    <option value="" disabled selected>Choose your option</option>
    <option :value="item" v-for='item in items'>{{ item }}</option>
    <label>Materialize Select</label>
</select>

Solution 5

You can make the dynamic select in Vue + Materializecss work with simple hacks

$('#select').val(1).material_select(); // Set value and reinitialize materializecss select

mounted () {
   $("#select").change(function(){
            this.update_result.category = $("#select").val();
        }.bind(this)); // To set the user selected value to the data property 
   update_result.
}

If you are using meterializecss beta version the function name to initialize the select will differ.

Share:
11,765
Desprit
Author by

Desprit

Updated on July 02, 2022

Comments

  • Desprit
    Desprit almost 2 years

    I have this code in my template:

    <div class="input-field col s6">
        <select v-on:change="selectChaned" v-model="item.size">
            <option value="" disabled selected>Choose your option</option>
            <option v-on:click="optionClicked" v-for="size in case_sizes" v-bind:value="{{ size }}">{{ size }}</option>
        </select> 
        <label for="size">Size</label>
    </div>
    

    According to Materializecss docs, I call $('select').material_select(); to transform default select field into something cutie. What it also does - it replaces <select> and <option> tags with <ul> and <li>. As a result I can't access value of item.size in my ViewModel js file. I even tried to listen for a click on option field and call optionClicked method (which should simply alert a message then), tried to listen for selectChaned. Nothing.

    How can I get option value in ViewModel?

    p.s. just for information: I only have problem with select field. Input field for example works fine:

    <input placeholder="" name="name" type="text" class="validate" v-model="item.name">
    

    In ViewModel I'm able to access item.name