summaryrefslogtreecommitdiffstatshomepage
path: root/www/content/examples/sortable.md
blob: 8b5717883affe8cd04344ec5645191ca000ab1ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
+++
title = "Sortable"
template = "demo.html"
+++

In this example we show how to integrate the [Sortable](https://sortablejs.github.io/Sortable/)
javascript library with htmx.

To begin we initialize the `.sortable` class with the `Sortable` javascript library:

```js
htmx.onLoad(function(content) {
    var sortables = content.querySelectorAll(".sortable");
    for (var i = 0; i < sortables.length; i++) {
      var sortable = sortables[i];
      var sortableInstance = new Sortable(sortable, {
          animation: 150,
          ghostClass: 'blue-background-class',

          // Make the `.htmx-indicator` unsortable
          filter: ".htmx-indicator",
          onMove: function (evt) {
            return evt.related.className.indexOf('htmx-indicator') === -1;
          },

          // Disable sorting on the `end` event
          onEnd: function (evt) {
            this.option("disabled", true);
          }
      });

      // Re-enable sorting on the `htmx:afterSwap` event
      sortable.addEventListener("htmx:afterSwap", function() {
        sortableInstance.option("disabled", false);
      });
    }
})
```

Next, we create a form that has some sortable divs within it, and we trigger an ajax request on the `end` event, fired
by Sortable.js:

```html
<form class="sortable" hx-post="/items" hx-trigger="end">
  <div class="htmx-indicator">Updating...</div>
  <div><input type='hidden' name='item' value='1'/>Item 1</div>
  <div><input type='hidden' name='item' value='2'/>Item 2</div>
  <div><input type='hidden' name='item' value='3'/>Item 3</div>
  <div><input type='hidden' name='item' value='4'/>Item 4</div>
  <div><input type='hidden' name='item' value='5'/>Item 5</div>
</form>
```

Note that each div has a hidden input inside of it that specifies the item id for that row.

When the list is reordered via the Sortable.js drag-and-drop, the `end` event will be fired.  htmx will then post
the item ids in the new order to `/items`, to be persisted by the server.

That's it!

{{ demoenv() }}

<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script>

    //=========================================================================
    // Fake Server Side Code
    //=========================================================================
    htmx.onLoad(function(content) {
        var sortables = content.querySelectorAll(".sortable");
        for (var i = 0; i < sortables.length; i++) {
          var sortable = sortables[i];
          var sortableInstance = new Sortable(sortable, {
              animation: 150,
              ghostClass: 'blue-background-class',

              // Make the `.htmx-indicator` unsortable
              filter: ".htmx-indicator",
              onMove: function (evt) {
                return evt.related.className.indexOf('htmx-indicator') === -1;
              },

              // Disable sorting on the `end` event
              onEnd: function (evt) {
                this.option("disabled", true);
              }
          });

          // Re-enable sorting on the `htmx:afterSwap` event
          sortable.addEventListener("htmx:afterSwap", function() {
            sortableInstance.option("disabled", false);
          });
        }
    })
    
    var listItems = [1, 2, 3, 4, 5]
    // routes
    init("/demo", function(request, params){
      return '<form id="example1" class="list-group col sortable" hx-post="/items" hx-trigger="end">' +
      listContents()
      + "\n</form>";
    });
    
    onPost("/items", function (request, params) {
      console.log(params);
      listItems = params.item;
      return listContents();
    });
    
    // templates
    function listContents() {
      return '<div class="htmx-indicator" style="cursor: default">Updating...</div>' + listItems.map(function(val) {
        return `  <div style="border:1px solid #DEDEDE; padding:12px; margin: 8px; width:200px; cursor: grab" ondrag="this.style.cursor = 'grabbing'" ><input type="hidden" name="item" value="` + val + `"/> Item ` + val + `</div>`;
      }).join("\n");
    }

</script>