Stacklists - Drag and Drop - Part II: Saving List Changes

Jul 7, 2024

So I've now got drag and drop working on the frontend now. Now it is time to save it to the backend.

The backend uses PostgreSQL as a database and the NestJS framework to handle all the server-side logic.

Intuitively, I came up with two solutions that I could try. However I realised I may run into issues implementing them.

Solution 1

Each element in the list has a unique ID using UUID. So in the PostgreSQL, I could make a new column in a table, that stores a String Array with the UUIDs in the list order like below.

["uuid1", "uuid2", "uuid3"]

The problem I realised with this method is, every time a change is made; the entire array would be updated. This doesn't seem ideal if the array begins to get quite long.

Solution 2

Another idea is to add an Integer column called Order in the PostgreSQL table for each element. Basically every element would have a number starting from 0 and increments by 1 like below.

Element 1: 0
Element 2: 1
Element 3: 2
...

However I realised this also has an issue, especially when adding a new element.

A new element would have 0 in the Order column because it is at the start of the list. Which means every element in the list has to be added by 1 in the Order column.

Research

Both of the solutions I may be fine for very simple small tasks. But I wanted to find a solution that would not have scaling issues.

I did some research online and the solutions people came up with are similar to mine that may also have some scaling issues.

This is when I found out about the LexoRank solution. The exact same solution that Atlassian uses for apps with giant lists like Jira and Trello.

It is similar to my idea with solution 2, but instead of ordering an Integer; it is using alphanumberic values (in other words a string).

I looked up how to implement it and it is possible to write my own code. However to save time, I found an npm package that does exactly what I need.

The reason why this approach is ideal, is because it allows me to only update the moved element's order column. This means I don't have to keep updating an entire array of elements in the PostgreSQL table.

As mentioned before, I ended up choosing the Pragmatic Drag and Drop (PDnD) library by Atlassian for reordering the list on the frontend.

This ended up being an excellent choice that pairs well with LexoRank because the PDnD library can detect the closest edge of surrounding elements of where you want to drop the dragged element.

In other words, this makes it easy to find the elements that sit between where I want to drop the dragged element; which therefore allows me to get the LexoRank values of these elements.

I can then use these LexoRank values to generate a new LexoRank that is between these two values.

See below for a simplified example:

(Note: Simplified LexoRank-like values used to make it easier to understand. Not 100% accurate to how it actually works.)

Element 1: 0|aaa111
Element 2: 0|ccc333
Element 3: 0|eee555

Initial State

Element 1: 0|aaa111
Element 3: 0|bbb222
Element 2: 0|ccc333

Updated State

The PDnD library also lets me detect if the dragged element is being placed and the top or bottom of the list. All I have to do in this case is to check if there is a previous or a next element. If not, then I just get the initial top or bottom element's LexoRank value and then generate a new LexoRank for the dragged element that is either smaller or larger respectively.

I think this combination of the PDnD library and LexoRank has been a perfect solution. I will most likely use it again when I need to.