How to pass data to nested child components vue js? - javascript

I get how to pass data from parent to child with props in a situation like:
<template>
<div>
<div v-for="stuff in content" v-bind:key="stuff.id">
<ul>
<li>
{{ stuff.items }}
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: stuff,
props: ['content'],
data () {
return {
}
}
}
</script>
And then bind the data to the component in the parent component like,
<template>
<div>
<stuff v-bind:content="stuffToPass"></stuff>
</div>
</template>
<script>
import stuff from './stuff.vue';
export default {
data () {
return {
stuffToPass: [
{id: 1, items: 'foo'},
{id: 2, items: 'bar'},
{id: 3, items: 'baz'}
]
}
},
components: {
stuff
}
}
</script>
But say I have the root component, and I want to pass data to the stuff component, like in the above, but when I have a number of other components like parent > x > y > stuff, and it's still the stuff component that will ultimately be receiving that data, I don't know how to do that.
I heard of provide/inject, but I'm not sure that's the appropriate use, or at least I couldn't get it working.
Then I tried passing props, but then I found myself trying to bind a prop to a component to pass as a prop to a child component and that doesn't sound right, so then I just re-wrote my components in the 'stuff' component, but I feel that's probably re-writing way to much code to be close to reasonable.

there are a few possibilities to pass data parent > x > y > stuff
props - applicable but you would have to pipe the data through all components...
store (vuex) - applicable but could become complicated to handle
event bus - the most flexible and direct way
below, a simple example on how to implement the event bus:
// src/services/eventBus.js
import Vue from 'vue';
export default new Vue();
the code from where you want to emit the event:
// src/components/parent.vue
<script>
import EventBus from '#/services/eventBus';
export default {
...
methods: {
eventHandler(val) {
EventBus.$emit('EVENT_NAME', val);
},
},
...
};
</script>
the code for where you want to listen for the event:
// src/components/stuff.vue
<script>
import EventBus from '#/services/eventBus';
export default {
...
mounted() {
EventBus.$on('EVENT_NAME', val => {
// do whatever you like with "val"
});
},
...
};
</script>

Use watchers or computed properties https://v2.vuejs.org/v2/guide/computed.html
const Stuff = Vue.component('stuff', {
props: ['content'],
template: `<div>
<div v-for="stuff in content" v-bind:key="stuff.id">
<ul>
<li>
{{ stuff.items }}
</li>
</ul>
</div>
</div>`
});
const Adapter = Vue.component('adapter', {
components: { Stuff },
props: ['data'],
template: `<div>
<Stuff :content="newData"/>
</div>`,
data() {
return {
newData: []
};
},
created() {
this.changeData();
},
watch: {
data: {
deep: true,
handler: function() {
this.changeData();
}
}
},
methods: {
changeData() {
this.newData = JSON.parse(JSON.stringify(this.data));
}
}
});
const app = new Vue({
el: '#app',
components: { Adapter },
data() {
return {
stuffToPass: [
{ id: 1, items: 'foo' },
{ id: 2, items: 'bar' },
{ id: 3, items: 'baz' }
]
};
},
methods: {
addItem() {
this.stuffToPass.push({ id: this.stuffToPass.length + 1, items: 'new' });
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.1/vue.js"></script>
<div id="app">
<button #click="addItem">Add</button>
<Adapter :data="stuffToPass"/>
</div>

Related

Vue not reacting to a computed props change

I am using the Vue composition API in one of my components and am having some trouble getting a component to show the correct rendered value from a computed prop change. It seems that if I feed the prop directly into the components render it reacts as it should but when I pass it through a computed property it does not.
I am not sure why this is as I would have expected it to be reactive in the computed property too?
Here is my code:
App.vue
<template>
<div id="app">
<Tester :testNumber="testNumber" />
</div>
</template>
<script>
import Tester from "./components/Tester";
export default {
name: "App",
components: {
Tester,
},
data() {
return {
testNumber: 1,
};
},
mounted() {
setTimeout(() => {
this.testNumber = 2;
}, 2000);
},
};
</script>
Tester.vue
<template>
<div>
<p>Here is the number straight from the props: {{ testNumber }}</p>
<p>
Here is the number when it goes through computed (does not update):
{{ testNumberComputed }}
</p>
</div>
</template>
<script>
import { computed } from "#vue/composition-api";
export default {
props: {
testNumber: {
type: Number,
required: true,
},
},
setup({ testNumber }) {
return {
testNumberComputed: computed(() => {
return testNumber;
}),
};
},
};
</script>
Here is a working codesandbox:
https://codesandbox.io/s/vue-composition-api-example-forked-l4xpo?file=/src/components/Tester.vue
I know I could use a watcher but I would like to avoid that if I can as it's cleaner the current way I have it
Don't destruct the prop in order to keep its reactivity setup({ testNumber }) :
setup(props) {
return {
testNumberComputed: computed(() => {
return props.testNumber;
}),
};
}

how to render component from a component in vue js

I have a component with following template:
<div v-for:"item in store" v-bind:key="item.type">
<a>{{item.type}}</a>
</div>
I have another component called 'StoreComponent'
On click of a element in first component I want to clear the current component and show the StoreComponent and able to pass item.type to StoreComponent.
I don't want to use router-link or router.push as I don't want to create a new url but override the current component with the new one depending on the item.type value.
StoreComponent.vue
export default{
name: 'StoreComponent',
props: ['item'],
data: function () {
return {
datum: this.item
}
},
methods: {
//custom methods
}
}
You could use dynamic components and pass the item-type as a prop.
Vue.component('foo', {
name: 'foo',
template: '#foo'
});
Vue.component('bar', {
name: 'bar',
template: '#bar',
props: ['test']
});
new Vue({
el: "#app",
data: {
theComponent: 'foo', // this is the 'name' of the current component
somethingWeWantToPass: {
test: 123 // the prop we are passing
},
},
methods: {
goFoo: function() {
this.theComponent = 'foo';
},
goBar: function() {
this.theComponent = 'bar';
},
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<button #click="goFoo">Foo</button>
<button #click="goBar">Bar</button>
<component :is="theComponent" v-bind="somethingWeWantToPass"></component>
</div>
<template id="foo">
<div>
Foo
</div>
</template>
<template id="bar">
<div>
Bar
<div>This is a prop: {{ this.test }}</div>
</div>
</template>

Vue: Access component object properties

I'm trying to use a statement within a element:
v-if="currentstep < maxStep"
The maxStep should be obtained from the number of components listed on my defauld export
export default {
name: 'step',
data () {
return {
maxStep: 8,
currentstep: 0
}
},
components: {
ConfigPublicador,
ConfigServico,
ModeloReceita,
Integracoes,
ConfigTema,
ConfigApp,
ConfigExtras,
Assets,
Revisao
}
}
Something like
maxStep = components.length
Any Ideias?
Thanks
This is definitely a code smell. But, you can get that value via Object.keys(this.$options.components).length.
Here's an example:
const Foo = {
template: '<div></div>',
}
new Vue({
el: '#app',
components: { Foo },
data() {
return {
count: 0,
}
},
created() {
this.count = Object.keys(this.$options.components).length;
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<div id="app">
<div v-if="count > 0">
There is at least one component
</div>
</div>

How can I change data value from one component to another component in Vue Js?

I am new in Vue Js. So, I am facing a problem to changes data value from another component.
I have a component A:
<template>
<div id="app">
<p v-on:click="test ()">Something</p>
</div>
</template>
import B from '../components/B.vue';
export default {
components: {
B
},
methods: {
test: function() {
B.data().myData = 124
B.data().isActive = true
console.log(B.data().myData);
console.log(B.data().isActive);
}
}
}
Component B:
export default {
data() {
return {
myData: 123,
isActive: false
}
}
}
It still component B data.
But it cannot be affected component B data. I want to data changes of component B from component A. How can I do that?
Please explain me in details. I have seen vue js props attribute but I don't understand.
You're looking for Vuex.
It's the centralized store for all the data in your applications.
Take a look at their documentation, it should be pretty straightforward.
You can pass down props to the component B. These props can be updated by the parent component. You can think of B as a stupid component that just renders what the parent tells it to rendern. Example:
// Component A
<template>
<div id="app">
<p v-on:click="test ()">Something</p>
<b data="myData" isActive="myIsActive"></b>
</div>
</template>
<script>
import B from '../components/B.vue';
export default {
components: {
B
},
data() {
return {
myData: 0,
myIsActive: false,
};
},
methods: {
test: function() {
this.myData = 123
this.myIsActive = true
}
}
}
</script>
// Component B
<template>
<div>{{ data }}{{ isActive }}</div>
</template>
<script>
export default {
props: {
data: Number,
isActive: Boolean
};
</script>
There are few ways...
if your components have a parent child relationship you can pass data values from parent into child.
If your want to communicate back to parent component when child component has changed something, you can use vuejs event emitter(custom event) to emit a event when data value change and that event can be listened in another component and do what you want.
If your components doesn't have a relationship, then you have to use use something else than above things. You can use two things.one is event bus, other one is state management library.for vue there is a official state management library called VueX.it is very easy to use.if you want to use something else than vuex, you can use it such as redux, mobx etc.
This documentation has everything what you want to know. I don't want to put any code, because of doc is very clear.
VueX is the most preferable way to do this! Very easy to use..
https://v2.vuejs.org/v2/guide/components.html
//component A
Vue.component('my-button', {
props: ['title'],
template: `<button v-on:click="$emit('add-value')">{{title}}</button>`
});
Vue.component('my-viewer', {
props: ['counter'],
template: `<button>{{counter}}</button>`
});
new Vue({
el: '#app',
data: {
counter: 0,
},
methods: {
doSomething: function() {
this.counter++;
}
}
})
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
});
//parent
new Vue({
el: '#blog-post-demo',
data: {
posts: [{
id: 1,
title: 'My journey with Vue'
},
{
id: 2,
title: 'Blogging with Vue'
},
{
id: 3,
title: 'Why Vue is so fun'
}
]
}
});
Vue.component('blog-post2', {
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
<div v-html="post.content"></div>
</div>`
})
new Vue({
el: '#blog-posts-events-demo',
data: {
posts: [{
id: 1,
title: 'My journey with Vue'
},
{
id: 2,
title: 'Blogging with Vue'
},
{
id: 3,
title: 'Why Vue is so fun'
}
],
postFontSize: 1
},
methods: {
onEnlargeText: function() {
this.postFontSize++;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<p>Two components adding & viewing value</p>
<div id="app">
<my-button :title="'Add Value'" v-on:add-value="doSomething"></my-button>
<my-viewer :counter="counter"></my-viewer>
</div>
<br>
<br>
<p>Passing Data to Child Components with Props (Parent to Child)</p>
<div id="blog-post-demo">
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title"></blog-post>
</div>
<p>Listening to Child Components Events (Child to Parent)</p>
<div id="blog-posts-events-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post2 v-for="post in posts" v-bind:key="post.id" v-bind:post="post" v-on:enlarge-text="onEnlargeText"></blog-post2>
</div>
</div>
First, you need a parent so two component can communicate. when my-button component is clicked triggers an event add-value that calls doSomething() function, then updates the value & show it to my-viewer component.
HTML
<!--PARENT-->
<div id="app">
<!--CHILD COMPONENTS-->
<my-button :title="'Add Value'" v-on:add-value="doSomething"></my-button>
<my-viewer :counter="counter"></my-viewer>
</div>
VUE.JS
//component A
Vue.component('my-button',{
props:['title'],
template:`<button v-on:click="$emit('add-value')">{{title}}</button>`
});
//Component B
Vue.component('my-viewer',{
props:['counter'],
template:`<button>{{counter}}</button>`
});
//Parent
new Vue({
el: '#app',
data:{
counter:0,
},
methods:{
doSomething:function(){
this.counter++;
}
}
})
This is base on Vue Components Guide
Passing Data to Child Components with Props (Parent to Child)
VUE.JS
//component (child)
//Vue component must come first, else it won't work
Vue.component('blog-post', {
/*Props are custom attributes you can register on a component. When a
value is passed to a prop attribute, it becomes a property on that
component instance*/
props: ['title'],
template: '<h3>{{ title }}</h3>'
});
//parent
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
});
HTML:
v-for will loop on posts and pass data to blog-post component
<div id="blog-post-demo">
<blog-post v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"></blog-post>
</div>
Listening to Child Components Events (Child to Parent)
HTML
You must first register the event by v-on:enlarge-text="onEnlargeText" to use $emit and make sure that it's always set to lower case or it won't work properly. example enlargeText and Enlargetext will always be converted to enlargetext, thus use enlarge-text instead, because its easy to read & valid, for a brief explanation about $emit you can read it here
<div id="blog-posts-events-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:post="post"
v-on:enlarge-text="onEnlargeText"></blog-post>
</div>
</div>
VUE.JS
When user clicks the button the v-on:click="$emit('enlarge-text')" will trigger then calling the function onEnlargeText() in the parent
//component (child)
Vue.component('blog-post', {
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
<div v-html="post.content"></div>
</div>`
})
//parent
new Vue({
el: '#blog-posts-events-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
],
postFontSize: 1
},
methods:{
onEnlargeText:function(){
this.postFontSize++;
}
}
})
Actually props suck sometimes you got some old external library in jquyer and need just damn pass value. in 99% of time use props that do job but.
A) spend tons of hours debuging changing tones of code to pass variables
B) one line solution
Create main variable in data letmeknow as object {}
this.$root.letmeknow
then somewhere in code from component
this.$root.letmeknow = this;
and then boom i got component console.log( this.$root.letmeknow ) and see now can change some values

Vue.js pass function as prop and make child call it with data

I have a posts list component and a post component.
I pass a method to call from the posts list to the post component, so when a button is click it will be called.
But I want to pass the post id when this function is clicked
Code:
let PostsFeed = Vue.extend({
data: function () {
return {
posts: [....]
}
},
template: `
<div>
<post v-for="post in posts" :clicked="clicked" />
</div>
`,
methods: {
clicked: function(id) {
alert(id);
}
}
}
let Post = Vue.extend({
props: ['clicked'],
data: function () {
return {}
},
template: `
<div>
<button #click="clicked" />
</div>
`
}
as you can see in Post component you have a click that runs a method he got from a prop, I want to add a variable to that method.
How do you do that?
Normally, the click event handler will receive the event as its first argument, but you can use bind to tell the function what to use for its this and first argument(s):
:clicked="clicked.bind(null, post)
Updated answer: However, it might be more straightforward (and it is more Vue-standard) to have the child emit an event and have the parent handle it.
let Post = Vue.extend({
template: `
<div>
<button #click="clicked">Click me</button>
</div>
`,
methods: {
clicked() {
this.$emit('clicked');
}
}
});
let PostsFeed = Vue.extend({
data: function() {
return {
posts: [1, 2, 3]
}
},
template: `
<div>
<post v-for="post in posts" #clicked="clicked(post)" />
</div>
`,
methods: {
clicked(id) {
alert(id);
}
},
components: {
post: Post
}
});
new Vue({
el: 'body',
components: {
'post-feed': PostsFeed
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<post-feed></post-feed>
Using Vue 2 and expanding on #Roy J's code above, I created a method in the child component (Post) that calls the prop function and sends back a data object as part of the callback. I also passed in the post as a prop and used its ID value in the callback.
Back in the Posts component (parent), I modified the clicked function by referencing the event and getting the ID property that way.
Check out the working Fiddle here
And this is the code:
let Post = Vue.extend({
props: {
onClicked: Function,
post: Object
},
template: `
<div>
<button #click="clicked">Click me</button>
</div>
`,
methods: {
clicked() {
this.onClicked({
id: this.post.id
});
}
}
});
let PostsFeed = Vue.extend({
data: function() {
return {
posts: [
{id: 1, title: 'Roadtrip', content: 'Awesome content goes here'},
{id: 2, title: 'Cool post', content: 'Awesome content goes here'},
{id: 3, title: 'Motorcycle', content: 'Awesome content goes here'},
]
}
},
template: `
<div>
<post v-for="post in posts" :post="post" :onClicked="clicked" />
</div>
`,
methods: {
clicked(event) {
alert(event.id);
}
},
components: {
post: Post
}
});
new Vue({
el: '#app',
components: {
'post-feed': PostsFeed
}
});
And this is the HTML
<div id="app">
<post-feed></post-feed>
</div>
this is the service:
export const getBuilding = () => {
console.log("service");
return 0;
};
in the parent component:
<Update-Theme :method="parentMethod"/>
import { getBuilding } from "./service";
methods: {
parentMethod() {
console.log("Parent");
getBuilding();
},
}
and in the child component
<v-btn color="green darken-1" text #click="closeDialog()">
props: [ "method"],
methods: {
closeDialog() {
this.method();
//this.$emit("update:dialog", false);
},
}

Categories

Resources