2017-08-14 64 views
0

我创建了Vue公司的自定义选择2输入元素2.的Vue 2自定义选择2:为什么不@change工作时@input工作

我的问题是:为什么是

<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处理程序,这将是很好,如果我的自定义选择2输入具有相同的。


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

该元素的目的是呈现所有<option>元素,但只有那些需要的,因为我们有内部的一个页面上的许多选择2输入和多种选择一个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') 
    } 

回答

2

的原因是因为你正在听的事件组件<select2>而不是实际的DOM节点上。组件上的事件将参考custom events emitted from withinunless you use the .native modifier

自定义事件与原生DOM事件不同:它们不会冒泡DOM树,并且无法捕获,除非使用.native修饰符。 From the docs

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

如果你看看你发布的代码,你会看到这个在它的结束:

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

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

+1

我说''VM。在''nextTick''里面''让我的select2组件听取''change''事件 –

1

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() { 
 
    \t console.log('on change'); 
 
    } 
 
    } 
 
});
<script src="https://unpkg.com/[email protected]/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>

fiddle

+1

你的回答很好,但':change''仍然不同来自'@ change''的行为。特里的回答指出我正确的方向来“修复”组件的行为。 –