2017-08-14 63 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''的行爲。特里的回答指出我正確的方向來「修復」組件的行爲。 –