Skip to content

Instantly share code, notes, and snippets.

@dragg
Last active August 27, 2021 05:31
Show Gist options
  • Save dragg/963a90c448f23ebfb1f3d9c78493bb91 to your computer and use it in GitHub Desktop.
Save dragg/963a90c448f23ebfb1f3d9c78493bb91 to your computer and use it in GitHub Desktop.
Vue Components
<template>
<select :name="name" class="form-control">
<slot></slot>
</select>
</template>
<script>
import 'select2-bootstrap-theme/dist/select2-bootstrap.css';
export default {
props: {
value: [String, Number, Boolean, Array],
name: String,
initConfig: {
type: Object,
default: () => {
return {};
}
},
/**
* Must pass next object with next properties:
* url
* handler - function that receive item and return object with id and text properties OR pass as object with mapping of properties id and text
* lengthPerPage - Number
*
* For example
* url: '/tags',
* handler: {id: 'id', text: 'title'} or handler: (tag) => { return {id: tag.id, text: tag.title};}
* lengthPerPage: 15
*/
ajax: {
type: Object,
default: () => {
return null;
},
},
},
data() {
return {
optionsObserver: null,
};
},
watch: {
value: function (value) {
this.update(value);
},
},
mounted: function () {
this.initialize();
this.setOptionsObserver();
},
beforeDestroy: function () {
this.destroyOptionsObserver();
this.destroy();
},
methods: {
initialize() {
let self = this;
let config = self.getConfig();
$(this.$el)
.select2({
theme: 'bootstrap',
width: '100%',
dropdownAutoWidth: true,
...config,
})
.val(this.value)
.trigger('change')
.on('change', (event) => {
if (self.initConfig.multiple) {
let selectedOptions = [].map.call(event.target.selectedOptions, function (option) {
return option.value;
});
self.$emit('input', selectedOptions);
} else {
self.$emit('input', event.target.value);
}
self.$emit('change', event);
});
},
getConfig() {
let config = {
...this.initConfig,
};
if (this.ajax) {
let ajaxConfig = {};
ajaxConfig.url = this.ajax.url;
let handlerIsCallable = typeof this.ajax.handler === "function";
let handler = handlerIsCallable ? this.ajax.handler(item) : (item) => {
return {
id: item[this.ajax.handler.id],
text: item[this.ajax.handler.text]
}
};
ajaxConfig.processResults = (data, params) => {
let items = data.data;
let page = params.page || 1;
return {
results: items.map(item => {
return handler(item);
}),
pagination: {
more: (page * this.ajax.lengthPerPage) < data.recordsFiltered
}
};
};
ajaxConfig.data = function (params) {
return {
...params,
page: params.page || 1
};
};
config.ajax = ajaxConfig;
}
return config;
},
update(value) {
if (this.initConfig.multiple && $(this.$el).select2('data').length !== value.length) {
let self = this;
// ToDo: need resolve if this is ajax then we can have a case when selected item not loaded!
// Handle options only on next vue tick if an option was delete but still not redraw
// Added here because '.val(value)' not worked in all cases. ToDo: need recheck it!
Vue.nextTick(() => {
// Find all value's options and check that it's selected
value.forEach(oneValue => {
let foundOption;
for (let i = 0, option; option = $(self.$el)[0].options[i]; i++) {
if ($(option).val() == oneValue) {
foundOption = option;
break;
}
}
if (!$(foundOption).is(':selected')) {
$(foundOption).attr('selected', 'selected');
}
});
// ToDo: need add removing individually selected options
// Handle if need reset all selected options
if (value.length === 0) {
// for (let i = 0, option; option = $(self.$el.options[i]); i++) {
// option.removeAttr('selected');
// }
}
$(this.$el).val(value).trigger('change');
});
return;
}
if (!this.initConfig.multiple && $(this.$el).select2('data').length === 1) {
let selectedOption = $(this.$el).select2('data')[0];
if (selectedOption.id !== value) {
$(this.$el).val(value);
$(this.$el).trigger('change');
}
}
},
destroy() {
$(this.$el).off().select2('destroy');
},
setOptionsObserver() {
this.optionsObserver = new MutationObserver(() => {
this.destroy();
this.initialize();
});
// Setup the observer
this.optionsObserver.observe($(this.$el)[0], {childList: true});
},
destroyOptionsObserver() {
this.optionsObserver.disconnect();
this.optionsObserver = null;
},
}
};
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment