我们经常有这样的需求:如下图所示,拖动不同item从页面上实现<li>
的重新排序,并将其保存在数据库中。
技术背景
1、jquery-sortable交互工具
jQuery UI 是一个建立在 jQuery JavaScript 库上的小部件和交互库,你可以使用它创建高度交互的 Web 应用程序。而 jquery-sortable 则可以实现使用鼠标调整列表中或者网格中元素的排序。它的 API 你可以点进link来了解。
2、acts_as_list rubygem
acts_as_list 是一个开源的rubygem,提供了方法实现列表中元素的sort和reorder,具体的API和source code点进link查看,在此不再赘述。
前端JS的实现
举例来说,我们要使用jquery-sortable为名为“article”的model实现页面交互排序,并使用ajax通过发送POST请求将JS获取的数据传到controller中,前端JS代码如下:
$(function () {
if($('.Articles').length) {
$('ol.articles').sortable({
stop: function(event, ui) {
ui.item.data('endPos', ui.item.index()+1);
var data = {'end_position': ui.item.data('endPos'), 'article_id': ui.item.attr('id')};
$.ajax({
data: data,
type: 'POST',
url: '/your/controller/action'
});
}
});
}
});
注:在下一步controller的处理中,我们要使用到acts_as_list rubygem,所以这里只需要传递 移动到了什么位置
和 移动了哪篇文章
两个参数即可,即 end_position
和 article_id
。
很简单的JS代码,无需过多解释,详细请看jquery-sortable的 API 。
后端controller sort action的实现
在实现controller action之前,参照这里为你的model的数据表加上“position”的字段,并设置model的scope参数。
在上一步我们获取到JS传递给controller的article_params,例如
...
# raise article_params.inspect
=> {'end_position': "6", 'article_id': 10086}
下一步我们在 articles_controller 中写一个action:
def sort_articles
@article = Article.find(params[:article_id])
@article.insert_at(params[:end_position].to_i)
render :nothing => true
end
这里的“insert_at”请参考这里acts_as_list的API,它提供了reorder list的方法。
定义路由表
当然,由前端发送POST请求,你需要定义一条路由:
...
resources :articles do
...
post "sort-articles" => "articles#sort_articles", on: :collection
end
...
What is more
我们在实现上面所说的基本功能之后,欣喜地发现,在browser上的表现堪称完美。但是在测试过程中又遇到了新的难题:在移动端无法实现排序的功能。为此,我提供了一种解决方案。
jQuery UI Touch Punch
jQuery UI Touch Punch 提供了在移动端实现上述排序功能的小工具,并且使用起来非常方便,按照官网所说,总共分3步(这里我结合RoR来举例,读者可参考上面的link在自己的code中实现):
1、在页面中引用jQuery和jQuery UI的库 - application.js
...
//= require jquery
//= require jquery-ui
...
2、引用touch-punch的自定义js库
...
//= require ./vendors/jquery.ui.touch-punch
...
自定义js库的代码在jQuery UI Touch Punch中有提供下载。
3、在你的js代码中应用Touch Punch的方法.draggable()
,我们在此引用上面的js代码
$(function () {
if($('.Articles').length) {
$('ol.articles').sortable(...);
// or as below to move the whole article block
$('ol.articles').draggable();
}
});
用你的iPhone来访问你的页面,是不是可以实现拖动了?