Vue: Setting data from binded prop is using the default value - vue.js

When I pass in name as a prop, it works as expected and sets the nameData data field so that I can change it within the component.
Parent
<child name="charles"></child>
Child
data() {
return {
nameData: this.name
}
},
props: {
name: {type: String, default: "NONE"}
}
When I bind the prop like below, the nameData data field is set to the default prop, which is "None". Why is that?
Parent
data() {
return {
firstName: "Charles"
}
}
<child :name="firstName"></child>
Child
data() {
return {
nameData: this.name
}
},
props: {
name: {type: String, default: "NONE"}
}

See my example
First child component works as expected (your code)
Second displays "NONE" because it's data is initialized with prop value, which is undefined at the time the (child's) data() is executed. Any change to the prop in the future (in mounted in my example) wont affect child's data...
const child = Vue.component('child', {
data() {
return {
nameData: this.name
}
},
props: {
name: {
type: String,
default: "NONE"
}
},
template: `<div> {{ nameData }} </div>`
})
const vm = new Vue({
el: "#app",
components: {
child
},
data() {
return {
firstName: "Charles",
secondName: undefined
}
},
mounted() {
this.secondName = "Fred"
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<child :name="firstName"></child>
<child :name="secondName"></child>
</div>

name="charles" - you passed down the string "charles";
:name="firstName" - you passed down a variable "firstName" which seems to be undefined in the parent component at the time of child rendering and the prop in the child component gets the default value you provided it with.
UPD: I played a little with Michal's example. You can use computed instead of data() {} or directly a prop itself if you don't need any data transformation. Because it seems that you assign parent's firstName value in async mode or just later.
const child = Vue.component('child', {
computed: {
nameData() {
return this.name;
}
},
props: {
name: {
type: String,
default: "NONE"
}
},
template: `<div> {{ nameData }} </div>`
})
const vm = new Vue({
el: "#app",
components: {
child
},
data() {
return {
firstName: "Charles",
secondName: undefined
}
},
mounted() {
this.secondName = "Fred"
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.10/vue.js"></script>
<div id="app">
<child :name="firstName"></child>
<child :name="secondName"></child>
</div>

Related

how to reset the object prop when restart the vue component?

it's a simple example : https://codepen.io/homor/pen/vYZVodj
<div id="components-demo">
<nameinput ref="nameinput" v-if="statusInput"></nameinput>
<button #click="statusInput=true">open</button>
<button #click="statusInput=false">close</button>
</div>
Vue.component('nameinput', {
props: {
subject:{
type: Object,
default: {
name: 'homor'
}
},
namey: {
type: String,
default: 'homor'
}
},
data: function (){
return {
}
},
template: '<div>subject.name<input v-model="subject.name" type="input" /><br/>namey<input v-model="namey" type="input" /></div>'
});
new Vue({
el: '#components-demo',
data: {
statusInput: true
}
})
component "nameinput" has two default props.
changes props value
close the component
open the component
prop namey come to default value. But prop subject.name keep the changed value.
How to let subject.name change to default value ?
It says here that:
// Object with a default value
propE: {
type: Object,
// Object or array defaults must be returned from
// a factory function
default() {
return { message: 'hello' }
}
},
In your case, default value will be set if you do this:
subject:{
type: Object,
default() {
return { name: 'homor' }
}
}

Vue computed methods can't change data using setter in template

I need change data using computed:
<template>
<div>{{ userDataTest }}</div>
</template>
props: {
exampleData: {
type: Object,
required: true,
},
},
computed: {
userDataTest: {
get: function() {
return this.exampleData;
},
set: function(newValue) {
console.log(newValue);
return newValue;
},
},
}
mounted () {
setTimeout(() => {
console.log('Change now to null!');
this.userDataTest = null;
}, 5000);
},
I get data using props, next I create computed methods with getter and setter. I added userDataTest in <template>. And the I change (using mounted) data in this.userDataTest to null using setter.
In console.log(newValue); in setter I see newValue is null, but in <template> nothing change still I have data from getter.
Why setter not change data in <template> to null ?
It seems you're trying to set the computed property's value by returning a new value, but Vue doesn't actually check the setter's return value. Perhaps you were trying to proxy a data variable through a computed property. If so, the setter should set that data variable in the setter body.
For instance, your component could declare a data variable, named userData, which always has the latest value of the exampleData prop through a watcher:
export default {
props: {
exampleData: Object
},
data() {
return {
userData: {}
}
},
watch: {
exampleData(exampleData) {
this.userData = exampleData
}
},
}
Then, your template and computed prop would use userData instead:
<template>
<div>{{ userData }}</div>
</template>
<script>
export default {
//...
computed: {
userDataTest: {
get() {
return this.userData
},
set(newValue) {
this.userData = newValue
}
}
}
}
</script>
Mutating a prop locally is considered an anti-pattern
However, you can use the .sync modifier as shown below, but you can't set the prop to null because you are specifying that it has to be an Object type.
Vue.component('my-component', {
template: `<div>{{ userDataTest }}</div>`,
props: {
exampleData: {
type: Object,
required: true
}
},
computed: {
userDataTest: {
get: function() {
return this.exampleData
},
set: function(newValue) {
this.$emit('update:exampleData', newValue)
}
}
},
mounted() {
setTimeout(() => {
console.log('Change now!')
this.userDataTest = {}
}, 2500)
}
})
new Vue({
el: '#app',
data() {
return {
exampleData: {
foo: 'bar'
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<my-component :example-data.sync="exampleData"></my-component>
</div>

I need fresh eyes to fix VUE props not working

This is a silly task in VUE.JS... but I'm missing it.
I have a sub parent component having:
<teamPlayers :teamId="team.id_team"></teamPlayers>
The value teamId is sent to the child component and it works: I can see the value in child template <h2>{{teamId}}</h2> properly.
But in same child component I got undefined inside the methods using this.teamId.
Here the whole child code:
export default {
props: ['teamId'],
methods: {
getJokess: function () {
console.log(this.teamId);
},
},
created() {
this.getJokess();
}
}
The console should return the correct value but it returns undefined instead of the {{teamId}} is render perfectly.
All that I can think of is that teams may not be declared in your data() function. If it isn't it won't be reactive. Consider the example below:
const teamPlayers = {
props: ["teamId"],
methods: {
getJokess() {
console.log(this.teamId);
}
},
created() {
this.getJokess();
},
template: "<h2>{{teamId}}</h2>"
};
const app = new Vue({
el: "#app",
components: {
"team-players": teamPlayers
},
data() {
return {
teams: []
};
},
mounted() {
setTimeout(() => {
this.teams = [{
id_team: "fizz"
},
{
id_team: "buzz"
},
{
id_team: "foo"
},
{
id_team: "bar"
}
]
}, 1000);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="team of teams">
<team-players :team-id="team"></team-players>
</div>
</div>

Vue.js - data value does not set from prop

What do I have: two components, parent and child.
Parent
<UserName :name=user.name></UserName>
...
components: {UserName},
data() {
return {
user: {
name: '',
...
}
}
},
created() {
this.fetchUser()
console.log(this.user) //<- object as it is expected
},
methods: {
fetchUser() {
let that = this
axios.get(//...)
.then(response => {
for (let key in response.data) {
that.user[key] = response.data[key]
}
})
console.log(that.user) //<- object as it is expected
}
}
Child
<h3 v-if="!editing" #click="edit">{{ usersName }}</h3>
<div v-if="editing">
<div>
<input type="text" v-model="usersName">
</div>
</div>
...
props: {
name: {
type: String,
default: ''
},
},
data() {
return {
editing: false,
usersName: this.name,
...
}
},
Problem: even when name prop is set at child, usersName data value is empty. I've inspected Vue debug extension - same problem.
What have I tried so far (nothing helped):
1) props: ['name']
2)
props: {
name: {
type: String
},
},
3) usersName: JSON.parse(JSON.stringify(this.name))
4) <UserName :name="this.user.name"></UserName>
P. S. when I pass static value from parent to child
<UserName :name="'just a string'"></UserName>
usersName is set correctly.
I've also tried to change name prop to some foobar. I guessed name might conflict with component name exactly. But it also didn't helped.
user.name is initially empty, and later gets a value from an axios call. usersName is initialized from the prop when it is created. The value it gets is the initial, empty value. When user.name changes, that doesn't affect the already-initialized data item in the child.
You might want to use the .sync modifier along with a settable computed, or you might want to put in a watch to propagate changes from the prop into the child. Which behavior you want is not clear.
Here's an example using .sync
new Vue({
el: '#app',
data: {
user: {
name: ''
}
},
methods: {
fetchUser() {
setTimeout(() => {
this.user.name = 'Slartibartfast'
}, 800);
}
},
created() {
this.fetchUser();
},
components: {
userName: {
template: '#user-name-template',
props: {
name: {
type: String,
default: ''
}
},
computed: {
usersName: {
get() { return this.name; },
set(value) { this.$emit('update:name', value); }
}
},
data() {
return {
editing: false
}
}
}
}
});
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
{{user.name}}
<user-name :name.sync=user.name></user-name>
</div>
<template id="user-name-template">
<div>
<input type="text" v-model="usersName">
</div>
</template>
should be passed like this...
<UserName :name="user.name"></UserName>
if data property is still not being set, in mounted hook you could set the name property.
mounted() {
this.usersName = this.name
}
if this doesn't work then your prop is not being passed correctly.
sidenote: I typically console.log within the mounted hook to test such things.

Vue.js parent child communication using props

I have the setup below using vue2.0. The method something() is called which updates 'place' on the parent. I want the child to watch for changes to place, and when it updates to react accordingly. However, the watch method in the child is never called. Any idea what's wrong here please?
Thanks,
// parent
import home from './components/home.vue'
var App = window.App = new Vue({
data: {
place: '',
},
methods: {
something: function(event) {
this.place = 'some new place'
})
},
components: {
'home': home,
}
}).$mount('#app')
// child ('./components/home.vue')
export default {
props: ['place'],
// need to react here when place changes
watch: {
place: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
}
Do you pass the place prop to the component?
Here is a working snippet:
var home = {
props: ['place'],
template: '<div>home: {{ place }}</div>',
watch: {
place: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
}
}
};
var App = new Vue({
el: '#app',
components: {
'home': home
},
data: {
place: 'somewhere'
},
methods: {
something: function(event) {
this.place = 'some new place'
}
}
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<button #click="something">set place</button>
<p>app: {{ place }}</p>
<home :place="place"></home>
</div>