Как создать двунаправленную привязку из экземпляра vue.js к собственному веб-компоненту? - PullRequest
0 голосов
/ 26 апреля 2018

Ниже приведен пример для пользовательского веб-компонента my-input. Я хотел бы связать атрибут value моего пользовательского компонента ввода с атрибутом email экземпляра vue. (В примере может потребоваться, чтобы Chrome поддерживал пользовательские веб-компоненты.)

=> Как мне настроить пример веб-компонента, чтобы привязка работала?

Если я заменим my-input на простой тег input, то переплет будет работать. Поэтому мой синтаксис для части vue.js, кажется, просто отлично.

https://jsfiddle.net/j5f9edjt/

 new Vue({
  el: '#app',
  template: '#app-template',
  data: {
  //email data is blank initially
    email: ''
  }
})
<script type="text/javascript" src="https://unpkg.com/vue@2.2.4"></script>

<script>
  class MyInput extends HTMLElement {

		static get observedAttributes() {
        return ['value'];
    }
    
    constructor(){
       super();
       this.wrappedInput=undefined;
    }

    connectedCallback(){
        var self=this;
        if(!self.wrappedInput){
            var wrappedInput = document.createElement('input');
            wrappedInput.type='text';
            wrappedInput.onchange = ()=>this.wrappedInputChanged();
            self.appendChild(wrappedInput);
            self.wrappedInput = wrappedInput;
        }
    }
    
    attributeChangedCallback(attr, oldValue, newValue) {
      if(attr==='value'){
          console.log('attribute changed ' + newValue);
          if(this.wrappedInput){
      	      this.wrappedInput.value= newValue; 
          }   
                                                 
      } 
    }
    
    wrappedInputChanged(){
      console.log('wrapepd input changed')
    	var newValue = this.wrappedInput.value;
      this.value = newValue;
    }
    
     get value() {
        console.log('get value')
				return this.getAttribute('value');
		}

	 set value(newValue) {	
    	this.setAttribute('value',newValue);     
      console.log('set value ' + newValue);
		}

  }
  window.customElements.define('my-input', MyInput);

</script>

<div id="app"></div>

<template id="app-template">
  <div>
  <my-input v-model="email"></my-input>
    <h1>
       You entered {{email}}
    </h1>
  </div>
  
</template>

Я пытался отправить дополнительное событие ввода, но это не помогло:

     var myInput = new CustomEvent("input", 
        {
          detail: {
            message: "Hello World!",
            type: 'text',
          },
          bubbles: true,
          cancelable: true
        }
      );
      this.dispatchEvent(myInput);

Где найти исходный код директивы v-model, чтобы понять, что она делает?

Похожие вопросы:

Как настроить целевой элемент (собственный веб-компонент) в vue.js?

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Кажется, что директива v-model проверяет тип тега и обрабатывает их индивидуально:

https://github.com/vuejs/vue/blob/dev/src/platforms/web/compiler/directives/model.js

Я не получил v-model для работы с моим пользовательским компонентом без дополнительных оболочек. (Я разочаровался в понимании того, как именно мой случай обрабатывается в model.js.)

Как альтернатива «разложению связывания», как предложил Рой Дж.

<my-input :value="email" @input="email = $event.target.value"></my-input>

Я создал пользовательскую директиву v-property. Он работает как для my-input, так и для input (как использовалось в моем исходном примере; не пробовал все возможные варианты):

<my-input v-property="email"></my-input>
<input v-property="email"></my-input>

-

Vue.directive('property', { 
  bind: function (el, binding, vnode) {
    var viewModel = vnode.context;
    var propertyName = binding.expression;
    el.addEventListener('input', (event)=>{ 
      var oldValue = viewModel[propertyName];
      var newValue = event.target.value;
      if(newValue != oldValue){
        viewModel[propertyName] = newValue;        
      }     
    });    

    viewModel.$watch(propertyName, ()=>{
      var oldValue = el.value;
      var newValue = viewModel[propertyName];
      if(newValue != oldValue){
        el.value = newValue;        
      } 
    });

  }
});

https://jsfiddle.net/7dqppqo2/

0 голосов
/ 26 апреля 2018

Чтобы заставить v-model работать, вам нужно создать компонент-обертку для вашего веб-компонента. Обертка будет соответствовать требованиям для использования v-model с компонентом .

Кроме того, вы можете разложить v-model на две части: установить value опору и обработать input события. Vue, похоже, не распознает веб-компонент как нативный элемент, пока идет v-model.

class MyInput extends HTMLElement {

  static get observedAttributes() {
    return ['value'];
  }

  constructor() {
    super();
    this.wrappedInput = undefined;
  }

  connectedCallback() {
    var self = this;
    if (!self.wrappedInput) {
      var wrappedInput = document.createElement('input');
      wrappedInput.type = 'text';
      wrappedInput.onchange = () => this.wrappedInputChanged();
      self.appendChild(wrappedInput);
      self.wrappedInput = wrappedInput;
    }
  }

  attributeChangedCallback(attr, oldValue, newValue) {
    if (attr === 'value') {
      console.log('attribute changed ' + newValue);
      if (this.wrappedInput) {
        this.wrappedInput.value = newValue;
      }
    }
  }

  wrappedInputChanged() {
    var newValue = this.wrappedInput.value;
    this.value = newValue;
  }

  get value() {
    console.log('get value')
    return this.getAttribute('value');
  }

  set value(newValue) {
    this.setAttribute('value', newValue);
    console.log('set value ' + newValue);
  }

}

window.customElements.define('my-input', MyInput);

new Vue({
  el: '#app',
  template: '#app-template',
  data: {
    //email data is blank initially
    email: ''
  },
  methods: {
    handleInput(event) {
      this.email = event.target.value;
    }
  },
  components: {
    wrappedMyInput: {
      template: '#wmi-template',
      props: ['value'],
      methods: {
        emitInput(event) {
          this.$emit('input', event.target.value);
        }
      }
    }
  }
})
<script type="text/javascript" src="https://unpkg.com/vue@2.2.4"></script>

<div id="app"></div>

<template id="app-template">
  <div>
    <my-input :value="email" @input="handleInput"></my-input>
    <h1>
       You entered {{email}}
    </h1>
    <wrapped-my-input v-model="email"></wrapped-my-input>
    <h1>
       You entered {{email}}
    </h1>
  </div>
</template>

<template id="wmi-template">
  <my-input :value="value" @input="emitInput"></my-input>
</template>
...