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
Related
I'm trying to add a button to the bootstrap table using Vue3, but I ran into the following problem.
The logic of the button is stored in the parent component, and I plan to $emit on the logic from there. But in order for the button to delete a certain row, for example, I need to pass a special ID, I can't pass it to the ID, because I don't understand how to iterate through the data array in the bootstrap table correctly.
Maybe there is a soft and correct way to solve this problem?
ChildComponent.vue
<template>
<div v-if="data.length !== 0">
<div class="card inline" >
<div>
<b-table striped hover :items="data">
<button #click="$emit('remove', hereIneedTheID)">Remove</button>
</b-table>
</div>
</div>
</div>
<div class="card center" v-else>
<h4>No data available in the list</h4>
<button class="btn" #click="$emit('load')">Load list of data</button>
</div>
</template>
<script>
export default {
name: "AppDataList",
emits: ['load', 'remove'],
props: ['data']
}
</script>
I wonder how to add components as slot content. Instead of doing it like:
<template v-slot:content>CONTENT HERE</template>
I'd like to pass a component.
PkDynModalCenter:
<div class="...">
<div id="modalIndicator" class="...">
<slot name="content"></slot>
</div>
</div>
Parent:
<PkDynModalCenter v-if="togglePayment" #closeModal="togglePayment = false">
<PaymentTemplate :info="voucherInfo" name="content"></PaymentTemplate>
</PkDynModalCenter>
But it's not displayed. Docs are very vague there...
Have a look at the docs for named slots: https://vuejs.org/guide/components/slots.html
You can only use your parent code with the default slot, so your PkDynModalCenter should look like this:
<div class="...">
<div id="modalIndicator" class="...">
<slot></slot> <!-- no name → default slot -->
</div>
</div>
With your current code for the modal, you can only call it like you did in your first code snippet.
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 would like to automatically include the contents of one component in a named slot of another component. I.e., something like this:
Vue.component('comp-one', {
template: `
<div class="comps>
<div class="comp-two">
<slot name="compTwo"></slot>
</div>
<div class="comp-one">
<slot></slot>
</div>
</div>
`
})
Vue.component('comp-two', {
slot: 'compTwo',
template: `
<div class="sub-comp-two">
<!-- content goes here -->
<slot></slot>
</div>
`
})
The idea is that if someone uses <comp-two>Some content</comp-two>, it will automatically get added to the slot compTwo in comp-one. Is there a way to do this?
NOTE: I made up slot: 'compTwo'. Just trying to illustrate what I'd like to accomplish.
I'm new to Vue.js and I'm having a bit of trouble using components with sub-components. I have the following .vue files
app.vue
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider>
<skin></skin>
</slider>
</section>
</template>
slider.vue
<template>
<div id="slider-panel">
<h3>{{* heading}}</h3>
<div class="slider">
<slot>
Some content
</slot>
</div>
</div>
</template>
<script>
import skin from "./skin";
export default {
components: {
skin: skin
}
};
</script>
skin.vue
<template>
<div v-for="colour in colours">
<div :style="{ backgroundColor: colour }">
<img src="../assets/images/MIA.png"/>
</div>
</div>
</template>
<script>
export default {
data() {
return {
heading: "Choose Skin Tone"
};
}
};
</script>
I'm trying to load the skin sub component into the component. Everything works well except for the skin sub component as it doesn't get compiled. I do not get any compile or vue related errors though. I also wanted to be able to have several instances of the slider component like this
app.vue
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider>
<skin></skin>
</slider>
<slider>
<foo></foo>
</slider>
<slider>
<bar></bar>
</slider>
</section>
</template>
I'm not sure what I am doing wrong.
I'm not 100% sure of what you want to achieve here, but to compile a component inside a component, you need to add the child component inside the parent's template, like this:
Slider.vue (I've simplified the structure):
<template>
<div id="slider-panel">
<h3>{{* heading}}</h3>
<skin></skin>
</div>
</template>
<script>
import skin from './skin'
export default {
components : {
'skin': skin
}
}
</script>
App.vue:
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider></slider>
</section>
</template>
Actually, if you add skin in the app's template inside of adding it in the slider component template, it gets included (and rendered) assuming that its scope is app, not slider. In order to add skin inside slider scope, it needs to be added to slider's template. Check this: https://vuejs.org/guide/components.html#Compilation-Scope
Some other things:
Use a hyphen-separated name for the components, with at least 2 words: <custom-slider> instead of <slider>, for example, following the Web Components API (otherwise it might collide with current or upcoming web components).
Slots are complicated to grasp, so read this carefully: https://vuejs.org/guide/components.html#Content-Distribution-with-Slots
Good luck!
Update:
If you want the slider component to be content agnostic and be able to insert anything you want inside it, you have two options (that I can think of):
Remove all the logic from the slider component and make skin a descendant from app. Then use slots in the slider component, as follows:
Slider.vue:
<template>
<div id="slider-panel">
<h3>{{* heading}}</h3>
<slot></slot>
</div>
</template>
<script>
export default {}
</script>
App.vue:
<template>
<section>
<menu></menu>
<h1>Create Your MIA</h1>
<div id="board"></div>
<slider>
<skin></skin>
</slider>
</section>
</template>
<script>
import skin from './skin'
export default {
skin: skin
}
</script>
If you know that the slider will always have a closed set of components inside, you can use dynamic components: https://vuejs.org/guide/components.html#Dynamic-Components
After some research I found this which refers to a is= attribute that will transclude the sub-component template
so in app.vue
<slider-component>
<div is="skin-component" v-for="colour in colours"></div>
</slider-component>
and then add child components