具有同步和事件的动态Vue组件

时间:2017-06-27 00:18:25

标签: vue.js vue-component

我在Vue.js 2.3中使用<component v-for="...">标签来动态呈现组件列表。

模板如下所示:

<some-component v-for="{name, props}, index in modules" :key="index">
    <component :is="name" v-bind="props"></component>
</some-component>

modules数组位于我的组件data()中:

modules: [
    {
        name: 'some-thing',
        props: {
            color: '#0f0',
            text: 'some text',
        },
    },
    {
        name: 'some-thing',
        props: {
            color: '#f3f',
            text: 'some other text',
        },
    },
],

我使用v-bind={...}对象语法来动态绑定道具,这非常有效。我还想用这种方法将事件监听器绑定到v-on(并使用.sync&#39; d props),但我不知道如果没有创建自定义,它是否可行指令。

我尝试添加到这样的props个对象,但它没有工作:

props: {
    color: '#f3f',
    text: 'some other text',
    'v-on:loaded': 'handleLoaded', // no luck
    'volume.sync': 'someValue', // no luck
},

我的目标是让用户使用vuedraggable在侧边栏中重新排序小部件,并将其布局首选项保留在数据库中,但某些小部件包含@event.sync ed prop s。这可能吗?我欢迎任何建议!

1 个答案:

答案 0 :(得分:3)

我不知道如何使用动态组件完成此操作。但是,您可以使用渲染功能来完成它。

考虑这个数据结构,这是对你的修改。

modules: [
  {
    name: 'some-thing',
    props: {
      color: '#0f0',
      text: 'some text',
    },
    sync:{
      "volume": "volume"
    },
    on:{
      loaded: "handleLoaded"
    }
  },
  {
    name: 'other-thing',
    on:{
      clicked: "onClicked"
    }
  },
],

我在这里定义了另外两个属性:synconsync属性是一个对象,其中包含您希望sync所需的所有属性的列表。例如,其中一个组件的sync属性包含volume: "volume"。这表示您希望通常添加为:volume.sync="volume"的属性。没有办法(我知道)你可以动态地将它添加到动态组件中,但是在渲染功能中,你可以将它分解为它的去糖部分并添加属性和updated:volume的处理程序。

on属性类似,在渲染函数中,我们可以为由键调用的事件添加处理程序,该事件调用值中标识的方法。以下是该渲染函数的可能实现。

render(h){
  let components = []
  let modules = Object.assign({}, this.modules)
  for (let template of this.modules) {
    let def = {on:{}, props:{}}
    // add props
    if (template.props){
      def.props = template.props
    } 
    // add sync props
    if (template.sync){
      for (let sync of Object.keys(template.sync)){
        // sync properties are just sugar for a prop and a handler
        // for `updated:prop`. So here we add the prop and the handler.
        def.on[`update:${sync}`] = val => this[sync] = val
        def.props[sync] = this[template.sync[sync]]
      }
    }
    // add handers
    if (template.on){
      // for current purposes, the handler is a string containing the 
      // name of the method to call
      for (let handler of Object.keys(template.on)){
        def.on[handler] = this[template.on[handler]]
      }
    }
    components.push(h(template.name, def))
  }
  return h('div', components)
}

基本上,render方法会查看templatemodules中的所有属性,以决定如何渲染组件。在属性的情况下,它只是传递它们。对于sync属性,它将其分解为属性和事件处理程序,对于on处理程序,它会添加适当的事件处理程序。

以下是此工作的一个示例。

&#13;
&#13;
console.clear()

Vue.component("some-thing", {
  props: ["volume","text","color"],
  template: `
    <div>
     <span :style="{color}">{{text}}</span>
      <input :value="volume" @input="$emit('update:volume', $event.target.value)" />
      <button @click="$emit('loaded')">Click me</button>
    </div>
  `
})

Vue.component("other-thing", {
  template: `
    <div>
      <button @click="$emit('clicked')">Click me</button>
    </div>
  `
})

new Vue({
  el: "#app",
  data: {
    modules: [{
        name: 'some-thing',
        props: {
          color: '#0f0',
          text: 'some text',
        },
        sync: {
          "volume": "volume"
        },
        on: {
          loaded: "handleLoaded"
        }
      },
      {
        name: 'other-thing',
        on: {
          clicked: "onClicked"
        }
      },
    ],
    volume: "stuff"
  },
  methods: {
    handleLoaded() {
      alert('loaded')
    },
    onClicked() {
      alert("clicked")
    }
  },
  render(h) {
    let components = []
    let modules = Object.assign({}, this.modules)
    for (let template of this.modules) {
      let def = {
        on: {},
        props: {}
      }
      // add props
      if (template.props) {
        def.props = template.props
      }
      // add sync props
      if (template.sync) {
        for (let sync of Object.keys(template.sync)) {
          // sync properties are just sugar for a prop and a handler
          // for `updated:prop`. So here we add the prop and the handler.
          def.on[`update:${sync}`] = val => this[sync] = val
          def.props[sync] = this[template.sync[sync]]
        }
      }
      // add handers
      if (template.on) {
        // for current purposes, the handler is a string containing the 
        // name of the method to call
        for (let handler of Object.keys(template.on)) {
          def.on[handler] = this[template.on[handler]]
        }
      }
      components.push(h(template.name, def))
    }
    return h('div', components)
  },
})
&#13;
<script src="https://unpkg.com/vue@2.2.6/dist/vue.js"></script>
<div id="app"></div>
&#13;
&#13;
&#13;