Home » vue » Problem when accessing to child component data

Problem when accessing to child component data

Posted by: admin November 26, 2021 Leave a comment

Questions:

I must be doing something wrong when trying to access the data of my child component.

I have two sets of data : targets and comparators. They can be updated with inputs by the user. Computed props update the data of the child component, and then I wanted to emit an event to the parent component thanks to the watchers I put on these two sets of data. However, these watchers seems to never be triggered..

This how the script of the child component looks like (remark, I only pasted two computed props):

<script>
    export default {
        name: "Targets",
        props: ['mealPlan'],
        data() {
            return {
                targets:{
                    weight: 0,
                    calories: 0,
                    carbs: 0,
                    proteins: 0,
                    fats: 0,
                    sugar: 0,
                    fibers: 0,
                    cholesterol: 0,
                    calcium: 0,
                    iron: 0,
                    sodium: 0,
                    potassium: 0,
                    cost: 0,
                    vitaminA: 0,
                    vitaminB6: 0,
                    vitaminB12: 0,
                    vitaminC: 0,
                    vitaminD: 0,
                    vitaminE: 0,
                    vitaminK: 0
                },
                comparators:{
                    weight: 0,
                    calories: 0,
                    carbs: 0,
                    proteins: 0,
                    fats: 0,
                    sugar: 0,
                    fibers: 0,
                    cholesterol: 0,
                    calcium: 0,
                    iron: 0,
                    sodium: 0,
                    potassium: 0,
                    cost: 0,
                    vitaminA: 0,
                    vitaminB6: 0,
                    vitaminB12: 0,
                    vitaminC: 0,
                    vitaminD: 0,
                    vitaminE: 0,
                    vitaminK: 0
                }
            }
        },
        computed:{
            weightTarget:{
                get:function(){
                    this.targets.weight = this.mealPlan.targetWeight;
                    return this.mealPlan.targetWeight;
                },
                set:function(newValue){
                    this.targets.weight = newValue
                }
            },


            weightComparator:{
                get:function(){
                    this.comparators.weight = this.mealPlan.comparatorWeight;
                    return this.mealPlan.comparatorWeight;
                },
                set:function(newValue){
                    this.comparators.weight = newValue
                }
            },

        },
        watch:{
            targets:function(){
                console.log(this.targets);
                this.$emit('targetsUpdated', this.targets);
            },
            comparators:function(){
                this.$emit('comparatorsUpdated', this.comparators);
            }
        }
    }
</script>

The parent component receives the event like this :

<template>
    <div>
        <button @click="save" type="button" class="btn btn-success pull-left">Save</button>

        <div>

                <b-tab title="Targets">
                    <Targets
                    :meal-plan="mealPlan"
                    @targetsUpdated="targetsUpdated"
                    @comparatorsUpdated="comparatorsUpdated"
                    ></Targets>
                </b-tab>
        </div>
    </div>
</template>

<script>
    import MealTable from "../components/MealTable";
    import DayTable from "../components/DayTable";
    import Targets from "./Targets";
    import Specifications from "./Specifications";

    export default {
        name: "Edit",
        components: {DayTable, MealTable,Targets,Specifications},
        data(){
            return{
                mealPlan: {},
                currentDay: '1',
                targets:{},
                comparators:{},
            }
        },

        computed: {

        methods:{
            targetsUpdated(newTargets){
                this.targets = {
                    weight: newTargets.targetWeight,
                    calories: newTargets.targetCalories,
                    carbs: newTargets.targetCarbs,
                    proteins: newTargets.targetProteins,
                    fats: newTargets.targetFats,
                    sugar: newTargets.targetSugar,
                    fibers: newTargets.targetFibers,
                    cholesterol: newTargets.targetCholesterol,
                    calcium: newTargets.targetCalcium,
                    iron: newTargets.targetIron,
                    sodium: newTargets.targetSodium,
                    potassium: newTargets.targetPotassium,
                    cost: newTargets.targetCost,
                    vitaminA: newTargets.targetVitaminA,
                    vitaminB6: newTargets.targetVitaminB6,
                    vitaminB12: newTargets.targetVitaminB12,
                    vitaminC: newTargets.targetVitaminC,
                    vitaminD: newTargets.targetVitaminD,
                    vitaminE: newTargets.targetVitaminE,
                    vitaminK: newTargets.targetVitaminK
                }
            },
            comparatorsUpdated(newComparators){
                this.comparators = {
                    weight: newComparators.comparatorWeight,
                    calories: newComparators.comparatorCalories,
                    carbs: newComparators.comparatorCarbs,
                    proteins: newComparators.comparatorProteins,
                    fats: newComparators.comparatorFats,
                    sugar: newComparators.comparatorSugar,
                    fibers: newComparators.comparatorFibers,
                    cholesterol: newComparators.comparatorCholesterol,
                    calcium: newComparators.comparatorCalcium,
                    iron: newComparators.comparatorIron,
                    sodium: newComparators.comparatorSodium,
                    potassium: newComparators.comparatorPotassium,
                    cost: newComparators.comparatorCost,
                    vitaminA: newComparators.comparatorVitaminA,
                    vitaminB6: newComparators.comparatorVitaminB6,
                    vitaminB12: newComparators.comparatorVitaminB12,
                    vitaminC: newComparators.comparatorVitaminC,
                    vitaminD: newComparators.comparatorVitaminD,
                    vitaminE: newComparators.comparatorVitaminE,
                    vitaminK: newComparators.comparatorVitaminK
                }
            }
        }
    }
</script>

To sum up, I do not understand while the watcher on targets or comparators is not triggered, thanks for the help guys !

Answers:

So when you watch an array or an object, Vue has no idea that you’ve changed what’s inside that data. You have to tell Vue that you want it to inspect inside of the data when watching for changes.

You can do this by setting deep to true on your watcher and rearranging the handler function.

watch: {
    targets: {
      // This will let Vue know to look inside the object
      deep: true,

      // We have to move our method to a handler field
      handler()
        this.$emit('targetsUpdated', this.targets);
      }
    },
    comparators: {
      // This will let Vue know to look inside the object
      deep: true,

      // We have to move our method to a handler field
      handler()
        this.$emit('comparatorsUpdated', this.comparators);
      }
    }
 }

###

In addition to the usage of watchers one can simply add the same logic to the setters:

e.g:

       weightComparator:{
            get:function(){
                this.comparators.weight = this.mealPlan.comparatorWeight;
                return this.mealPlan.comparatorWeight;
            },
            set:function(newValue){
                let oldValue = this.comparators.weight
                this.$emit('change:comparators:weight', newValue, oldValue)
                let oldComparators = Object.assign({}, this.comparators) // shallow copy
                this.comparators.weight = newValue
                this.$emit('changed:comparators', Object.assign({}, this.comparators), oldComparators)                                          

            }
        },