Rob Erskine
Rob Erskine

Reputation: 927

Nuxt/Vue.js - Dynamically loading in a child component based on a prop

I have a sectionHeader.vue component that I want to use on lots of different pages. Inside that sectionHeader.vue is a <canvas> element (that I am using for some robust three.js animations) that I dynamically want to change inside of each header through props. I'd like to get this working to take advantange of Vue's inherent code-splitting so that each page doesn't load every single other pages animation js code, only the code that is relevant to it.

I'm attempting to dynamically load this in using dynamic components but I don't think this is what I'm looking for...

My folder structure:

components
  animations
    robust-animation-1.vue
    robust-animation-2.vue
    maybe-like-15-more-of-these.vue
  headings
    heading2.vue
  SectionHeader.vue
pages
  index.vue

pages/index.vue:

<sectionHeader post-title="Menu" class-name="menu" canvas="./animations/robust-animation-1"></sectionHeader>

components/SectionHeader.vue:

<template>
    <section class="intro section-intro" :class="className">
        <heading2 :post-title="postTitle"></heading2>
        <component v-bind:is="canvas"></component> <!-- this is what i am dynamically trying to load --> 
        {{canvas}} <!-- this echos out ./animations/robust-animation-1 -->
    </section>
</template>

<script>
import heading2 from './headings/heading2';

export default {
    components: {
        heading2
    },
    props: [
        'postTitle', 'className', 'canvas'
    ]
}
</script>

components/animations/robust-animation-1.vue:

<template>
    <div>
        <canvas class="prowork-canvas" width="960" height="960"></canvas>
    </div>
</template>

<script>
// 200 lines of js here that manipulates the above canvas
</script>

My heading2 component works great, but no matter what I try I can't figure out how to dynamically pull in my animation component. I get varying degrees of the below error:

client.js 76 DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('./animations/robust-animation-1') is not a valid name.

I looked into scoped slots, and that looks close to what I need, but not exactly. Is there a better way to do what I'm attempting?

Upvotes: 4

Views: 1149

Answers (1)

Pratik Patel
Pratik Patel

Reputation: 6978

I have successfully done Dynamically loading in a child component based on a prop. Please refer to the following code.

components
  animations
    robust-animation-1.vue
    robust-animation-2.vue
    maybe-like-15-more-of-these.vue
  headings
    heading2.vue
  SectionHeader.vue
pages
  index.vue

vuepages/index.vue: 

  <section-header post-title="Menu" class-name="menu" canvas="./animations/robust-animation-1">
  </section-header>
  <script>
    import SectionHeader from "../components/SectionHeader";

    export default {
        name: 'PageIndex',
        components: {SectionHeader}
    }
  </script>

components/SectionHeader.vue:

<template>
  <div>
    post_title {{postTitle}}
    <br/>
    class-name {{className}}
    <br/>
    canvas {{canvas}}
    <br/>
    <heading2 post-title="postTitle"></heading2>
    <br/>
    <component v-bind:is="stepComponent"></component>
  </div>
</template>

<script>
    import Heading2 from "./headings/heading2";

    export default {
        name: "SectionHeader",
        components: {
            Heading2,
        },
        props: ['postTitle', 'className', 'canvas'],
        computed: {
            stepComponent() {
                let data = this.canvas.split('/')[2];
                return () => import(`./animations/${data}`);
            }
        },
    }
</script>

You're missing that last computed step as part of the SectionHeader.vue component

components/animations/robust-animation-1.vue

<template>
  <div>
    From - robust-animation-1
  </div>
</template>

<script>
    export default {
        name: "robust-animation-1"
    }
</script>

Output:

post_title Menu 
class-name menu 
canvas ./animations/robust-animation-1 
postTitle 
From - robust-animation-1

Upvotes: 1

Related Questions