Search code examples
javascriptvuejs3vue-sfcvue3-sfc-loader

Dynamically adding new .vue component


I am using vue3-sfc-loader (https://github.com/FranckFreiburger/vue3-sfc-loader) to register and load .vue components that works fine:

sample 1

<div id="app">
    <ae-studio></ae-studio>
</div>
<script>
    const app = Vue.createApp({});
    app.component('ae-studio', loadVM("/components/aeStudio.vue"));
    const vueInstance = app.mount("#app");
</script>

sample 2

<template>
    <div>
        append studio header
    </div>
    <table style="width:100%;">
        <tr>
            <td style="width:50%">
                <ae-nav></ae-nav>
            </td>
            <td style="width:50%">
                <ae-docsview></ae-docsview>
            </td>
        </tr>
    </table>
</template>
<script>
    app.component('ae-nav', loadVM("/components/aeNav.vue"));
    app.component('ae-docsview', loadVM("/components/aeDocsview.vue"));    
</script>

they are working but I need to dynamically add a component to the page like this :

<template>
    <button @click="show('this is a message')">Click to add Component here</button>
    <div id="comPlace">A place for new component</div>
</template>

<script>    
    app.component('ae-test', loadVM("/components/aeTest.vue"));

    export default {
        setup(props) {
            return {
                show: (msg) => {

                    console.log(msg);
                    let cPlace = document.getElementById("comPlace");
                    cPlace.innerHTML='<ae-test>qqqqqqqqqqqqqqqqqqq</ae-test>';
                    let a = app.component('ae-test', loadVM("/components/aeTest.vue"));
                }
            };
        }
    }
</script>

Above code is not working. I tried forceUpdate but the issue not resolve. How can I resolve this? I created a repository on github that you can use it for tracking the issue.

https://github.com/mirshahreza/DynamicVueComponen


Solution

  • You can use component in vue 3

     <script>
    
          const { loadModule, vueVersion } = window["vue3-sfc-loader"];
          console.log("vueVersion", vueVersion);
          const options = {
            moduleCache: {
              vue: Vue
            },
            getFile(url) {
              return fetch(url).then((response) =>
                response.ok ? response.text() : Promise.reject(response)
              );
            },
            addStyle(styleStr) {},
            log(type, ...args) {
              console.log(type, ...args);
            }
          };
          window.loadVM = async (path) => {
            return loadModule(path, options);
          };
     
        </script>
    

    and you can use Vue.defineAsyncComponent

    <template>
      <button @click="() => showTestOneComponent('this is a message')">
        Click to add test here
      </button>
      <button @click="() => show2TestOneComponent('this is a test2 component')">
        Click to add test2 here
      </button>
      <div id="comPlace">
        <component :is="dynamicComponent" :title="title" />
      </div>
    </template>
    
    <script>
    import { ref } from "vue";
    export default {
      setup(props) {
        const dynamicComponent = ref();
        const title = ref();
        const showTestOneComponent = async (msg) => {
          dynamicComponent.value = Vue.defineAsyncComponent(() =>
            window.loadVM("./test.vue")
          );
          title.value = msg;
        };
        const show2TestOneComponent = async (msg) => {
          dynamicComponent.value = Vue.defineAsyncComponent(() =>
            window.loadVM("./test2.vue")
          );
          title.value = msg;
        };
        return {
          dynamicComponent,
          title,
          showTestOneComponent,
          show2TestOneComponent,
        };
      },
    };
    </script>
    

    updated full code here.