[Vuejs]-Blind dynamic load vuejs component by name with properties

2👍

App.vue

<template>
  <div id="app">
    <DynamicComponentSet :definition="response"/>
  </div>
</template>

<script>
import DynamicComponentSet from "@/components/DynamicComponentSet";

export default {
  name: "App",
  components: {
    DynamicComponentSet
  },
  data() {
    return {
      response: [
        { is: "Title", text: "Hello World" },
        { is: "Status", type: "info", text: "Look at these beautiful photos" },
        { is: "Carousel", imgs: ["/img/1.jpg", "/img/2.jpg", "/img/3.jpg"] },
        {
          is: "Status",
          type: "alert",
          text: "These images are the property of the creator"
        }
      ]
    };
  }
};
</script>

DynamicComponentSet.vue

<template>
  <div>
    <component v-for="(comp, index) in definition" :key="index" v-bind="comp" :is="comp.is"/>
  </div>
</template>

<script>
export default {
  name: "DynamicComponentSet",
  components: {
    Title: () => import("@/components/Title.vue"),
    Status: () => import("@/components/Status.vue"),
    Carousel: () => import("@/components/Carousel.vue")
  },
  props: {
    definition: Array
  }
};
</script>

Note 1: all possible is values has to be specified here in components. Components are loaded on demand (async components).

Note 2: All individual response object’s properties are passed into individual components with v-bind="comp" using props as an object syntax so data properties names/types must match to each component props….

Note 3: :is="comp.is" is actually not needed to make it work because is is passed along with other props. I added that just to make ESLint happy…

demo

1👍

instead of importing components directly, you can wrap them with import like below

this will only loads(from server) that components when actually needed

<template>
  <component is="Title" text="Hello World"/>
  <component is="Status" type="info" text="Look at these beautiful photos"/>
  <component is="Carousel" imgs="['/img/1.jpg','/img/2.jpg','/img/3.jpg']"/>
  <component is="Status" type="alert", text="These images are the property of the creator"/>
</template>
<script>
    // removed imports
    export default {
        components: {
            Title: () => import('@/components/libs/Title.vue'),
            Status: () => import('@/components/libs/Status.vue'),
            Carousel: () => import('@/components/libs/Carousel.vue')
        },
    }
</script>

1👍

Instead of importing components dynamically using the computed method.

    computed: {
        Title() {
            return () => import('@/components/libs/Title.vue');
        },
        Status() {
            return () => import('@/components/libs/Status.vue');
        }
    },

In template

    <component v-bind:is="Title"></component>
    <component v-bind:is="Status"></component>

0👍

Will the response always have those fields? You want to load the component before the ajax request? Like with default values?

parent component

<template>
  <CustomComponent :responseData="Title" />
</template>
<script>
    import CustomComponent from '@/components/libs/CustomComponent.vue'
    export default {
        components: {
            CustomComponent
        },
        data() {
          return {
            responseData: null, // populate this with ajax response
          }
        }
    }
</script>

CustomComponent

<template>
  <div> <!-- components should only have one root element so wrap in a div -->
    <p v-if="response.text">{{ response.text }}</p>
    <p type="info">Look at these beautiful photos<p/>
    <!-- etc. etc. -->
  </div>
</template>
<script>
    import Status from '@/components/libs/Status.vue'
    import Carousel from '@/components/libs/Carousel.vue'
    export default {
        props: {
          responseData: {
            text: 'default text', // these defaults allow you to load before the ajax response comes in
            images: ['/img/1.jpg','/img/2.jpg','/img/3.jpg'],
          }
        },
        components: {
            Status,
            Carousel
        },
    }
</script>

Leave a comment