I have a Vue counter which counts up from 1 to 10. It's implemented as a component. I need to reset the counter from the main app. What's the best way to do this? As you can see from my example, the watch method doesn't do the job:
Vue.component('my-counter', {
template: "#counter-vue",
data: function() {
return {
age_now: null
}
},
props: {
age: Number
},
mounted: function() {
this.age_now = this.age;
TweenLite.to(this, 10, { age_now: this.$root.age_end, roundProps: "age_now", ease:Linear.easeNone });
},
watch: {
age(val) {
this.age_now = val;
}
}
});
var app = new Vue({
el: '.container',
data: {
age: 1,
age_end: 10
},
methods: {
reset() {
this.age = 1;
}
}
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script type="text/x-template" id="counter-vue">
<div>
<p>Age: {{age_now}}</p>
</div>
</script>
<div class="container">
<h1>Vue Component Reset</h1>
<my-counter :age="age"></my-counter>
<button @click.prevent="reset">Reset</button>
<p>What's the best way to implement the reset of the component to restart the count up from 1 to 10?</p>
</div>
See also on CodePen: https://codepen.io/MSCAU/pen/OagMJy
First of all, your watcher for age
in the my-counter
component isn't ever firing because you aren't ever changing the value of the age
prop. If it's initialized to 1
and gets set to 1
in the click-handler, it won't trigger the watcher because the value didn't change.
But second of all, it'd make more sense, in this case, to just move the reset
method to the my-counter
component and then call it from the parent scope via a ref
, like so:
<my-counter ref="counter" :age="age"></my-counter>
<button @click.prevent="$refs.counter.reset">Reset</button>
It looks like you'll also need to call the TweenLite.to
method again if you want the counter to increment again. So it'd be good to pull that logic into its own method (say count
) that you can call from the mounted
hook and from the reset
method.
Also, I noticed that it also seems like the TweenLite.to
method is overriding the binding for the age
prop until the counter has finished incrementing. If you want to reset the counter before the TweenLite.to
method has finished, you'll need to store a reference to the returned tween object and then call its kill
method before the counter starts.
Finally, I'm seeing that you're referencing the this.$root.age_end
in the object passed to the TweenLite.to
. Except in rare cases, this is considered bad practice, as now the component unnecessarily depends on the root Vue instance always having an age_end
property and it obscures the flow of data. Since that value appears to be static, you should just set it as a data property of the component. Or at least pass it in as a prop from the parent.
Here's a working example with my suggested changes:
Vue.component('my-counter', {
template: "#counter-vue",
data: function() {
return {
age_now: null,
age_end: 10,
tween: null,
}
},
props: {
age: Number
},
mounted: function() {
this.age_now = this.age;
this.count();
},
methods: {
count() {
if (this.tween) {
this.tween.kill();
}
this.tween = TweenLite.to(this, 10, {
age_now: this.age_end,
roundProps: "age_now",
ease: Linear.easeNone
});
},
reset() {
this.age_now = 1;
this.count();
}
}
});
var app = new Vue({
el: '.container',
data: {
age: 1,
}
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script type="text/x-template" id="counter-vue">
<div>
<p>Age: {{age_now}}</p>
</div>
</script>
<div class="container">
<h1>Vue Component Reset</h1>
<my-counter ref="counter" :age="age"></my-counter>
<button @click.prevent="$refs.counter.reset">Reset</button>
</div>