12/08/2018, 15:27

Sử dụng JQuery Ajax với một số chức năng cơ bản

Giới thiệu chung AJAX ( A synchronous J avaScript A nd X ML) là một kỹ thuật phát triển web có tính tương tác cao thông qua việc kết hợp các công nghệ: XML, HTML, CSS và JavaScript. Trong đó: HTML (hoặc XHTML) với CSStrong việc hiển thị thông tin Mô hình DOM (Document Object Model), được ...

Giới thiệu chung

AJAX (Asynchronous JavaScript And XML) là một kỹ thuật phát triển web có tính tương tác cao thông qua việc kết hợp các công nghệ: XML, HTML, CSS và JavaScript. Trong đó:

  • HTML (hoặc XHTML) với CSStrong việc hiển thị thông tin
  • Mô hình DOM (Document Object Model), được thực hiện thông qua JavaScript, nhằm hiển thị thông tin động và tương tác với những thông tin được hiển thị.
  • Đối tượng XMLHttpRequest để nhận dữ liệu từ server một cách không đồng bộ.
  • XML thường là định dạng cho dữ liệu truyền, mặc dầu bất cứ định dạng nào cũng có thể dùng, bao gồm HTML định dạng trước, văn bản thuần (plain text), JSON và ngay cả EBML. Đây là một công nghệ giúp chúng ta tạo ra những Web động mà hoàn toàn không reload lại trang. Đối với công nghệ web hiện nay thì ajax không thể thiếu, nó là một phần làm nên sự sinh động cho website. Ajax được viết bằng ngôn ngữ Javascript nên nó chạy trên client, tức là mỗi máy (user) sẽ chạy độc lập không hoàn toàn ảnh hưởng lẫn nhau. Hiện nay có nhiều thư viện javascript như jQuery hay Angular đều hỗ trợ kỹ thuật này nhằm giúp chúng ta thao tác dễ dàng hơn. Ở bài viết này mình sẽ dùng Ajax với thư viện JQuery.

Làm quen với Ajax đối với 1 số chức năng cơ bản

Mình sẽ tạo app nhỏ để đăng kí và quản lý user và dùng Ajax cho 3 chức năng là tạo mới(create) user, xóa(destroy) user và tìm kiếm(search) user. Đầu tiên, khởi tạo 1 app mới:

rails new ajax_practice

sau đó tạo scaffold user:

rails g scaffold user name:string

Chạy migrate:

rails db:migrate

Để thuận tiện thì mình sẽ gộp toàn bộ các chức năng tạo mới và xóa user trong view index:

Create User

Bình thường khi submit 1 form, params user nhập vào từ client trong 1 request sẽ được routes điều hướng lên server xử lí sau đó sẽ redirect user tới 1 trang khác với thông tin được trả về từ phía server. Đối với Ajax, khi ấn submit, request sẽ thông qua Ajax gửi ngầm lên phía server, dữ liệu trả về sẽ được Ajax tiếp nhận và xử lí theo từng thẻ html riêng biệt trong màn hình hiển thị hiện tại. trong file index.html.erb

<p>
  We have total <span class="users-size"><%= @users.size %></span> users!
</p>
<table>
.
.
.

Tạo file ajax.js trong assets/javascripts:

$(document).ready(function() {   # ready when html elements and DOM are loaded
  $('#new_user').on('submit', function(e) {  #Bắt event submit của thẻ có id: new_user
    var name = $('#user_name').val();  # Khai báo và gán giá trị thẻ có id: user_name vào biến name
    e.preventDefault();   # ngăn chặn hành động mặc định của button submit khi xảy ra sự kiện
    if (name) { 
      $.ajax({
        url: '/users',        # gửi request tới server theo đường dẫn /users
        type: 'post',        # phương thức tương ứng là POST
        dataType: 'json',    # data gửi đi dưới dạng JSON
        data: {user: {name: name}},    # data gửi đi tương ứng với params[:user][:name]
      })
      .done(function(data) {    #sau khi Ajax nhận được data từ server trả về và user được save
        $('tbody').append(data.user_data);   #chèn thêm data.user_data vào cuối thẻ <tbody>
        $('.users-size').text((parseInt($('.users-size').text()) + 1));  # lấy giá trị text của thẻ span có class = users-size,  chuyển về dạng int rồi cộng thêm 1, sau đó gán lại vào giá trị text của chính nó
      })
      .fail(function(){   # trường hợp user không save được
        alert("Can create new user! We got some errors");
      })
    } else {
      alert("Name can be blank")
    }
   return false;    # kết thúc sự kiện
  })
})

trong users_controller:

class UsersController < ApplicationController
.
.
.
  def create
    @user = User.new(user_params)
    if @user.save
      render json: {      # sau khi user save, trả về client user_data dạng json
        user_data: render_to_string(@user)  # data trả về được render từ html sang chuỗi string, ở đây là partial _user.html.erb
      }, status: :ok  # Rails status code symbol
    else
      render error, status: :unauthorized
    end
  end

  private
  def user_params
    params.require(:user).permit(:name)
  end
end

Đã xong chức năng tạo mới user, để rõ hơn có thể dùng debugger để thấy sau khi user nhấn submit, hành động mặc định bị hoãn, request từ client được Ajax gửi lên dưới dạng chuỗi JSON { user, { name: name} }. Sau khi xử lí, trên server trả về chuỗi JSON được format thành thẻ <li> rồi dùng javascript chèn vào đầu thẻ <tbody>, đồng thời cũng cập nhật lại giá trị thẻ <p> tương ứng với tổng số user hiện tại.

Delete User

Về chức năng xóa user, cũng gần như tương tự, trong file ajax.js mình thêm sự kiện ấn vào button delete:

$(document).ready(function() {
.
.
.
$('.tbody').on('click', '.destroy', function() { 
    var id = $(this).data('id')    # Khai báo biến id và gán vào id của user cần delete thông qua thuộc tính data-id của thẻ có class = "destroy"
    var childTr = $(this).closest('tr')   # Khai báo biến childTr và gán vào thẻ <tr> gần nhất  của thẻ có class = "destroy"
    $.ajax({
      url: '/users/' + id,
      type: 'delete'
    })
    .done(function() {
      childTr.hide();   # ẩn đi thẻ được gán
      $('.users-size').text((parseInt($('.users-size').text()) - 1));  # cập nhật lại giá trị text của thẻ có class = "user-size"
    })
    return false;
  })
})

Trong partial _user.html.erb mình set data-id và class ="destroy" cho thẻ <td>

<tr>
  <td><%= user.name %></td>
  <td><%= link_to 'Destroy', user, method: :delete, data: {id: user.id},
    class: "destroy" %></td>
</tr>

Thêm action destroy trong Userscontroller:

class UsersController < ApplicationController
.
.
.
 def destroy
   @user = User.find_by id: params[:id]
   @user.destroy
 end
.
.

Sau đó chúng ta có kết quả:

Search User

Bây giờ chúng ta tới chức năng Search, để dễ nhìn thì mình sẽ tạo 1 controller và view search riêng: Đầu tiên, tạo route cho search:

Rails.application.routes.draw do
  .
  .
  resources :searches, only: [:index]
end

Ở view search index.html.erb :

<%= form_tag searches_path, method: :get do %>    # build form search với request method là get 
  <%= text_field_tag :q, params[:name],    # đặt thẻ text input với params[:name] và class = "search-box"
    class: "search-box", autocomplete: :off %>
  <span>
    <%= button_tag "search" %>
    <%= link_to "Back to index", users_path %>
  </span>
<% end %>
<br><br>
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody class="user-table">
    <%= render @users %>
  </tbody>
</table>

Trong User model:

class User < ApplicationRecord
  validates :name, presence: true
  scope :search, ->q{where "name LIKE ?", "%#{q}%"}
end

Ở phía controller:

class SearchesController < ApplicationController
  def index
    if request.xhr?    # kiểm tra request gửi lên có phải là xhr request của ajax không?
      @users = User.search(params[:name])    #gọi đến hàm scope :search ở User model
      render json: {
        search_result: render_to_string(@users)
      }, status: :ok
    else
      @users = User.all
    end
  end
end

Còn phần xử lí ajax:

$(document).ready(function() {
.
.
  $('.search-box').on('keyup', function() {
      var key_name = $(this).val();
      $.ajax({
        url: '/searches',
        type: 'get',
        dataType: 'json',
        data: {name: key_name},
      })
      .done(function(data) {
        $('.user-table').html(data.search_result);  # Gán giá trị html search_result (được controller trả về) vào thẻ có class = "user-table"
        // console.log(data.search_result)
      })
      return false;
  })
})

Và ta được kết quả:

Lời kết

Bài viết này nhằm giới thiệu qua về Jquery Ajax và ứng dụng nó trong 1 số chức năng cơ bản. Bài viết còn tồn tại nhiều hạn chế, rất mong nhận được những đóng góp và phản hồi tích cực. Cảm ơn bạn đã theo dõi bài viết.

0