Vue:如何基于父数据渲染动态组件?

时间:2019-01-15 16:59:32

标签: javascript vue.js vuejs2

我正在尝试根据传递到父页面的数据来呈现组件。

我正在尝试使用动态组件基于JSON对象 @type 呈现正确的组件,并在URL的(不带扩展名)中获取文件名该对象然后呈现相关组件,并将数据从 Page.vue 传递到呈现的组件。

如果我设置:is =“ content。@ type” (例如),则输出将渲染名称为 Image Video的子组件(删除URL中所有要求文件名的内容)以呈现正确的组件。

这是否可行,或者有更好的方法吗?

数据:

Content: object
  Content [Array]
    0:Object
      @id: "4effb045"
      @type: "http://url/images.json"
      _meta: Object
         name: "TestingImage"
    1:Object
      @id: "4effb045"
      @type: "http://url/video.json"
      _meta: Object
         name: "TestingVideo"

数据来自CMS,我无法更改数据。

Page.vue

<template>
  <div id="page" v-if="content" class="page-wrapper container" :cid="this.$root.cidItem">

    <div class="row">

      <div class="page__items">

        <component v-for="content in content.content" :is="content.@type" :key="content.id"></component>

      </div>
    </div>

  </div>
</template>

<script>
  import axios from 'axios'
  import Images from "./components/Images"
  import Video from "./components/Video"
  import TextType from "./components/TextType"
  import Card from "./components/Card"

  export default {
    name: 'page',
    props: ['cid'],
    components: {
      Images,
      Video,
      TextType,
      Card
    },

    mounted () {
      axios({
        method: 'GET',
        'url': this.$root.contentDeliveryUrl + this.$root.encodedQuery + this.$root.cidItem + this.$root.store
      }).then(result => {
        // eslint-disable-next-line
        this.content = amp.inlineContent(result.data)[0];
        console.log(this.content)
      }, error => {
        console.error(error)
      })
    },
    data () {
      return {
        content: []
      }
    }

  }
</script>

<style lang="scss">
  @import '../node_modules/bootstrap-css-only/css/bootstrap.min.css';
  .container {
    max-width: 1366px;
  }
</style>

子组件示例 Images.vue

<template>
  <div class="images-wrapper container">
    <div class="row">
      <div class="col">
        <div class="images">
          {{content.imageAltText}}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>

export default {
  name: 'Images'
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
  .images-wrapper {
    &__image {

    }
  }
</style>

2 个答案:

答案 0 :(得分:2)

如评论中所述,您可以使用v-bind:is指令在方法中解析URL。您可能现在已经知道了,但这是我的快速尝试。

const content = {
  content: [{
    '@id': '4effb045',
    '@type': 'http://url/images.json',
    _meta: {
      name: 'TestingImage'
    }
  }, {
    '@id': '4effb046',
    '@type': 'http://url/videos.json',
    _meta: {
      name: 'TestingVideo'
    }
  }]
}

new Vue({
  el: '#app',

  data() {
    return {
      content
    }
  },

  methods: {
    extractName(content) {
      const [name] = /[^\/]*(?=\.\w+$)/.exec(content['@type']);

      return name;
    }
  },

  components: {
    Images: {
      template: '<img src="https://via.placeholder.com/150x45" />'
    },

    Videos: {
      template: '<div>Some video component</div>'
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <component 
    v-for="content in content.content" 
    :is="extractName(content)" 
    :key="content['@id']">
  </component>
</div>

顺便说一句,我建议不要使用 HTML保留字作为组件名称(例如您的情况<video>),因为它可能无法编译。在GitHub上查看此related issue

  

[Vue警告]:请勿将内置或保留的HTML元素用作组件ID:对话框

答案 1 :(得分:1)

Your approach is correct. You can use the is directive to call your components. It is the best way.

As you asked, you need a method in your Page.vue to parse your @type.

methods: {
    parseType: function(type) {
        let typeParts = type.split("/")
        let filenameParts = typeParts[typeParts.length - 1].split('.')
        let parsedType = filenameParts[0]
        return parsedType 
    }
}

Now you can use this function in your template.

<component v-for="content in content.content" :is="parseType(content.@type)" :key="content.id"></component>