意见:将属性数据动态注入到<component>

时间:2019-03-20 14:28:46

标签: vue.js vuetify.js

我正在尝试创建一个布局组件,该组件可以动态地从一个或另一列中的父级创建给定的组件。在布局组件中,我使用指令创建子组件。可以,但是我很难将数据放入组件本身(它们具有不同的属性集)。

因此:

如何将属性数据注入到指令中?

我有一些工作要做,但这很混乱。我可以通过包含所有数据的“ genericProps”属性来实现。然后强迫孩子进行更新,他们通过updated()使用Object.assign(this, 'props', this.genericProps)解压缩genericProps并覆盖$props

感觉好像我正在对付vue.js的某些原理。调试时会填充布局组件的this.$options.components,但是所有这些组件都没有分配任何propsData。但是直接在Object.assign()上使用this.$options.components是行不通的,因为布局组件实际上是this.$childrend上这些组件的实际实例。

我也尝试遍历this.$childrend的{​​{1}}并分配propsData,但是必须有一个密钥来匹配具有正确子元素的组件,并且指出子级没有任何属性。

代码片段说明了可以工作的(丑陋的)示例:

父模板

this.$options.components

父代码

从API异步调用完成后,可通过mount()或通过watch调用此代码。

<template>
  <two-column-layout :firstColumn="$vuetify.breakpoint.mdAndUp ? firstColumn : singleColumn"
                      :secondColumn="$vuetify.breakpoint.mdAndUp ? secondColumn : []"
                      />
</template>

TwoColumnLayout模板

  createMetadataContent() {

    let currentContent = this.currentMetadataContent;
    const components = this.$options.components;

    currentContent = this.mixinMethods_enhanceMetadataEntry(currentContent, this.cardBGImages);

    if (currentContent && currentContent.title !== undefined) {

      this.body = metaDataFactory.createBody(currentContent);
      this.$set(components.MetadataBody, 'genericProps', this.body);

      this.citation = metaDataFactory.createCitation(currentContent);
      this.$set(components.MetadataCitation, 'genericProps', this.citation);
      // a few more components and data here


      this.firstColumn = [
        components.MetadataBody,
        // a few more components here
      ];

      this.secondColumn = [
        components.MetadataCitation,
        // a few more components here
      ];

      this.singleColumn = [
        components.MetadataBody,
        components.MetadataCitation,
        // a few more components here
      ];

      this.$forceUpdate();
    }
  }

TwoColumnLayout代码

<template>          
  <v-layout row wrap>
    <v-flex>

      <v-layout column>
        <v-flex mb-2
                v-for="(entry, index) in firstColumn"
                :key="`left_${index}`"
                >
          <component :is="entry"
                      :genericProps="entry.genericProps"
                      />
        </v-flex>
      </v-layout>
    </v-flex>

    <v-flex v-if="secondColumn" >
      <v-layout column>
        <v-flex mb-2
                v-for="(entry, index) in secondColumn"
                :key="`right_${index}`"
                >
          <component :is="entry"
                      :genericProps="entry.genericProps"
                      />
        </v-flex>
      </v-layout>
    </v-flex>
  </v-layout>
</template>

子代码

updated() {
  this.$children.forEach((child) => {
    child.$forceUpdate();
  });
},

2 个答案:

答案 0 :(得分:0)

我曾经有过类似的情况,我有一个动态<compnonent :is="currentComponent">并通过存储数据的'childPayload'属性并将其作为道具传递给孩子来解决。每当我的有效载荷发生变化时,它们的子项就会刷新。

<template>
    <div>
        <div class="btn btn-primary" @click="changePayload">Change Payload</div>
        <component :is="isWhat" :payload="payload"></component>
    </div>
</template>

<script>
    export default {
        name: "Test",
        data() {
            return {
                isWhat: 'div',
                payload: { any: 'data', you: 'want', to: 'pass'}
            }
        },
        methods: {
            changePayload() { // any method or api call can change the data
                this.payload = { some: 'changed', data: 'here' }
            }
        }
    }
</script>

答案 1 :(得分:0)

我可以通过插槽来解决它,但是我仍然需要在LayoutComponent中使用forceUpdate()。 但是,对于我的情况而言,使用genericProps就足够了。

父模板

  <template v-slot:leftColumn >

    <div v-for="(entry, index) in firstColumn" :key="`left_${index}`" >

      <component :is="entry" :genericProps="entry.genericProps" />
    </div>
  </template>

  <template v-slot:rightColumn>

    <div v-for="(entry, index) in secondColumn" :key="`right_${index}`" >

      <component :is="entry" :genericProps="entry.genericProps" />
    </div>
  </template>

父代码

当来自API的异步调用完成时,可通过mounted()或通过watch调用此代码。

  createMetadataContent() {

    let currentContent = this.currentMetadataContent;
    const components = this.$options.components;

    currentContent = this.mixinMethods_enhanceMetadataEntry(currentContent, this.cardBGImages);

    if (currentContent && currentContent.title !== undefined) {

      this.body = metaDataFactory.createBody(currentContent);
      this.$set(components.MetadataBody, 'genericProps', this.body);

      this.citation = metaDataFactory.createCitation(currentContent);
      this.$set(components.MetadataCitation, 'genericProps', this.citation);
      // a few more components and data here


      this.firstColumn = [
        components.MetadataBody,
        // a few more components here
      ];

      this.secondColumn = [
        components.MetadataCitation,
        // a few more components here
      ];

      this.singleColumn = [
        components.MetadataBody,
        components.MetadataCitation,
        // a few more components here
      ];

      this.$forceUpdate();
    }
  }

TwoColumnLayout模板

    <v-flex v-bind="firstColWidth" >

      <v-layout column>

        <slot name="leftColumn" />

      </v-layout>
    </v-flex>


    <v-flex v-if="secondColumn" v-bind="secondColWidth" >

      <v-layout column>

        <slot name="rightColumn" />

      </v-layout>
    </v-flex>

TwoColumnLayout代码

updated() {
  this.$children.forEach((child) => {
    child.$forceUpdate();
  });
},

子代码

所有子组件都有genericProps对象,可以通过mixin方法访问

    props: {
      genericProps: Object,
    },
    computed: {
      title() {
        return this.mixinMethods_getGenericProp('title');
      },
      id() {
        return this.mixinMethods_getGenericProp('id');
      },
      description() {
        return this.mixinMethods_getGenericProp('description');
      },
    }

Mixin方法

    mixinMethods_getGenericProp(propName) {
      return this.genericProps[propName] ? this.genericProps[propName] : null;
    },