how to reset the object prop when restart the vue component? - vue.js

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' }
}
}

Related

Vue: Setting data from binded prop is using the default value

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>

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>

Access props value in another props' validator

I want to access a props value in another props' validator:
props: {
type: {
type: String,
default: "standard"
},
size: {
type: String,
default: "normal",
validator(value) {
// below I want to access the props `type` from above.
return (this.type !== "caution" || this.type !== "primary") && value !== "mega"
}
}
}
But I'm getting TypeError: Cannot read property 'type' of undefined.
Any idea?
The this variable in a prop's validator does not reference the Vue instance. And, unfortunately, there's no real way to reference another prop's value in a prop's validator function.
One thing you could do would be to set a watcher on the Vue instance's $props object, setting the immediate option to true so that the watcher fires when the component is created. That watcher could trigger the validation logic where this is a reference to the Vue instance.
Here's a simple example:
Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.component('child', {
template: '<div></div>',
props: {
type: {
type: String,
default: "standard"
},
size: {
type: String,
default: "normal"
}
},
methods: {
validateProps() {
if ((this.type === "caution" || this.type === "primary") && this.size === "mega") {
console.error('Invalid props');
}
}
},
watch: {
$props: {
immediate: true,
handler() {
this.validateProps();
}
}
}
});
new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<child type="caution" size="mega"></child>
</div>
Another option would be to pass an object with a type and size property as a single prop. That way the validator of that prop would have a reference to both values.
Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.component('child', {
template: '<div></div>',
props: {
config: {
type: Object,
default: () => ({ type: "standard", size: "normal" }),
validator({ type, size }) {
return !((type === "caution" || type === "primary") && size === "mega");
}
}
}
});
new Vue({
el: '#app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<child :config="{ type: 'caution', size: 'mega'}"></child>
</div>
(And just a note: your validation logic is probably incorrect. As it's written, the statement in parenthesis will always evaluate to true. I updated that logic in my examples to be what I think you meant.)
If you only need to validate once, do it simply in the mounted() handler. This fails the jest tests for us, and logs an error in the browser.
props: {
type: {
type: String,
default: "standard"
},
size: {
type: String,
default: "normal"
}
},
mounted() {
if ((this.type === "caution" || this.type === "primary") && this.size === "mega") {
console.error('Invalid props');
}
}
It is not possible. There were few propositions in VUE project but all were rejected:
https://github.com/vuejs/vue/issues/3495
https://github.com/vuejs/vue/issues/6787
https://github.com/vuejs/vue/issues/9925
The trivial way for such case is to use a computed property or to validate properties on created hook. In this case, all properties are available.

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 computed cannot get the the attributes in the object

Account to the defind of vuex
// inside mutations
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
// html
<input v-model="message">
// ...
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
and I code this
<input type="text" v-model="data.reference">
data () {
return {
data:{
...
reference: '',
}
}
},
computed: {
'data.reference':{
get () {
return this.$store.state.currentKbdata.reference
},
set (value) {
console.log(222)
this.$store.commit('updateReference', value)
}
}
}
And when i enter the input the 222 is not show up
.........................................................................
You cannot define computed getters with dot notation like you can watchers. Here's a fiddle of that not working, where you can see the error in the console reads:
Cannot read property 'reference' of undefined.
Also, it appears that you are attempting to define a computed property that already exists as a property defined in the data method. In this fiddle, you can see that that also won't work. The value defined in the data method takes precedence over the computed property definition.
Anyways, from your example, I don't see why you need to create a computed property on a nested object property at all.
Just use a normal definition for a computed property:
const store = new Vuex.Store({
state: {
reference: '',
},
mutations: {
updateReference(state, reference) {
state.reference = reference;
}
}
});
new Vue({
el: '#app',
store,
computed: {
reference: {
get() {
return this.$store.state.reference;
},
set(val) {
this.$store.commit('updateReference', val);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.min.js"></script>
<div id="app">
<input type="text" v-model="reference">
{{ $store.state.reference }}
</div>
If you really need to set the value of data.reference, then there are many ways you could make that happen. One would be to turn it into a computed property as well:
const store = new Vuex.Store({
state: {
reference: '',
},
mutations: {
updateReference(state, reference) {
state.reference = reference;
}
}
});
new Vue({
el: '#app',
store,
computed: {
reference: {
get() {
return this.$store.state.reference;
},
set(val) {
this.$store.commit('updateReference', val);
}
},
data() {
return { reference: this.reference };
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.min.js"></script>
<div id="app">
<input type="text" v-model="reference">
<div>data.reference: {{ data.reference }}</div>
<div>$store.state.reference: {{ $store.state.reference }}</div>
</div>