Passing undeclared props to child component (like ...args) - vue.js

I am making a wrapper component for a Vue component.
For example, I want to wrap Quasar's q-input (let's say because I am doing some custom styling:
// q-input-wrapper.vue
<template>
<q-input v-model="inputValue" label="Some Label">
</template>
But then I want q-input-wrapper to be able to accept all props that q-input supports. I do not want to re-declare all of them in q-input-wrapper.vue.
Is there a way to bind props that are passed in q-input-wrapper for q-input to consume?
Example:
<q-input-wrapper color="blue" icon="mail" class="some-class" style="some-style: value" />
color, icon are both q-input props and I do not want to explicitly declare them as q-input-wrapper's props but still want to use them.
Probably something similar to how ...args work?

You can pass along any props with v-bind="$attrs" and any listeners with v-on="$listeners":
<template>
<q-input v-model="inputValue" label="Some Label"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
demo

Related

How to pass an instantiated Vue component as a prop in Vue.js?

I want to pass an instantiated Vue Component as a prop to antoher component and then render it, like so:
<Button :icon=<IconComponent size="25" /> :link="http://wikipedia.org">Click here to visit Wikipedia</Button>
This is at least how I would do this in React. How can I achieve the same with Vue?
You need to use named slots in the child component :
<template>
<button>
<slot name="icon"></slot>
<slot></slot>
</button>
</template>
in parent component:
<Button link="http://wikipedia.org">
<template #icon>
<IconComponent size="25" />
</template>
Click here to visit Wikipedia</Button>

How do I provide the component that is replacing <slot> with properties?

I have a component that functions as a basis for other components.
//The Basis Component lets call it slotComponent
<template>
<div>
//somestuff
<slot :someProperties="someLocalValues"></slot>
</div>
</template>
As you can see I want to give the component that is replacing the slot some Properties that only this component will know.
However if I do this:
//Some page Component lets call it mainPage
<template>
<slotComponent>
<someOtherComponent/>
</slotComponent>
</template>
Then "someOtherComponent" will not have access to "someProperties". How can I provide this component with said property?
Note that someLocalValues are defined in the scope of slotComponent and not its parent. So I cant provide said information in the mainPage.
You need to specify the v-slot directive in order to bind the props. Check the documentation here: https://v2.vuejs.org/v2/guide/components-slots.html#Scoped-Slots
You also will need to pass the props to your child component.
In your example, you would need to do something like:
<template>
<slotComponent>
<someOtherComponent v-slot="slotData" :data="slotData.someProperties"/>
</slotComponent>
</template>
This is the same, but with an additional <template> for clarity:
<template>
<slotComponent>
<template v-slot="slotData">
<someOtherComponent :data="slotData.someProperties"/>
</template>
</slotComponent>
</template>

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

vue emit data back to parent when using a slot

I have input on custom component and when i click on the next button on the wrapper component i need to emit details to the parent component.
How is this possible in vue?
wrapper.vue
<template>
<div :id="contentId" class="mt-3">
<div>
<slot></slot>
</div>
<b-row class="float-right">
<b-col>
<button cssClass="e-outline" v-on:click="btnNext">{{nextButtonText}}</button>
</b-col>
</b-row>
</div>
</template>
parent.vue
<template>
<div>
<Wrapper contentId="1">
<CustomComponent1 />
</wrapper>
<Wrapper contentId="2">
<CustomComponent1 />
</wrapper>
</div>
</template>
customComponent1.vue
<template>
<div>
<input v-model="name" />
<input v-model="name2" />
</div>
</template>
code above is for illustrative purposes.
The problem is that the wrapper doesn't innately have access to data of the scoped component, therefore these links have to be created manually. There is no way to tell how many children or slots the component may have, so this kind of functionality is not part of the vue magic.
So in an example where you have parent App component, which holds a Wrapper that has a MyInput component in the slot...
MyInput
The MyInout component doesn't automatically update other components, so it needs to be setup to $emit the internal data.
This can be done using a watch, #change listener for the input, or some other way. You can emit multiple datum as they change, or use a single payload with all the data
this.$emit("input", myData);
App
The App needs to explicitly connect the data between MyInout and Wrapper
<Wrapper> <MyInput #input="onInput" slot-scope="{ onInput }" /> </Wrapper>
The magic/trick happens here, where we bind the input emit function of the input to the onInput function using slot-scope.
Wrapper
The wrapper then needs to listen to the events passed (via App) from Wrapper
<slot :onInput="onInput" />
where onInput is a method that would process the data
see example below
I would recommend the following reading
https://github.com/vuejs/vue/issues/4332 (specifically Evan's response why it's not possible)
https://adamwathan.me/renderless-components-in-vuejs/ Adam has a thoroughly documented way of using render functions and slots to abstract functionality from the UI. While it's not directly related, it's a worthwhile read and may provide more info on using slot-scope as well as some perspective on improving the structure of UI components.

How expose child element v-model as the vue component v-model

I Was using a simple text area in my vue component.:
<input v-model="someRootProperty"/>
I would like to encapsulate this input inside another component, for instance
<template>
<div>
<div>...</div>
<input v-model="???"/>
</div>
</template>
I would like to use
<my-component v-model="someRootProperty" />
instead and them bypass this to the input inside the component.
What should I do inside the component to expose its input v-model as the component v-model?
<input v-model="someRootProperty"/>
Is the same as (syntactic sugar):
<input :value="someRootProperty" #input="someRootProperty = $event.target.value"/>
That means that you could accept value as a prop in the component and emit input to achieve the same thing.
MyComponent.vue
<template>
<input :value="value" #input="$emit('input', $event.target.value)>
</template>
<script>
export default {
props: ['value']
}
And then use it like this.
<MyComponent v-model="someRootProperty"/>