Search code examples
javascriptvue.jstransitionvuejs-transition-group

Can you change an item's ID in vue.js?


I have a list of items, contained within a javascript array. Similar to the examples found on the vue.js documentation.

This is the workflow I'm struggling with:

  • A user clicks "Add".
  • I immediately add an item to the list (in the spot where they asked it to be)
  • I send an ajax request to the server to create the item.
  • The call returns with the database row for the new item.

I'm using the database primary key as my vue key - which I have no way of knowing up to the point the ajax call finally gets back to me. But if I simply update the ID on the object, I end up with that row transitioning out and then back in.

Is there a way to tell vue that I'm changing the ID of an item?

My ideal case would be something along the lines of:

click: function () {
    let item = {
        id: "placeholder id, possibly randomly generated"
    }
    this.array.splice(index, 0, item);
    ajax_call().complete(function (new_id) {
        // item = data.new_id
        vue.update_id_for_object(item, data.new_id)
    })
}

The commented out line is the one that causes the undesired transition.

Someone voted to close this as a duplicate of this question - as far as I can tell, it's talking about how to set ids dynamically which I'm already doing in my example. Feel free to clarify how it helps my issue though.


Solution

  • By behavior key is supposed to determine the uniqueness of the item in a list. So change in the key attribute is and will always represent a different component.

    Having this limitation in mind, you have two options. First is the simplest one with a compromise - just disable the animations/transitions.

    The second option is to create a localized map of unique id for each existing and new item. For example,

    created() {
    
        this.counter = 0;
        this.mappedKeys = {};
    
    },
    
    // Consider using this method as part of watcher
    // Or API call success method.
    modifiedList(list) {
    
        const newList = list.map((x) => {
    
            if (!this.mappedKeys[x.id]) {
                this.mappedKeys[x.id] = this.counter++;
            }
    
            return { ...x, id: this.mappedKeys[x.id] };
        });
    
        // Render this newList
        return newList;
    }
    

    For a newly added item, add the entry as:

    const nextValue = this.counter++;
    this.mappedKeys[nextValue] = newValue;
    

    When ajax call succeeds, add another entry as:

    // draftId is the id created in earlier step.
    this.mappedKeys[newItem.id] = draftId;
    

    This is the only thing you can do to map draft id to real id returned from database/API. There is not Vue.js way of doing things for this problem.