godbasin / vue-select2

select2 component in vue.
MIT License
123 stars 62 forks source link

How to set selected option when data source is AJAX? #24

Closed cwilby closed 5 years ago

cwilby commented 5 years ago

Say I have an App component, which loads a Parent from an API.

A Parent has a thingId property, so the App component renders a ThingSelector to get that value.

A ThingSelector fetches a paginated list of things from the API and emits a thingId value when an option is selected.

That works, but how would I modify this example to display a selected option when App is refreshed?

I found this problem solved in jQuery here, so I'm wondering how to do it in Vue.

ThingSelector.vue -

<template>
  <select2 
    :value="value" 
    @input="(value) => $emit('input', value)" 
    :settings="settings" />
</template>

<script>
import Select2 from "v-select2-component";

export default {
  components: { Select2 },
  props: ["value"],
  data: () => ({
    settings: {
      ajax: {
        url: `/api/v1/things`,
        dataType: "json",
        data: params => {
          return {
            term: params.term,
            page: params.page || 1,
            pageSize: params.pageSize || 50
          };
        },
        processResults: (data) => {
          return {
            results: data.items.map(({ id, name: text }) => ({ id, text })),
            pagination: {
              more: data.current_page < data.last_page
            }
          };
        }
      }
    }
  })
};
</script>

App.vue

<template>
  <thing-selector v-model="parent.thingId" />
</template>

<script>
export default {
  data: () => ({
    parent: {
       thingId: null
    }
  }),
  async mounted() {
    const { data: parent } = await this.$http.get('/api/v1/parents/1');

    this.parent = parent;
  }
};
</script>
cwilby commented 5 years ago

Found a solution by appending an option with selected attribute. In the example, I would have added a name prop, a ref, and the following in mounted to ThingSelector.

if (this.name) {
  this.$refs.select.select2
    .append(
      $(`<option selected value="${this.value}">${this.name}</option>`)
    )
    .trigger('change');
}
godbasin commented 5 years ago

According to Default (pre-selected) values and Preselecting options in an remotely-sourced (AJAX) Select2, it will be quite complex to make it compatible with ajax setting like this. It will be better to use your solution instead of making the component compatible with select2. Thanks!

baddwin commented 3 years ago

Found a solution by appending an option with selected attribute. In the example, I would have added a name prop, a ref, and the following in mounted to ThingSelector.

if (this.name) {
  this.$refs.select.select2
    .append(
      $(`<option selected value="${this.value}">${this.name}</option>`)
    )
    .trigger('change');
}

how did you add ref with vue 3 @cwilby ?

cwilby commented 3 years ago

Not 100% as I haven't actually dived into Vue 3 fully yet, might look like this after reading through docs:

import Select2 from "v-select2-component";
import { ref, onMounted } from 'vue';

export default {
  components: { Select2 },

  props: ["value"],

  data: () => ({
    settings: {
        ajax: {
            url: `/api/v1/things`,
            dataType: "json",
            data: params => {
                return {
                    term: params.term,
                    page: params.page || 1,
                    pageSize: params.pageSize || 50
                };
            },
            processResults: (data) => {
                return {
                    results: data.items.map(({ id, name: text }) => ({ id, text })),
                    pagination: {
                    more: data.current_page < data.last_page
                    }
                };
            }
        }
    }
  }),

  setup() {
    const selectRef = ref(null);

    onMounted(() => {
        if (this.name) {
            selectRef
                .append($(`<option selected value="${this.value}">${this.name}</option>`))
                .trigger('change');
        }
    });

    return { ref };
  }
};
baddwin commented 3 years ago

I have tried like so, but still failed.

This is the Select2 template

<Select2 id="itemName" :options="trxitems" :settings="settings" @select="getProduct($event)" v-model="item.id" ref="selectRef" />

and this is the JS

...
       setup() {
           onMounted(() => {
                console.log(selectRef);
                selectRef
                    .append($(`<option selected value="1">foo</option>`))
                    .trigger('change');
            });

            return { selectRef }
       }
...

error in js console:

Uncaught (in promise) TypeError: selectRef.append is not a function

log of selectRef var:

RefImpl {_rawValue: null, _shallow: false, __v_isRef: true, _value: null}
__v_isRef: true
_rawValue: null
_shallow: false
_value: null
value: (...)
__proto__: Object