Search ajax với jquery-datatable + ajax-datatables-rails + ransack
Bài đăng này đã không được cập nhật trong 5 năm
I. Giới thiệu
Thư viện datatable.js
Jquery DataTables plugin. là một plugin rất mạnh cho jquery, khi cần phải list dữ liệu hiển thị ra table html mà không phải render lại toàn bộ trang. Để sử dụng bạn cần download về và cho vào assets pipeline:
assets/javascripts/application.js
# assets/javascripts/jquery-datatable.js
//= require dataTables/jquery.dataTables
# assets/stylesheets/application.css
*= require dataTables/jquery.dataTables
Bạn cũng có thể sử dụng gem jquery-datatables-rails 1 - Thêm vào Gemfile:
gem 'jquery-datatables-rails', '~> 3.4.0'
2 - Cài gem:
bundle install
3 - Generate assets:
$ rails generate jquery:datatables:install
Hoặc thêm vào asset file tương ứng
#app/assets/javascripts/application.js
//= require dataTables/jquery.dataTables
#app/assets/stylesheets/application.css
*= require dataTables/jquery.dataTables
Gem ajax-datatables-rails
ajax-datatables-rails Để sử dụng Gem này bạn cần thêm gem 'ajax-datatables-rails' vào trong Gemfile và chạy bundle install để cập nhật các gói thư viện cho Rails.
Thêm dòng dưới này vào gemfile của bạn:
gem 'ajax-datatables-rails'
Chạy dòng dưới:
$ bundle install
Gem ransack
Đây là gem hỗ sợ query data rất mạnh của rails. Hãy thêm vào Gemfile và bundle:
gem 'ransack'
$ bundle install
II. Sử dụng
1. Gọi ajax với datatable js
Bước này trở nên rất dễ chịu khi bạn chỉ cần vẽ 1 cái table html với cái thẻ tbody trống không.
app/views/posts/index.html.erb
...
<table id="datatable" data-source="<%= posts_path(format: :json) %>">
<thead>
<tr>
<th><%= t ".post_id" %></th>
<th><%= t ".post_name" %></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
<%= render "datatable.js" %>
</script>
file datatable.js sẽ xử lý request đến server lấy dữ liệu dạng json và hiển thị trong thẻ tbody.
$(document).ready(function() {
function call_datatable() {
$("#datatable").dataTable({
"processing": true,
"serverSide": true,
"ajax": $("#datatable").data("source"),
"pagingType": "simple_numbers",
"columns": [
{"data": "id"},
{"data": "name"}
],
"searching": false,
"stateSave": false,
"destroy": true
}
});
};
call_datatable();
});
fuction call_datatable(); sẽ gọi đến controller qua url: $("#datatable").data("source")
class PostsController < ApplicationController
def index
respond_to do |format|
format.html{@q = Company.ransack}
format.json{render json: post_datatable}
end
end
def post_datatable
PostDatatable.new params, view_context: view_context
end
end
2. Trả về data
Trọng tâm của bài viết là cách kết hợp AjaxDatatable với jquery-datatable. với class kê thừa AjaxDatatablesRails::ActiveRecord ta cần query đúng data và trả về đúng kiểu dữ liệu cho jquery-datatable:
# app/datatables/post_datatable.rb
class PostDatatable < AjaxDatatablesRails::ActiveRecord
extend Forwardable
include Admin::PostHelper
def_delegators :@view, :link_to, :link_to_if, :edit_post_path, :content_tag
def initialize params, opts = {}
@view = opts[:view_context]
super
end
def view_columns
@view_columns ||= {
id: { source: "Post.id", searchable: false },
name: { source: "Post.name", searchable: false },
user_name: { source: "User.name", searchable: false}
}
end
private
def data
records.map do |record|
{
id: record.id,
name: record.name
}
end
end
def get_raw_records
Post.all
end
end
Giải thích:
- Tối thiểu cần có 3 method trong class PostDatatable đó là view_columns, data, get_raw_records
- viewcolumns định nghĩa cấu trúc data được trả về cho jquery-datatable
- data map dữ liệu lấy được bởi method get_raw_records với viewcolumns
- get_raw_records lấy dữ liệu
- Để sử dụng helper và các ActionViewHelper method cần khai báo như sau:
- Để include được helper cần extend Forwardable
- def_delegators cần sử dụng view helper method nào phải delegate từ view_context được truyền vào từ controller.
extend Forwardable
include Admin::PostHelper
def_delegators :@view, :link_to, :link_to_if, :edit_post_path, :content_tag
def initialize params, opts = {}
@view = opts[:view_context]
super
end
- params được truyền từ controller để dùng ransack query
- để custom các thông tin như: tổng số bản ghi bạn có thể thêm:
def as_json(options = {})
{
draw: params[:draw].to_i,
recordsTotal: total_count,
recordsFiltered: total_count,
data: data
}
end
def total_count
get_raw_records.count(:all)
end
3. query bằng ransack
Model Post:
app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :author
belongs_to :topic
has_many: :comments
ransacker :id do
Arel.sql "CONVERT(#{table_name}.id, CHAR(8))"
end
ransacker :name do
arel_table[:name].lower
end
ransack_alias :author, :author_first_name_or_author_last_name
end
Form Search:
app/views/posts/index.html.erb
<%= search_form_for Post.ransack, remote: true, id: :form_search_datatable do |f| %>
<div class="row">
<div class="col-md-4">
<%= f.label :id_or_name_cont, "id or name") %>
<%= f.search_field :id_or_name_cont, placeholder: "id or name" %>
</div>
<div class="col-md-4">
<%= f.label :comments_content_cont, "has comments content" %>
<%= f.search_field :comments_content_cont, placeholder: "has comments content" %>
</div>
<div class="col-md-4">
<%= f.label :author_cont, "author" %>
<%= f.search_field :author_cont %>
</div>
</div>
<div class="row">
<div class="col-md-4">
<%= f.label :topic_id_eq, "has topic" %>
<%= f.select :topic_id_eq, (Topic.pluck :name, :id), {include_blank: true}, placeholder: "has topic" %>
</div>
<div class="col-md-4">
<%= f.label :content_cont, "content cont" %>
<%= f.search_field :content_cont, placeholder: "contentcont" %>
</div>
</div>
<% end %>
<table id="datatable" data-source="<%= posts_path(format: :json) %>">
...
app/views/posts/_datatable.js
$(document).ready(function() {
var params = $("#form_search_datatable :input").serialize();
function call_datatable() {
...
ajax: $("#datatable").data("source").concat("?" + params),
...
};
call_datatable();
$("body").on("change keyup", "#form_search_datatable :input", function(){
call_datatable();
});
});
app/datatables/post_datatable.rb
# app/datatables/post_datatable.rb
class PostDatatable < AjaxDatatablesRails::ActiveRecord
...
def get_raw_records
Post.joins(:user).ransack(params[:q]).result distinct: true
end
end
III. Vài tip sử dụng jquery datatable js có thể hữu ích
- Datatable.js
custom cấu trúc thanh phân trang:
"pagingType": "simple_numbers_no_ellipses",
simple_numbers_no_ellipses.js
$.fn.DataTable.ext.pager.numbers_no_ellipses = function(page, pages){
var numbers = [];
var buttons = $.fn.DataTable.ext.pager.numbers_length;
var half = Math.floor( buttons / 2 );
var _range = function ( len, start ){
var end;
if ( typeof start === "undefined" ){
start = 0;
end = len;
} else {
end = start;
start = len;
}
var out = [];
for ( var i = start ; i < end; i++ ){ out.push(i); }
return out;
};
if ( pages <= buttons ) {
numbers = _range( 0, pages );
} else if ( page <= half ) {
numbers = _range( 0, buttons);
} else if ( page >= pages - 1 - half ) {
numbers = _range( pages - buttons, pages );
} else {
numbers = _range( page - half, page + half + 1);
}
numbers.DT_el = 'span';
return [ numbers ];
};
bạn hãy thử để thấy kết quả:
app/views/posts/index.html.erb
...
<%= render "datatable.js" %>
<%= render "simple_numbers_no_ellipses.js" %>
Custom language thêm tùy chỉnh ngôn ngữ vào file datatable.js.erb:
language: {
"decimal": "",
"emptyTable": "No data available in table",
"info": "Showing _START_ to _END_ of _TOTAL_ entries",
"infoEmpty": "Showing 0 to 0 of 0 entries",
"infoFiltered": "(filtered from _MAX_ total entries)",
"infoPostFix": "",
"thousands": ",",
"lengthMenu": "Show _MENU_ entries",
"loadingRecords": "Loading...",
"processing": "Processing...",
"search": "Search:",
"zeroRecords": "No matching records found",
"paginate": {
"first": "First",
"last": "Last",
"next": "Next",
"previous": "Previous"
},
"aria": {
"sortAscending": ": activate to sort column ascending",
"sortDescending": ": activate to sort column descending"
}
}
disable sorting add class 'no-sort' vào các cột thead của table mà không cần sort:
columnDefs: [
{ targets: 'no-sort', orderable: false }
]
- AjaxDataTable
Có thể bạn cần mở rộng query và sorting (đặc biệt là với elasticsearch). Cần custom lại các method như: where, order, limit, offset ...
IV. Tham khảo
All rights reserved