Updating an item in an array updates all of them

I am working on an application using vuejs with vuex that uses projects, with each project having one or more tasks.

I can add, delete and update tasks. Adding and removing works fine, but updating isn't.

State in vuex dev tools:

projects

My HTML:

<div class="job-compact row" v-for="(job, index) in project.jobs">
        <div class="col-md-6">
            <div class="form-group" :class="{'has-error' : errors.has('jobs.' + index + '.function')}">
                <input type="text" name="jobs[function][]" class="form-control" v-model="job.function" @change="updateJobValue(index, 'function', $event.target.value)"/>
            </div>
        </div>
        <div class="col-md-4">
            <div class="form-group" :class="{'has-error' : errors.has('jobs.' + index + '.profiles')}">
                <input type="number" name="jobs[profiles][]" class="form-control" v-model="job.profiles" @change="updateJobValue(index, 'profiles', $event.target.value)"/>
            </div>
        </div>
        <div class="col-md-2">
            <button v-if="index == 0" class="btn btn-success btn-sm" @click="addJob"><i class="fa fa-plus"></i></button>
            <button v-if="index > 0" class="btn btn-danger btn-sm" @click="deleteJob(index);"><i class="fa fa-minus"></i></button>
        </div>
    </div>

      

As you can see, I have v-for

one that shows all my assignments. When editing a value inside my jobs, I use an event @change

to update my value. And, at the bottom, I have two buttons for adding and removing a job line.

enter image description here

My stores are divided into modules. The main store looks like this:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

const state = {};

const getters = {};

const mutations = {};

const actions = {};

//Separate Module States
import jobCreator from './modules/job-creator/store';

export default new Vuex.Store({
    modules: {
        jobCreator: jobCreator
    },
    state,
    actions,
    mutations,
    getters
});

      

The module store for this specific issue:

import store from './../../store'

const state = {
    project: {
        title: null,
        description: null,
        jobs: []
    },
    defaultJob: {
        function: '',
        title: '',
        description: '',
        profiles: 1,
        location_id: '',
        category_id: '',
        budget: '',
    },
};

const getters = {}

const mutations = {

    addJob(state, job) {
        state.project.jobs.push(job);
    },
    deleteJob(state, index) {
        state.project.jobs.splice(index, 1);
    },
    updateJobValue(state, params) {
        Object.assign(state.project.jobs[params.jobIndex], {
            [params.field]: params.value
        });
    }
};

const actions = {
    addJob: function (context) {
        context.commit('addJob', state.defaultJob);
    },
    deleteJob: function (context, index) {
        context.commit('deleteJob', index);
    },
    updateJobValue: function (context, params) {
        context.commit('updateJobValue', params);
    },
};

const module = {
    state,
    getters,
    mutations,
    actions
};

export default module;

      

The project state is mapped to the computed property of my vue instance:

computed: {
       ...mapState({
           project: state => state.jobCreator.project,
       }),
}

      

The problem is this: in the application image, you can see that I entered "vin" in one of the fields, but all the fields are updated.

enter image description here

So, all the fields function

all tasks have been updated to my last entry, not just the one that I want.

What am I doing wrong?

PS:

I have also tried the following on my mutation:

updateJobValue(state, params) {
    var job = state.project.jobs[params.jobIndex];
    job[params.field] = params.value;

    Vue.set(state.project.jobs, params.jobIndex, job);
}

      

But it gives me the same result.

UPDATE: as requested, I created a jsFiddle to show my problem

+3


source to share


2 answers


The problem is in your action addJob

:

addJob: function (context) {
    context.commit('addJob', state.defaultJob);
},

      

You reference the object state.defaultJob

every time you add a new job. This means that each element in the array state.project.jobs

refers to the same object.

You must create a copy of the object when passing in its mutation addJob

:

addJob: function (context) {
    context.commit('addJob', Object.assign({}, state.defaultJob));
},

      



Or just pass a new object with default properties every time:

addJob: function (context) {
    context.commit('addJob', {
        function: '',
        title: '',
        description: '',
        profiles: 1,
        location_id: '',
        category_id: '',
        budget: '',
    });
},

      

The violin works here.

Here's a post explaining how variables are passed in Javascript: Javascript by reference or by value

+8


source


I would recommend the following:

Use v-bind:value="job.function

instead v-model="job.function"

because you only want one way snapping. This code is more predictable.

Add v-key="job"

to your element v-for="(job, index) in project.jobs"

to make sure the rendering is working properly.



The first two lines should be sufficient, the object is still responsive.

var job = state.project.jobs[params.jobIndex];
job[params.field] = params.value;


Vue.set(state.project.jobs, params.jobIndex, job);

      

PS: In my fiddle, @change

only worked when I pressed the enter button or left the input.

+1


source







All Articles