I started learning vuejs and wanted to make a simple "like" project using components in vue. I made two button (like and dislike) that every one has separate counter. Until now everything was OK. Now, I want show sum of these counters in under. for this I need these 2 variables next to each other outside the component so I try to bind their values with external variables.But external variables didn't change! and also said in the console:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "lval" found in --->
See my Code:
Vue.component('like',{ template: '#like' , props: ['lval','lname','lstep','lclass'], methods:{ changeCounter : function(step){ this.lval += parseInt(step); } } }); new Vue({ el: '#app', data:{ counterlike: 0, counterdislike: 0 } });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> <div id="app"> <like lname="Like" lstep="1" :lval="counterlike" lclass="btn-success"></like> <like lname="Dislike" lstep="-1" :lval="counterdislike" lclass="btn-danger"></like> <br> {{ counterlike + counterdislike }} </div> <template id="like"> <button :class="['btn',lclass]" @click="changeCounter(lstep)" >{{ lname + ' ' + lval }}</button> </template>
There is a reason why props can't be mutated directly, this way the information has one-way flow, from parent to child and not the other way around.
True two-way bindings can't be achieved, but the child can emit an event with needed info to the parent, and the latter could update bound variable.
Since 2.3.0 Vue has a special .sync
modifier to do just that, and here is your modified snippet using .sync
modifier. Note, that changeCounter
method doesn't change lval
directly, but emits an event.
Here
Vue.config.productionTip = false; Vue.component('like', { template: '#like', props: ['lval', 'lname', 'lstep', 'lclass'], methods: { changeCounter: function(step) { const newlval = this.lval + parseInt(step); this.$emit('update:lval', newlval); } } }); new Vue({ el: '#app', data: { counterlike: 0, counterdislike: 0 } });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> <div id="app"> <like lname="Like" lstep="1" :lval.sync="counterlike" lclass="btn-success"></like> <like lname="Dislike" lstep="-1" :lval.sync="counterdislike" lclass="btn-danger"></like> <br> {{ counterlike + counterdislike }} </div> <template id="like"> <button :class="['btn',lclass]" @click="changeCounter(lstep)" >{{ lname + ' ' + lval }}</button> </template>
For your situation you can simply use .sync
modifier to avoid mutating props directly. Sync modifier means your prop receiver component (child) receives props data from parent and generates an event when parent must change passed value. And parent component should bind prop with '.sync' modifier that means auto subscription on child event with update. More info you can read here
This way your code will work fine
Vue.component('like',{ template: '#like' , props: ['lval','lname','lstep','lclass'], methods:{ changeCounter : function(step){ this.$emit('update:lval', this.lval + parseInt(step)); } } }); new Vue({ el: '#app', data:{ counterlike: 0, counterdislike: 0 } });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> <div id="app"> <like lname="Like" lstep="1" :lval.sync="counterlike" lclass="btn-success"></like> <like lname="Dislike" lstep="-1" :lval.sync="counterdislike" lclass="btn-danger"></like> <br> {{ counterlike + counterdislike }} </div> <template id="like"> <button :class="['btn',lclass]" @click="changeCounter(lstep)" >{{ lname + ' ' + lval }}</button> </template>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.