Hướng dẫn code chức năng pagination bằng Ajax trong Wordpress
2nd Jul 2021Kỹ thuật AJAX (Asynchronous JavaScript and XML) bây giờ đã quá phổ biến trên website rồi, nó sẽ giúp việc truyền gửi dữ liệu từ backend ra frontend mà không cần tải lại trang. Hàng ngày bạn có thể sẽ thấy rất nhiều ví dụ về việc sử dụng AJAX, như việc bấm nút like hoặc gửi comment trên Facebook cũng đều là AJAX cả.
>> Web Development - Bảng giá Web Development
>> Hướng dẫn Decimals as Superscript without decimal separator trong Woocommerce
Trong bài viết này, mình sẽ hướng dẫn bạn cách làm quen với kỹ thuật AJAX áp dụng vào WordPress với một ví dụ phổ biến nhất là làm phân trang bằng AJAX. Từ ví dụ này, bạn có thể hiểu cách AJAX hoạt động trong WordPress để gửi dữ liệu về front-end.
Tạo thư mục plugin
Bây giờ bạn hãy vào thư mục /wp-content/plugins/ và tạo thêm một thư mục với tên là ajax-pagination. Trong thư mục ajax-pagination, bạn tạo ra một file tên là plugin.php với nội dung sau:
<?php /* Plugin Name: AJAX Pagination Description: Phân trang website bằng AJAX Version: 1.0 Author: Tk Web giá rẻ Author URI: https://web.expressmagazine.net/ */
Từ bây giờ, mọi code bằng PHP bạn sẽ đều viết trong file plugin.php này. Sau đó bạn có thể vào Plugins -> Installed Plugins rồi kích hoạt plugin này lên.
Nhúng file Javascript vào theme
Khi làm việc với AJAX chúng ta sẽ cần phải viết thêm một số đoạn Javascript nên sẽ cần tạo một file .js trong plugin rồi viết code cho nó tự nhúng vào theme. Bây giờ bạn hãy tạo một file tên ajax-pagination.js, sau đó viết thêm code này vào file plugin.php để chèn file này vào theme đang sử dụng tự động thông qua hàm wp_enqueue_script rồi móc vào hook wp_enqueue_scripts để nó thực thi.
/* @ ajax_pagination_scripts() @ Nhúng file ajax-pagination.js vào theme */ add_action( ‘wp_enqueue_scripts’, ‘ajax_pagination_scripts’ ); function ajax_pagination_scripts() { /* * Chèn file ajax-pagination.js vào frontend */ wp_enqueue_script( ‘ajax-pagination-script’, plugins_url( ‘/ajax-pagination.js’, __FILE__ ), array( ‘jquery’ ) ); }
Về cách hoạt động của hook wp_enqueue_scripts và hàm wp_enqueue_script ra sao thì mình sẽ giải thích ở một bài khác. Nhưng trước hết bây giờ bạn có thể hiểu rằng với đoạn trên, nó sẽ tự động chèn một file ajax-pagination.js trong thư mục plugin hiện tại của nó nhờ hàm plugins_url(), cái array('jquery') có nghĩa là cho WordPress tự hiểu được script này sẽ sử dụng jQuery nhằm tự động chèn vào theme và luôn load sau jQuery để bị lỗi.
Nếu bạn cần chèn nhiều file Javascript khác nhau thì chỉ cần viết nhiều đoạn wp_enqueue_script() trong một hàm nào đó rồi móc vào hook wp_enqueue_scripts là được.
Bây giờ bạn hãy ra ngoài website kiểm tra qua mã nguồn HTML ngoài frontend của nó, file ajax-pagination.js đã được gọi ra thế này.
Tìm class phân trang và viết event cho AJAX
Event cho AJAX nghĩa là một hành động nào đó mà chúng ta muốn dựa vào đó để kích hoạt việc gửi truy vấn, ở đây chúng ta sẽ sử dụng event click để gửi truy vấn khi click vào một vị trí nào đó. Và vị trí đó chúng ta sẽ xác định là các đường link bên trong thanh phân trang.
Bây giờ bạn hãy tìm đến thanh phân trang, ấn chuột phải vào đường link và chọn Inspect Element (hoặc Firebug) để xem vùng chọn khu vực đó, nó như thế này.
Bạn hãy để ý như trong hình, thanh phân trang sẽ được nằm trong một vùng chọn với class .nav-links
. Sau đó các liên kết bên trong sẽ có class là .page-numbers
. Riêng liên kết cho nút next thì sẽ có thêm class .next
. Vậy nên để tạo ra event chính xác, chúng ta sẽ thiết lập một event click dựa trên thẻ a trong vùng chọn .nav-links
(viết là .nav-links a
).
Trước tiên hãy viết đoạn này vào file ajax-pagination.js để thiết lập event và thử nghiệm nó.
// chắc chắn là đang sử dụng chế độ No Conflict của jQuery, sử dụng jQuery() thay vì $() jQuery.noConflict(); // load sau khi website được tải xong jQuery( document ).ready( function($) { $(document).on ( ‘click’, ‘.nav-links a’, function( event ) { event.preventDefault(); // kiểm tra event click alert( "Bạn vừa click vào phân trang đấy!" ); } ) // end event } );
Và bây giờ hãy thử click vào phân trang đi xem có thấy cái bảng alert, nếu thấy thì thành công và xóa đoạn alert()
trong code đi.
Sử dụng AJAX trong WordPress
Mặc định WordPress đã tích hợp sẵn các phương thức xử lý dữ liệu bằng AJAX, nên chúng ta có sử dụng thì chỉ cần gọi nó ra mà thôi. Để gọi nó ra, chúng ta sẽ tiến hành gọi file admin-ajax.php có trong WordPress ra với code sau (đặt bên trong hàm ajax_pagination_scripts
mà ta đã khai báo ở trên):
/* * Gọi AJAX trong WordPress */ global $wp_query; wp_localize_script( ‘ajax-pagination-script’, ‘ajax_object’, array( // Các phương thức sẽ sử dụng ‘ajax_url’ => admin_url( ‘admin-ajax.php’ ), ‘query_vars’ => json_encode( $wp_query->query ) ));
Hàm wp_localize_script()
sẽ có tác dụng dò tìm và sử dụng một script nào đó đã có trong mã nguồn WordPress vì file admin-ajax.php đã có sẵn rồi nên không dùng wp_enqueue_script()
như ở trên nữa. Trong đó, ajax_object
là đối tượng mà chút nữa chúng ta sẽ sử dụng trong file .js để xác định đối tượng AJAX. Còn global $wp_query;
là ta sẽ đưa đối tượng $wp_query
ra toàn cục để có thể sử dụng trong file của plugin nhằm lấy dữ liệu của post.
Sau khi chèn xong, bạn kiểm tra source thì sẽ thấy một đoạn script được in ra như sau:
[html] <script type=’text/javascript’> /* <![CDATA[ */ var ajaxobject = {"ajax_url":"http:\/\/domain\/wp-admin\/admin-ajax.php"}; /* ]]> */ </script> [/html]
Bây giờ việc còn lại mà bạn cần làm ở bước này đó là viết Javascript để khai báo AJAX.
// chắc chắn là đang sử dụng chế độ No Conflict của jQuery, sử dụng jQuery() thay vì $() jQuery.noConflict(); // load sau khi website được tải xong jQuery( document ).ready( function($) { $(document).on ( ‘click’, ‘.nav-links a’, function( event ) { event.preventDefault(); // AJAX $.ajax({ url: ajax_object.ajax_url, type: ‘post’, data: { action: ‘ajax_pagination_data’ }, success: function( ketqua ) { // Thử nghiệm var ketqua = "AJAX working"; alert( ketqua ); } }) } ) // end event } );
Từ đoạn 10 đến đoạn 23 là những code mà chúng ta cần viết bổ sung vào file ajax-pagination.js, mình giải thích như sau:
$.ajax()
– Đây là một hàm đặc biệt trong jQuery để xử lý AJAX đơn giản và gọn gàng hơn.url
: Đường dẫn của file mà các dữ liệu sẽ được gửi vào khi thực thi AJAX từ event click. ajax_object.ajax_url có nghĩa là đường dẫn của file admin-ajax.php mà ta đã khai báo ở file plugin.php.type
: loại phương thức gửi dữ liệu, sử dụng POST hay GET gì tùy bạn nhưng nếu chỉ là nhận dữ liệu thôi thì mình khuyến khích bạn dùng POST.data
: khai báo đối tượng chứa dữ liệu trả về sau khi gửi request từ AJAX. ajax_pagination_data nghĩa là cái hàm trả dữ liệu về, chút nữa chúng ta sẽ viết nó sau.success
: hành động sau khi AJAX truyền dữ liệu thành công, tạm ở ở đây sẽ kiểm tra bằng cách in một đoạn chữ ra để thử nghiệm. Nếu nó trả về được thì thành công.
Bây giờ lưu lại và hãy thử click vào cái link phân trang xem biến ketqua có trả về không, nếu có thì thành công.
Tạo hàm trả về dữ liệu từ WordPress
Như mình nói ở trên, tham số data là sẽ chứa đối tượng trả kết quả về, nên bây giờ chúng ta sẽ xây dựng đối tượng ajax_pagination_data để trả về cho user khi họ truyền dữ liệu về. Trước tiên, bạn sẽ cần sửa lại file ajax-pagination.js thành như sau:
// chắc chắn là đang sử dụng chế độ No Conflict của jQuery, sử dụng jQuery() thay vì $() jQuery.noConflict(); // load sau khi website được tải xong jQuery( document ).ready( function($) { /* @ hàm xác định số trang sẽ được tải @ bằng cách bóc tách số trong chuỗi dữ liệu */ function set_page( element ) { element.find(‘span’).remove(); return parseInt( element.html() ); } $(document).on ( ‘click’, ‘.nav-links a’, function( event ) { event.preventDefault(); page = set_page( $(this).clone() ); // AJAX $.ajax({ url: ajax_object.ajax_url, type: ‘post’, data: { action: ‘ajax_pagination_data’, query_vars: ajax_object.query_vars, page: page }, beforeSend: function() { /* @ Tạo các hiệu ứng trước khi request gửi đi */ $( ‘#main’ ).find( ‘article’ ).remove(); $( ‘#main nav’ ).remove(); $( ‘#main’ ).scrollTop(0); $( ‘#main’ ).append( ‘<div id="loading">Đang lấy dữ liệu bài viết</div>’ ); }, success: function( ketqua ) { /* @ Xóa nút loading @ và khôi phục lại dữ liệu trả về */ $( ‘#main’ ).find( ‘#loading’ ).remove(); $( ‘#main’ ).append( ketqua ); console.log(page); } }) } ) // end event } );
Ở đoạn code mới, chúng ta có thêm một hàm mới mang tên set_page()
trong Javascript. Hàm này sẽ có tác dụng tìm kiếm tất cả nội dung trong thẻ span của tài liệu. Sau đó tiến hành dùng hàm parseInt()
trong Javascript để tách số trong chuỗi dữ liệu tìm thấy. Mục đích của nó là để chúng ta lấy được con số của trang trong các đoạn text trên website, ví dụ trên thanh pagination sẽ có các số 1,2,3,4,…nó sẽ lấy các số này để làm giá trị của tham số page nhằm giúp query của WordPress hiểu được sẽ cần lấy dữ liệu của trang nào.
Chúng ta sẽ khai báo thêm tham số query_vars
và page
vốn sẽ được dùng trong việc gửi truy vấn trong WordPress để lấy danh sách post. Tham số query_vars
được xem là một phương thức của đối tượng ajax_object
mà chúng ta đã khai báo ở file plugin.php. Còn tham số page là nó sẽ sử dụng giá trị trả về của hàm set_page() ta tạo ở trên.
Kế tiếp là tham số beforeSend
để thêm cái gì đó trước khi dữ liệu của truy vấn gửi đi. Chúng ta sẽ dùng để xóa các post (thẻ aritlce
trong #main
của TwentyFifteen), xóa thẻ khung pagination đang có (thẻ nav
có class pagination
trong #main
của TwentyFifteen) và cuối cùng là thêm vào dữ liệu nhận được vào trong #main
. Và quan trọng là tự động nhảy lên đầu website với phương thức scrollTop
và in một đoạn nội dung cho người dùng biết là dữ liệu đang được tải.
Cuối cùng là tham số success sẽ trả kết quả về sau khi gửi truy vấn AJAX đến WordPress vì tham số ketqua WordPress tự hiểu được đó là hàm hứng dữ liệu trả về. Đồng thời xóa cái chữ Đang lấy dữ liệu bài viết kia đi.
Mình cũng xin lưu ý như sau:
#main
– vùng chọn của khung hiển thị danh sách các post.article
– vùng chọn riêng lẻ của post, bạn có thể dùng class .post cũng được.nav.pagination
– vùng chọn cho khung phân trang.
Và tiếp theo là tạo hàm để trả dữ liệu về cho AJAX với hook là tên action wp_ajax_ajax_pagination_data
, trong đó ajax_pagination là tên action mà ta đã khai báo ở ajax-pagination.js, còn wp_ajax là tiền tố bắt buộc cho hook khi làm việc với AJAX trong WordPress.
/* @ Hàm chứa dữ liệu trả về */ add_action( ‘wp_ajax_nopriv_ajax_pagination_data’, ‘set_ajax_pagination_data’ ); add_action( ‘wp_ajax_ajax_pagination_data’, ‘set_ajax_pagination_data’ ); function set_ajax_pagination_data() { $query_vars = json_decode( stripslashes( $_POST[‘query_vars’] ), true ); $query_vars[‘paged’] = $_POST[‘page’]; $posts = new WP_Query( $query_vars ); $GLOBALS[‘wp_query’] = $posts; if( ! $posts->have_posts() ) { get_template_part( ‘content’, ‘none’ ); } else { while ( $posts->have_posts() ) { $posts->the_post(); get_template_part( ‘content’, get_post_format() ); } } the_posts_pagination( array( ‘mid_size’ => 5, ‘prev_text’ => __( ‘Previous page’, ‘twentyfifteen’ ), ‘next_text’ => __( ‘Next page’, ‘twentyfifteen’ ), ‘before_page_number’ => ‘<span class="meta-nav screen-reader-text">’ . __( ‘Page’, ‘twentyfifteen’ ) . ‘ </span>’, ) ); die(); }
Ở đoạn trên, đầu tiên là chúng ta chuyển dữ liệu ở biến $_POST['query_vars']
được trả về bởi AJAX vì mặc định hàm $.ajax()
của jQuery sẽ trả dữ liệu về là kiểu JSON và lưu vào biến $query_vars
.
Kế tiếp, chúng ta đưa giá trị của $_POST['page']
khi được AJAX gửi về vào biến $query_vars['paged']
để thiết lập phân trang cho query.
Cuối cùng là ta tạo một đối tượng $posts
từ biến $query_vars
ở trên và gọi template content.php ra để hiển thị nội dung. Trong theme TwentyFifteen thì file này sẽ chứa các đoạn code hiển thị từng thành phần của một post và vòng lặp cứ thế lặp đi lặp lại. Đồng thời chèn thêm một thanh phân trang mới với hàm the_posts_pagination()
và kết thúc quá trình cho đến khi nó đã hoàn tất việc lặp.
Bây giờ bạn có thể lưu lại và ra ngoài thử click vào liên kết chuyển trang ở các con số trong phần phân trang để xem nó có hoạt động hay không, nếu bạn làm từ đầu bài tới giờ thì nó sẽ hoạt động được.
Cũng nên lưu ý là kể từ WordPress 4.1 trở đi thì hàm the_posts_pagination()
sẽ hiển thị thanh phân trang dạng số rồi.
Vấn đề gặp phải trong tutorial này
Thực ra tutorial này mình đang gặp phải một vấn đề là nếu bạn nhấp vào liên kết dạng số trên phân trang thì nó sẽ làm việc được, nhưng nếu bạn click vào nút Next hay đại loại như vậy thì nó sẽ trả về trang chủ. Bởi vì theo tutorial này thì dữ liệu từng trang của WordPress sẽ được lấy nhờ vào biến $query_vars['paged']
mà nó hứng được từ dữ liệu mà jQuery gửi về bằng AJAX, trong khi đó ở file Javascript mình đã xác định rằng số trang sẽ được tìm bằng cách dùng hàm parseInt()
trong Javascript để tách số trên thẻ <span>, vốn là liên kết của từng trang trong phân trang. Nhưng nếu thẻ <span> đó có nội dung là chữ Next thì dĩ nhiên nó sẽ trả về giá trị là “NaH”, tương ứng với việc không có dữ liệu kiểu số nào, thành ra sẽ không hoạt động được.
Add new comment