[a note to my future self from my current self as of 17 April 2018: are you here again? Is there definitely a unique key on that loop? Are you sure? GO PUT ONE ON NOW. Use the uuid package, it’s easy. Then come back if it still doesn’t work.]
This took me a while yesterday to figure out. There were a couple of issues I had: how to keep the individual items reactive (spoiler: easy once I stopped being lazy) and how to make an array of objects reactive.
I’m making a list of items, each of which is editable and which can be sorted. It looks like this:
At first I had one component which handled everything but had difficulty making the iterated objects reactive so that I could edit the name and have the item update.
I am getting them from a database query made on created ()
but don’t know how many are in the array and looping through and using Vue.set
on each object and property seemed a bit extreme and wrong. It was. But easily sorted! I ended up passing each item to a separate component as outlined here and whooo, it works. I have my list in which each item’s name can be edited.
What about sorting? This had me flummoxed for a while. I couldn’t get it so that only the two items which are being switched are updated. (When a button is pressed, one item and either the one above or one below — depending on which button has been pressed — swap places.)
For one, I wasn’t thinking of it correctly. What I want, it seems obvious now but wasn’t then, is I need to swap the array elements. Eg items[ i ] = items[ i – 1] and vice versa. Ok, so that’s straightforward.
BUT the list doesn’t update! The array elements / list items aren’t reactive in this context. The swap is handled by the parent component of the individual item component (<list-component> in the image above) and the objects which make up the array of items aren’t reactive here. (I am glossing over how the parent component knows of a button press in the child component — it emits an event which the parent component is listening for.)
First, it helps me to think of what I want to do. On button click I need to:
- Update the order of both items in the database
- If the order has been updated, swap the array elements.
The first is straightforward (WP REST API ftw 🙂 ) and I chained the requests to swap the elements if they were successful.
Next I need to make the two array elements reactive to swap them (I am using the response data, ie the updated object, in the swap) and have the list update. Once I got it, it was kind of obvious (it’s one of those problems):
One tricky part was that I couldn’t understand why self.items.$set({ ... })
wasn’t working. I had read the “Common Beginner Gotchas” (I’m a common beginner, definitely) and thought I could. self.items
is an array.
For whatever reason (again, I’ve been using vue for about 3 weeks here), the $set method wasn’t available to the array in that context, so I had to use the method on this / self. Which, I have to admit, is explained in “Change Detection Caveats” here.
So done and dusted, right?
Almost. Initially the key on <item-component> was item.ID. Which is, of course, unique when I was just iterating over the initial array. But once I went to swap items, there was a millisecond or so in which two array elements had the same item.ID — more than enough time for Vue to throw up its hands and complain about duplicate keys.
I decided to use the uuid npm package which solved the problem. The keys now stay unique.
And that was it! phew.
Leave a Reply