Drag and Drop Reordering of a Repeating Group with Custom States

Hello :slight_smile:

I’m not sure if this solution has been posted before (my apologies if I missed it!), but I just wanted to write a short guide on how to reorder a repeating group using custom states incase anyone was looking for it as well. This can be useful if you want to allow the User to upload a list of things, files, or images, change the order, and then save the list in that exact order to a list field (without needing to use any “Make Changes to a List of Things” actions for every item in the repeating group).

Preview:

Editor:

On this page, there are two repeating groups which display a list of Tasks, and a list of files (images):


(I set the second repeating group as as a list of files incase your app’s images are being uploaded through the Multifileuploader plugin)

In this example, each “Board” Thing has a list of Tasks:

Each “Task” Thing contains “Title” and “Order” fields (though the Order field is not used at all in this example):

Every User has a new Board Thing created for them on page load (if they don’t already have one), which is set as the value of their “Board” field. This is just for the example as well so multiple people aren’t reordering the same list of Tasks:

When the page is loaded, the “ListofTasks” custom state value (type: Task, list: yes) on the Group Board element is set to be the Current User’s Board’s Tasks. Here is the custom state:

And here is the action which changes the custom state’s value to the Current User’s Board’s Tasks:

This “ListofTasks” custom state is used as the data source of the Repeating Group of Tasks:
ListOfTasksDataSource

Within each of the Task repeating group cells are 5 elements:

  1. The “Hidden Input (Task) Current cell’s Index -1” element which is always equal to the Current cell’s index minus 1. This is hidden and underneath the other elements in the cell. We need to have this value for the drag/drop custom state actions:
    HiddenInput

  2. The “Drop Area Task” element which expands the height and width of the entire cell:


    It has this conditional statement which causes it to turn blue when another Group is dragged over the top of it:

  3. The “Drag/Drop Group Task” element which takes up the full width of the cell, but leaves some space at the top so the sliver of blue is shown when a group is dragged over the cell:


    As seen here in preview mode:
    BlueLine

The “Drag/Drop Group Task” element has this conditional which changes its color if it is being dragged:

  1. A text element containing the title of the Task:

  2. A text element containing the row number of the cell (using Current cell’s index):

There may be a simpler way to set this up, but I have two workflows on the page which detect whether the Task that was just dropped was moved upwards or downwards from its previous position. For example, if we have a simple list of Tasks in this order (1,2,3,4):

Then we decide to move 3 to the second cell, in order to make the order (1,3,2,4), the workflow below occurs:

This workflow’s conditional states “When DropArea Task has a group dropped on it *only when: Repeating Group Tasks list of Tasks: items until Current cell’s index doesn’t contain the Current Workflow Task”. This condition is true in this example because the Current cell’s index value is 2, meaning that the Repeating Group Tasks list of Tasks items until 2 doesn’t contain Task 3. In this case, Bubble knows the Current User is moving a cell upwards, and we need to insert Task 3 between Task 1 and Task 2:

In order to do this there are two custom states on the Group Board element called “NewTasksBefore” (type: Task, list: yes) and “NewTasksAfter” (type: Task, list: yes):

First we need to store the value of the items that we need “above” (i.e., before) Task 3, followed by Task 3 itself. Since we just moved Task 3 into the second cell (making the order 1, 3, 2, 4), we want this custom state value to equal the Task 1 (the Task(s) above Task 3) and Task 3 itself. This is accomplished using this expression:

It looks probably overly complicated, but this is the only way I could get it to work at the moment (if anyone knows of a more simple way, please let me know! :slight_smile: )

Here’s what the expression means:
Group Board's ListofTasks:items until #HiddenInput (Task) Current Cell Index - 1's value
Since Task 3 was just dropped into cell number 2, the Hidden Input value of that cell is currently 1 (since 2 - 1 = 1). This makes the expression “Group Board’s ListofTasks:items until #1” which returns Task 1. Now we just need to add Task 3 beneath Task 1.

:plus item Current Workflow Task
This part of the expression makes sure that Task 3 is after the Task 1 element. In summary, the value of the NewTasksBefore custom state becomes a list of two Tasks: Task 1, Task 3

Now we need to save Tasks 2 and 4 underneath Task 1 and Task 3. To do this, the second action sets this NewTasksAfter custom state (type: Task, list: yes) to be:

Here’s what the expression means:
Group Board's ListofTasks:items from #Current cell's index
Since the 3 was just dropped into the second cell, this expression is “Group Board’s List of Tasks:items from 2”, which returns Task 2, 3, and 4.

:minus item Current Workflow Task
We don’t want Task 3 in this custom state since it’s already; we only need the Tasks that go after Task 3: Task 2 and Task 4. We can remove Task 3 from this expression using :minus item.

Then, we just need to combine the two custom states (NewTasksBefore and NewTasksAfter) to become the new “ListofTasks” custom state in order to be displayed in the new, correct order in the Repeating Group of Tasks element. To do this we can combine the custom state values from Step 1 and Step 2 using the :merged with operator:

Now that we have the workflow setup for when a Task is moved upwards, we need a slightly different second action which occurs when a Task is moved downwards. For example, let’s say our list is back in the Order of 1,2,3,4:

If we moved Task 1 into the 3rd cell, to make the order 2, 1, 3, 4, this action occurs:

This workflow’s conditional states “When DropArea Task has a group dropped on it *only when: Repeating Group Tasks list of Tasks: items until Current cell’s index contains the Current Workflow Task”. This condition is true in this example because the “Current cell’s index value is 3”, meaning that the Repeating Group Tasks list of Tasks items until 3 contains the first cell Task (Task 1) of the repeating group. In this case, Bubble knows the Current User is moving a cell downwards, and we need to insert the Task 1 between Task 2 and Task 3.

In order to do this, we can first modify the “NewTasksBefore” custom state (type: Task, list: yes):

Here’s what the expression means:
Group Board's ListofTasks:items until #Hidden Input (Task) Current Cell Index - 1's value
Since the 1st Task was just dropped into the 3rd cell, this expression becomes Group Board’s ListofTasks:items until #2 (the Hidden Input’s value is 2 since 3 - 1 = 2). This expression returns the first two Tasks in the list: Task 1, Task 2

:minus item Current Workflow Task
We don’t want Task 1 to remain as the first item in the list, so we can remove it from the first expression using :minusitem

:plus item Current Workflow Task
Then we can add Task 1 back into the list, reordering it to be after Task 2 by using :plus item. It probably looks confusing, but we’re just subtracting Task 1 from the first part of the expression and adding it as the last item in the “NewTasksBefore” custom state.

Next, we need to update the “NewTasksAfter” custom state:

Here’s what the only when condition means:
Group Board's ListofTasks:items from #Current cell's index
Since Task 1 was just dropped into the 3rd cell of the repeating group, between Task 2 and Task 3, this expression becomes Group Board’s List of Tasks: items from #3 (since the current cell’s index is 3). This expression returns the 3rd and 4th Tasks making the NewTasksAfter custom state value be: Task 3, Task 4

Similar to the other workflow, we just need to combine the NewTasksBefore and NewTasksAfter custom states to be the value of the ListofTasks custom state using :merged by:

In this example, the order is saved when the Current User clicks the “Save” button by using the “set list”:


However, if you need to continuously save the value of the reordered list, you can have this Make Changes to a Board action within the Drag/Drop workflows.

And that should work! :slight_smile: The image example works in the same way (all of the workflows and data types are just of the “file” type instead of “Task”). Please feel free to let me know if you have any questions! (or suggestions!)

39 Likes

Thanks for making this

1 Like

No problem at all! :slight_smile:

I haven’t tried it out in my app yet, I will later. But thank you so much for putting this out here!

1 Like

My pleasure, @marcuslate! :slight_smile: let me know if you have any questions as you set it up!

1 Like

Absolutely Awesome Faye! Your a :star2:.

1 Like

@timgarrett111 Aw! That’s so nice! Thank you so much, Tim! :relaxed:

Hi @Faye do you think I can replicate this video with bubble:

1 Like

Hi @danielowega :slight_smile: Yes, I think so! I haven’t tried with custom states but you can move draggable groups and drop them into other repeating groups as long as the repeating groups are of the same type.

3 Likes

I’m trying to add “Shift click” to move multiple items at once - has anyone had luck with this?

Hi everyone, thought this might be another alternative for people who are going through this pain of reordering!

@fayewatson This is very cool, and just thing I was looking for. Maybe it’s just me but in both your demo and the drag-and-drop I built based on your tutorial there is something wrong with drag and drop from the first position. Maybe bubble has changed something since you created it that caused an error in the workflow?

The task in the first position (Task 1 on page load). Won’t drop into the next position unless you drag it to position 3. In other words:
grab task 1, drag it to position 2 and release --> returns to position 1.
drag task 1 to position 3 and drop --> moves to position 2.
drag task 1 to position 4 and drop --> moves to position 3.

Tasks in other positions (2,3,4) behave as expected/documented.

I’m not knowledgeable enough in bubble or your solution to understand why though. Seem’s like there may need to be a special workflow when cell position is 1? Thoughts?

2 Likes

Thank you so much for this! Exactly what I needed.

Thanks to this and other posts in the Forum, I was able to build out a solution that really works great! Wondering if anyone out there can give me some advice on how best to expand my functionality to include more repeating groups without going crazy copying and pasting workflows.

Thanks in advance for any suggestions!!

@fayewatson Just implemented this and there’s a bug in the logic as @clint.ivy mentioned

For ordering an item lower down the repeating group, I created another hidden integer (Current Cell Index + 1) within each item of the repeating group and then changed the ‘GroupTasksAfter’ custom state to the following:


1 Like

Is there a more simple way to do this nowadays?

Yes. Check this one Sortable RG - Drag & Drop Plugin | Bubble

Does this work anymore? I can’t seem to find an option for ‘Current Workflow Thing’

Was that a result of bubble’s updates, and if so, is there another way I can achieve this with similar logic?
For example, what is current workflow equal to and can I get that info from another option?

Figured out what I was doing wrong, for anyone else who has this issue

Needed to make sure the parent “DropArea workflow’s” thing type was set, then the option became available.