简体   繁体   中英

Child Component not updating when parent updates vuejs

I have a vue instance that passes an object to a child component. The child component has a checkbox that when clicked calls an event that the vue instance handles to update the object on the parent that is passed to the child component. Based on the vue documentation I thought this would cause the child component to update the related fields. However, the date field is not updating as I would expect when I click on the checkbox. In the image below, when I check the Management Name checkbox, I would expect the current day to appear, but I am not seeing any date. What am I missing here?

Design

子组件的布局

Parent Instance

new Vue({
    el: "#evaluations-app",
    data: {
        evaluation: new Evaluation()        
    },
    methods: {        
        updateEmployeeSO: function (newSO, newSODate) {
            this.evaluation.EmployeeSO = newSO;
            this.evaluation.EmployeeSODate = newSODate;
        },
        updateReviewerSO: function (newSO, newSODate) {
            this.evaluation.ReviewerSO = newSO;
            this.evaluation.ReviewerSODate = newSODate;
        },
        updateManagementSO: function (newSO, newSODate) {
            this.evaluation.ManagementSO = newSO;
            this.evaluation.ManagementSODate = newSODate;
        }
});

Child Component

Vue.component('sign-off', {
    props: ['initEvaluation', 'perspective'],
    template: `
        <div class="sign-off-comp">
            <div class="sign-off-item">
                <div class="sign-off-field-1 col-1">{{evaluation.EmployeeName}}</div>
                <input :disabled="!enableEmployeeSO" v-model="evaluation.EmployeeSO" class="sign-off-field-2 col-2" type="checkbox" @click="EmployeeSOChanged"/>
                <div class="sign-off-field-3 col-3">{{employeeSODate}}</div>                
            </div>  
            <div class="sign-off-item">
                <div class="sign-off-field-1 col-1">{{evaluation.ReviewerName}}</div>
                <input :disabled="!enableReviewerSO" v-model="evaluation.ReviewerSO" class="sign-off-field-2 col-2" type="checkbox" @click="ReviewerSOChanged"/>
                <div class="sign-off-field-3 col-3">{{reviewerSODate}}</div>                
            </div>   
            <div class="sign-off-item">
                <div class="sign-off-field-1 col-1">{{evaluation.ManagementName}}</div>
                <input :disabled="!enableManagementSO" v-model="evaluation.ManagementSO" class="sign-off-field-2 col-2" type="checkbox" @click="ManagementSOChanged"/>
                <div class="sign-off-field-3 col-3">{{managementSODate}}</div>                
            </div>                   
        </div>
    `,
    data: function () {
        return {
            evaluation: this.initEvaluation,
            employeeClicked: false,
            reviewerClicked: false,
            managementClicked: false,
            currentCommentSource: this.perspective
        }
    },
    methods: {
        EmployeeSOChanged: function () {
            this.employeeClicked = true;
            //this.evaluation.EmployeeSODate == null || this.evaluation.EmployeeSODate == "" ? this.evaluation.EmployeeSODate = Helpers.getCurrentDate() : this.evaluation.EmployeeSODate = "";
            this.$emit('employee-so-changed', this.evaluation.EmployeeSO, this.evaluation.EmployeeSODate);
        },
        ReviewerSOChanged: function () {
            this.reviewerClicked = true;
            //this.evaluation.ReviewerSODate == null || this.evaluation.ReviewerSODate == "" ? this.evaluation.ReviewerSODate = Helpers.getCurrentDate() : this.evaluation.ReviewerSODate = "";
            this.$emit('reviewer-so-changed', this.evaluation.ReviewerSO, this.evaluation.ReviewerSODate);
        },
        ManagementSOChanged: function () {
            this.managementClicked = true;
            //this.evaluation.ManagementSODate == null || this.evaluation.ManagementSODate == "" ? this.evaluation.ManagementSODate = Helpers.getCurrentDate() : this.evaluation.ManagementSODate = "";
            this.$emit('management-so-changed', this.evaluation.ManagementSO, this.evaluation.ManagementSODate == null || this.evaluation.ManagementSODate == "" ? Helpers.getCurrentDate() : "");
        }
    },
    computed: {
        enableEmployeeSO: function () {
            return (this.perspective == "Employee" && !this.evaluation.EmployeeSO) || this.employeeClicked;
        },
        enableReviewerSO: function () {
            return (this.perspective == "Reviewer" && !this.evaluation.ReviewerSO && this.evaluation.EmployeeSO) || this.reviewerClicked;
        },
        enableManagementSO: function () {
            return (this.perspective == "Management" && !this.evaluation.ManagementSO && this.evaluation.ReviewerSO && this.evaluation.EmployeeSO) || this.managementClicked;
        },
        employeeSODate: function () {
            return this.evaluation.EmployeeSODate != null && this.evaluation.EmployeeSODate == new Date("01-01-1900") ? "" : this.evaluation.EmployeeSODate != null && this.evaluation.EmployeeSODate.length >= 10 ? this.evaluation.EmployeeSODate.substring(0, 10) : this.evaluation.EmployeeSODate;
        },
        reviewerSODate: function () {
            return this.evaluation.ReviewerSODate != null && this.evaluation.ReviewerSODate == new Date("01-01-1900") ? "" : this.evaluation.ReviewerSODate != null && this.evaluation.ReviewerSODate.length >= 10 ? this.evaluation.ReviewerSODate.substring(0, 10) : this.evaluation.ReviewerSODate;
        },
        managementSODate: function () {
            return this.evaluation.ManagementSODate != null && this.evaluation.ManagementSODate == new Date("01-01-1900") ? "" : this.evaluation.ManagementSODate != null && this.evaluation.ManagementSODate.length >= 10 ? this.evaluation.ManagementSODate.substring(0, 10) : this.evaluation.ManagementSODate;
        }
    }
});

Model

export class Evaluation {
    private _EmployeeName: string;
    private _EmployeeSO: boolean;
    private _EmployeeSODate: Date;
    private _ReviewerName: string;
    private _ReviewerSO: boolean;
    private _ReviewerSODate: Date;
    private _ManagementReviewerName: string;
    private _ManagementReviewerSO: boolean;
    private _ManagementReviewerSODate: Date;

    constructor() {
        this._EmployeeName = "";
        this._EmployeeSO = false;
        this._EmployeeSODate = new Date("01-01-1900");
        this._ReviewerName = "";
        this._ReviewerSO = false;
        this._ReviewerSODate = new Date("01-01-1900");
        this._ManagementReviewerName = "";
        this._ManagementReviewerSO = false;
        this._ManagementReviewerSODate = new Date("01-01-1900");
    }

    get EmployeeName(): string {
        return this._EmployeeName;
    }
    set EmployeeName(employeeName: string) {
        if (this._EmployeeName != employeeName) {
            this._EmployeeName = employeeName;
        }
    }
   
    get EmployeeSO(): boolean {
        return this._EmployeeSO;
    }
    set EmployeeSO(employeeSO: boolean) {
        if (this._EmployeeSO != employeeSO) {
            this._EmployeeSO = employeeSO;
        }
    }

    get EmployeeSODate(): Date {
        return this._EmployeeSODate;
    }
    set EmployeeSODate(employeeSODate: Date) {
        if (this._EmployeeSODate != employeeSODate) {
            this._EmployeeSODate = employeeSODate;
        }
    }

    get ReviewerName(): string {
        return this._ReviewerName;
    }
    set ReviewerName(reviewerName: string) {
        if (this._ReviewerName != reviewerName) {
            this._ReviewerName = reviewerName;
        }
    }

    get ReviewerSO(): boolean {
        return this._ReviewerSO;
    }
    set ReviewerSO(reviewerSO: boolean) {
        if (this._ReviewerSO != reviewerSO) {
            this._ReviewerSO = reviewerSO;
        }
    }

    get ReviewerSODate(): Date {
        return this._ReviewerSODate;
    }
    set ReviewerSODate(reviewerSODate: Date) {
        if (this._ReviewerSODate != reviewerSODate) {
            this._ReviewerSODate = reviewerSODate;
        }
    }

    get ManagementReviewerName(): string {
        return this._ManagementReviewerName;
    }
    set ManagementReviewerName(managementReviewerName: string) {
        if (this._ManagementReviewerName != managementReviewerName) {
            this._ManagementReviewerName = managementReviewerName;
        }
    }

    get ManagementReviewerSO(): boolean {
        return this._ManagementReviewerSO;
    }
    set ManagementReviewerSO(managementReviewerSO: boolean) {
        if (this._ManagementReviewerSO != managementReviewerSO) {
            this._ManagementReviewerSO = managementReviewerSO;
        }
    }

    get ManagementReviewerSODate(): Date {
        return this._ManagementReviewerSODate;
    }
    set ManagementReviewerSODate(managementReviewerSODate: Date) {
        if (this._ManagementReviewerSODate != managementReviewerSODate) {
            this._ManagementReviewerSODate = managementReviewerSODate;
        }
    }
}

Update

I just noticed that in my child component I am using MangementSO & ManagementSODate while the model has ManagementReviewerSO & ManagementReviewerSODate . Changing these fixes my code. However, based on discussion below, I am a little confused as to why putting props into the local data is the incorrect way to handle this situation. Can someone please explain?

There is nothing wrong with initializing data properties with props. I think the biggest source of confusion in a lot of the comments here is the fact that initEvaluation is an object . That being the case, any changes to that object will be reflected everywhere that the object is used.

In the code in question, evaluation in the parent component, and evaluation in the child component are the same object . Changes made to that object will be reflected in the parent and there will no no complaint on Vue's part because you are not technically mutating the reference to the object, you are just changing values of it's properties .

Generally, where Vue will throw a warning is if you pass a primitive value as a property. Lets say you passed a string value and then you used that value with v-model . In that case, Vue will throw a warning (in the development version) that you are mutating a property. There are two reasons for the warning; first because that value is not propagated to the parent (which is likely unexpected behavior), and second, because whenever the data is changed in the parent it will overwrite changes in the child.

When an object or array is passed as a property, however, Vue will only complain if you change the object reference. For example in your code if you were to do this:

this.initEvaluation = new Evaluation()

Vue would complain that you are mutating a property. In your code, however you are simply changing the properties of initEvaluation and this does not result in a warning, and also results in those values being reflected everywhere because you are updating the same object everywhere.

One of the ramifications of this, is that effectively in your code, setting evaluation: this.initEvaluation is spurious. You could use initEvaluation in your template and get the same results as using evaluation . Again, this is because they are the same object. This is part of what Luiz is attempting to explain. Luiz's first sentence is slightly misleading in this case. The data function is only called once which means that data initialized with properties will only receive the value of the property once. However, because initEvaluation is an object, it doesn't really matter in the question's code. The object reference never changes, only the properties do. If the parent were to change the reference for some reason, evaluation in the child would not be updated with the new reference .

Whether or not updates being immediately reflected everywhere for an object is desirable is debatable. In many cases you want to control when updates occur. In those cases you might do something like evaluation: Object.assign({}, this.initEvaluation) which makes a copy of the initEvaluation object. The advanatage of doing this is you can make as many changes as you want to the object in the child component without those changes being reflected outside the component. Then, after you have validated all the changes are correct, you can emit those changes.

When you create the data properties based on the props ( initEvaluation and perspective ), they no longer depend on the props and you're exclusively referencing the data, as if they were simply copies, but not the actual props received. So, they don't react on the props.

Since you don't seem to be updating them in the component, you could just reference the props directly, instead of the data created from the props .

EDIT: Scratch that, you're using v-model on evaluation so you'd change the prop if you referenced them directly.

So, in general, I would remove this from data:

evaluation: this.initEvaluation
// ...
currentCommentSource: this.perspective

And reference the props directly, and replace v-model with :value and @input , as recommended in the docs.

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