Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue computed property changes the data object

I have basically this structure for my data (this.terms):

{
    name: 'First Category',
    posts: [
        {
            name: 'Jim James',
            tags: [
                'nice', 'friendly'
            ]
        },
        {
            name: 'Bob Ross',
            tags: [
                'nice', 'talkative'
            ]
        }
    ]
},
{
    name: 'Second Category',
    posts: [
        {
            name: 'Snake Pliskin',
            tags: [
                'mean', 'hungry'
            ] 
        },
        {
            name: 'Hugo Weaving',
            tags: [
                'mean', 'angry'
            ]
        }
    ]
}

I then output computed results so people can filter this.terms by tags.

computed: {
    filteredTerms: function() {
        let self = this;
        let terms = this.terms; // copy original data to new var

        if(this.search.tags) {
            return terms.filter((term) => {
                let updated_term = {}; // copy term to new empty object: This doesn't actually help or fix the problem, but I left it here to show what I've tried.
                updated_term = term;

                let updated_posts = term.posts.filter((post) => {
                    if (post.tags.includes(self.search.tags)) {
                        return post;
                    }
                });

                if (updated_posts.length) {
                    updated_term.posts = updated_posts; // now this.terms is changed even though I'm filtering a copy of it
                    return updated_term;
                }
            });
        } else {
            return this.terms; // should return the original, unmanipulated data
        }
    }
},

filteredTerms() returns categories with only the matching posts inside it. So a search for "angry" returns just "Second Category" with just "Hugo Weaving" listed.

The problem is, running the computed function changes Second Category in this.terms instead of just in the copy of it (terms) in that function. It no longer contains Snake Pliskin. I've narrowed it down to updated_term.posts = updated_posts. That line seems to also change this.terms. The only thing that I can do is reset the entire data object and start over. This is less than ideal, because it would be loading stuff all the time. I need this.terms to load initially, and remain untouched so I can revert to it after someone clears their search criterea.

I've tried using lodash versions of filter and includes (though I didn't really expect that to make a difference). I've tried using a more complicated way with for loops and .push() instead of filters.

What am I missing? Thanks for taking the time to look at this.

like image 902
Joel Abeyta Avatar asked Jan 18 '26 12:01

Joel Abeyta


2 Answers

Try to clone the object not to reference it, you should do something like :

   let terms = [];
   Object.assign(terms,this.terms);
like image 163
Boussadjra Brahim Avatar answered Jan 21 '26 01:01

Boussadjra Brahim


let terms = this.terms;

This does not copy an array, it just holds a reference to this.terms. The reason is because JS objects and arrays are reference types. This is a helpful video: https://www.youtube.com/watch?v=9ooYYRLdg_g

Anyways, copy the array using this.terms.slice(). If it's an object, you can use {...this.terms}.

like image 22
Steffan Avatar answered Jan 21 '26 01:01

Steffan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!