janedoe
janedoe

Reputation: 937

Vue doesn't re-render component after third time updating property

So I have the following code that either assigns or removes an existing 'role' to a user:

    onAddRole(role) {
        let roles = this.user.roles
        roles.push(role)

        this.$set(this.user, roles)

        console.log(this.user.roles)

        this.success.role_update = true
    },

    onRoleDelete(selected_role) {
        let i = this.user.roles.map(role => role.id).indexOf(selected_role.id)

        if (i !== -1) {
            let roles = this.user.roles

            roles.splice(i, 1)

            this.$set(this.user, roles)

            console.log(this.user.roles)

            this.success.role_update = true
        }
    }

Here is my data property for the component:

data: function() {
    return {
        user: null,
        success: {
            permission: null,
            invalidation: null,
            fetch: null,
            save: null,
            role_update: null
        },
        loading: false
    }
},

And here is a snippet of the relevant html:

    <b-row>
        <b-col md="6">
            <b-row v-if="user">
                <b-col md="12">
                    <user-roles
                        :user="user"
                        @add_role="onAddRole"
                        @set_role="onRoleSet"
                        @role_failure="roleFail"
                        @role_delete="onRoleDelete"
                    />
                </b-col>
            </b-row>
            <loading v-if="loading" />
        </b-col>
    </b-row>

Basically this code works fine if I add a role once or twice. However, after the third time I remove or add a user role, Vue stops re-rendering the component. Any ideas why this would happen?

The console.logs show that the property itself is updated correctly, but the view does not update.

Upvotes: 1

Views: 142

Answers (2)

janedoe
janedoe

Reputation: 937

So the solution to having it work consistently was to separate the roles property from users, like so:

data: function() {
    return {
        user: null,
        roles: null,
        success: {
            permission: null,
            invalidation: null,
            fetch: null,
            save: null,
            role_update: null
        },
        loading: false
    }
},

then just update the roles property for the event handler methods like you normally would. no need to use .$set:

    onRoleSet({ role, i }) {
        this.roles.splice(i, 1, role)

        this.success.role_update = true
    },

    onAddRole(role) {
        this.roles.push(role)

        this.success.role_update = true
    },

    onRoleDelete(selected_role) {
        let i = this.roles.map(role => role.id).indexOf(selected_role.id)

        if (i !== -1) {
            this.roles.splice(i, 1)

            this.success.role_update = true
        }
    }

so far this is working consistently for me and the component updates like it's supposed to.

It seems that having a nested property in the component's data object seems to cause me a lot of problems with Vue, so it's best to avoid doing that

Upvotes: 0

Steven B.
Steven B.

Reputation: 9362

This may not be the final resolution but I see two issues that may be the root cause.

Whenever you do:

let roles = this.user.roles

you're creating a reference to that array so when you next do:

roles.push(role)

You're actually just essentially doing

this.user.roles.push(role)

So really there's not much of a reason to even allocate the let roles variable.

Secondly, this.$set needs the name of the property. So it should be

this.$set(this.user, 'roles', roles)

or you'll be creating an undefined property on the user with roles assigned to it.

I'm not quite sure why you're using this.$set though since your already modifying the roles when you push() and it would just be assigning the same array back to the property the way you're currently doing it.

Upvotes: 1

Related Questions