Vue JS Non prop attributes and Disabling Attribute Inheritance - javascript

I have read the docs more than 5 times and I still can't understand what is Disabling Attribute Inheritance is used for and I don't understand the example given.
I understand how props works, it is passing data from parent component to child component.
Parent.vue
<template>
<div id="app">
<Child :num="num"></Child>
</div>
</template>
<script>
import Child from "#/components/Child";
export default {
name: "App",
components: {
Child
},
data() {
return {
num: 42
};
}
};
</script>
Child.vue
<template>
<div>
<h2>Child</h2>
<h4>num is {{num}}</h4>
<div id="total">
<h4>total is {{total}}</h4>
</div>
</div>
</template>
<script>
export default {
name: "Child",
props: {
num: {
type: Number,
default: 100
}
},
data() {
return {
};
},
computed: {
total() {
return this.num + 20;
}
}
};
</script>
This will output num is 42 and total is 62. Which I understand perfectly.
However when it comes to Disabling Attribute Inheritance section , by base component, I assume they are referring to the child components.
"This pattern allows you to use base components more like raw HTML elements, without having to care about which element is actually at its root:"
What does this even mean ? Does this mean that parent don't have to pass on props anymore to the child and the child can automatically pick up the props from it's parent which frankly doesn't make sense or there is no parent component altogether, they are only using child component?
From their example, props: ['label' , 'value'] , how is the child component receiving these two props without the parent component passing these props to them?
I would be very thankful if someone can use the parent and vue component analogy above to provide a simple example of what does this even mean.
Thank you.

If you put a random html attribute onto the component, it will appear on the rendered component. If you disable inheritance, it won't.
<my-component dorkus="whatever"></my-component>
when rendered, perhaps expands to
<div dorkus="whatever"> ... stuff ... </div>
but if you set inheritAttrs: false, it looks like
<div> ... stuff ... </div>
that's all there is to it.
dorkus="whatever" is still stored in the $attrs object, should we want to do something else with it.

Disabling Attribute Inheritance is used when you don't want html attributes assigned to the component to be passed to the root element in a component.
Parent.vue
<template>
<div id="app">
<Child title="I am the child"></Child>
</div>
</template>
<script>
import Child from "#/components/Child";
export default {
name: "App",
components: {
Child
}
};
</script>
Child.vue
<template>
<div>
<h2 v-bind="$attrs">Child</h2> <!-- This h2 will inherit all the html attributes -->
</div>
</template>
<script>
export default {
inheritAttrs: false, // This is what disables attribute inheritance
name: "Child"
};
</script>
inheritAttrs: false prevents the root component div from inheriting the title assigned to the Child component in Parent.vue.
Now notice how I use v-bind="$attrs" on the h2 in Child.vue. $attrs contains all the attributes that would have been assigned to the root div element. Now the attributes are assigned to the h2 and not the div.
inheritAttrs: false doesn't affect props or vue attributes, it affects normal html attributes. (It also doesn't affect style and class attributes)
Base Components
By "Base Components" the docs at vuejs.org means components like buttons, inputs, etc.
Using inheritAttrs: false is really useful for input components:
<template>
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
</template>
<script>
export default {
name: 'base-input',
inheritAttrs: false,
props: ['label', 'value'],
}
</script>
This allows you to do:
<base-input
v-model="username"
required
placeholder="Enter your username"
></base-input>
Which means that the placeholder and required attributes are applied to the input in the component and not the label.

Something to add
If you have multiple non-prop attributes and multiple root nodes in a component, and the non-prop attributes are meant for different elements, you can bind them like this
<mycomponent
data-haha="haha"
data-hehe="hehe">
</mycomponent>
// in the component
template: `
<p v-bind:data-yoyo="$attrs['data-haha']">only data-haha is used, and attribute data-haha renamed to data-yoyo</p>
<p v-bind="$attrs">all attributes here</p>
`
Rendered HTML
<p data-yoyo="haha">only data-haha is used, and attribute data-haha renamed to data-yoyo</p>
<p data-haha="haha" data-hehe="hehe">all attributes here</p>

Related

Vue3 pass the data from Parent to Child

I bumped into this error when trying to pass the data from parent to child.
vue#next:1616 [Vue warn]: Extraneous non-props attributes (parentdata) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.
at <Child parentdata="Data From Parent" >
at <App>
My source code is here below, just simply pass the data to Child from Parent.
I guess this is related with text root nodes? if so how can I fix this?
<div id="app">
<child parentData="Data From Parent"></child>
</div>
<template id="child_comp">
Child component test [[ parentData]]
</template>
<script>
const Child = {
props:['parentData'],
template: '#child_comp',
data (){
return {
}
},
mounted() {
console.log(this.parentData);
},
delimiters: ['[[', ']]']
}
Vue.createApp({
components:{
"child":Child,
},
methods:{
}
}).mount('#app')
</script>
Since your template is placed directly in the HTML, the parentData cannot be bound and generates the warning.
You should bind it with kebab-case like this
<child parent-data="Data From Parent" ></child>
Check the Vue Docs DOM Template Parsing Caveats for details.
BTW, also good to know:
The warning comes while you component does not have a single root node.
To prevent the warning you can put a <div> around your component's content, like this:
<div>Child component test : [[ parentData ]]<br/></div>
Or use inheritAttrs: false to suppress it.
And here is a great answer clarifying the problem with the warning.
Example
Here is the playground showing it. The first parentData="Data From Parent" generates the warning and does not work. The second parentData="Data From Parent" works.
const Child = {
props:['parentData'],
template: '#child_comp',
mounted() {
console.log(this.parentData);
},
delimiters: ['[[', ']]']
}
Vue.createApp({
components:{
"child": Child,
}
}).mount('#app')
<div id="app">
<child parentData="Data From Parent" ></child>
<child parent-data="Data From Parent" ></child>
</div>
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script type="text/x-template" id="my-component">
</script>
<template id="child_comp">
Child component test : [[ parentData ]]<br/>
</template>

Child component emits an event I didn't ask for in Vue 3

I'm in the process of migrating my medium-sized app from Vue2 to Vue3.
I noticed that Vue3 Child-Parent emit mechanism is different compared to Vue2 and confuses me a lot.
Here's a basic example for demonstration
// App.vue
<script>
import InputWrap from './InputWrap.vue';
export default {
name: 'App',
components: {
InputWrap,
},
data() {
return {
msg: 'Hello world',
}
},
methods: {
onChange() {
console.log('why change?');
}
}
}
</script>
<template>
<h1>{{ msg }}</h1>
<input-wrap v-model="msg" #change="onChange" />
</template>
// InputWrap.vue
<script>
import MyInput from './MyInput.vue';
export default {
name: 'InputWrap',
components: {
MyInput,
},
props: ['modelValue'],
methods: {
onUpdate(v) {
this.$emit('update:modelValue', v);
}
}
}
</script>
<template>
<my-input :modelValue="modelValue" #update:modelValue="onUpdate" />
</template>
// MyInput.vue
<script>
export default {
name: 'MyInput',
props: {
modelValue: String,
},
methods: {
onInput(e) {
this.$emit('update:modelValue', e.target.value);
}
}
}
</script>
<template>
<input :value="modelValue" #input="onInput" />
</template>
Link to Vue SFC playground.
I'm curious why does onChange event handler is called in App.vue?
It seems that change event is generated by input element in MyInput.vue. But it's totally not transparent why this event is going up through all components and being captured in App.vue. Imagine a tree with dozens nested components and a root component listening for change event. It's a total mess.
Vue2 have a different approach and I like it because it has a transparent child-parent communication. Is it possible to turn on Vue2 emit mechanism in Vue3?
The reason is that Vue implements Fallthrough Attributes mechanism
A "fallthrough attribute" is an attribute or v-on event listener that is passed to a component, but is not explicitly declared in the receiving component's props or emits.
When a component renders a single root element, fallthrough attributes will be automatically added to the root element's attributes
In your example, your component meets 2 conditions:
InputWrap.vue render a single root element (MyInput.vue components)
InputWrap.vue does not declare onChange as the component's emits
The same with MyInput.vue component
So the #change listener will fall through to the input element.
To disable fallthrough attributes you can declare the attributes as the props/emits of the child component or disable the whole feature for that component by adding inheritAttrs: false:
// InputWrap.vue
<script>
import MyInput from './MyInput.vue';
export default {
name: 'InputWrap',
components: {
MyInput,
},
inheritAttrs: false
}
</script>
SFC link
Vue 2 only inherit attributes

Vue dynamic component, how to handle different emit and pass props and replace different slot?

Just know some basic knowledge about dynamic components, but what if the component is complex or different from each other, how to handle the emit, props and slot in parent?
Suppoese I got 3 components:
Component 1
<template>
<div class="app-container">
<spinner :color="this.colore"></spinner>
<slot></slot>
</div>
</template>
It's about loading, and I need to pass a prop "color" and some buttons in slot.
Component 2
<template>
<div class="app-container">
<form>
......
<button #click="onClick"></buttom>
</form>
</div>
</template>
method: {
onClick: function () {
this.$emit('form-submit');
}
}
This component doesn't hold any props or slot, but there is an emit when button clicked.
Component 3
<template>
<div class="app-container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
This component is just about to display info. But there are 3 different slots.
And finally, in parent view, if I want to use dynamic component, what is the right way to to?
<template>
<div class="homepage">
<header></header>
<keep-alive>
<component :is="currentComp"/>
</keep-alive>
</div>
</template>
#Component({
components: {
Component1,
Component2,
Component3,
},
})
export default class HomepageView extends Vue {
public currentComp: VueConstructor = Component1;
public switchToComp1: void {
currentComp = Component1;
}
public switchToComp2: void {
currentComp = Component2;
}
public switchToComp3: void {
currentComp = Component3;
}
}
I mean both comp1 and comp3 need to pass props or replace slots. The switchToCompX methods, how can I complement it in the right way? And how to receive emit from comp2 since only comp2 will emit.
Both props and events can be passed using objects so you can keep that objects in data and switch the content same way you switch active component
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
Slots must be defined in the template so only way is probably to define them all and use v-if to activate only those needed by active component...
Anyway it would be much easier to use v-if instead of dynamic component in this simple case

Is it possible to pass a component as props and use it in a child Component in Vue?

In a Vue 2.0 app, let's say we have components A, B and C.
A declares, registers and uses B
Is it possible to pass C from A to B?
Something like this:
<template>
<div class="A">
<B :child_component="C" />
</div>
</template>
And use C in B somehow.
<template>
<div class="B">
<C>Something else</C>
</div>
</template>
The motivation: I want to create a generic component B that is used in A but receives from A its child C. Actually A will use B several times passing different 'C's to it.
If this approach is not correct, what is the proper way of doing it in Vue?
Answering #Saurabh
Instead of passing as props, I tried the suggestion inside B.
<!-- this is where I Call the dynamic component in B -->
<component :is="child_component"></component>
//this is what I did in B js
components: {
equip: Equipment
},
data () {
return {
child_component: 'equip',
_list: []
}
}
Basically I'm trying to render Equipment, but the dynamic way
I get 3 errors in console and a blank page
[Vue warn]: Error when rendering component at /home/victor/projetos/tokaai/public/src/components/EquipmentFormItem.vue:
Uncaught TypeError: Cannot read property 'name' of undefined
TypeError: Cannot read property 'setAttribute' of undefined
Apparently I'm doing something wrong
Summing up:
<!-- Component A -->
<template>
<div class="A">
<B>
<component :is="child_component"></component>
</B>
</div>
</template>
<script>
import B from './B.vue';
import Equipment from './Equipment.vue';
export default {
name: 'A',
components: { B, Equipment },
data() {
return { child_component: 'equipment' };
}
};
</script>
<!-- Component B -->
<template>
<div class="B">
<h1>Some content</h1>
<slot></slot> <!-- Component C will appear here -->
</div>
</template>
You can use special attribute is for doing this kind of thing. Example of dynamic component and its usage can be found here.
You can use the same mount point and dynamically switch between multiple components using the reserved element and dynamically bind to its is attribute.
Here's how is can be used with either an imported component or one passed as a prop:
<template>
<div class="B">
<component :is="myImportedComponent">Something</component>
--- or ---
<component :is="myPassedComponent">Something else</component>
</div>
</template>
<script>
import myImportedComponent from "#/components/SomeComponent.vue"
export default {
props: {
myPassedComponent: Object
},
components: {
myImportedComponent
},
}
</script>
Here's solution to forward custom component through props of another component
:is is special attribute and it will be used to replace your actual component and it will be ignored if you try to use it as a prop in your component. Luckily you can use something else like el and then forward this to component like so:
<template>
<div>
<component :is="el">
<slot />
</component>
</div>
</template>
<script>
export default {
name: 'RenderDynamicChild',
props: {
el: {
type: [String, Object],
default: 'div',
},
},
}
</script>
Any valid element you use in el attribute will be used as a child component. It can be html or reference to your custom component or div by default as specified in component declaration.
Passing custom component to prop is little bit tricky. One would assume you declare in a components property of parent component and then use it for el attribute but this doesn't work. Instead you need to have your dynamic component in data or computed property so you can use it in a template as a prop. Also note AnotherComponent doesn't need to be declared in components property.
<template>
<RenderDynamicChild :el="DynamicComponent">
Hello Vue!
</RenderDynamicChild>
</template>
<script>
import RenderDynamicChild from './DynamicChild';
import AnotherComponent from './AnotherComponent';
export default {
name: "ParentComponent",
components: { DynamicChild },
data() {
return {
DynamicComponent: AnotherComponent,
};
},
};
</script>
Using computed property for your dynamic component allows you to switch between components easily:
<script>
import DynamicChild from './DynamicChild';
import AnotherComponent from './AnotherComponent';
export default {
name: "ParentComponent",
components: { DynamicChild },
data() { return { count: 0 } },
computed: {
DynamicComponent() {
return this.count % 2 > 1 ? AnotherComponent : 'article';
},
},
};
</script>
Increase this.count to alternate between AnotherComponent and simple article html element.
Maybe it's too late to answer this question. But I think it could help others with this same issue.
I've been looking for a way to pass components throw others in vue, but it looks that VUE3 have a approach for that using named slots:
Here it's the documentation about that:
https://v3.vuejs.org/guide/component-slots.html#named-slots
Basically you can have:
<template>
<div class="A">
<slot name="ComponentC"></slot> <!-- Here will be rendered your ComponentC -->
</div>
<div class="A">
<slot name="ComponentD"></slot> <!-- Here will be rendered your ComponentD -->
</div>
<div class="A">
<slot></slot> <!-- This is going to be children components -->
</div>
</template>
And from your B component
<template>
<div class="B">
<A>
<template v-slot:ComponentC>
<h1>Title of ComponentC </h1>
</template>
<template v-slot:ComponentD>
<h1>Title of ComponentD </h1>
</template>
<template v-slot:default>
<h1>Title of child component </h1>
</template>
</A>
</div>
</template>
If you would like to use another component within your functional component you can do the following:
<script>
import Vue from 'vue'
import childComponent from './childComponent'
Vue.component('child-component')
export default {}
</script>
<template functional>
<div>
<child-component/>
</div>
</template>
Reference:
https://github.com/vuejs/vue/issues/7492#issue-290242300
If you mean Dynamically importing a component in a parent component, so yes, you can do that in Vue3 using:
<component :is="child_component" />
but to render "child_component" itself dynamically, you can use
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
Let me give you an example:
let's say you have several multiple child components (ChildA, ChildB, ChildC) that you want to load dynamically based on what you pass to the parent component (Parent), so the Parent component will be something like this:
Parent
<script setup lang="ts">
import { defineAsyncComponent } from 'vue';
const props = defineProps<{
childComponent?: string;
}>();
const AsyncComp = defineAsyncComponent(() =>
import(`./${props.childComponent}.vue`)
)
</script>
<template>
<component :is="AsyncComp"/>
</template>
and then you can call the Parent component dynamically wherever you want like this:
<Parent :childComponent="child-a"/>
<Parent :childComponent="child-b"/>
<Parent :childComponent="child-c"/>
For a better explanation, you can check this article:
https://medium.com/#pratikpatel_60309/dynamic-importing-component-templates-with-vue-js-78d2167db1e7

vuejs update parent data from child component

I'm starting to play with vuejs (2.0).
I built a simple page with one component in it.
The page has one Vue instance with data.
On that page I registered and added the component to html.
The component has one input[type=text]. I want that value to reflect on the parent (main Vue instance).
How do I correctly update the component's parent data?
Passing a bound prop from the parent is not good and throws some warnings to the console. They have something in their doc but it is not working.
Two-way binding has been deprecated in Vue 2.0 in favor of using a more event-driven architecture. In general, a child should not mutate its props. Rather, it should $emit events and let the parent respond to those events.
In your specific case, you could use a custom component with v-model. This is a special syntax which allows for something close to two-way binding, but is actually a shorthand for the event-driven architecture described above. You can read about it here -> https://v2.vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events.
Here's a simple example:
Vue.component('child', {
template: '#child',
//The child has a prop named 'value'. v-model will automatically bind to this prop
props: ['value'],
methods: {
updateValue: function (value) {
this.$emit('input', value);
}
}
});
new Vue({
el: '#app',
data: {
parentValue: 'hello'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<p>Parent value: {{parentValue}}</p>
<child v-model="parentValue"></child>
</div>
<template id="child">
<input type="text" v-bind:value="value" v-on:input="updateValue($event.target.value)">
</template>
The docs state that
<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>
is equivalent to
<custom-input v-model="something"></custom-input>
That is why the prop on the child needs to be named value, and why the child needs to $emit an event named input.
In child component:
this.$emit('eventname', this.variable)
In parent component:
<component #eventname="updateparent"></component>
methods: {
updateparent(variable) {
this.parentvariable = variable
}
}
From the documentation:
In Vue.js, the parent-child component relationship can be summarized as props down, events up. The parent passes data down to the child via props, and the child sends messages to the parent via events. Let’s see how they work next.
How to pass props
Following is the code to pass props to a child element:
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
How to emit event
HTML:
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
JS:
Vue.component('button-counter', {
template: '<button v-on:click="increment">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
increment: function () {
this.counter += 1
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
Child Component
Use this.$emit('event_name') to send an event to the parent component.
Parent Component
In order to listen to that event in the parent component, we do v-on:event_name and a method (ex. handleChange) that we want to execute on that event occurs
Done :)
I agree with the event emitting and v-model answers for those above. However, I thought I would post what I found about components with multiple form elements that want to emit back to their parent since this seems one of the first articles returned by google.
I know the question specifies a single input, but this seemed the closest match and might save people some time with similar vue components. Also, no one has mentioned the .sync modifier yet.
As far as I know, the v-model solution is only suited to one input returning to their parent. I took a bit of time looking for it but Vue (2.3.0) documentation does show how to sync multiple props sent into the component back to the parent (via emit of course).
It is appropriately called the .sync modifier.
Here is what the documentation says:
In some cases, we may need “two-way binding” for a prop.
Unfortunately, true two-way binding can create maintenance issues,
because child components can mutate the parent without the source of
that mutation being obvious in both the parent and the child.
That’s why instead, we recommend emitting events in the pattern of
update:myPropName. For example, in a hypothetical component with a
title prop, we could communicate the intent of assigning a new value
with:
this.$emit('update:title', newTitle)
Then the parent can listen to
that event and update a local data property, if it wants to. For
example:
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
For convenience, we offer a shorthand for this pattern with the .sync modifier:
<text-document v-bind:title.sync="doc.title"></text-document>
You can also sync multiple at a time by sending through an object. Check out the documentation here
The way more simple is use this.$emit
Father.vue
<template>
<div>
<h1>{{ message }}</h1>
<child v-on:listenerChild="listenerChild"/>
</div>
</template>
<script>
import Child from "./Child";
export default {
name: "Father",
data() {
return {
message: "Where are you, my Child?"
};
},
components: {
Child
},
methods: {
listenerChild(reply) {
this.message = reply;
}
}
};
</script>
Child.vue
<template>
<div>
<button #click="replyDaddy">Reply Daddy</button>
</div>
</template>
<script>
export default {
name: "Child",
methods: {
replyDaddy() {
this.$emit("listenerChild", "I'm here my Daddy!");
}
}
};
</script>
My full example: https://codesandbox.io/s/update-parent-property-ufj4b
It is also possible to pass props as Object or Array. In this case data will be two-way binded:
(This is noted at the end of topic: https://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow )
Vue.component('child', {
template: '#child',
props: {post: Object},
methods: {
updateValue: function () {
this.$emit('changed');
}
}
});
new Vue({
el: '#app',
data: {
post: {msg: 'hello'},
changed: false
},
methods: {
saveChanges() {
this.changed = true;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<p>Parent value: {{post.msg}}</p>
<p v-if="changed == true">Parent msg: Data been changed - received signal from child!</p>
<child :post="post" v-on:changed="saveChanges"></child>
</div>
<template id="child">
<input type="text" v-model="post.msg" v-on:input="updateValue()">
</template>
In Parent Conponent -->
data : function(){
return {
siteEntered : false,
};
},
In Child Component -->
this.$parent.$data.siteEntered = true;
2021 ANSWER - Vue 2.3+
SHORT ANSWER: Just add .sync modifier in the parent and pass the data as props to the children:
// PARENT:
data () {
return {
formData: {
members: [] //<- we wanna pass this one down to children and add/remove from the child component
}
}
// PARENT TEMPLATE:
<!-- ADD MEMBERS -->
<add-members :members.sync="formData.members" />
Nested child component: AddMembers.vue
export default {
name: 'AddMembers',
props: ['members'],
methods: {
addMember () {
this.members.push(new Member()) // <-- you can play and reactivity will work (in the parent)
},
removeMember (index) {
console.log('remove', index, this.members.length < 1)
this.members.splice(index, 1)
}
}
}
Long story: changes from the child component in reallity are being $emitted and updating formData.members[] of the parent.
source: Mauro Perez at medium
In the child
<input
type="number"
class="form-control"
id="phoneNumber"
placeholder
v-model="contact_number"
v-on:input="(event) => this.$emit('phoneNumber', event.target.value)"
/>
data(){
return {
contact_number : this.contact_number_props
}
},
props : ['contact_number_props']
In parent
<contact-component v-on:phoneNumber="eventPhoneNumber" :contact_number_props="contact_number"></contact-component>
methods : {
eventPhoneNumber (value) {
this.contact_number = value
}
The correct way is to $emit() an event in the child component that the main Vue instance listens for.
// Child.js
Vue.component('child', {
methods: {
notifyParent: function() {
this.$emit('my-event', 42);
}
}
});
// Parent.js
Vue.component('parent', {
template: '<child v-on:my-event="onEvent($event)"></child>',
methods: {
onEvent: function(ev) {
v; // 42
}
}
});
When we want to pass the data to the parent component as well as another nested child component of the current child component, using a data property would be useful as shown in the following example.
Example:
Calling your child component from the parent component like this.
Parent component:
<template>
<TodoItem :todoParent="todo" />
</template>
<script>
export default {
data() {
return {
todo: {
id:1,
task:'todo 1',
completed:false
}
};
}
}
</script>
Child component:
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todoParent"],
data() {
return {
todo: this.todoParent,
};
},
methods: {
markCompleted() {
this.todo.completed = true
},
},
};
</script>
Even you can pass this property to the nested child component and it won't give this error/warning.
Other use cases when you only need this property sync between parent and child component. It can be achieved using the sync modifier from Vue. v-model can also be useful. Many other examples are available in this question thread.
Example2: using component events.
We can emit the event from the child component as below.
Parent component:
<template>
<TodoItem :todo="todo" #markCompletedParent="markCompleted" />
</template>
<script>
export default {
data() {
return {
todo: {
id:1,
task:'todo 1',
completed:false
}
};
},
methods: {
markCompleted() {
this.todo.completed = true
},
}
}
</script>
Child component:
<template>
<div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
<p>
<input type="checkbox" #change="markCompleted" />
{{todo.task}}
<button class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props: ["todo"],
methods: {
markCompleted() {
this.$emit('markCompletedParent', true)
},
}
};
</script>
Another way is to pass a reference of your setter from the parent as a prop to the child component, similar to how they do it in React.
Say, you have a method updateValue on the parent to update the value, you could instantiate the child component like so: <child :updateValue="updateValue"></child>. Then on the child you will have a corresponding prop: props: {updateValue: Function}, and in the template call the method when the input changes: <input #input="updateValue($event.target.value)">.
I don't know why, but I just successfully updated parent data with using data as object, :set & computed
Parent.vue
<!-- check inventory status - component -->
<CheckInventory :inventory="inventory"></CheckInventory>
data() {
return {
inventory: {
status: null
},
}
},
Child.vue
<div :set="checkInventory">
props: ['inventory'],
computed: {
checkInventory() {
this.inventory.status = "Out of stock";
return this.inventory.status;
},
}
his example will tell you how to pass input value to parent on submit button.
First define eventBus as new Vue.
//main.js
import Vue from 'vue';
export const eventBus = new Vue();
Pass your input value via Emit.
//Sender Page
import { eventBus } from "../main";
methods: {
//passing data via eventbus
resetSegmentbtn: function(InputValue) {
eventBus.$emit("resetAllSegment", InputValue);
}
}
//Receiver Page
import { eventBus } from "../main";
created() {
eventBus.$on("resetAllSegment", data => {
console.log(data);//fetching data
});
}
I think this will do the trick:
#change="$emit(variable)"
Intro
I was looking for sending data from parent to child (and back) in vue3 (I know the question was about vue2, but there are no references for vue3 on SO at the time).
Below is the working boilerplate result, pure "html + js", no packagers, modules, etc with few caveats I had, explained.
Notes:
Tnserting the child - line
<component-a :foo="bar" #newfooevent="bar = $event"></component-a>`
I bind parent.bar to child.foo using short-hand :foo="bar", same as v-bind:foo="bar". It passes data from parent to child through props.
Caveat: Event listener should be placed in the child component tag only!
That is the #newfooevent="bar = $event" part.
You cannot catch the signal in the <div id="app"> or anywhere else inside the parent.
Still, this is the parent's side of the universe, and here you can access all parent's data and extract the data from the child's signal to deal with it.
You can create app, and define component after it (the app.component("component-a", ...) part.
Caveat: there are no need in forward declaration of components, e.g. functions in C/C++. You can create app which uses the component, and define the component afterwards. I lost a lot of time looking for the way to declare it somehow - no need.
Here you can find a nice example of the v-model usage, and the code I used to sort things out: https://javascript.plainenglish.io/vue-3-custom-events-d2f310fe34c9
The example
<!DOCTYPE html>
<html lang="en">
<head>
<title>App</title>
<meta charset="utf-8" />
<script src="https://unpkg.com/vue#next"></script>
</head>
<body>
<div id="app">
<component-a :foo="bar" #newfooevent="bar = $event"></component-a>
<p>Parent copy of `bar`: {{ bar }}</p>
<button #click="bar=''">Clear</button>
</div>
<script>
const app = Vue.createApp({
data() {
return {
bar: "bar start value"
};
}
});
app.component("component-a", {
props: {
foo: String
},
template: `
<input
type="text"
:value="foo"
#input="$emit('newfooevent', $event.target.value)">
`
});
app.mount("#app");
</script>
</body>
</html>
There is another way of communicating data change from child to parent which uses provide-inject method. Parent component "provides" data or method for the child component, and this data or method is then "injected" into child component - but it can also be used for triggering a method in parent and passing it a parameter.
This approach can be especially useful when having a child component which happens to be embedded in multiple other components. Also, in a large project care must be taken not to lose overview of provide and inject usage.
Example of parent (top level) component App.vue using provide to give access to it's method updateParentValue (if method is provided and not data, provide is in form of a method):
<template>
<h2>App.vue, parentValue is: <em>{{ parentValue }}</em></h2>
<ChildComponent1 />
</template>
<script>
import ChildComponent1 from "./components/ChildComponent1.vue";
export default {
data() {
return {
parentValue: "",
};
},
components: {
ChildComponent1,
},
provide() {
return {
updateParent: this.updateParentValue,
};
},
methods: {
updateParentValue($value) {
this.parentValue = $value;
},
},
};
</script>
In this example component Component4.vue is in the "bottom", that is, App.vue contains Component1, Component1 contains Component2... until Component4 which actually utilizes inject to get access to parent method which is then invoked and a parameter $value is passed (just a random number here):
<template>
<div>
<h2>ChildComponent4.vue</h2>
<button #click="updateParent(Math.random())">
Update parent value in App.vue
</button>
</div>
</template>
<script>
export default {
inject: ["updateParent"],
};
</script>
Entire example is available here.
Vue.js documentation

Categories

Resources