Search ajax Autocomplete có phân trang - Học Ajax - PHP Ajax - jQuery Ajax
Đây là bài thứ 12 trong loạt serie ajax toàn tập. Nếu như bạn đã đọc đến bài này thì tôi tin rằng bạn đã nắm vững được ajax jquery rồi, nên để nâng cao hơn xíu hôm nay tôi sẽ hướng dẫn làm chức năng search autocomplete ajax có phân trang. Search autocomplete là gì? Nếu bạn chưa biết nó là gì thì ...
Đây là bài thứ 12 trong loạt serie ajax toàn tập. Nếu như bạn đã đọc đến bài này thì tôi tin rằng bạn đã nắm vững được ajax jquery rồi, nên để nâng cao hơn xíu hôm nay tôi sẽ hướng dẫn làm chức năng search autocomplete ajax có phân trang.
Search autocomplete là gì? Nếu bạn chưa biết nó là gì thì tôi xin trích dẫn thế này, chắc hẳn bạn đã từng vào một số website có chức năng search và khi bạn nhập nội dung vào nó tự động lấy nội dung và hiển thị cho bạn mà không cần phải reload trang. Đây là chức năng rất thân thiện với người dùng nên nếu áp dụng vào website thì tuyệt cú mèo.
Kết quả của bài này ta có form như hình sau:
1. Xây dựng database
Trước tiên bạn cần tạo một database tên test và chạy nội dung sql bên dưới để tạo bảng và thêm một số data.
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; SET time_zone = "+00:00"; CREATE TABLE IF NOT EXISTS `member` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(30) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=30 ; INSERT INTO `member` (`id`, `username`, `email`) VALUES (1, 'thehalfheart', 'thehalfheart@gmail.com'), (2, 'Zaidap.com', 'Zaidap.com.net@gmail.com'), (3, 'kingston', 'kingston@gmail.com'), (4, 'cafeviet', 'cafeviet@gmail.com'), (5, 'emailer', 'emailer@gmail.com'), (6, 'domain', 'domain@gmail.com'), (7, 'root', 'root@gmail.com'), (8, 'admin', 'admin@gmail.com'), (9, 'supper', 'supper@gmail.com'), (10, 'hoconline', 'hoconline@gmail.com'), (11, 'codon', 'codon@gmail.com'), (12, 'thesky', 'thesky@gmail.com'), (13, 'khoanguyen', 'khoanguyen@gmail.com'), (14, 'trinhut', 'trinhut@gmail.com'), (15, 'sanhot', 'sanhot@gmail.com'), (16, 'nammoitetden', 'nammoitetden@gmail.com'), (17, 'nhatkydoitoi', 'nhatkydoitoi@gmail.com'), (18, 'Zaidap.comonline', 'Zaidap.comonline@gmail.com'), (19, 'sinhthoi', 'sinhthoi@gmail.com'), (20, 'xatoiradioco', 'xatoiradioco@gmail.com'), (21, 'emailaothu1', 'emailaothu1@gmail.com'), (22, 'emailaothu2', 'emailaothu2@gmail.com'), (23, 'emailaothu3', 'emailaothu3@gmail.com'), (24, 'emailaothu4', 'emailaothu4@gmail.com'), (25, 'emailaothu5', 'emailaothu5@gmail.com'), (26, 'emailaothu6', 'emailaothu6@gmail.com'), (27, 'emailaothu7', 'emailaothu7@gmail.com'), (28, 'emailaothu8', 'emailaothu8@gmail.com'), (29, 'emailaothu9', 'emailaothu9@gmail.com');
2. Xây dựng thư viện phân trang
Vì trong bài search autocomplete ajax này tôi có phân trang nên bắt buộc phải xây dựng lớp phân trang rồi :D. Bạn tạo file pagination.php
và copy nội dung bên dưới vào:
class Pagination { protected $_config = array( 'current_page' => 1, // Trang hiện tại 'total_record' => 1, // Tổng số record 'total_page' => 1, // Tổng số trang 'limit' => 10, // limit 'start' => 0, // start 'link_full' => '', // Link full có dạng như sau: domain/com/page/{page} 'link_first' => '', // Link trang đầu tiên 'range' => 9, // Số button trang bạn muốn hiển thị 'min' => 0, // Tham số min 'max' => 0 // tham số max, min và max là 2 tham số private ); function init($config = array()) { foreach ($config as $key => $val) { if (isset($this->_config[$key])) { $this->_config[$key] = $val; } } if ($this->_config['limit'] < 0) { $this->_config['limit'] = 0; } $this->_config['total_page'] = ceil($this->_config['total_record'] / $this->_config['limit']); if (!$this->_config['total_page']) { $this->_config['total_page'] = 1; } if ($this->_config['current_page'] < 1) { $this->_config['current_page'] = 1; } if ($this->_config['current_page'] > $this->_config['total_page']) { $this->_config['current_page'] = $this->_config['total_page']; } $this->_config['start'] = ($this->_config['current_page'] - 1) * $this->_config['limit']; $middle = ceil($this->_config['range'] / 2); if ($this->_config['total_page'] < $this->_config['range']) { $this->_config['min'] = 1; $this->_config['max'] = $this->_config['total_page']; } else { $this->_config['min'] = $this->_config['current_page'] - $middle + 1; $this->_config['max'] = $this->_config['current_page'] + $middle - 1; if ($this->_config['min'] < 1) { $this->_config['min'] = 1; $this->_config['max'] = $this->_config['range']; } else if ($this->_config['max'] > $this->_config['total_page']) { $this->_config['max'] = $this->_config['total_page']; $this->_config['min'] = $this->_config['total_page'] - $this->_config['range'] + 1; } } } function get_config($key){ return $this->_config[$key]; } private function __link($page) { if ($page <= 1 && $this->_config['link_first']) { return $this->_config['link_first']; } return str_replace('{page}', $page, $this->_config['link_full']); } function html() { $p = ''; if ($this->_config['total_record'] > $this->_config['limit']) { $p = '<ul>'; // Nút prev và first if ($this->_config['current_page'] > 1) { $p .= '<li><a href="' . $this->__link('1') . '" title="1">First</a></li>'; $p .= '<li><a href="' . $this->__link($this->_config['current_page'] - 1) . '" title="'.($this->_config['current_page'] - 1).'">Prev</a></li>'; } // lặp trong khoảng cách giữa min và max để hiển thị các nút for ($i = $this->_config['min']; $i <= $this->_config['max']; $i++) { // Trang hiện tại if ($this->_config['current_page'] == $i) { $p .= '<li><span>' . $i . '</span></li>'; } else { $p .= '<li><a href="' . $this->__link($i) . '" title="'.$i.'">' . $i . '</a></li>'; } } // Nút last và next if ($this->_config['current_page'] < $this->_config['total_page']) { $p .= '<li><a href="' . $this->__link($this->_config['current_page'] + 1) . '" title="'.($this->_config['current_page'] + 1).'">Next</a></li>'; $p .= '<li><a href="' . $this->__link($this->_config['total_page']) . '" title="'.$this->_config['total_page'].'">Last</a></li>'; } $p .= '</ul>'; } return $p; } }
Nếu bạn chưa biết cách viết thư viện phân trang thì có thể tham khảo hai bài sau:
- Tìm hiểu thuật toán phân trang
- Giới hạn số trang trong thuật toán phân trang
3. Xây dựng model xử lý database
Để cho tiện lợi trong quá trình xử lý database để search autocomplete thì tôi sẽ xây dựng một file chuyên dùng để lấy danh sách và truy vấn theo tham số search truyền vào. Bạn tạo file database.php
và copy nội dung bên dưới vào.
// Khai báo biến toàn cục kết nối global $conn; // Hàm kết nối database function connect(){ global $conn; $conn = mysqli_connect('localhost', 'root', 'vertrigo', 'test') or die ('{error:"bad_request"}'); } // Hàm đóng kết nối function disconnect(){ global $conn; if ($conn){ mysqli_close($conn); } } // Hàm đếm tổng số thành viên function count_all_member($username = '') { global $conn; if ($username){ $query = mysqli_query($conn, 'select count(*) as total from member where username like '%'.mysql_escape_string($username).'%''); } else{ $query = mysqli_query($conn, 'select count(*) as total from member'); } if ($query){ $row = mysqli_fetch_array($query, MYSQLI_ASSOC); return $row['total']; } return 0; } // Lấy danh sách thành viên function get_all_member($username = '', $limit = 10, $start = 0) { global $conn; if ($username){ $sql = 'select * from member where username like '%'.mysql_escape_string($username).'%' limit '.(int)$start . ','.(int)$limit; } else{ $sql = 'select * from member limit '.(int)$start . ','.(int)$limit; } $query = mysqli_query($conn, $sql); $result = array(); if ($query) { while ($row = mysqli_fetch_array($query, MYSQLI_ASSOC)){ $result[] = $row; } } return $result; }
Trong file tôi đã comment rất kỹ rồi nên ko giải thích gì thêm
4. Xây dựng hàm lấy danh sách search autocomplete và trả về chuỗi JSON
Bạn tạo file get_data.php
và copy nội dung sau:
// Require Lib require "database.php"; require "pagination.php"; // Lấy thong tin lọc $username = isset($_GET['username']) ? $_GET['username'] : ''; // Lấy trang hiện tại $page = isset($_GET['page']) ? $_GET['page'] : 1; // Khởi tạo đối tượng Pagination mới $pagination = new Pagination(); // Kết nối db connect(); // Cấu hình thông số phân trang $pagination->init(array( 'current_page' => $page, 'total_record' => count_all_member($username), 'link_full' => 'get_data.php?page={page}&username='.$username, 'link_first' => 'get_data.php' )); // Lấy limit và Start $limit = $pagination->get_config('limit'); $start = $pagination->get_config('start'); // Lấy danh sách người dùng $data = get_all_member($username, $limit, $start); // Ngắt kết nối disconnect(); // Trả kết quả cho client die (json_encode(array( 'data' => $data, 'paging' => $pagination->html() )));
Các bạn lưu ý là tôi trả về kết quả là chuỗi JSON nên ở file xử lý ajax ta phải truyền dataType là JSON nhé.
5. Xây dựng trang hiển thị danh sách và search autocomplete
Bạn tạo file index.php và copy nội dung sau:
<!DOCTYPE html> <html> <head> <title>Tìm kiếm theo ajax</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script language="javascript" src="http://code.jquery.com/jquery-2.0.0.min.js"></script> <style> html{width:900px; margin:0px auto} input{padding: 5px} li{float:left; margin: 3px; border: solid 1px gray;} a{padding: 5px;} span{display:inline-block; padding: 0px 3px; background: blue; color:white} li{list-style: none; padding: 0px 5px} </style> <script language="javascript"> $(document).ready(function() { // Biến lưu trữ thông số tìm kiếm var data = { username : '', // tên đăng nhập page : 1 // trang cần đến, dùng trong trường hợp có phân trang }; // Khi nhập dữ liệu thì gọi ajax $('#q').keyup(function(){ data.username = $(this).val(); search(); }); // Hàm xử lý khi click vào phân trang $('#content').on('click', 'a', function (){ data.page = $(this).attr('title'); search(); return false; }); // Hàm gửi ajax function search() { $.ajax({ url : 'get_data.php', data : data, type : 'get', dataType : 'json', success : function (result) { // Nếu dữ liệu trả về đúng thì mới xử lý if (result.hasOwnProperty('data') && result.hasOwnProperty('paging')) { var html = '<table border="1" cellspacing="0" cellpadding="5">'; html += '<tr style="font-weight:bold">'; html += '<td>Username</td>'; html += '<td>Email</td>'; html += '</tr>'; // Lặp qua từng record và gán html $.each(result['data'], function (index, item){ html += '<tr>'; html += '<td>'+item.username+'</td>'; html += '<td>'+item.email+'</td>'; html += '</tr>'; }); html += '</table>'; // Thêm html paging html += result['paging']; // Gán kết quả vào div#content $('#content').html(html); } } }); } }); </script> </head> <body> <input type="text" id="q" value="" placeholder="Nhập nội dung muốn tìm kiếm" size="50"/> <div id="content" style="margin-top: 20px;"></div> </body> </html>
Trong file này thẻ div#content dùng để gán data vào, và tôi đã sử dụng hàm html() jquery để gán.
Trong đoạn ajax tôi có định nghĩa một hàm search dùng để gửi ajax và tôi cũng có gán 2 sự kiện khi keyUp vào ô search và khi click vào phân trang. Trong phần comment tôi đã trình bày rõ ràng rồi nên tôi cũng ko nói gì thêm nữa nhé.
6. Lời kết
Trong bài này quan trọng nhất là đoạn code xử lý jquery ajax search autocomplete, các bạn cố gắng gõ và làm theo nhé, copy nhưng cũng phải hiểu thì mới học tốt được. Còn một vấn đề tôi không trình bày, đó là khi ta nhập từ search nó gửi rất nhiều request, mỗi ký tự một request nên không được hay lắm, để giải quyết vấn đề này ta sẽ xây dựng hệ thống timer, nghĩa là khi người dùng dừng không gõ chừng 0.5 giây ta mới gửi, như vậy sẽ giảm được số request gửi lên server. Vấn đề này ta sẽ học ở bài tiếp they delay keyup event.