How can I update the data in a child component from within the parent? I am trying to update the autores property from within the parent and have that update the child data. Currently nothing happens, I don't think I have the data linked correctly. If I add it as data to the parent component then the parent re-renders when the code runs and no results can be seen.
Parent:
<template>
<div>
<input #keyup="editName(lender.id, $event)">
<autocomplete-suggestions :autores="[]"></autocomplete-suggestions>
</div>
</template>
<script type="text/javascript">
export default {
props: ['lender'],
data(){
return {
}
},
methods: {
editName: function(lenderid, event) {
var self = this;
axios.post('/lenders/autocomplete', {...})
.then(function (data) {
self.autores = data.data.suggestions;
})
.catch(function (error) {
console.log("Error occurred getting the autocomplete");
});
},
},
watch: {
},
mounted: function() {
}
};
</script>
Child:
<template>
<div class="list">
<div v-for="(res, i) in autores">{{res}}</div>
</div>
</template>
<script type="text/javascript">
export default {
props: ['autores'],
data(){
return {
}
}
};
</script>
Update:
If I change my code in parent to:
data(){
return {
autores:[]
}
},
and:
<autocomplete-suggestions :autores="autores"></autocomplete-suggestions>
Then simply whenever I update this.autores, whatever text has been typed into the text box in parent in reset, as if it were rerendering the whole component. How can ths be stopped?
Parent:
<template>
<div>
<input #keyup="editName(lender.id, $event)">
<autocomplete-suggestions ref="autocompleteSuggestions"></autocomplete-suggestions>
</div>
</template>
<script type="text/javascript">
export default {
props: ['lender'],
methods: {
editName: function (lenderid, event) {
var self = this;
axios.post('/lenders/autocomplete', {...})
.then(function (data) {
self.$refs.autocompleteSuggestions.autores = data.data.suggestions;
})
.catch(function (error) {
console.log("Error occurred getting the autocomplete");
});
},
},
};
</script>
Child:
<template>
<div className="list">
<div v-for="(res, i) in autores">{{res}}</div>
</div>
</template>
<script type="text/javascript">
export default {
data() {
return {
autores: []
};
},
};
</script>
Related
datalist.js
import axios from "axios";
export const datalist = () => {
return axios.get("myapiurl/name...").then((response) => response);
};
HelloWorld.vue
<template>
<div>
<div v-for="item in items" :key="item.DttID">
<router-link
:to="{
name: 'UserWithID',
params: { id: item.DepaD },
query: { DepaD: item.DepaID },
}"
>
<div class="bt-color">{{ item.DepaName }}</div>
</router-link>
</div>
<br /><br /><br />
<User />
</div>
</template>
<script>
import User from "./User.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
},
data() {
return {
items: datalist,
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
};
</script>
User.vue
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item.Accv }}
</div>
</div>
</template>
<script>
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
lists: datalist,
};
},
computed: {
user: function () {
return this.lists.filter((item) => {
if (item.DepaD === this.$route.params.id) {
return item;
}
});
},
},
};
</script>
Error with the code is,
[Vue warn]: Error in render: "TypeError: this.lists.filter is not a function"
TypeError: this.lists.filter is not a function
The above error i am getting in User.vue component in the line number '20'
From the api which is in, datalist.js file, i think i am not fetching data correctly. or in the list filter there is problem in User.vue?
Try to change the following
HelloWorld.vue
data() {
return {
items: [],
};
},
mounted() {
datalist().then((r) => {
this.items = r.data;
});
},
User.vue
data() {
return {
lists: []
};
},
mounted() {
datalist().then((r) => {
this.lists = r.data;
});
},
At least this suppress the error, but i cant tell more based on your snippet since there are network issues :)
Since your datalist function returns a Promise, you need to wait for it to complete. To do this, simply modify your component code as follows:
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
// empty array on initialization
lists: [],
};
},
computed: {
user: function() {
return this.lists.filter((item) => {
if (item.DeploymentID === this.$route.params.id) {
return item;
}
});
},
},
// asynchronous function - because internally we are waiting for datalist() to complete
async-mounted() {
this.users = await datalist() // or datalist().then(res => this.users = res) - then async is not needed
}
};
now there will be no errors when initializing the component, since initially lists is an empty array but after executing the request it will turn into what you need.
You may define any functions and import them, but they wont affect until you call them, in this case we have datalist function imported in both HelloWorld and User component, but it did not been called in User component. so your code:
data() {
return {
lists: datalist,
};
},
cause lists to be equal to datalist that is a function, no an array! where .filter() should be used after an array, not a function! that is the reason of error.
thus you should call function datalist and put it's response in lists instead of putting datalist itself in lists
Extra:
it is better to call axios inside the component, in mounted, created or ...
it is not good idea to call an axios command twice, can call it in HelloWorl component and pass it to User component via props
Hello I have a component Carousel, and I have method:
carousel () {
return this.$refs.carousel
}
When I try pass to prop of other component, I get undefined in prop data, why?
<Dots :carousel="carousel()" />
In child component I have:
export default {
name: 'Dots',
props: ['carousel']
}
In mounted when I try call console.log(this.carousel), I get undifined. But I need get a component data of Carousel. How I can do it?
Sandbox: https://codesandbox.io/s/flamboyant-monad-5bhjz?file=/src/components/Test.vue
You can set a function in carousel to return the data you want to send to dots. Then, in the parent component, set its return value to an attribute in data which will be passed as a prop:
const dotsComponent = Vue.component('dotsComponent', {
template: '#dotsComponent',
props: ['carousel']
});
const carouselComponent = Vue.component('carouselComponent', {
template: '#carouselComponent',
data() { return { id:1 } },
methods: {
getData() { return { id: this.id } }
}
});
new Vue({
el: "#app",
components: { dotsComponent, carouselComponent },
data() { return { carousel:null } },
mounted() { this.carousel = this.$refs.carousel.getData(); }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div><carousel-component ref="carousel"/></div>
<div><dots-component :carousel="carousel"/></div>
</div>
<template id="dotsComponent"><p>Dots: {{carousel}}</p></template>
<template id="carouselComponent"><p>Carousel</p></template>
You could pass the ref directly without using a method any other property :
<Dots :carousel="$refs.carousel" />
I'm using Vue.js. From my template I include the child component (componentB) which includes several input elements. I want to initialize those input elements from my parent template. I found a way to do this (see code below). However, I'm wondering if this is a correct way, as the articles I have read so far use different approaches (e.g. with $emit):
https://simonkollross.de/posts/vuejs-using-v-model-with-objects-for-custom-components
https://zaengle.com/blog/using-v-model-on-nested-vue-components
https://alligator.io/vuejs/add-v-model-support/
Can you confirm that my code below matches the Vue.js design concepts or are there flaws?
<template>
<div>
<div class="md-layout">
<div class="md-layout-item md-size-100">
<ComponentB ref="componentB" v-model="componentB"></ComponentB>
</div>
</div>
</div>
</template>
<script>
import { ComponentB } from "#/components";
export default {
components: {
ComponentB
},
data() {
return {
componentB: {
textInputField: "my-initial-value"
}
};
},
methods: {
validate() {
return this.$refs.componentB.validate().then(res => {
this.$emit("on-validated", res);
return res;
});
}
}
};
</script>
<style></style>
Form componentB
<template>
<div>
<md-field
:class="[
{ 'md-valid': !errors.has('textInputField') && touched.textInputField },
{ 'md-form-group': true },
{ 'md-error': errors.has('textInputField') }
]"
>
<md-icon>label_important</md-icon>
<label>My text input</label>
<md-input
v-model="textInputField"
data-vv-name="textInputField"
type="text"
name="textInputField"
required
v-validate="modelValidations.textInputField"
>
</md-input>
<slide-y-down-transition>
<md-icon class="error" v-show="errors.has('textInputField')"
>close</md-icon
>
</slide-y-down-transition>
<slide-y-down-transition>
<md-icon
class="success"
v-show="!errors.has('textInputField') && touched.textInputField"
>done</md-icon
>
</slide-y-down-transition>
</md-field>
</div>
</template>
<script>
import { SlideYDownTransition } from "vue2-transitions";
export default {
name: "componentB",
props: ['value'],
components: {
SlideYDownTransition
},
computed: {
textInputField: {
get() {return this.value.textInputField},
set(textInputField) { this.$emit('input', { ...this.value, ['textInputField']: textInputField })}
}
},
data() {
return {
touched: {
textInputField: false
},
modelValidations: {
textInputField: {
required: true,
min: 5
}
}
};
},
methods: {
getError(fieldName) {
return this.errors.first(fieldName);
},
validate() {
return this.$validator.validateAll().then(res => {
return res;
});
}
},
watch: {
textInputField() {
this.touched.runnerName = true;
}
}
};
</script>
<style></style>
The simplest way to pass data to child component is to use props, which are then available in the child component and can pass the values back up to the parent.
https://v2.vuejs.org/v2/guide/components-props.html
// PARENT COMPONENT
<ComponentB :textInputField="textInputField" ...></ComponentB>
// CHILD COMPONENT
// TEMPLATE SECTION
<md-input
v-model="textInputField"
value="textInputField"
...
>
// SCRIPT SECTION
export default {
props: {
textInputField: String
}
}
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);
},
}
I'm trying to get a method to execute on a parent component when a button in one of its child components is clicked. I am using single file components with Webpack. Here's the code for the child component:
<template>
<button v-on:click="add">Click</button>
</template>
<script>
export default {
methods: {
add: () => {
console.log('foo')
this.$dispatch('addClick')
}
}
}
</script>
And the code for the parent:
<template>
<div id="app">
<count :total="total"></count>
<click></click>
</div>
</template>
<script>
import count from './components/count.vue'
import click from './components/click.vue'
export default {
components: {
count,
click
},
data: () => {
return {
total: 0
}
},
methods: {
addToTotal: () => {
console.log('bar')
this.total += 1
}
},
events: {
addClick: 'addToTotal'
}
}
</script>
The count component is just an h1 element that displays the total variable. When I click the button, "foo" logs to the console, but "bar" does not and the total doesn't change. Any ideas on what I'm doing wrong? Thanks in advance!
You are using lambda notation for your methods, which is giving them the wrong this. If you use function instead, it will work.
Vue.component('click', {
template: '#clicker',
methods: {
add: function() {
console.log('foo')
this.$dispatch('addClick')
}
}
})
new Vue({
el: '#app',
data: () => {
return {
total: 0
}
},
methods: {
addToTotal: function () {
console.log('bar')
this.total += 1
}
},
events: {
addClick: 'addToTotal'
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<template id="clicker">
<button v-on:click="add">Click</button>
</template>
<div id="app">
<count :total="total"></count>
<click></click>
{{total}}
</div>
Use two-way .sync binding type modifier on total property of count component so that the value will be updated when parent total value is changed. Here is an example:
Vue.component('click', {
template: '<button v-on:click="add">Click</button>',
methods: {
add: function () {
this.$dispatch('addClick');
}
}
});
Vue.component('count', {
template: '<h1 v-text="total"></h1>',
props: {
total: {
type: Number,
twoWay: true
}
}
});
new Vue({
el: '#app',
data: {
total: 1
},
methods: {
addTotal: function () {
this.total++;
}
},
events: {
addClick: 'addTotal'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.25/vue.min.js"></script>
<div id="app">
<count :total.sync="total"></count>
<click></click>
</div>