Reputation: 9549
I have two fiddles: A, B (using Vuejs 2.2.4)
I have a computed property which can be changed programmatically (I am using the get
and set
methods).
Expectations:
If the default parameter changes (this.message
), the computed property (computedMessage
) must change (default behaviour).
If the secondary parameter changes (this.messageProxy
), only then the computed property must reflect the secondary parameter.
Fiddle A works as expected but Fiddle B doesn't.
Error: The default behaviour (point 1) stops after the secondary parameter changes.
The only difference between the fiddles is a console
statement in the computed property.
Background: I was trying to set a computed
property programatically. The computed
property is set like:
computedMessage: {
get () {
let messageProxy = this.messageProxy
this.messageProxy = null
console.log(messageProxy, this.messageProxy, this.message)
return messageProxy || this.message
},
set (val) {
this.messageProxy = val
}
}
This allows me to set the value of computedMessage
like:
this.computedMessage = 'some string'
If these lines:
get () {
let messageProxy = this.messageProxy
this.messageProxy = null
return messageProxy || this.message
}
were to be replaced with:
get () {
return this.messageProxy || this.message
}
then computedMessage
can no longer get access to this.message
the moment this.messageProxy
is set.
By setting this.messageProxy
to null
I ensure that the
computedMessage = this.messageProxy
only if an assignment is made.
Upvotes: 8
Views: 8537
Reputation: 43881
The actual problem with your code is that you are changing data values in your get
function, and they are data values that trigger the re-computation of the get
function. Don't do that. The get
should just be computing a value based on other values. In this case, it should be
get () {
console.log(this.messageProxy, this.message);
return this.messageProxy || this.message;
},
With or without the console
message, it will do what it is supposed to do.
Having re-checked your expectations, I see that you want the override to be cleared whenever the default message changes. You can do that with an additional watch
:
var demo = new Vue({
el: '#demo',
data() {
return {
message: 'I am a great guy',
messageProxy: null,
someText: ''
}
},
computed: {
computedMessage: {
get() {
return this.messageProxy || this.message
},
set(val) {
this.messageProxy = val
}
}
},
methods: {
overrideComputed() {
this.computedMessage = this.someText
}
},
watch: {
message: function() {
this.messageProxy = null;
}
}
})
div {
margin: 5px;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<div id="demo">
<p>This message must reflect value of input1</p>
<div>
{{ computedMessage }}
</div>
input1: <input type="text" v-model='message'>
<div>
<p>This will cause computed message to reflect input2</p>
input2: <input type="text" v-model='someText'>
<button @click='overrideComputed'>Override</button>
</div>
</div>
PS: You don't really need a settable computed here. You could have overrideComputed
set messageProxy
directly.
Upvotes: 2
Reputation: 55644
The reference to this.message
in the return statement isn't triggering computedMessage
to update. This is because its location in the logical ||
statement makes it inaccessible. It's a gotcha documented in the Vue.js Computed Properties Documentation.
From the Docs:
status: function () {
return this.validated
? this.okMsg
: this.errMsg // errMsg isn't accessible; won't trigger updates to status
}
The workaround is to explicitly access dependencies:
status: function () {
// access dependencies explicitly
this.okMsg
this.errMsg
return this.validated
? this.okMsg
: this.errMsg
}
So in your example add a reference to this.message
:
get() {
this.message
let messageProxy = this.messageProxy
this.messageProxy = null
return messageProxy || this.message
}
The reason your first fiddle was working as expected was because the console.log
call had this.message
as a parameter.
Upvotes: 9