Register Vue.js component dynamically from a string

1.1k views Asked by At

I have a CRUD that enables me to write Vue.js component's code in the textarea like:

<template>
<div><p class='name-wrapper'>{{ model.name }}</p></div>
</template>
<script>
module.exports = {
  name: 'NameWrapper',
  props: ['model']
}
</script>
<style lang='sass'>
.name-wrapper
  color: red
</style>

Then in other component, I fetch this data and want to register it as a dynamic/async, custom component like:

<template>
<component :is='dynamicName' :model='{name: "Alex"}'></component>
</template>
<script>
import httpVueLoader from 'http-vue-loader'
import Vue from 'vue'

export default {
name: 'DynamicComponent',
props: ['dynamicName', 'componentDefinitionFromTextareaAsString'],
beforeCreate: {
  // I know that as a second parameter it should be an url to the file, but I can't provide it, but I would like to pass the contents of the file instead there:
  httpVueLoader.register(Vue, this.$options.propsData.componentDefinitionFromTextareaAsString)
  // I was trying also:
  Vue.component(this.$options.propsData.dynamicName, this.$options.propsData.componentDefinitionFromTextareaAsString)
}
}
</script>

As far as I know, httpVueLoader needs the url to the .vue file instead - is there a way to pass there the code itself of the component?

I am aware that passing and evaluating <script></script> tag contents can cause security issues, but I really need to do it that way.

I've read also about Vue.js compile function, but that works only for templates, not the code of the component (so the script tags again).

Is it even possible to achieve such functionality in Vue.js?

1

There are 1 answers

0
skirtle On BEST ANSWER

It should be possible to use a data: URI with http-vue-loader, like this:

const vueText = `
  <template>
      <div class="hello">Hello {{who}}</div>
  </template>
 
  <script>
  module.exports = {
      data: function() {
          return {
              who: 'world'
          }
      }
  }
  <\/script>
 
  <style>
  .hello {
      background-color: #ffe;
  }
  </style>
`

const MyComponent = httpVueLoader('data:text/plain,' + encodeURIComponent(vueText))

new Vue({
  el: '#app',
  
  components: {
    MyComponent
  }
})
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<script src="https://unpkg.com/[email protected]/src/httpVueLoader.js"></script>

<div id="app">
  <my-component></my-component>
</div>

If that doesn't work for some reason (maybe because one of your target browsers doesn't support it) then you could get it working by patching httpRequest. See https://www.npmjs.com/package/http-vue-loader#httpvueloaderhttprequest-url-. The documentation focuses on patching httpRequest to use axios but you could patch it to just resolve the promise to the relevant text.