materialize: Materialize + Framework not updating select value

Model value is not updated when material_select() is applied to the <select>.

I’ve made a fiddle to show you what’s going on: http://jsfiddle.net/maxflex/j8xcm7d2/

There are also other people facing the same problem: one, two

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Comments: 42 (4 by maintainers)

Most upvoted comments

For change the materializecss select I had to do that:

    $("#idSelect").val(newValue);
    $("#idSelect").material_select();

No problem @ferchoman09 😄 It’s defo a MaterializeCSS problem with the implementation of the select When using the browser-default class it all works fine so… 😟

Hello, I’ve read @nihattunali 's solution and it worked for me. I’m using async/await which are on top of promises, however I will share my code just in case anyone needs it:

async mounted(){
        this.$store.commit("activarEspera");
        try{
            let respuesta = await this.$http.post("/api/v1.0/escuelas", {
                sigla: "IPN"
            });
            respuesta.body.escuelas.forEach(escuela => this.escuelas.push(escuela));

            //These two awaits are important. Both are necessary
            await M.updateTextFields();
            await M.FormSelect.init(document.querySelectorAll("select"));
        }
        catch(respuesta){
            M.toast({
                    html: respuesta.body,
                    displayLength: 1500
                });
        }
        this.$store.commit("desactivarEspera");
    }

Tested on VueJS 2.5.13 and Materialize 1.0.0-rc.2.

in vuejs + axios ajax call is promises logic. you can solve your problem like that…

css regions:

<link type="text/css" rel="stylesheet" href="./node_modules/materialize-css/dist/css/materialize.min.css" media="screen,projection"/>

html regions:

<div class="input-field col s12 m6">
                    <i class="material-icons prefix">map</i>
                    <select id="country"  v-model="country" >
                        <option v-for="option in countrylist" v-bind:value="option.code">
                        {{option.name}}
                        </option>
                    </select>
                    <label for="country">Country</label>
                </div>

scripts regions:

`<script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script>

<script type="text/javascript" src="node_modules/materialize-css/dist/js/materialize.js"></script> <script type="text/javascript" src="node_modules/vue/dist/vue.min.js"></script> <script type="text/javascript" src="node_modules/axios/dist/axios.min.js"></script> <script type="text/javascript" src="node_modules/vue-axios/dist/vue-axios.min.js"></script> <script type="text/javascript" src="node_modules/vue-router/dist/vue-router.min.js"></script> `

and app :

methods:{
    fetchCountry:function(){
        const self=this;
        this.$http.get("./app/country.json")
        .then(function(res){
            res.data.unshift({name:'Please Select Your Country',code:''});
            self.countrylist= res.data;
        }).then(function(){
            $('select').material_select();  // ***** important ******
        })
        .catch(function(err){
            console.log(err);
        });
    },
},

In angular2 you can use it:

For get:

$('select').material_select();
$('select').change((e) => {
     this.model[e.currentTarget.name] = e.currentTarget.value;
});

For set:

this.model.gender = 'male'; // or this.model = newValues;
$('select').material_select();

@ferchoman09 thanks for the workaround. shame on you authors.

It works. Thnx

Every two-way binding framework seems to be affected. There are surely ways to get around this problem, I use a custom directive myself – but yeah, we are waiting for some improvements on this one.

of course @MichaelDeBoey

This is my select component:

                        <div class="input-field col s12 m6">
                            <select id="usuario" name="Consecutivo" ng-model="idUsuario" my-material-select>
                              <option value="" disabled selected>Seleccione un usuario</option>
                              <option ng-repeat="usuario in usuarios" value="{{usuario.idusuario}}">{{usuario.usr_login}}</option>
                            </select>
                            <label for="usuario">Usuario</label>
                        </div>  

I use a custom directive:

    .directive("myMaterialSelect", [ "$timeout", function($timeout) {
        return {
            restrict: 'A',
            require : 'ngModel',
            link : function(scope, element, attrs, ngModelCtrl) {
                $(function() {
                    $(element).material_select();

                    //Cambia el modelo cuando cambia el elemento seleccionado
                    $(element).change(function() {
                        ngModelCtrl.$setViewValue($(element).val());
                    });
                });
            }
        }
    }])

And in the controller I am watching the variable that I have in the ng-model of select component:

        $scope.$watch("formCaja.consecutivo.id", function(newValue, oldValue) {
            $timeout(function() {
                $("#consecutivo").val(newValue);
                $("#consecutivo").material_select();
            }, 0);
        });

So, I am simulating the two ways binding. Note that you must call the function “material_select()” every time that changes the collection of your select component.

            $timeout(function() {
                $("#consecutivo").material_select();
            }, 0);

Not the best, but I had to do so. Hope this can help you

You can use https://github.com/sherweb/ng2-materialize. We did a wrap for the select that work on both template driven and reactive forms.

@ferchoman09 @virtualvoid Can you guys tell me more about the solution for vue2?thanks!

Hello, I’ve read @nihattunali 's solution and it worked for me. I’m using async/await which are on top of promises, however I will share my code just in case anyone needs it:

async mounted(){
        this.$store.commit("activarEspera");
        try{
            let respuesta = await this.$http.post("/api/v1.0/escuelas", {
                sigla: "IPN"
            });
            respuesta.body.escuelas.forEach(escuela => this.escuelas.push(escuela));

            //These two awaits are important. Both are necessary
            await M.updateTextFields();
            await M.FormSelect.init(document.querySelectorAll("select"));
        }
        catch(respuesta){
            M.toast({
                    html: respuesta.body,
                    displayLength: 1500
                });
        }
        this.$store.commit("desactivarEspera");
    }

Tested on VueJS 2.5.13 and Materialize 1.0.0-rc.2.

It works for me. What’s the drawback about use await inside mounted?

Hi, for this issue specific for Vue.JS I created a component that contains a select and handle the add, update options with jQuery, and every time the user changes the selected value it emits the change with the new selected value. Hope it help some people. To not copy the entire code here, I link it.

Code: MaterialSelect.vue

@Thanood is on right track just add else.

new Vue({
    el: 'body',
    ready: function() {
        var suspend = false;
        $('#materialize-select').material_select();
        $('#materialize-select').on('change', function() {
            if (!suspend) {
                suspend = true;
                var event = new CustomEvent('change', {
                    detail: 'change',
                    bubbles: true
                });
                $(this).get(0).dispatchEvent(event);
            } else {
                suspend = false;
            }
        });
    }
})

http://jsfiddle.net/GreyZact/qukwodpv/

It even works if there are multiple select objects.

I don’t know Vue.js enough but with Aurelia I had to issue a custom event on the jQuery change event to make that work. Maybe it helps.

This seems to work:

new Vue({
    el: 'body',
        ready: function() {
        var suspend = false;
        $('#materialize-select').material_select();
      $('#materialize-select').on('change', function() {
        if (!suspend) {
            suspend = true;
          var event = new CustomEvent('change', {
            detail: 'change',
            bubbles: true
          });
          $(this).get(0).dispatchEvent(event);
        }
      });
    }
})

But only once in this example since the custom change also triggers jQuery’s change. You’d have to prevent stack overflow in a cleverer way than me. 😄 (f.i. use a real callback and reset suspend there)

Updated fiddle: http://jsfiddle.net/jk5800wh/