Working with JavaScript in Rails
Bài đăng này đã không được cập nhật trong 9 năm
1. Unobtrusive JavaScript
Rails sử dụng một kỹ thuật được gọi là "Unobtrusive JavaScript" để kết hợp xử lý javascript với DOM. Việc này được coi là thực hiện tốt trong cộng đồng frontend.
Sau đây là cách đơn giản để viết javaScript. Bạn có thể xem nó như 1 dòng javaScript:
<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>
Khi bạn click vào link thì màu của link sẽ chuyển sang màu đỏ("#990000"). Và điều gì sẽ xảy ra nếu bạn muốn thực hiện nhiều javaScript khi click chuột.
<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>
Đoạn code trên rất phức tạp. Để xử lý việc này gọn hơn chúng ta có thể định nghĩa hàm để xử lý việc click chuột và chuyến nó về dạng coffeeScript:
paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
Bây giờ bạn có thể viết ngắn gọn hơn như sau:
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
Nhưng nếu có nhiều liên kết có hành động tương tự thì sao?
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
<a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a>
<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
Làm như vậy đoạn mã của bạn sẽ ko được DRY. Chúng ta sửa lại bằng cách thêm một thuộc tính data-*
vào link và sau đó xử lý sự kiện của mọi link có thuộc tính đó:
paintIt = (element, backgroundColor, textColor) ->
element.style.backgroundColor = backgroundColor
if textColor?
element.style.color = textColor
$ ->
$("a[data-background-color]").click (e) ->
e.preventDefault()
backgroundColor = $(this).data("background-color")
textColor = $(this).data("text-color")
paintIt(this, backgroundColor, textColor)
<a href="#" data-background-color="#990000">Paint it red</a>
<a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>
Chúng tôi gọi điều này là "unobtrusive" javaScript bởi vì chúng tôi không còn trộn lẫn JavaScript vào HTML nữa. Nó được tách riêng ra và giúp dễ dàng thay đổi về sau này. Chúng tôi có thể dễ dàng thêm vào behavior cho bất kỳ link nào bằng cách thêm vào các thuộc tính data. Chúng tôi có thể chạy tất cả JavaScript của chúng tôi thông qua một minimizer và concatenator. JavaScript se được tải về khi tải trang đầu tiên và được lưu trữ cho mỗi trang sau đó. Và còn rất nhiều lợi ích khác nữa.
2. Built-in Helpers
Rails cung cấp một loạt các phương thức hỗ trợ view được viết bằng ruby để hỗ trợ việc tạo ra HTML. Đôi khi, bạn muốn thêm Ajax cho các phần tử và Rails cung cấp cho bạn có thể làm việc đó. Bởi vì Unobtrusive JavaScript, "Ajax helpers" của Rails thực sự là 2 phần: một nửa là javaScript và một nửa là ruby. rails.js
cung cấp một nửa javaScript, hỗ trợ view thêm các thẻ thích hợp cho DOM của bạn. CoffeeScript trong rails.js sau đó lắng nghe các tuộc tính và gắn xử lý thích hợp cho nó.
2.1 form_for
form_for
là một helper chuyên hỗ trợ các form ghi. form_for có một lựa chọn :remote. Được sử dụng như sau:
<%= form_for(@article, remote: true) do |f| %>
...
<% end %>
Nó sẽ sinh ra (tương đương) đoạn HTML sau:
<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post">
...
</form>
Lưu ý data-remote="true"
. Bây giờ, form sẽ được submit bằng ajax chứ không phải bằng cơ chế thông thường của trình duyệt.
Chắc bạn không muốn chỉ ngồi một chỗ mặc dù form được điền đẩy đủ. Bạn có thể muốn làm điều gì đó với một submit thành công. Để làm điều đó có thể ràng buộc sự kiện ajax:success và nếu lỗi thì sử dụng ajax:error. Ví dụ:
$(document).ready ->
$("#new_article").on("ajax:success", (e, data, status, xhr) ->
$("#new_article").append xhr.responseText
).on "ajax:error", (e, xhr, status, error) ->
$("#new_article").append "<p>ERROR</p>"
Bạn có thể tham khảo nhiều hơn về các sự kiện trong jquery-ujs
2.2 form_tag
form_tag tương tự như form_for. Nó cũng có lựa chọn :remote được sử dụng như sau:
<%= form_tag('/articles', remote: true) do %>
...
<% end %>
Tương ứng với đoạn HTML sau:
<form accept-charset="UTF-8" action="/articles" data-remote="true" method="post">
...
</form>
Những thứ khác cũng tương tự với form_for.
2.3 link_to
link_to là một helper hỗ trợ việc tạo các link liên kết. cú pháp:
<%= link_to "an comment", @comment, remote: true %>
tương ứng với:
<a href="/comments/1" data-remote="true">an comment</a>
Bạn có thể kết hợp sự kiện ajax giống như form_for. Ví dụ: giả sử chúng ta có một danh sách các comments mà có thể được xoá chỉ với một cú click chuột. Chúng ta có thể làm điều đó như sau:
<%= link_to "Delete comment", @comment, remote: true, method: :delete %>
Và sử dụng coffeeScript như sau:
$ ->
$("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
alert "The comment was deleted."
2.4 button_to
button_to là một helper hỗ trợ bạn tạo các button. Nó có một lựa chọn :remote có thể gọi như sau:
<%= button_to "An comment", @comment, remote: true %>
Tương ứng với:
<form action="/comments/1" class="button_to" data-remote="true" method="post">
<div><input type="submit" value="An comment"></div>
</form>
Khi nó là một <form>, tất cả các thông tin trên form_for cũng đc áp dụng.
3. Server-Side Concerns
Ajax không chỉ với client-side, bạn cũng cần thực hiện một số việc bên phía máy chủ để hỗ trợ nó. Thông thường mọi người thích request của họ trả về dạng JSON hơn là HTML. Vậy làm sao để làm được việc đó?
Ví dụ đơn giản:
Giả sử có một loạt các users mà muốn hiển thị và cung cấp một form trên cùng một trang để tạo một user. method index trong controller sẽ giống như sau:
class UsersController < ApplicationController
def index
@users = User.all
@user = User.new
end
# ...
view (app/views/users/index.html.erb) của nó sẽ gồm:
<b>Users</b>
<ul id="users">
<%= render @users %>
</ul>
<br>
<%= form_for(@user, remote: true) do |f| %>
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Tạo một partial app/views/users/_user.html.erb có nội dung:
<li><%= user.name %></li>
Đầu trang sẽ hiển thị tất cả các users, phía dưới sẽ là một form để đăng ký một user mới.
Form ở cuối sẽ gọi hành động create trong UsersController. Bởi vì lựa chọn remote của form được thiết lập là true, request sẽ được gửi đến UsersController như một request Ajax. Để thực hiện điều đó hành đông create trong controller của bạn sẽ giống như sau:
# app/controllers/users_controller.rb
# ......
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.js {}
format.json { render json: @user, status: :created, location: @user }
else
format.html { render action: "new" }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
Cần lưu ý đến format.js, nó cho phép controller đáp ứng yêu cầu Ajax của bạn. Ta có một file js tương ứng app/views/users/create.js.erb tạo ra mã js mà sẽ được gửi và thực hiện bên phía khách.
$("<%= escape_javascript(render @user) %>").appendTo("#users");
4. Turbolinks
Rail 4 đi kèm với gem Turbolinks. Đây là gem sử dụng Ajax để tăng tốc đọ render trang trong hầu hết các ứng dụng.
4.1 làm thế nào để làm việc với Turbolinks
Turbolinks gắn một xử lý nhấp chuột cho tất cả các thẻ <a> trong trang. Nếu trình duyệt của bnaj hỗ trợ PushState, Turbolinks sẽ tạo một request Ajax cho trang, phân tích response, và thay thế toàn bộ <body> của trang với các <body> của response.
Bạn chỉ cần cho phép Turbolinks có trong Gemfile của bạn và thêm //= require turbolinks
trong app/assets/javascripts/application.js của bạn.
Nếu bạn không muốn vô hiệụ hoá Turbolink cho các link nhất định thì hãy thêm một thuộc tính data-no-turbolink
vào thẻ tương ứng.
<a href="..." data-no-turbolink>No turbolinks here</a>.
4.2 Page Change Events
Khi viết CoffeeScript, bạn sẽ thường muốn thêm vào một số chi tiết cho tiến trình tải trang. Với jQuery, bạn sẽ làm tương tự:
$(document).ready ->
alert "page has loaded!"
Tuy nhiên, bởi vì Turbolinks ghi đè lên quá trình tải trang, sự kiện này sẽ không được thực hiện. Khi đó bạn cần phải thay đổi code của bạn:
$(document).on "page:change", ->
alert "page has loaded!"
Để hiểu hơn bạn có thể tìm hiểu tại đây
Tham khảo: http://guides.rubyonrails.org/working_with_javascript_in_rails.html
All rights reserved