Enable Dark Mode!
how-to-use-the-usesortable-hook-in-odoo-19.jpg
By: Deepika V

How to use the useSortable Hook in Odoo 19

Technical Odoo 19 Owl

Working with drag and drop interfaces is an integral part of UI management in Odoo 19. This version of Odoo provides good support for sorting operations in the web client. You can reorder a number of records in the system or move elements across the screen to use them easily within custom views.

This blog provides a detailed overview of the useSortable hook in Odoo 19, along with a practical implementation of sorting custom embedded actions. In this blog, we explain how to implement custom sorting in an OWL component, i.e., sorting embedded actions using a hook. We first create an OWL component inside the Odoo 19 environment. The hook is initialized in the setup using enable: true to activate it.

With the help of Odoo’s web core module sortable_owl, we can create a sortable environment by calling useSortable(). The reference is linked to the container using useRef(), and then the details of each draggable record are managed by the hook for each item.

In some cases, the standard list view may not be enough for your needs, and a custom sortable solution may need to be implemented to meet specific requirements. This provides the developer with more control over the DOM structure and other sorting options. The following code snippet provides an example of how a custom sortable hook for Odoo 19 can be implemented.

// Initialize the drag and drop hook
useSortable({
   enable: true,
   ref: this.root,
   elements: ".o_draggable",
   cursor: "move",
   delay: 200,
   tolerance: 10,
   onWillStartDrag: (params) => this._sortActionStart(params),
   onDrop: (params) => this._sortActionDrop(params),
});

This function enables drag and drop functionality for a container in Odoo 19.

  • useSortable({ ... }): This line of code initializes a sortable object for the sortable_owl module.
  • enable: true: indicates that the drag and drop feature is active.
  • ref: this.root: Wraps the hook around the main container reference defined in the setup.
  • elements: ".o_draggable": Only targets elements with this specific class.
  • cursor: "move": Changes the mouse pointer to a move icon when dragging.
  • delay: 200 — Adds a delay before dragging starts.
  • tolerance: 10 — Requires slight movement before dragging begins.

The drag action gets delayed to prevent accidental clicks. There are several options available for handling accidental drags. Here are a few options to choose from:

  • delay: This waits a specific amount of milliseconds before starting the drag.
  • tolerance: This requires the mouse to move a certain number of pixels before dragging starts.
  • handle: This restricts the drag action to a specific icon class.

We import from sortable_owl and not sortable because the OWL version automatically removes all drag listeners when the component is destroyed. If you used sortable directly, you'd have to clean up the listeners yourself on every unmount — and forgetting that causes memory leaks.

import { Component, useRef, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { useSortable } from "@web/core/utils/sortable_owl";
export class SortableDemo extends Component {
   setup() {
       this.root = useRef("root");
       // Local state to hold our mock data
       this.state = useState({
           actions: [
               { id: 1, name: "Review pull requests" },
               { id: 2, name: "Update Odoo 19 modules" },
               { id: 3, name: "Write documentation" },
               { id: 4, name: "Deploy to staging server" },
           ]
       });
       // Initialize the drag and drop hook
       useSortable({
           enable: true,
           ref: this.root,
           elements: ".o_draggable",
           cursor: "move",
           delay: 200,
           tolerance: 10,
           onWillStartDrag: (params) => this._sortActionStart(params),
           onDrop: (params) => this._sortActionDrop(params),
       });
   }
   _sortActionStart({ element, addClass }) {
       // Highlight the element while dragging
       addClass(element, "text-bg-warning");
   }
   _sortActionDrop({ element, previous }) {
       const elementId = Number(element.dataset.id);
       // 1. Get the current order of IDs
       const order = this.state.actions.map((el) => el.id);
       // 2. Remove the dragged element from its old position
       const elementIndex = order.indexOf(elementId);
       order.splice(elementIndex, 1);
       // 3. Insert it into the new position
       if (previous) {
           const prevIndex = order.indexOf(Number(previous.dataset.id));
           order.splice(prevIndex + 1, 0, elementId);
       } else {
           order.splice(0, 0, elementId); // Dropped at the very top
       }
       // 4. Update the state array to reflect the new order
       this.state.actions.sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
   }
}

Finally, the sorting code captures the drag events in a format that provides the positional data using Odoo 19’s parameter tracking feature. Here’s how it works:

  • onWillStartDrag calls _sortActionStart to add a CSS class while dragging.
  • The addClass function is provided by useSortable itself as part of the callback params. It safely adds a CSS class to the element without you needing to touch classList directly.
  • It then listens for the user to release the mouse button.
  • It then calls _sortActionDrop to handle the final placement of the element.

So, in summary, _sortActionDrop() generates the dropped element data in a format that gets processed as an object containing the current and the previous sibling elements. This gets processed to update the sequence in the database.

The data is extracted using element.dataset.id from the dragged item. addClass(element, "text-bg-warning"). This sets the visual styling for the item currently being dragged.

Finally, we need an XML template to define the structure and link our draggable items to their record IDs:

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
   <t t-name="use_sortable_demo.SortableDemoTemplate" owl="1">
       <div class="o_action p-4 bg-view h-100 overflow-auto">
           <h2 class="mb-3">useSortable Hook Testing Area</h2>
           <p class="text-muted mb-4">Click and hold an item for 200ms, then drag to reorder.</p>
           <div t-ref="root" class="border p-3 rounded bg-100" style="max-width: 500px;">
               <t t-foreach="state.actions" t-as="action" t-key="action.id">
                   <div class="o_draggable d-flex align-items-center p-3 mb-2 bg-white border rounded shadow-sm"
                        t-att-data-id="action.id">
                       <span class="fa fa-arrows me-3 text-muted drag-handle" style="cursor: grab;"/>
                       <span t-esc="action.name" class="fw-bold"/>
                   </div>
               </t>
           </div>
       </div>
   </t>
</templates>
  • t-ref="root": This is for passing the reference of the container to identify the specific sortable area.
  • class="o_draggable": This is for specifying the class to be used for the draggable items.
  • t-att-data-id="action.id": This is for specifying the field that contains the record ID.

Drag and drop operations in Odoo 19 give developers the flexibility to build dynamic interfaces that fit exactly what a business process needs. The useSortable hook handles everything from listener setup to teardown, so the developer can focus entirely on the reorder logic rather than managing the DOM. It covers a wide range of use cases — from simple list reordering to cross-container drag between Kanban columns — making it a reliable choice for any custom sorting requirement in Odoo 19.

To read more about Overview of Advanced OWL Components In Odoo 19, refer to our blog Overview of Advanced OWL Components In Odoo 19.


Frequently Asked Questions

Can I restrict the drag operation to a specific icon in Odoo 19?

Yes, you can. In fact, the configuration provided by Odoo 19 allows you to manually map your drag action to a specific class. This means that even if your element is large, you can still map the handle parameter to a specific icon class during the setup before initiating the drag.

What happens if there are accidental clicks on elements in Odoo 19?

It turns out that the Odoo 19 hook provides a tolerance check before it actually starts to drag the element on the screen. Even if there are accidental clicks, the system waits for delay or tolerance thresholds, allowing normal clicks without triggering a drag. This means you don’t have to worry about incorrect sequences being processed.

Can I customize the hook to disable sorting dynamically?

Yes, you can definitely customize the hook to disable sorting dynamically, rather than just keeping it active, as shown in the enable: true parameter above. You can also pass a dynamic boolean state instead of just true. You can even link this to user permissions or edit modes to control access.

If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, KINFRA Techno Park
Kakkanchery, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message