2017-06-27 4 views
1

Vue.js 2.3の<component v-for="...">タグを使用して、コンポーネントのリストを動的にレンダリングしています。同期とイベントのある動的Vueコンポーネント

テンプレートは次のようになります。

<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 '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である。これは可能ですか?私はどんな提案も歓迎する!

答えて

2

動的コンポーネントを使用してこれを達成する方法がわかりません。ただし、レンダリング機能を使用して行うこともできます。

あなたの変更であるこのデータ構造を検討してください。 syncon

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

は、ここで私は他の二つのプロパティを定義しています。 syncプロパティは、 syncにするすべてのプロパティのリストを含むオブジェクトです。たとえば、いずれかのコンポーネントの syncプロパティの上には、 volume: "volume"が含まれています。これは通常、 :volume.sync="volume"として追加したいプロパティを表します。動的コンポーネントに動的に追加できる方法はありませんが(レンダリング機能では、それを分解して部分に分解し、 updated:volumeのプロパティとハンドラを追加することができます)。

同様に、render関数では、値で識別されるメソッドを呼び出すキーによって識別されるイベントのハンドラーを追加できます。レンダリング機能の実装方法は次のとおりです。

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メソッドは、コンポーネントをレンダリングする方法を決定するためにmodulesであなたのtemplate内のすべてのプロパティを調べます。プロパティの場合は、プロパティを渡すだけです。 syncプロパティの場合はプロパティとイベントハンドラに分割し、onハンドラの場合は適切なイベントハンドラを追加します。

この作業の例を次に示します。

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) 
 
    }, 
 
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script> 
 
<div id="app"></div>

+0

ありがとう!私はカスタムレンダリング機能を実行していないので、私は一読する必要があります。カスタムディレクティブよりも理にかなっているようです。私はそれを試して、それがどうなるか見てみましょう! – DMack

+0

もう一度ありがとう、これは間違いなく正しい方向に私を指しているように見えます。技術的には私の質問に答えますが、私は道に沿っていくつかの合併症を予見しています。私は 'vuedraggable'でそれを使うつもりなので、このカスタムレンダリングされたコンポーネントを' draggable'でラップしますが、すべての変数、コールバックなどは親のスコープに含まれます。どのように私は非厄介な方法でそれらを使用することができますか? (個別の小道具としてすべての単一のvarを渡すことは、このケースでは "乱雑"と感じ、私はここでイベントを処理する方法を知らない)。何かご意見は? – DMack

+0

@DMack複数の変数を渡すことは、 'v-bind =" someObject "'を使うことで比較的簡単に行うことができます。 'someObject'は、キーがコンポーネントのプロパティで値を渡すオブジェクトです。 – Bert

関連する問題