VueJS动态组件订阅从子组件中发出

时间:2019-05-06 11:55:53

标签: vue.js

我正在使用Typescript编写VueJS(2.5.22)应用程序,并尝试在运行时动态添加组件。我遇到了Typescript的两个问题。将子组件添加为命名插槽,并订阅以从子组件发出事件。我可以通过添加子组件来解决插槽问题,但是我想使用命名插槽。感谢插槽上的任何输入。

我仍在尝试解决父组件订阅子事件并更新父组件属性的第二个问题。

子组件将发出“更新值”。如何在父组件上动态订阅此事件?

谢谢

请为以下项目创建标签:vuejs组件vuejs-动态组件vuejs-typescript vuejs-emit

parent component adding dynamically at run-time in created method


<div>
    <p>Dynamic components</p>
    <div ref="controls"></div>
</div>

export default class ParentComponent extends Vue {

public $refs: Vue['$refs'] & {
    controls: HTMLElement
}
public $slots: Vue['$slots'] & {
    TextBox: TextBox
}

vuejs created method
--------------------

const labelControlContainerClass = Vue.extend(LabelControlContainer)
const textBoxClass = Vue.extend(TextBox)

const fields = table.fields
for (const key in fields) {
  if (fields.hasOwnProperty(key)) {
      const element = fields[key]
        if (element.fieldType === 'Char') {
          const textBoxInstance = new textBoxClass({
            // props
            propsData: {
              value: '',
              placeholder: element.translatedCaption,
              name: key
            },
            // how to subscript to "update-value" event???
          })
          textBoxInstance.$mount()

          const instance = new labelControlContainerClass({
            // props
            propsData: {
              caption: element.translatedCaption,
              bold: false
            },
          })

          instance.$mount()
          instance.$el.appendChild(textBoxInstance.$el) // add child component, try adding named slots, but didn't work
          this.$refs.controls.appendChild(instance.$el)
      }
  }
}

}
label component with slot. named slot didn't worked

<template>
  <div class="controlContainer" :class="{vertial: labelTop}">
    <span v-bind:class="{bold: bold}" class=".controlContainer__cisLabel">{{caption}}</span>
    <slot name='control'></slot>
    <slot></slot>
  </div>
</template>

<script lang='ts'>
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'

@Component({
})

export default class LabelControlContainer extends Vue {
  @Prop({ type: String, default: '' }) public caption: string
  @Prop({ type: Boolean, default: true }) public bold: boolean
  @Prop({ type: Boolean, default: false }) public labelTop: boolean
}

</script>

child component that is going to added to slot and emit on value change

export default class TextBox extends Vue {
  @Prop({ type: String, default: 'placeholder text' }) public placeholder: string
  @Prop({ type: Object, default: () => ({}) }) public attributes: any
  @Prop({ type: Boolean, default: false }) public readonly: boolean
  @Prop({ type: String, default: 'text' }) public mode: string
  @Prop({ type: Boolean, default: true }) public isValid: boolean
  @Prop({ type: String, default: '' }) public value: string
  @Prop({ type: String, default: '' }) public name: string
  @Prop({ type: Boolean, default: false }) public setFocus: boolean

  private default = {}
  private unlockable: boolean = this.readonly

  private valueChanged(data: any): void {
    this.$emit('update-value', data.value, this.name)
  }
}

1 个答案:

答案 0 :(得分:0)

我不使用TypeScript,但我认为这与TypeScript无关,因此我将使用普通JavaScript进行回答。

要以编程方式添加slot元素,可以使用$slots属性,如下所示:

vm.$slots.default = ['Hello']
vm.$slots. foo = ['Hello'] // for named slots

…但是不幸的是,插槽必须是VNode的数组,而不是普通的DOM元素或Vue组件。您可以使用$createElement方法来创建它们,如下所示:

vm.$slots.default = [vm.$createElement('div', ['Hello'])]
vm.$slots.default = [vm.$createElement('HelloWorld')] // custom component

因此您的代码将如下所示:

const TextBox = {
  template: `
    <div>{{ text }}</div>
  `,
  props: ['text']
}

const LabelControl = {
  template: `
    <div>
      <slot name='control'></slot>
    </div>
  `,
  components: {
    TextBox
  }
}

const LabelControlComponent = Vue.extend(LabelControl)

new Vue({
  el: '#app',
  template: `
    <div>
      <div ref='controls'></div>
    </div>
  `,
  mounted () {
    let texts = ['a', 'b', 'c', 'd']
    texts.forEach(text => {
      let labelControl = new LabelControlComponent()
      labelControl.$slots.control = [
        labelControl.$createElement('TextBox', {
          props: { text }
        })
      ]
      labelControl.$mount()
      this.$refs.controls.appendChild(labelControl.$el)
    })
  }
})

听取来自slot子级的事件似乎是不可能的,因为它的作用域不同,但是您仍然可以使用$parent属性直接调用父级方法或从父级发出事件。 / p>

const TextBox = {
  template: `
    <div @click='$parent.show(text)'>{{ text }}</div>
  `
}

const LabelControl = {
  methods: {
    show (text) {
      this.text = text
    }
  }
}

JSFiddle Example

参考文献: