最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to programmatically create Vue Components indirectly - Stack Overflow

programmeradmin1浏览0评论

I'm attempting to add ponents programatically to a page in my Vue Single file Component which works beautifully if you know what you'd like to do ahead of time. In my case, the ponents and their layout will be received via JSON and I'm attempting to create them dynamically. Below you can see I'd like to do something like eval but this doesn't work for uninstantiated objects unfortunately. The code below is simplified with hardcoded values to get the point across.

<template>
  <div id="app">
    <h2>Static template:</h2>
    <InputTemplate type="text"></InputTemplate>
    <h2>Dynamically inserted:</h2>
    <div ref="container">
      <button @click="onClick">Click to insert</button>
      <br/>
    </div>
  </div>
</template>  

<script>
import Vue from 'vue'
//the ponent I'd like to create
import InputTemplate from './ponents/InputTemplate.vue'

export default {
  name: 'app',
  ponents: { InputTemplate },
  methods: {
    onClick() {
      //I'd like to do something like this, but eval doesn't work like this
      var ComponentClass = Vue.extend(eval('InputTemplate'))
      var instance = new ComponentClass({
        propsData: {type: 'text' }
      })
      instance.$mount()
      this.$refs.container.appendChild(instance.$el)
    }
  }
}
</script>

I put together a helper function below that replaces eval and works, but feels kinda inelegant for the solution. It also requires a lot of upkeep for each new ponent added. Is there a better way to do this?

returnComponent(ponentString){
      if(ponentString === 'InputTemplate'){
        return InputTemplate;
      }
    }

I'm attempting to add ponents programatically to a page in my Vue Single file Component which works beautifully if you know what you'd like to do ahead of time. In my case, the ponents and their layout will be received via JSON and I'm attempting to create them dynamically. Below you can see I'd like to do something like eval but this doesn't work for uninstantiated objects unfortunately. The code below is simplified with hardcoded values to get the point across.

<template>
  <div id="app">
    <h2>Static template:</h2>
    <InputTemplate type="text"></InputTemplate>
    <h2>Dynamically inserted:</h2>
    <div ref="container">
      <button @click="onClick">Click to insert</button>
      <br/>
    </div>
  </div>
</template>  

<script>
import Vue from 'vue'
//the ponent I'd like to create
import InputTemplate from './ponents/InputTemplate.vue'

export default {
  name: 'app',
  ponents: { InputTemplate },
  methods: {
    onClick() {
      //I'd like to do something like this, but eval doesn't work like this
      var ComponentClass = Vue.extend(eval('InputTemplate'))
      var instance = new ComponentClass({
        propsData: {type: 'text' }
      })
      instance.$mount()
      this.$refs.container.appendChild(instance.$el)
    }
  }
}
</script>

I put together a helper function below that replaces eval and works, but feels kinda inelegant for the solution. It also requires a lot of upkeep for each new ponent added. Is there a better way to do this?

returnComponent(ponentString){
      if(ponentString === 'InputTemplate'){
        return InputTemplate;
      }
    }
Share Improve this question edited Feb 18, 2018 at 21:36 Matthew Hiles asked Feb 18, 2018 at 21:11 Matthew HilesMatthew Hiles 1933 silver badges9 bronze badges 3
  • 1 Have you tried Async ponents? – thibautg Commented Feb 18, 2018 at 22:19
  • If I'm understanding correctly, using Async ponents would allow me to dynamically import the ponents as necessary by calling them with something like myComponent: () => import(myComponent)? – Matthew Hiles Commented Feb 18, 2018 at 22:48
  • I haven't tried myself but this also how I understand it. I think you can replace the import function with another function returning a ponent object inside a Promise. – thibautg Commented Feb 18, 2018 at 23:02
Add a ment  | 

2 Answers 2

Reset to default 4

It turns out that Vue has <ponent :is="yourComponentType"> which does exactly what I was hoping to acplish if you throw it into a v-for loop. Documents here.

You can use Async ponents to retrieve a ponent via an API.

I've tested with GraphQL via Axios but it should work with any REST service.

The main ponent looks like that:

<template>
  <div>
    <h1>Test page</h1>
    <div><button @click="clicked = true">Load ponent</button></div>
    <async-ponent v-if="clicked" />
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      clicked: false,
    };
  },
  ponents: {
    'async-ponent': () => axios.post('http://localhost:5000/graphql', { query: `
      {
        asyncComponentByRowId(rowId: 1) {
          ponent
          content
        }
      }
    `,
    }).then((response) => {
      const p = response.data.data.asyncComponentByRowId.content;
      // eval can be harmful
      return eval(`(${p})`);
    }),
  },
};
</script>

The retrieved ponent is in the following format:

{
  "data": {
    "asyncComponentByRowId": {
      "ponent": "my-async-ponent",
      "content": "{\r\n\ttemplate: '<div>{{ msg }}</div>',\r\n\tdata: function() {\r\n\t\treturn {\r\n\t\t\tmsg: 'Async ponent works!'\r\n\t\t};\r\n\t}\r\n}"
    }
  }
}

The decoded data.asyncComponentByRowId.content property is a string representing a JavaScript object:

{
  template: '<div>{{ msg }}</div>',
  data: function() {
    return {
      msg: 'Async ponent works!'
    };
  }
}

When the button is pressed, the ponent is shown after being downloaded from the API asynchronously.

The only problem is that I'm using eval to decode the object.

Result:

发布评论

评论列表(0)

  1. 暂无评论