I would like to dynamically append Vue Components to a container.
I have a Dashboard.vue like this:
<template>
<div id="container"></div>
</template>
Then I have 3 components: LineChart.vue , Bar.vue , Cake.vue
I will be fetching data from a database depending on what users want to search, and everytime they make a query, I want to append one of the 3 components into "container"
So for example if a user makes 4 querys, generating 2 LineCharts, 1 Bar and 1 Cake, I want the Dashboard.vue to look like this:
<template>
<div id="container">
<LineChart id="lineChart1"></LineChart>
<LineChart id="lineChart2"></LineChart>
<Bar id="bar1"></Bar>
<Cake id="cake1"></Cake>
</div>
</template>
You can use vuejs dynamic components.
Let's say that your backend returns this result, where component is the name of your vue component :
items = [
{component:"LineChart", ...},
{component:"LineChart", ...},
{component:"Bar", ...},
{component:"Cake", ...},
]
Your html template should look like :
<template>
<div id="container">
<template v-for="(item,index) in items" >
<component :key="iindex" :is="item.component" :id="`${item.component}${index}`"></component>
</template>
</div>
</template>
The easiest way I can think of for this is to make a v-for for each component.
You will have something like this:
<template>
<LineChart v-for="i in lineCount" :key="'line'+i" />
<Bar v-for="i in barCount" :key="'bar'+i" />
<Cake v-for="i in cakeCount" :key="'cake'+i" />
</template>
However, I am really sure though, there is a better approach for this.
Related
Is it possible to pass as a prop when calling another component?
Essentially, I have components, and views, I build my views using various components. I want to have 1 styled component which I can reuse, so I was thinking to have a WebsiteLayout.vue:
<template>
<a-layout-content :style="{ padding: '0 24px', minHeight: '280px' }" />
{{ content }}
</template>
<script>
export default {
name: "View",
components: {
},
props: ["content"],
};
</script>
And in my App.vue:
<template>
<Content content=<router-view /> />
</template>
This isnt correct, but wondering if something like this is possible, and how I could achieve it?
With Vue 3 and Router 4, you can do something like
<router-view v-slot="{ Component }">
<SomeComponent>
<component :is="Component" />
</SomeComponent>
</router-view>
and then
//SomeComponent.js
<template>
<div class="wrapper">
<slot></slot>
</div>
</template>
here is an example with transition component as a wrapper for the router-view component
to know more about scoped slots you can see this
I have a component which renders a standard .
I would like to use slots from my component, I would like to write something like:
<MyComponent>
<header>
Titolo
</header>
<body>
my component body
</body>
</MyComponent>
then final component should be:
<v-dialog>
<h1>
// header slot content
</h1>
// body slot content
</v-dialog>
how can I do this? This only works with <slot> but not with named slot.
To use multiple slots you can use the following syntax:
<MyComponent>
<template v-slot:header>
Titolo
</template>
<template v-slot:body>
<p>my component body</p>
</template>
</MyComponent>
So you can pass some HTML in the template blocks and it will render in the component.
MyComponent.vue has the next content:
<template>
<v-dialog>
<h1>
<slot name="header"></slot>
</h1>
<slot name="body"></slot>
</v-dialog>
</template>
You can define names for your slots in your custom component by using the name attribute available for the <slot> element, e.g. <slot name="header">. If you don't define a name for the slot, its name will just be default. See the Vue.js slots documentation here: https://v2.vuejs.org/v2/guide/components-slots.html
Also, I made a simple usage example that you can check out here: https://codesandbox.io/s/unruffled-mopsa-f47hm?file=/src/App.vue
So in your case, your custom component could look something like this:
<v-dialog>
<slot name="header" />
<slot name="body" />
</v-dialog>
And to use it in the parent component, you could have:
<MyComponent>
<template v-slot:header>
Titolo
</template>
<template v-slot:body>
<p>my component body</p>
</template>
</MyComponent>
I am trying to make a grid component that is not too opinionated. I have 2 different types of components that have different cover images, DVD and Cassette.
I am assuming the best way to do this is by not using v-ifs like I am below:
Parent.vue
<MyUniversalComponent
:items="items"
>
</MyUniversalComponent>
MyUniversalComponent.vue
import DVD from '#/components/DVD.vue';
import Cassette from '#/components/Cassette.vue';
<template>
<div class="grid">
<div v-for="item in items">
<div v-if="item.type === 'dvd'">
<DVD :data="item" />
</div>
<div v-else-if="item.type === 'cassette'">
<Cassette :data="item" />
</div>
</div>
</div>
</template>
Is there a more flexible way to do this using slots? I sort of want it to be a "shell" grid that can be used in different ways so I assume I'd want to take out the logic of having these components living in here. Can I translate this to use slots?
In this case, the built-in <component> would be more appropriate than slots. It takes an is prop that sets the component type, and any bindings are passed through the resolved component:
<script>
import DVD from '#/components/DVD.vue';
import Cassette from '#/components/Cassette.vue';
export default {
components: {
DVD,
Cassette,
}
}
</script>
<template>
<div class="grid">
<div v-for="item in items">
<component :is="item.type" :data="item" />
</div>
</div>
</template>
demo
I just got started to Vue and i'm trying to understand some basic concepts such as conditional rendering and how to pass data from where i load the app to a component. Suppose i'm rendering a Vue component like this:
<div id="app">
<myComponent></myComponent>
</div>
Suppose myComponent looks like this:
<template>
<div>
<h1>First block</h1>
</h1>Second block</h1>
</div>
</template>
I want to be able to render First block or Second block when i load the Vue app according to a parameter i pass to the component, like:
<div id="app">
<myComponent id="first"></myComponent>
</div>
In this case i should see First block, whereas if instead of id="first" there was id="second" the output was supposed to be Second block. How can i do this?
I know it's a very basic question, but most of the sources i found explained how to do the opposite. Any kind of advice is appreciated!
In vue you could pass props (parameters) to component which defines this ones in its script like :
<template>
<div>
<h1 v-if="block==='first'">First block</h1>
</h1 v-else>Second block</h1>
</div>
</template>
<script>
export default{
props:{
block:{
type:String,
default:'first'
}
}
}
</script>
in parent component pass the prop like :
<div id="app">
<myComponent block="first"></myComponent>
</div>
or
<div id="app">
<myComponent block="second"></myComponent>
</div>
I have a child component that uses v-for. I want to be able to have the parent pass down a slot, or something similar of how it wants each item in the v-for display. However, the problem is that the parent does not have access to each individual item in the v-for as it's rendered.
Some things i've tried is passing a slot with specific keys. e.g.
<child-comp :items="items">
<div v-text="item.text" slot="body"/>
</child-comp>
Basic code may look like this for what i'm trying (though it doesn't work)
Parent component would look something like
<template>
<child-comp :items="items>
<div v-text="item.text"
</child-comp>
</template>
items = [{ text: 'hello'}]
Child would look something like this
<template>
<div>
<span v-for="item in items">
<slot></slot>
</span>
</div>
</template>
Note this has to be dynamic because one item might do v-text, another may do something like add more html such as an image, and another may do something completely different.
I believe you're looking for scoped slots.
https://v2.vuejs.org/v2/guide/components-slots.html#Scoped-Slots
Note that the preferred syntax for using scoped slots changed in Vue 2.6.0 so the exact way you write this will depend on which version you're using.
Your child would pass the item to the slot, like this:
<template>
<div>
<span v-for="item in items">
<slot :item="item"></slot>
</span>
</div>
</template>
The parent would look like this for Vue 2.6.0+:
<template>
<child-comp :items="items">
<template v-slot:default="slotProps">
<!-- The parent can put whatever it needs here -->
{{ slotProps.item }}
</template>
</template>
</child-comp>
</template>
Any props passed to the slot by the child will be included in the slotProps. There's no need to call it slotProps and in practice it is usually destructured (see the docs for more details).
For Vue 2.5 you'd use slot-scope instead:
<template>
<child-comp :items="items">
<template slot-scope="slotProps">
<!-- The parent can put whatever it needs here -->
{{ slotProps.item }}
</template>
</template>
</child-comp>
</template>
Prior to Vue 2.5.0 slot-scope was called scope.