Create Sortable Lists in OutSystems

slow code_
6 min readFeb 20, 2023

In this post, we’ll explain how to use the SortableJS library on OutSystems, with the help of a Forge component, with the same name.

Sortable is a JavaScript library for reorderable drag-and-drop lists.

Example of what you can achieve with the usage of the SortableJS component

Where to place the component

After having it installed on your environment you have to add its dependencies to the module where you wish to make use of the component, like this:

Add SortableJS dependencies

The next step is to drag and drop the Webblock SortableJSList into the screen in the position you’ll put your list.
This Webblock has a placeholder, inside of which you should place the list to which you intend to add the sorting behavior. This list can be inside of another element, like a container, it doesn’t need to be the direct child inside of the placeholder, however, for readability purposes, you should avoid wrapping the list with too many elements.

Besides the placeholder, this block has three inputs, namely:

  • ListWrapperQuerySelector
  • ListName
  • OptionsJSON

Let’s look at each one separately.

Inputs

ListWrapperQuerySelector:

This is a mandatory input, and it should take in a CSS selector string referencing the direct parent of the items you wish to add the drag and drop behaviour to.

<table id=“tableid”>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody> <!—- this is the parent -->
<tr> <!— this is a children —>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>$80</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Sum</td>
<td>$180</td>
</tr>
</tfoot>
</table>

Looking at the example table above, if you wanted the lines of a table to be draggable, one option would be to use “#tableid tbody” since the rows are direct children of the tbody. Some other examples would be just “tbody” or “table tbody”, however, these have the disadvantage of not using the unique ID of the table, so they could lead to some strange behaviors if you have more than one table inside of the SortableJS placeholder.

In our app, we decided to use: “#” + Left_table.Id + “ tbody”

Example of the usage of the component with a table

Similarly, if using a List, you could do something like “.list.list-group” or, using id’s again, “#” + List.Id.

Example of the usage of the component with a list

ListName:

This input is also mandatory and is used to identify the list or lists that were edited in the onDragEnd event. This event is discussed later in this post.

Please note that to have a basic usage of this component, these two inputs are all you need.

OptionsJSON:

This input is not mandatory, since it has a default value. This one will allow you to customize the behavior of your draggable list. The format of the input is a JSON string and you can find all the possible parameters here, however, we’ll explore a few of them below.

{
group: {
name: 'list_group'
},
sort: true,
handle: '.list-handler'
}

From the example above , the first parameter is the “group -> name”, which is used to group lists, allowing its items to be dragged from one to the other. In order for the items in one list to be dropped into another, they must belong to the same group. You can further customize the conditions in which this is allowed, by following this documentation.

Example of what you can achieve with the usage of the SortableJS component

Then, we see the “sort” parameter. This one is simply to enable the sorting on this list, so, most of the time, you want to have it set to true.

Finally, the last one is the “handle”, which allows you to set a handle for your draggable element. This means that, when a handle is set up, to be able to drag an element, the user has to do so by dragging on that predefined handle. The value that this parameter accepts is a CSS selector string as well.

OutSystems Variables

So far we have been able to make the elements on a list or table draggable. However, all that does is affect the look of the webpage on the client’s browser, without any impact on the variables in OutSystems.

To be able to reflect the changes made by the user in your OutSystems variables, you have to code a bit and create some logic. There are a lot of different alternatives to this process, which would be too extensive to explore in one post. For example, one idea would be to read the order of the elements on the screen using JavaScript, only when the user stops moving things around and saves. This way you could store the sorted order of ids or add a parameter on the Database representing the “Order”, which would take the index of each item on the sorted list.
Another method is to store the changes as they happen, making use of the event OnDragEnd triggered by the SortableJS block.

OnDragEnd event

This event has one structure as its output, called OnDragEndElement, which is composed of four parameters that give you information about the element that was dragged by the user:

  • OldIndex — (Zero Indexed) The index of the element in the original list from which it was dragged from;
  • NewIndex — (Zero Indexed) The index of the element in the list to which it has been dragged to.
  • From — The original list group name where the element was dragged from;
  • To — The list group name where the element was dragged to;
Structure of the OnDragEnd event input

Using this event, we can create a Handler action that will manipulate the OutSystems List to reflect what has been performed by the client on the DOM.

One example of such an action could be as follows:

Example of an OutSystems Client Action handler for the OnDragEnd event

Here we start by locally storing a copy of the dragged element in the variable, SelectedElement. Then, we remove this element from its list, by referencing it by the OldIndex. Finally, we insert the copied element back into the list in its new index.

Please note that this example does not take into account the possibility of dragging and dropping elements between different lists. To achieve that, you’d have to add some logic to look at the parameters “From” and “To” of this event. That use case will not be covered in this post, but please reach out to us if you need help with that.

Conclusion

As you can see this is a component with almost infinite use cases, input combinations, and also a lot of different ways to interact with it depending on your necessities and Data Model. However, despite all of this customizability, the basic usage of the component is quite simple, since you only have to set the ListWrapperQuerySelector and the ListName.

Please, share with us your usage of this component and any ideas for further development.

--

--