alex.fratini's avatar

Problem with select2 and vue.js

I have this form:

<template>
  <div class="panel panel-default">
    <div class="panel-heading">
      {{title}}
    </div>
    <div class="panel-body">
      <form class="form" @submit.prevent="save">
        <div class="row">
          <div class="col-md-8 col-md-offset-2">
            <div class="form-group">
              <label>Scelta</label>
              <input type="text" class="form-control" v-model="form.choice">
              <small class="text-danger" v-if="errors.choice">{{errors.choice[0]}}</small>
            </div>
            <div class="form-group">
              <label>Tipo Spesa</label>
              <select2 :options="option.expenditures" name="form.expenditure_id" v-model="form.expenditure_id">
                <option disabled value="0">Seleziona un valore...</option>
              </select2>
              <small class="text-danger" v-if="errors.expenditure_id">{{errors.expenditure_id[0]}}</small>
            </div>
            <div class="form-group">
              <label>Importo</label>
              <input type="text" class="form-control" v-model="form.amount">
              <small class="text-danger" v-if="errors.amount">{{errors.amount[0]}}</small>
            </div>
            <div class="form-group">
              <label>Tipo Pagamento</label>
              <select2 :options="option.payments" name="form.payment_id" v-model="form.payment_id">
                <option disabled value="0">Seleziona un valore...</option>
              </select2>
              <small class="text-danger" v-if="errors.payment_id">{{errors.payment_id[0]}}</small>
            </div>
            <div class="form-group">
              <label>Descrizione</label>
              <input type="text" class="form-control" v-model="form.description">
              <small class="text-danger" v-if="errors.description">{{errors.description[0]}}</small>
            </div>
          </div>
        </div>
        <button class="btn btn-success">Salva</button>
      </form>
    </div>
  </div>
</template>
<script>
import Select2 from './../../../objects/Select2'
import { get, post, put } from './../../../helpers/api'
export default {
  name: 'AssistantForm',
  data() {
    return {
      form: {},
      errors: {},
      option: {},
      title: 'Crea Nuovo Aiuto',
      initialize: '/api/assistants/create',
      redirect: '/assistants',
      store: '/api/assistants',
      method: 'post'
    }
  },
  created() {
    if(this.$route.meta.mode === 'edit') {
      this.title = 'Aggiorna Aiuto'
      this.initialize = '/api/assistants/' + this.$route.params.id + '/edit'
      this.store = '/api/assistants/' + this.$route.params.id
      this.method = 'put'
    }
    this.fetchData()
  },
  watch: {
    '$route': 'fetchData'
  },
  components: {
    'select2': Select2
  },
  methods: {
    fetchData() {
      var vm = this
      get(this.initialize)
      .then(function(response) {
        Vue.set(vm.$data, 'form', response.data.form)
        Vue.set(vm.$data, 'option', response.data.option)
      })
      .catch(function(error) {
        console.log(error)
      })
    },
    save() {
      var vm = this
      if (this.method === "post") {
        post(this.store, this.form)
        .then(function(response) {
          if(response.data.saved) {
            vm.$router.push(vm.redirect)
          }
        })
        .catch(function(error) {
          Vue.set(vm.$data, 'errors', error.response.data)
        })
      } else {
        put(this.store, this.form)
        .then(function(response) {
          if(response.data.saved) {
            vm.$router.push(vm.redirect)
          }
        })
        .catch(function(error) {
          Vue.set(vm.$data, 'errors', error.response.data)
        })
      }
    }
  }
}
</script>

and this component:

<template>
  <select class="form-control" :name="name">
    <slot></slot>
  </select>
</template>
<style src="select2/dist/css/select2.min.css"></style>
<style src="select2-bootstrap-theme/dist/select2-bootstrap.min.css"></style>
<script>
import select2 from 'select2'
import $ from 'jquery'
export default {
  props: ['options', 'value', 'name'],
  mounted: function () {
    var vm = this
    $(this.$el)
    // init select2
    .select2({ data: this.options })
    .val(this.value)
    .trigger('change')
    // emit event on change.
    .on('change', function () {
      vm.$emit('input', this.value)
    })
  },
  watch: {
    value: function (value) {
      // update value
      $(this.$el).val(value).trigger('change');
    },
    options: function (options) {
      // update options
      $(this.$el).select2({ data: options })
    }
  },
  destroyed: function () {
    $(this.$el).off().select2('destroy')
  }
}
</script>

When I'm inserting a new record, the select2 work correctly. The variable this.form is correctly filled. But whe I'm using the form for update a record, the select2 doesn't work, and the two variables are empty (form.expenditure_id and form.payment_id). Can anyone help me?

0 likes
4 replies
spekkionu's avatar

The component uses the value prop to keep track of its value. Because it isn't a normal form input the v-model binding wont do anything.

http://vuejs.org/v2/guide/components.html#Dynamic-Props

Change the v-model into :value and the select2 should populate correctly.

<select2 :options="option.expenditures" name="form.expenditure_id" :value="form.expenditure_id">

However since component variable binding is only one way (from the parent to the child) the form.expenditure_id variable will not be updated when the selected item is changed.

http://vuejs.org/v2/guide/components.html#Custom-Events

The component fires a input event when the selected item is changed. You will need to listen to this in the parent to update the form.expenditure_id variable to the new value.

<select2 :options="option.expenditures" name="form.expenditure_id" :value="form.expenditure_id" v-on:input="methodToUpdateValue">

If you change the event that is emitted to update:value you can use the .sync shortcut to combine the binding of the value, the listening for the event, and the updating the value.

http://vuejs.org/v2/guide/components.html#sync-Modifier

In the component change vm.$emit('input', this.value) to vm.$emit('update:value', this.value)

<select2 :options="option.expenditures" name="form.expenditure_id" :value.sync="form.expenditure_id">
alex.fratini's avatar

It doesn't work. I put a console.log(response.data.form) and the values are correctly. But when I see in the Vue console, the form.expenditure_id and form.payment_id are empty

anzglobalsoft's avatar

** I hope this might help you**

<template>
    <select :name="name" :id="identifier" class="form-control">
        <option v-for="selecteditem in selecteditems" :key="selecteditem.id" :value="selecteditem.id">
            {{selecteditem.name}}
        </option>
    </select>
</template>

<script>

export default {
    props: ['selecteditems', 'identifier', 'name', 'url'],
    data() {
        return {

        }
    },
    created(){
        
    },
    mounted(){
        $('#' + this.identifier).select2({
            placeholder: 'Select Client',
            ajax: {
                url: this.url,
                data: function (params) {
                var query = {
                    search: params.term
                }

                // Query parameters will be ?search=[term]&type=public
                return query;
                },
                processResults: function (data) {
                    return {
                        results: data
                    };
                },
                cache: false
            }
        });
    }
}
</script>

** app.js **

Vue.component(
    'select2',
    require('./components/reusable/SelectComponent.vue')
);

** api controller code **

public function dropdown(Request $request)
{
    $term = trim($request->query('search'));
    return Product::search($term)->paginate(20);
}

** View **

<select2 
url="api/category/dropdown" 
name="category_id" 
:selecteditems="[{id: 1, name: 'abc'}]" 
identifier="product">
</select2>

Please or to participate in this conversation.