template cannot be keyed. Place the key on real elements instead - vue.js

I want to implement the image preview function,Use the image component in the View Design frame,this error occurs.
The Component url is enter link description here,Example 5。
The code is shown below:
<template>
<Space wrap>
<template v-for="(url, index) in urlList" :key="url">
<Image :src="url" fit="contain" width="120px" height="80px" preview :preview-list="urlList" :initial-index="index" />
</template>
</Space>
</template>
<script>
export default {
data() {
return {
urlList: [
'https://file.iviewui.com/images/image-demo-1.jpg',
'https://file.iviewui.com/images/image-demo-2.jpg',
'https://file.iviewui.com/images/image-demo-3.jpg',
'https://file.iviewui.com/images/image-demo-4.jpg',
'https://file.iviewui.com/images/image-demo-5.jpg',
'https://file.iviewui.com/images/image-demo-6.jpg'
]
}
}
}
</script>

<template> tags are removed when the component renders. That's why you should pass key attribute to the child element (which is real).
<template v-for="(url, index) in urlList">
<Image :key="url" :src="url" />
</template>
You can also use v-for directly on <Image>
<Image
v-for="(url, index) in urlList"
:key="url"
:src="url"
/>

Related

How to change styles of child component based on grid / list toggled in parent component, in vue?

I have a view in Vue project.
Home.vue
<template>
<TestLayout >
<Card/>
<Card/>
<Card/>
</TestLayout>
</template>
<script>
import TestLayout from "../components/TestLayout.vue"
import Card from "../components/Card.vue"
export default {
name: "Home",
props:{
isList:{
type: Boolean
}
},
components: {
TestLayout,
Card
},
}
</script>
The TestLayout has a section where we can display cards in list or grid view
TestLayout.vue
<template>
<div class="flex border-solid ">
<ListBulletIcon class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-l p-2"
#click="listView = true" />
<TableCellsIcon #click="listView = false"
class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-r p-2" />
</div>
<section
:class="[listView ? 'md:grid-cols-1 grid-cols-1' : 'md:grid-cols-4 grid-cols-2', 'rounded-md grid gap-5 col-span-full']">
<slot :listView="listView"></slot>
</section>
</template>
<script>
import {
ListBulletIcon,TableCellsIcon} from '#heroicons/vue/24/outline'
export default {
data: function () {
return {
listView: false,
}
},
components: {
ListBulletIcon,
TableCellsIcon,
},
}
}
</script>
I want to change the style of Card.vue based on whether user clicks grid view or list view icon.
For example, I want to add this style to Card.vue div tag in its template:
:class="[isList ? 'dark:bg-midnight' : 'dark:bg-red-300', 'min-h-80 w-full bg-gray-50 shadow-xl rounded-md flex flex-col']"
How will I check isList is clicked or not?
How can I achieve this?
You're half way there. After defining a slot prop (<slot :listView="listView"></slot>) you should access it in parent and pass it down to slot components.
<TestLayout>
<template v-slot="{ listView }">
<Card :isList="listView" />
<Card :isList="listView" />
</template>
</TestLayout>

<MenuItems /> is missing a parent <Menu /> component error (with MenuItems in slot)

I am creating a component in Vue3 called <Dropdown>.
The Dropdown component uses other components in turn: Menu, MenuItems, MenuItem and MenuButton from the headlessui/vue library.
My idea is that you can put any content in the Dropdown, that's why I created a slot called #contentdropdown.
The problem is that when I pass this slot content to the Dropdown component, Vue gives me the following error:
< MenuItems /> is missing a parent < Menu /> component
This is my Dropdown componente code:
<template>
<Menu as="div" class="relative inline-block text-left">
<div>
<MenuButton class="btn inline-flex justify-center w-full text-sm" :class="'btn-'+variant">
Options
<ChevronDownIcon class="-mr-1 ml-2 h-5 w-5" aria-hidden="true" />
</MenuButton>
</div>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<slot name="contentdropdown"></slot>
</transition>
</Menu>
</template>
<script>
import { Menu, MenuButton } from '#headlessui/vue'
import { ChevronDownIcon } from '#heroicons/vue/solid'
import { vVariantProp } from '../../../../constants'
import { reactive, computed } from 'vue';
export default {
name: 'dropdown',
props: {
...vVariantProp,
},
setup(props) {
props = reactive(props);
return {
}
},
};
</script>
Why does it need the parent component called Menu?, if in fact I am already painting the slot inside the component and also importing it inside the Dropdown component.
This is how I pass to the Dropdown component the content through its #contentdropdown slot:
<Dropdown v-bind="{'variant':'primary'}">
<template #contentdropdown>
<MenuItems class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
<div class="py-1">
<MenuItem>
Subitem1
</MenuItem>
<MenuItem>
Subitem2
</MenuItem>
</div>
</MenuItems>
</template>
</Dropdown>
The error <MenuItems /> is missing a parent <Menu /> component is not a Vue specific error. It is an error thrown by headlessui/vue - source
MenuItems component (as well as MenuButon etc - see doc) is designed to be used inside Menu component. It is using inject to tap into state provideded by the Menu component. There is nothing you can do about it - it is designed that way
Problem is that slot content (everything inside <template #contentdropdown> in the last code example) in Vue is always rendered in parent scope
Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope.
This means that MenuItems rendered as slot content has no access to data provideded by the Menu component rendered inside your Dropdown component
I don't see any way to overcome this limitation. You'll need to change your design (or describe your use case to headlessui/vue maintainers and ask them to implement alternative approach to share MenuContext with child components - for example using slot props)

How to pass through prop containing image path in Vue?

I am trying to display an image in Vue template using a prop that i passed through in my App.vue file.
App.vue:
<Header image='../assets/logo.png' />
Header.vue:
<template>
<div class="div-box">
<img :src={image} alt="Vue logo" />
</div>
</template>
<script>
export default {
name: "Header",
props: ["image"],
};
</script>
Thanks in advance!
You have to explicitly load the image "module":
<Header :image="require('../assets/logo.png')" />
vue-loader does this automatically for you for things like <img>, but if you've made your own component then you need to do it yourself.

Deep in slots vue 2

Does anybody know is there are some limitations on how deep my components with slots can be ?
Now I have 3 components, like
list.vue
<div class="list">
<slot name="list" />
</div
wrapper.vue
<list>
<template #list>
<div>hello</div>
<slot name="wrapper" />
</template>
</list>
last.vue
<wrapper>
<template #wrapper>
<search :value=value />
</template>
</wrapper>
So, i want to transfer some value from last component to search component. On init something is good, but if value is changing in my last component my search component it doesn't see.
Maybe someone know information about max deep for components + slots ?
You forgot to share your Search component and the rest of the other components (namely, the script section). There is no limit in the slot depth (whatever that means). I'm assuming you have some other errors in parts you haven't shared. My first guess would be the way you have set the props in your Search component.
Here is an example of a working version on how you should set a nullable prop on a component:
Last.vue
<template>
<Wrapper>
<template #wrapper>
<label>
<input type="text" v-model="value">
</label>
<Search :value=value />
</template>
</Wrapper>
</template>
<script>
import Wrapper from "#/components/Wrapper";
import Search from "#/components/Search";
export default {
components: {Search, Wrapper},
data() {
return {
value: null
}
}
}
</script>
Search.vue
<template>
<p>{{ value }}</p>
</template>
<script>
export default {
props: {
value: {
type: String,
default: null,
}
},
}
</script>
If this doesn't address your problem, please share the rest of your code and I'd be glad to help!

Vue js passing HTML button name attribute as a PROPS

I created a custom component which contains an HTML button tag, so I'm using this component many times as a shared component into a parent component. I would like to know if I can pass the name attribute from the HTML button tag as a prop, so I can have a dynamically HTML button tag name attribute.
<div class="toggle">
<button
class="btn"
name="ships">
</div>
<script>
export default {
props: {
ships: {
type: String,
required: true,
default: ''
}
}
</script>
<toggle
:ships="white"
/>
<toggle
:ships="grey"
/>
<toggle
:ships="black"
/>
CODE
I've updated your fiddle: https://codesandbox.io/s/00yxy5lqzn
Things I've changed:
Toogle.vue
<button class="btn" v-bind:name="ships">BUTTON </button>
To change an HTML attribute just add an v-bind: in front of it, because you can not use mustaches there.
App.vue
<div id="app">
<toggle ships="black" />
<toggle ships="grey" />
<toggle ships="white"/>
</div>
Removed the double dot -> now the content of the property will be interpreted as an String.
Edit: Alternative you could do it this way (result is the same): <toggle :ships="'black'" /> <-- it's probably the better way
You can do this without using a prop by using inheritAttrs.
export default {
inheritAttrs: false,
name: "toggle",
};
And then use $attrs to access any fallthrough attributes e.g. name.
<div class="toggle">
<button class="btn"
v-bind:name="$attrs.name">BUTTON </button>
</div>
And then using your component would just be
<div id="app">
<toggle name="black" />
<toggle name="grey" />
<toggle name="white"/>
</div>