How to pass v-model to parent in vue-multiselect - vue.js

i have two components.
One is vselect template and wrapper for vue-multiselect.
<template lang="pug">
field-wrapper.field(:label="label" :caption="caption" :error="error")
multiselect(v-model="value" :options="options")
</template>
Second is page.vue where im including it
vselect(v-model="select.value" :options='select.options')
p {{ select.value }}
But v-model works only inside vselect, how i cant pass selected data to page.vue, from vselect? I cant use new method for emit in page.vue. Vue-multiselect works only with v-model i guess?
When i have a case with input, what works for me
<template lang="pug">
field-wrapper.field(:label="label" :caption="caption" :error="error")
input.form-input(:type="computedType" :value="value" :disabled="disabled" :placeholder="placeholder" #keyup="removeError" #input="$emit('input', $event.target.value)")
</template>
#input="$emit('input', $event.target.value) - i solve problem like this, but ints not work with plugin vuemultiselect

Related

Vue slots props inside blade template

I have a Vue3 application that mounts on a blade/laravel. In one blade temlate, I have a Vue3 component (zest-dropzone) in which I insert a slot:
<template>
...
<slot name="hits" :button-label="buttonLabel" :files="files" :type="type" :state="state"></slot>
</template>
<script>
...
</script>
Inside the blade template, I have the following:
<zest-dropzone
accepted-files=".psd,application/pdf,audio/*,image/*,video/*"
button-label="{{ Lang::get('admin/button.edit') }}"
categories="{{ json_encode($categories) }}"
type="files">
<template #hits="hitsProps">
#{{ hitsProps.type }}
<zest-dropzone-files-preview :hitsProps="hitsProps" :button-label="buttonLabel" :files="files" :type="type" :state="state"></zest-dropzone-files-preview>
</template>
</zest-dropzone>
ZestDropzoneFilesPreview is another component which is registered globally and is technically rendered on the page, however the props are never coming no matter what I try.
Within the blade template, #{{ hitsProps.type }} renders correctly and the value type exists on hitsProps, however when I try to pass it in the next component, it doesn't come through and I get undefined inside ZestDropzoneFilesPreview.
Anyone knows how to deal with this? Thanks.
Fixed it, props were not passed accordingly (hitsProps -> hits-props).

My dynamic component (layout) doesn't work with named slots in vuejs

I have problems to combine dynamic generated layouts with named slots.
To define my layouts I'm using "component :is"
//app.vue
<template>
<component :is="layout">
<router-view />
</component>
</template>
<script>
computed: {
layout() {
const layout = this.$route.meta.layout || 'default'
return () => import(`#/app/layouts/${layout}.vue`)
}
},
</script>
//layouts/default.vue
<template>
<div>
<div>
<slot name="header" />
</div>
<div>
<div>
<slot name="sidebar" />
</div>
<div>
<slot name="default"/>
</div>
</div>
</div>
</template>
// views/page.vue
<template>
<div>
<template #header>
<h1>Primitives</h1>
</template>
<template #sidebar>
<ul>
<li v-for="primitive in sections.sections" :key="primitive">
<router-link :to="`/primitives/${primitive}`">{{primitive}}</router-link>
</li>
</ul>
</template>
<template #default>
<router-view :key="$router.path" />
</template>
</div>
</template>
But now I get this error inside my code
'v-slot' directive must be owned by a custom element, but 'div' is not.
and console displays this error
<\template v-slot> can only appear at the root level inside the receiving component
If I remove the main div I get the error
The template root requires exactly one element.
What I'm doing wrong?
This is not easy to explain so please cope with me...
I really understand what you are trying to do but unfortunately it is not possible in Vue.
Reason for that is slots are more template compiler feature than runtime feature of Vue. What I mean by that ? When Vue template compiler sees something like <template #header>, it will take the inner content and compile it into a function returning virtual DOM elements. This function must be passed to some component which can call it and include the result in it's own virtual DOM it is generating. To do that template compiler needs to know to what component it should pass the function (that is the real meaning of 'v-slot' directive must be owned by a custom element, but 'div' is not. error message...ie compiler is "looking" for a component to pass the slot content to...)
But you are trying to use the slots as if they were "discoverable" at runtime. For your code to work the dynamic layout component must at runtime somehow discover that it's child (also dynamic thanks to <router-view />) has some slot content it can use. And this is not how slots work in Vue. You can pass the slot content your component receives from parent to a child components but do not expect that parent component (layout in this case) can "discover" slot content defined in it's child components...
Unfortunately only solution for your problem is to import the layout component in every "page" and use it as a root element in the template. You can use mixins to reduce code duplication (to define layout computed)
#/mixins/withLayout.js
export default = {
computed: {
layout() {
const layout = this.$route.meta.layout || 'default'
return () => import(`#/app/layouts/${layout}.vue`)
}
}
}
views/page.vue
<template>
<component :is="layout">
<template #header>
<h1>Primitives</h1>
</template>
<template #sidebar>
<ul>
<li v-for="primitive in sections.sections" :key="primitive">
<router-link :to="`/primitives/${primitive}`">{{primitive}}</router-link>
</li>
</ul>
</template>
<template #default>
<router-view :key="$router.path" />
</template>
</component>
</template>
<script>
import withLayout from '#/mixins/withLayout'
export default {
mixins: [withLayout]
}
</script>

Passing slots through from Parent to Child Components

I have built a user-defined component (async-select) on top of another component (vue mutliselect) like this:
https://jsfiddle.net/2x7n4rL6/4/
Since the original vue-multiselect component offers a couple of slots, I don't want to loose the chance to use them. So my goal is to make these slots available from inside my custom component. In other words, I want to something like this:
https://jsfiddle.net/2x7n4rL6/3/
But that code oes not work.
However, if I add the slot to the child component itself, it works just fine (which you can see from the fact that options become red-colored).
https://jsfiddle.net/2x7n4rL6/1/
After surfing the web, I have come across this article, but it does not seem to work
Is there any way in VueJS to accomplish this ?
Slots can be confusing!
First, you need a template element to define the slot content:
<async-select :value="value" :options="options">
<template v-slot:option-tmpl="{ props }">
<div class="ui grid">
<div style="color: red">{{ props.option.name }}</div>
</div>
</template>
</async-select>
Then, in the parent component, you need a slot element. That slot element itself can be inside of another template element, so its contents can be put in a slot of its own parent.
<multiselect
label="name"
ref="multiselect"
v-model="localValue"
placeholder="My component"
:options="options"
:multiple="false"
:taggable="false">
<template slot="option" slot-scope="props">
<slot name="option-tmpl" :props="props"></slot>
</template>
</multiselect>
Working Fiddle: https://jsfiddle.net/thebluenile/ph0s1jda/

Vuetify custom v-text-field component is not updating the v-model

I’m following this documentation: https://v2.vuejs.org/v2/guide/components.html
I created custom v-text-field component which looks like that:
<template>
<div>
<v-text-field
:label="label"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"></v-text-field>
</div>
</template>
<script>
export default {
name: "txtbox",
props: ['value', 'label'],
}
</script>
I implemented it in my main component almost succesfully:
<txtbox
label="Name"
v-model="eventName"
/>
I don’t think it is necessary to paste all the code, but if it is I will edit the post. Here is how it works:
I have a list, when i click on the list element the text-field displays content of it, this works fine. Sadly when I’m editing the content of text-field the v-model value is not updating. I can also add that it works fine with normal (like in the docs) instead of . Is there anything I can do to make it work, or should i use simple input ?
Thanks
When you want to $emit the new value, you just have to emit the $event, and not $event.target.value
<template>
<div>
<v-text-field
:label="label"
v-bind:value="value"
v-on:input="$emit('input', $event)"></v-text-field>
</div>
</template>
v-on:input can also be shortened to just #input

How to make a component use v-for have dynamic slots

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.