ChatGPT解决这个技术问题 Extra ChatGPT

是否可以将组件作为道具传递并在 Vue 的子组件中使用它?

In a Vue 2.0 app, let's say we have components A, B and C.

A declares, registers and uses B

Is it possible to pass C from A to B?

Something like this:

<template>
  <div class="A">
    <B :child_component="C" />
  </div>
</template>

And use C in B somehow.

<template>
  <div class="B">
    <C>Something else</C>
  </div>
</template>

The motivation: I want to create a generic component B that is used in A but receives from A its child C. Actually A will use B several times passing different 'C's to it.

If this approach is not correct, what is the proper way of doing it in Vue?

Answering @Saurabh

Instead of passing as props, I tried the suggestion inside B.

<!-- this is where I Call the dynamic component in B -->

<component :is="child_component"></component>

//this is what I did in B js
components: {
 equip: Equipment
}, 
data () {
 return {
   child_component: 'equip',
   _list: []
 }
}

Basically I'm trying to render Equipment, but the dynamic way

I get 3 errors in console and a blank page

[Vue warn]: Error when rendering component at /home/victor/projetos/tokaai/public/src/components/EquipmentFormItem.vue: Uncaught TypeError: Cannot read property 'name' of undefined TypeError: Cannot read property 'setAttribute' of undefined

Apparently I'm doing something wrong

Are you sure these errors are coming from these code only, where are you using setAttribute or name attribute?
must be some internal process. I'm not doing it in this app

J
Jonatas Walker

Summing up:

<!-- Component A -->
<template>
  <div class="A">
    <B>
      <component :is="child_component"></component>
    </B>
  </div>
</template>

<script>
import B from './B.vue';
import Equipment from './Equipment.vue';

export default {
  name: 'A',
  components: { B, Equipment },
  data() {
    return { child_component: 'equipment' };
  }
};
</script>

<!-- Component B -->
<template>
  <div class="B">
    <h1>Some content</h1>
    <slot></slot> <!-- Component C will appear here -->
  </div>
</template>

this worked perfectly, thank you. I had a follow-up on this. Now imagine Component C emits an event, and I want to act on it in Component B. I tried to listen for it using v-on on but that didn't work. What are my options here?
Got the answer! I would simply listen for the event in the grandparent, i.e. instead of listening for it on the slot where it is being injected.
we must have to import all the child components where we have ` ` tag. wouldn't it make it slower if we need only specific child components from parent only. is there any workaround where we can import components in parent and pass it to child?
@RaviAnand you can register components as async dynamic async dynamic components components: { B, Equipment: () => import('./Equipment.vue')},
t
tony19

You can use special attribute is for doing this kind of thing. Example of dynamic component and its usage can be found here.

You can use the same mount point and dynamically switch between multiple components using the reserved element and dynamically bind to its is attribute:

Your code will look like following:

<template>
  <div class="B">
    <component :is="child_component"> Something else</component>
  </div>
</template>

not working here, but I don't know exactly what is going on. I Will update my question with more information
Works like a charm! Thanks! I would just like to know if that's possible to set custom type of the prop with passed component instead of Object type? Something like: passedComponent: { type: Component, required: true },
also, is it possible to get the name of the passed component in the component which receives this component via prop?
N
NiBi

Here's solution to forward custom component through props of another component

:is is special attribute and it will be used to replace your actual component and it will be ignored if you try to use it as a prop in your component. Luckily you can use something else like el and then forward this to component like so:

<template>
  <div>
    <component :is="el">
      <slot />
    </component>
  </div>
</template>
<script>
  export default {
    name: 'RenderDynamicChild',
    props: {
        el: {
            type: [String, Object],
            default: 'div',
        },
    },
  }
</script>

Any valid element you use in el attribute will be used as a child component. It can be html or reference to your custom component or div by default as specified in component declaration.

Passing custom component to prop is little bit tricky. One would assume you declare in a components property of parent component and then use it for el attribute but this doesn't work. Instead you need to have your dynamic component in data or computed property so you can use it in a template as a prop. Also note AnotherComponent doesn't need to be declared in components property.

<template>
  <RenderDynamicChild :el="DynamicComponent">
    Hello Vue!
  </RenderDynamicChild>
</template>

<script>
import RenderDynamicChild from './DynamicChild';
import AnotherComponent from './AnotherComponent';

export default {
  name: "ParentComponent",
  components: { DynamicChild },
  data() {
    return {
      DynamicComponent: AnotherComponent,
    };
  },
};
</script>

Using computed property for your dynamic component allows you to switch between components easily:

<script>
import DynamicChild from './DynamicChild';
import AnotherComponent from './AnotherComponent';

export default {
  name: "ParentComponent",
  components: { DynamicChild },
  data() { return { count: 0 } },
  computed: {
    DynamicComponent() {
      return this.count % 2 > 1 ? AnotherComponent : 'article';
    },
  },
};
</script>

Increase this.count to alternate between AnotherComponent and simple article html element.


J
Jar

If you would like to use another component within your functional component you can do the following:

<script>
  import Vue from 'vue'
  import childComponent from './childComponent'
  Vue.component('child-component')
  export default {}
</script>


<template functional>
  <div>
    <child-component/>
  </div>
</template>

Reference: https://github.com/vuejs/vue/issues/7492#issue-290242300


R
Ricardo Rodriguez

Maybe it's too late to answer this question. But I think it could help others with this same issue.

I've been looking for a way to pass components throw others in vue, but it looks that VUE3 have a approach for that using named slots:

Here it's the documentation about that: https://v3.vuejs.org/guide/component-slots.html#named-slots

Basically you can have:

<template>
  <div class="A">
    <slot name="ComponentC"></slot> <!-- Here will be rendered your ComponentC -->
  </div>
  <div class="A">
    <slot name="ComponentD"></slot> <!-- Here will be rendered your ComponentD -->
  </div>
  <div class="A">
    <slot></slot> <!-- This is going to be children components -->
  </div>
</template>

And from your B component

<template>
  <div class="B">
    <A>
      <template v-slot:ComponentC>
        <h1>Title of ComponentC </h1>
      </template>
      <template v-slot:ComponentD>
        <h1>Title of ComponentD </h1>
      </template>
      <template v-slot:default>
        <h1>Title of child component </h1>
      </template>
    </A>
  </div>
</template>

Thanks works in Vue 1.5 perfectly too !!!