Rails - Sortable Table With Ajax

  1. 前端使用sortable
  2. 後端在幫要排序的類別(這邊是Memo)加上position欄位

原理與流程

jquery-ui 的 sortable 套件內建了 serilize ,它的功用是把 id 變成 query string 依順序回傳,所以我們只要找出規則就可以知道移動的是哪些欄位,把移動後的結果用 ajax 存進資料庫即完成拖曳的動作。

程式結構(html)

html指定要排序的Container為id="memo-table",讓js可以幫他加上sortable()方法。要排序的欄位加上id="id-<%= memo.id %>",這樣 jquery-ui sortable 套件的 serialize 時就會以 query string 的方式把排序送到server。

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
<p id="notice"><%= notice %></p>

<h1>Listing Memos</h1>

Query string: <span></span>

<table class="table">
<thead>
<tr>
<th>Content</th>
<th>Position</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody id="memo-table">
<% @memos.each do |memo| %>
<tr id="id-<%= memo.id %>">
<td><%= memo.content %></td>
<td><%= memo.position %></td>
<td><%= link_to 'Show', memo %></td>
<td><%= link_to 'Edit', edit_memo_path(memo) %></td>
<td><%= link_to 'Destroy', memo, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>

<br>

<%= link_to 'New Memo', new_memo_path %>

程式結構(js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(function() {
$('#memo-table').sortable({
axis: 'y',
stop: function(event,ui) {
var data = $(this).sortable('serialize');
$('span').text(data);
$.ajax({
data: data,
type: 'POST',
url: '/memos/batch_update'
});
}
});
});

Controller

memo_controller 中新增一個action來接這個ajax。因為要表示的是一個列表(List)中許多欄位(column)的順序,所以回傳的params[:id]會是一個陣列,我們把它存成變數ids。我們把改變過的欄位一一更新資料庫的資料。這邊我有點偷懶,沒有比對是否順序不一樣就全部存進去,不過第一次做就先用笨一點的方法,把功能做出來,之後再改進效能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def batch_update
@memos = Memo.all
ids = params[:id]
ids.each.with_index do |id, index|
memo = @memos[id.to_i-1]
memo.position = index + 1
memo.save
end

respond_to do |format|
format.js {
render nothing: true
}
end
end

Route

因為batch_update沒有針對特定的memo,而是一次插入多筆post,所以我把url設定為/posts/batch_update而不是/posts/:id/batch_update

1
2
3
4
5
resources :memos do
collection do
post :batch_update
end
end

遇到的Bug與解決方法

Post 422

javascript - POST 422 (Unprocessable Entity) in Rails? Due to the routes or the controller? - Stack Overflow

Ajax造成的HTTP 500 Server Error

處理 missing template

1
2
3
4
5
6
7
8
9
10
def batch_update
.
.
respond_to do |format|
format.js {

render nothing: true
}
end
end

參考資源

評論