简体   繁体   中英

Can i change component prop variable binded to another variable in VueJS

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.

See: One Way Data Flow

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM