Vue 2 自定义选择2:为什么@change不工作而@input在工作



我为 Vue 2 创建了一个自定义的 select2 输入元素。

我的问题是:为什么

<select2 v-model="vacancy.staff_member_id" @input="update(vacancy)"></select2>

工作,但

<select2 v-model="vacancy.staff_member_id" @change="update(vacancy)"></select2>

不?

由于 Vue 中的普通<input>元素有一个@change处理程序,如果我的自定义 select2 输入具有相同的内容,那就太好了。


关于我的自定义元素的一些信息:

此元素的目的是呈现所有<option>元素,而只呈现所需的元素,因为我们在一个页面上有许多 select2 输入,而在 select2 输入中有许多选项,导致页面加载速度变慢。

此解决方案使其速度更快。

Vue.component('select2', {
props: ['options', 'value', 'placeholder', 'config', 'disabled'],
template: '<select><slot></slot></select>',
data: function() {
return {
newValue: null
}
},
mounted: function () {
var vm = this;
$.fn.select2.amd.require([
'select2/data/array',
'select2/utils'
], function (ArrayData, Utils) {
function CustomData ($element, options) {
CustomData.__super__.constructor.call(this, $element, options);
}
Utils.Extend(CustomData, ArrayData);
CustomData.prototype.query = function (params, callback) {
if (params.term && params.term !== '') {
// search for term
var results;
var termLC = params.term.toLowerCase();
var length = termLC.length;
if (length < 3) {
// if only one or two characters, search for words in string that start with it
// the string starts with the term, or the term is used directly after a space
results = _.filter(vm.options, function(option){
return option.text.substr(0,length).toLowerCase() === termLC ||
_.includes(option.text.toLowerCase(), ' '+termLC.substr(0,2));
});
}
if (length > 2 || results.length < 2) {
// if more than two characters, or the previous search give less then 2 results
// look anywhere in the texts
results = _.filter(vm.options, function(option){
return _.includes(option.text.toLowerCase(), termLC);
});
}
callback({results: results});
} else {
callback({results: vm.options}); // no search input -> return all options to scroll through
}
};
var config = {
// dataAdapter for displaying all options when opening the input
// and for filtering when the user starts typing
dataAdapter: CustomData,
// only the selected value, needed for un-opened display
// we are not using all options because that might become slow if we have many select2 inputs
data:_.filter(vm.options, function(option){return option.id === parseInt(vm.value);}),
placeholder:vm.placeholder
};
for (var attr in vm.config) {
config[attr] = vm.config[attr];
}
if (vm.disabled) {
config.disabled = vm.disabled;
}
if (vm.placeholder && vm.placeholder !== '') {
$(vm.$el).append('<option></option>');
}
$(vm.$el)
// init select2
.select2(config)
.val(vm.value)
.trigger('change')
// prevent dropdown to open when clicking the unselect-cross
.on("select2:unselecting", function (e) {
$(this).val('').trigger('change');
e.preventDefault();
})
// emit event on change.
.on('change', function () {
var newValue = $(this).val();
if (newValue !== null) {
Vue.nextTick(function(){
vm.$emit('input', newValue);
});
}
})
});
},
watch: {
value: function (value, value2) {
if (value === null) return;
var isChanged = false;
if (_.isArray(value)) {
if (value.length !== value2.length) {
isChanged = true;
} else {
for (var i=0; i<value.length; i++) {
if (value[i] !== value2[i]) {
isChanged = true;
}
}
}
} else {
if (value !== value2) {
isChanged = true;
}
}
if (isChanged) {
var selectOptions = $(this.$el).find('option');
var selectOptionsIds = _.map(selectOptions, 'value');
if (! _.includes(selectOptionsIds, value)) {
var missingOption = _.find(this.options, {id: value});
var missingText = _.find(this.options, function(opt){
return opt.id === parseInt(value);
}).text;
$(this.$el).append('<option value='+value+'>'+missingText+'</option>');
}
// update value only if there is a real change
// (without checking isSame, we enter a loop)
$(this.$el).val(value).trigger('change');
}
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}

原因是您正在侦听组件<select2>而不是实际 DOM 节点上的事件。组件上的事件将引用从内部发出的自定义事件,除非您使用.native修饰符。

自定义事件与本机 DOM 事件不同:它们不会在 DOM 树中冒泡,并且除非您使用.native修饰符,否则无法捕获。从文档中:

请注意,Vue 的事件系统与浏览器的 EventTarget API 是分开的。尽管它们的工作方式类似,但$on$emit不是 addEventListener 和 dispatchEvent 的别名。

如果您查看您发布的代码,您将在其末尾看到以下内容:

Vue.nextTick(function(){
vm.$emit('input', newValue);
});

此代码发出一个自定义事件input在 VueJS 事件命名空间中,并且不是本机 DOM 事件。此事件将由<select2>VueJS 组件上的v-on:input@input捕获。相反,由于没有使用vm.$emit发出change事件,绑定v-on:change永远不会被触发,因此您观察到的非操作。

Terry 指出了原因,但实际上你可以简单地将update事件作为道具传递给子组件。查看下面的演示。

Vue.component('select2', {
template: '<select @change="change"><option value="value1">Value 1</option><option value="value2">Value 2</option></select>',
props: [ 'change' ]
})
new Vue({
el: '#app',
methods: {
onChange() {
	console.log('on change');
}
}
});
<script src="https://unpkg.com/vue@2.4.2/dist/vue.min.js"></script>
<div id="app">
<div>
<p>custom select</p>
<select2 :change="onChange"></select2>
</div>
<div>
<p>default select</p>
<select @change="onChange">
<option value="value1">Value 1</option>
<option value="value2">Value 2</option>
</select>
</div>
</div>

小提琴

相关内容

最新更新