Bootstrap-select & Vue.js: selectpicker(‘refresh’) not working

Tags: , ,



I am trying to implement the bootstrap-select plugin (found here https://developer.snapappointments.com/bootstrap-select/) into a ruby on rails app with Vue.js as the javascript framework.

The objective is that in one select you can pick a category and in the other one you can pick all the teachers available for that category. To do this i use axios and vue to do a request to my own api and use that to populate the second select and it works fine with a simple select field, however I want bootstrap-select to be shown, I am aware the plugin has a function “selectpicker(‘refresh’)” to make the option reload possible but my browser console claims selectpicker is not a function when I call it on my vue instance, I can manually run it on the browser console however and it works as intended

My code:

js:

Vue.use(VueAxios, axios)
    document.addEventListener('DOMContentLoaded', () => {
      if(document.getElementById('enrollment_form')) {
      var app = new Vue({
        el: '#enrollment_form',
        data: {
          CategoryValue: null,
          TeacherValue: null,
          teachers: null,
        },
        methods: {
          fetchTeachers() {
            this.axios.get('/api/teachers/' + this.licenceTypeValue).then(response => (this.teachers = response.data))
              $('.selectpicker').selectpicker('refresh');
          }
        }, 
      })
    }})

view:

       <div class="row">
          <div class="col-lg-5">
            <div class="form-group">
              <%= f.label :category %>*<br />
              <%= f.collection_select(
                  :category,
                  Category.all,
                  :id,
                  :catgegory_name,
                  {include_blank: true},
                  {
                    class: 'form-control selectpicker',
                    "v-model" => "CategoryValue",
                    "v-on:change" => "fetchTeachers"

                  }
              )%>
            </div>
          </div>  
        </div>

        <div class="row">
          <div class="col-lg-5">
            <div class="form-group">
              <label for="teacher_id">Teacher</label>
              <div>
                <select id="teachers-list" class='form-control selectpicker' v-model="TeacherValue" data-fieldname = "TeacherValue">
                  <option label="" ></option>
                  <option v-for="teacher in teachers" :value="teacher.id"> {{teacher.name}}  </option>
                </select>
              </div>
            </div>
          </div>  
        </div>

        <div>
          <%= f.hidden_field :teacher_id, {"v-model" => "TeacherValue"} %>
        </div>

Finally my application.js

//= require rails-ujs
//= require activestorage
//= require jquery
//= require jquery_ujs
//= require popper
//= require bootstrap
//= require bootstrap-select

I would really appreciate if someone could help as i simply can’t figure out what to do

Answer

Try changing to the following:

var app = new Vue({
    el: '#enrollment_form',
    data: {
        CategoryValue: null,
        TeacherValue: null,
        teachers: null,
    },
    methods: {
        fetchTeachers() {
            // Get the Vue object in a variable so you can use it in the "then" 
            // since 'this' context changes in the "then" handler function.
            let theVue = this;

            this.axios
                .get('/api/teachers/' + this.licenceTypeValue)
                .then(response => {
                    theVue.teachers = response.data;
                    // Call bootstrap-select's 'refresh' using Vue's $nextTick function 
                    // to ensure the update is complete before bootstrap-select is refreshed.
                    theVue.$nextTick(function(){ $('#teachers-list').selectpicker('refresh'); });
                });
        }
    }, 
})

I ran into this problem in an application I’m developing, and wrote an article with a more detailed explanation, including broken and working examples. My examples change the options via JavaScript without AJAX, but the concept is the same.

In the code above, you are calling the bootstrap-select ‘refresh’ using Vue’s $nextTick function which waits until the Vue object’s update is complete before executing the code passed to the $nextTick call. (see $nextTick on Vue’s documentation)

Note: You could also do a more generic update in the Vue’s ‘update’ event handler by adding:

updated: function(){
  this.$nextTick(function(){ $('.selectpicker').selectpicker('refresh'); });
}

Please be aware that this will always update all bootstrap-selects that share the same class ‘selectpicker’. The update will occur on every update of the vue data, including those unrelated to the <select>s in question, which is wasteful and could potentially cause performance issues.

I believe the best practice is to refresh just the <select> that needs updating, at the moment it needs the refresh (in your case when the AJAX call returns and the ‘teachers’ property has been updated.)



Source: stackoverflow