Vue: What if I just... replace a component definition?

by Admiral Potato

HTML

<link rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css">
<script src="//unpkg.com/babel-polyfill@latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>

    <div id="app">
      <custom-content
        :content="content_a"
        :env="env"  
      ></custom-content>
      <custom-content
        :content="content_b"
        :env="env"  
      ></custom-content>
      <form class="mono" @submit.prevent>
        <p>env.contact</p>
        <label for="first_name">
          First Name
          <input id="first_name" v-model="env.contact.first_name" />
        </label>
        <label for="last_name">
          Last Name
          <input id="last_name" v-model="env.contact.last_name" />
        </label>
      </form>
      <form class="mono" @submit.prevent>
        <p>content_a</p>
        <textarea v-model="content_a.html"></textarea>
        <textarea v-model="content_a.js"></textarea>
      </form>
      <form class="mono" @submit.prevent>
        <p>content_b</p>
        <textarea v-model="content_b.html"></textarea>
        <textarea v-model="content_b.js"></textarea>
      </form>
    </div>

SCSS

.mono {
  * {
    font-family: monospace;
  }
  textarea {
    display: block;
    height: 120px;
    width: 100%;
  }
}

JavaScript

Vue.component('custom-content', {
  template: `
    <component
      :key="content.html + content.js"
      :is="componentName"
      :env="env"
    ></component>
  `,
  props: {
    content: Object,
    env: Object
  },
  beforeCreate () {
    this.componentName = 'dynamic-child-' + btoa(Math.random().toString().slice(2)).replace(/([0-9]|=)/g, '')
    Vue.component(this.componentName, {
      template: `<div class="dynamic_child" id="${this.componentName}"><h1>You should never see this</h1></div>`,
    })
    console.log('customConent.beforeCreate', this.componentName)
  },
  created () {
    console.log('customConent.created', this.componentName)
    this.replaceComponent()
  },
  methods: {
    replaceComponent () {
      console.log('replaceComponent started', 'what', this.componentName)
      const functionBodyString = `var view = {};${this.content.js};return view;`
      const mixin = (new Function(functionBodyString)).apply(null)
      Vue.component(this.componentName, {
        name: this.componentName,
        mixins: [mixin],
        template: `<div class="dynamic_child" id="${this.componentName}">${this.content.html}</div>`,
        data () {
          return {
            previousEnvKeys: []
          }
        },
        props: {
          env: Object
        },
        created () {
          this.updateEnv()
        },
        methods: {
          updateEnv () {
            let newEnvKeys = []
            this.previousEnvKeys.forEach((key) => {
              this.$set(this, key, null)
            })
            const scope = Object.assign(
              {
                general: this.env.account.general
              },
              this.env
            )
            console.log('dynamicChild.updateEnv', {
              scope,
              previousEnvKeys: this.previousEnvKeys
            })
            Object.entries(scope).map(([key, value]) => {
              newEnvKeys.push(key)
              this[key] = value
              this.$set(this,...