How can I use dynamic components inside Vue.js funcional components with templates?


#1

Hello everybody!

Since Vue.js 2.5, it is possible to use functional components with templates. How can I use custom components within templates in functional components?

The usual method of using import and then passing the import to the components option does not seem to work.

<template functional>
  <div class="translation controlClass">
    <translation-accordion :control-small="false"
                           :translation="translation"
                           :line-id="lineId">
      <div class="content" :style="{ fontSize: props.textSize + 'rem'}">
        <slot></slot>
      </div>
    </translation-accordion>
  </div>
</template>

<script>

import TranslationAccordion from "./TranslationAccordion.vue"

export default {
    name: 'TextLine',
    components: {
        TranslationAccordion
    },
    props: ['lineId', 'audio', 'translation', 'textSize']
}
</script>

<style lang="scss">

$break-small: 500px;

.controlClass:hover .control {
    display: block;
}

.control {

    display: block;

    @media screen and (min-width: $break-small) {
        display: none;
    }
}

</style>

#2

Props option should be declared in child component. Just as official documents said:

Data can be passed down to child components using props.A prop is a custom attribute for passing information from parent components. A child component needs to explicitly declare the props it expects to receive using the props option.

in your context, child component is TranslationAccordion.

In addition, read Template for Functional Components.


#3

Thank you for your reply!

I believe I didn’t write my post clearly.

The component I am presenting here is the functional one, not TranslationAccordion. I want to be use TranslationAccordion inside this functional component but I can not use “import” nor the “directives” option. So I get an error saying that TranslationAccordion can not be found.


#4

Can you tell me the actual error you are getting?


#5

If I use:

import TranslationAccordion from "./TranslationAccordion.vue"

and

components: {
TranslationAccordion
}

in my functional component i get the following:

[Vue warn]: Error in render: "TypeError: _vm is undefined"

found in

---> <TextLine> at src/components/TextLine.vue
       <Page> at src/components/Page.vue
         <App> at src/components/App.vue
           <Root>  client.js:9:6285
TypeError: _vm is undefined
Stack trace:

However, if I remove the import and the components option, I get the following:

[Vue warn]: Unknown custom element: <translation-accordion> - did you register the component correctly? For recursive components, make sure to provide the "name" option.

found in

---> <Page> at src/components/Page.vue
       <App> at src/components/App.vue
         <Root>

#6

Thanks.

Sounds like a bug. I will check it out later today.


#7

Awesome. I would really appreciate.

I have been looking forward to use this feature for a while, since I need it to optimize a page!

Thanks for the hardwork for the 2.5 release.


#8

@climatwearrior What versions of the vue supporting libraries do you have? Is it possible you upgraded the vue core but not the vue-loader?

It is working perfect for me. I have the following versions:

"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"vue": "^2.5.2"

EDIT:
I was wrong, it is not working for me either. I’m unsure whether this is intended behaviour or not - in my mind this should work though. I will open a bug report at least and see where that leads (@LinusBorg ). In the meantime, you could register the component globally.

<script>
import TestComp from './TestComp'
import Vue from 'vue'

Vue.component('TestComp', TestComp)

export default {
  name: 'HelloWorld',
  props: {
    msg: {
      type: String,
      required: true
    }
  }
}
</script>


#9

Thanks. When you open the bug report, please let me know the link so that I can follow. I also believe this should be supported since it works with functional render functions.


#10

#11

How to use custom component in funcional component?

A solution is to use slot mechanism to distribute content. for example,
in functional component(such as HelloWorld.vue):

<template functional>
  <div class="hello">
    <h1>{{ props.msg }}</h1>
    
    <!-- for custom component -->
    <slot name="test-comp"></slot>
</template>
...

in component which use functional component as child. such as app.vue:

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <HelloWorld :msg="msg">
      <!--in usage, contain custom component as child of functional component -->
      <test-comp slot="test-comp"></test-comp>
    </HelloWorld>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld'
// TestComp as custom component
import TestComp from './components/TestComp' 

export default {
  name: 'app',
  components: {
    HelloWorld,
    TestComp
  },
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>
...