Using CoffeeScript in Rails

I. Lời mở đầu

Ngôn ngữ lập trình CoffeeScript được xây dựng dựa trên JavaScript, nó biên dịch thành JS rõ ràng, hiệu quả để có thể chạy trên một trình duyệt web hoặc sử dụng với các công nghệ như Node.js cho các ứng dụng máy chủ.

CoffeScript giải quyết các điểm yếu của JS:

  • Cung cấp một cú pháp đơn giản hơn, làm giảm sự dập khuôn, chẳng hạn như các dấu ngoặc đơn và các dấu phẩy.
  • Sử dụng khoảng trắng như là một cách để tổ chức các đoạn mã.
  • Cung cấp cú pháp đơn giản để biểu diễn các hàm.
  • Cung cấp sự thừa kế dựa trên lớp.

Cài đặt:

Cho Ubuntu:

  • Để cài đặt CoffeeScript thì trước tiên cài đặt Nodejsnpm:
sudo apt-get install nodejs
sudo apt-get install npm
  • Cài đặt CoffeeScript:
npm install -g coffeescript

Sử dụng:

Sau khi cài đặt CoffeeScript, để biên dịch một file mã nguồn CoffeeScript (.coffee) ta dùng lệnh coffee -c <tên file coffee>, sau khi biên dịch ta sẽ được một một file JavaScript (.js) và có thể dùng trực tiếp file này để chạy. Một số lệnh biên dịch khác có thể tham khảo tại (http://coffeescript.org/).

II. Getting Started

  • Nếu phiên bản Ruby của bạn là 1.8.7 hoặc cũ hơn thì bạn cần thêm JSON gem vào trong Gemfile :
gem "json"
  • Cần tới một trình biên dịch để biên dịch CoffeeScript. Nếu sử dụng hệ điều hành OS-X, Ruby wrapper (có trong "coffee-script" gem) sẽ sử dụng trình biên dịch được built trong hệ thống nhưng nếu sử dụng nền tảng khác thì cần nhúng v8 vào trong application bằng cách thêm dòng lệnh sau vào trong Gemfile :
gem 'therubyracer', :require => nil
  • Tiếp theo bundle install để update gems:
bundle install

III. Writing Some Code

  • Đầu tiên cần xóa file JavaScript đã tồn tại và thay thế bằng jQuery bằng cách thêm dòng sau vào Gemfile:
gem "coffee-rails", "~> 4.0.0"
gem "jquery-rails"

Sau đó chạy lệnh :

bundle install
rails g jquery:install
  • Các đoạn lệnh js (định nghĩa các hàm, các event ... ) được chứa trong các file có đuôi là .js.coffee trong thư mục app/assets/javascripts

  • Trong application.js, có 2 cách gọi file .js.coffee đó :

Cách 1 :

Gọi file đó 1 lần trong application.js, những file nằm trực tiếp trong thư mục javascripts thì chỉ cần thêm dòng lệnh sau để load hết các file js cho tất cả các file trong app :

//= require_tree .

Nếu muốn load tất cả các file của 1 folder nào đó trong folder javascripts : ví dụ có folder app/assets/javascripts/home thì ta sẽ thêm dòng lệnh sau vào application.js :

//= require_tree ./home

Nếu muốn load riêng 1 file trong 1 folder thì dẫn trực tiếp tới file đó :

//= require_tree ./home/xyz

Trong file application.html.erb thêm dòng sau để nó load application.js :

<%= javascript_include_tag "application", "data-turbolinks-track" => true %>

Cách 2:

Đưa trực tiếp file js (example.js.coffee) cần vào trong view (form) muốn áp dụng , thêm dòng sau vào trong file html muốn áp dụng:

<%= javascript_include_tag "example" %>

Nếu không muốn viết javascript trong file .js.coffee thì có thể viết trực tiếp đoạn mã js đó vào trong file html. Giả sử có:

1 `button` - `id="side_arc_cat_list_body_disp_img"`
1 `div` - `class="side_arc_cat_list_body"`

Đoạn js sau xử lý event : khi click button có id="side_arc_cat_list_body_disp_img" thì sẽ toggleContents (đẩy ra / thu vào) thẻ divclass="side_arc_cat_list_body" :

<%= javascript_tag do %>
      $("#side_arc_cat_list_body_disp_img").click(function() {
        toggleContents("side_arc_cat_list_body");
      });
<% end %>

    // $ : sử dụng khi bắt đầu mỗi 1 sự kiện.

toggleContents là 1 hàm được định nghĩa từ trước trong 1 file .js.coffee nào đó :

@toggleContents = (strID) ->
      objT = $("##{strID}_disp_img")
      if objT.attr("class") != ""
        $("##{strID}").toggle()
      if $("##{strID}").is(":visible")
        $.cookie "co_#{strID}", "open"
        objT.removeClass "common_icon_window"
        objT.addClass "common_icon_minus"
        objT.attr "title", I18n.t("js.nanoty_core.home.index.scale_down")
      else
        $.cookie "co_#{strID}", "close"
        objT.removeClass "common_icon_minus"
        objT.addClass "common_icon_window"
        objT.attr "title", I18n.t("js.nanoty_core.home.index.scale_up")

  • 1 lưu ý khi dùng idclass cho js :

id là duy nhất thường được dùng cho đối tượng đi tác động (event)

class thường được gán cho nhiều đối tượng có chung 1 thuôc tính (chung css), thường là đối tượng bị tác động trong event.

id và class được phân biệt với nhau bởi dấu "#" cho id và "." cho class

IV. 1 số lưu ý trong khi sử dụng js :

  • Ví dụ nếu muốn toggleContent cho cùng 1 div trong nhiều trường hợp (nhiều id cho 1 class="side_diary_comment_write_body") (nhiều tab với nhiều id khác nhau). Nếu viết theo cách đơn thuần :
$("#side_diary_comment_write_body_disp_img").click ->
      toggleContents "side_diary_comment_write_body"

    $("#side_diary_comment_write_body2_disp_img").click ->
      toggleContents "side_diary_comment_write_body"

    $("#side_diary_comment_write_body3_disp_img").click ->
      toggleContents "side_diary_comment_write_body"

thì vẫn phóng to / thu nhỏ được div, nhưng toggleContent có 1 đặc điểm là khi event đươc thực hiện thì ảnh trên div có id tương ứng sẽ đổi từ common_icon_minus sang common_icon_window và ngược lại (được định nghĩa trong hàm toggleContents). Nếu 1 id cho 1 class --> ok, nhưng trong trường hợp này thì ảnh chỉ đổi đối với tab đầu tiên. Các tab sau nó sẽ tự động thêm index vào sau mỗi tên class của div đó và vì tên class của div không còn nguyên bản nên ko đổi được ảnh.

--> Giải pháp :

Bước 1 : Viết hàm toggleContents như sau :

toggleContents = (strID) ->
      $objT = $("##{strID}_disp_img")
      unless $objT.attr("class") == ""
      /* modified : ý nghĩa : cắt bỏ hết các đuôi của id để nó trở về id gốc (1,2,3......)
      $("#" + strID.replace(/[0-9]/g, '')).toggle() /* modified */
        switch $objT.attr("class")
        when "common_icon_window"
          $objT.removeClass "common_icon_window"
          $objT.addClass "common_icon_minus"
          $objT.attr "title", I18n.t("js.nanoty_core.home.index.scale_down")
        when "common_icon_minus"
          $objT.removeClass "common_icon_minus"
          $objT.addClass "common_icon_window"
          $objT.attr "title", I18n.t("js.nanoty_core.home.index.scale_up")

        setContentsCookie strID, $("#" + strID).is(":visible")

Bước 2 : Viết function gọi hàm toogle

/* ứng với div của 2 tab sau (gọi id giả của khối div cần toggle - thêm index vào cho khác với id gốc), ví dụ : "side_diary_comment_write_body2" */

    $("#side_diary_comment_write_body_disp_img").click ->
      toggleContents "side_diary_comment_write_body"

    $("#side_diary_comment_write_body2_disp_img").click ->
      toggleContents "side_diary_comment_write_body2"

    $("#side_diary_comment_write_body3_disp_img").click ->
      toggleContents "side_diary_comment_write_body3"

  • Muốn bắt sự kiện khi click 1 đối tượng nào đó nó sẽ redirect về trang trước đó thì viết đoạn js sau :
$ ->
      $("#cancel").click ->
        window.history.back()

V. Lời kết

  • Ngoài những ví dụ nêu trên thì js còn có rất nhiều ứng dụng hay cho web: kéo thả (dragdrop), ẩn hiện form, re-edit, định thời gian xuất hiện của 1 đối tượng ...

  • Trang web hữu ích cho việc biên dịch từ js thuần sang js coffee :

http://js2coffee.org/

  • Nguồn tham khảo hữu ích cho việc học js :

https://www.honeybadger.io/blog/2013/12/11/beginners-guide-to-angular-js-rails

http://excid3.com/blog/using-coffeescript-for-rails-views/ http://coffeescript.org/