Rails Unobtrusive JavaScript (UJS) 深度研究

紅寶鐵軌客
Join to follow...
Follow/Unfollow Writer: 紅寶鐵軌客
By following, you’ll receive notifications when this author publishes new articles.
Don't wait! Sign up to follow this writer.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.
寫程式中、折磨中、享受中 ......
1.16K   2  
·
2018/12/21
·
10 mins read


Cloudy Sky
unobtrusive? (snapwire)

UJS 是啥?

寫 Rails 的人,一定會常聽到 UJS,但是 UJS 到底是啥?我想很多人也不會去深度研究,反正,好像也感覺不到,直到有一天,被咬到了,才會「哇」,笑。

UJS 是 Unobtrusive JavaScript 英文縮寫,Unobtrusive 的英文就是“低調”的意思,所以,也很合乎大部分 Rails 的使用者感覺不到的原因,必竟,本來就是設計成,也希望是:不引人注目的,不張揚的。

Rails 5.1 以前,UJS 是外加的,但是 Rails 會自動幫你裝好,bundle 會有

gem 'jquery-rails'

在還就是 application.js 會有

//= require jquery

//= require jquery_ujs

Rails 5.1 版以後,UJS 就是內建了,而且是原生 JavaScript,不再是用 jQuery 的版本了,所以,以前很多人不喜歡它的,可以選擇不裝,現在,就如果不想用,就要「在不同的地方」,關掉它了,我是覺得 UJS 很好用啦,只是偶而會被咬到,了解它後,是好東西。

UJS 概觀 

UJS 沒有什麼魔術,他分成兩半:(1) 他就是 Rails 會在某些「特定行為」中,自動在 HTML 中加入 UJS 相關的 data-* attributes,然後,(2) UJS 的 jQuery script 就會「根據」這些 data-* 來執行,聽無?來來來,免驚,下面就是 Rails tutorial 中的簡明慨念介紹,一看就懂:

<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>

在上面這個例子中,一個連結加入了 data-background-color="#000099" 跟 data-text-color="#FFFFFF" 這兩個data-* ,然後,任何 JavaScript 都可以「綁定 (bind)」這樣的 data 屬性時,去執行想做的動作,在上面這個例子中,(1) 是 Rails 的 UJS 會自動寫好上面的 HTML,(2) UJS 會綁定 JavaScript 去執行設定文字與背景顏色,這就是 UJS 的原理啦,很簡單吧,下面是原文連結,給還是看不懂人了:Working with JavaScript in Rails — Ruby on Rails Guides:

UJS 綁定哪些「特定行為」?

不多,就四大類,但是都是很常用的功能,真的可以讓開發工作省很大 (ps: 用中文說,真是難啊):

  1. 跳出確認對話視窗:讓使用者按「確定」後才會執行的動作,像是“刪除”;
  2. 轉換「非 GET 」的動作:很繞口令,但很好懂,例如讓 GET url 去執行 DELETE;
  3. 讓表單或連結做一些非同步的 Ajax 動作:例如新增購物車,partial update DOM,檢查輸入等;
  4. 防「雙點擊」:自動將 form submit 設成 disable,防止被「雙點擊」;

真的很想用英文寫,中文真的不好翻譯網路行為,我盡力了,知道 UJS 大概做些什麼就好了,我們來詳細看看每一個怎麼用。

1. 確認對話視窗:讓使用者按「確定」後才會執行

這個最常用的應該就是「刪除」了,Rails 用 scaffold 產生時就會自動幫忙寫好:

link_to "Delete", pc_blog, method: :delete, data: { confirm: "Are you sure?" }, :class => 'btn btn-default'
<a data-confirm="Are you sure?" class="btn btn-default" rel="nofollow" data-method="delete" href="/blog/123">Delete</a>

第一行中的 data: { confirm: "Are you sure?" } 就是讓 UJS 產生確認對話框的寫法,真是很簡單,短短兩個字:data 跟 confirm,對話框就出來了,這省了好多 JavaScript,給 UJS 拍拍手!

2. 轉換「非 GET 」的動作

我們就還是用上面這個 Delete 的例子,各位看官會不會覺得很奇怪,這不是一個 link_to 嗎? Link_to 不就是一個 HTTP GET 動作嗎?怎麼會指到 controller action destroy?  沒錯,這又是 UJS 的傑作,有看到 method: :delete 解譯出 data-method="delete" 嗎?哈,這又是一個 data-*,沒錯,就是它,這個就是給 UJS 下動作轉換的指令,大致上來說,UJS 的轉換如下: 

  1. rails-ujs 會綁定所有有 data-method 的連結:
  2. 當使用者按下這個連接後,UJS 就停止了原來的動作,自己再做一個 form 把動作換成 method 中指定的動作,原來的 href 位置不變,然後 UJS 就 submit 它,而且很厲害的,也會把 CSRF tokens 一塊帶上,就這樣,很方便的,讓一個 link_to 可以做各種「HTTP Verb」,不只是 delete,你也可以用 post、patch,要不要再給 UJS 拍拍手!

還有一個 remote: true 可以加在 link_to 內,任何加了 remote: true 所呼叫的 controller 動作,都會從 html 呼叫改成 js 呼叫,也就是,controller 的回應就會使用 js,簡單說,就是會由以下這個回應:

respond_to do |format| 
  format.js
end

有趣的是,如果你沒有 respond_to format.js 的選項,它還是會用 respond_to html 選項走,但是這個 js 呼叫動作還繼續走下去,也就是說,下一個動作還是 remove: true,這個 bug 很不好抓,要小心。

有興趣看一下 UJS 怎麼做這個轉換動作嗎?他是用 coffee 寫的,我不喜歡 coffee... UJS method

3. 讓表單或連結做一些非同步的 Ajax 動作

Rails 提供了四個跟「Remote」有相關的 helper, 是的,你看到了關鍵字,remote,Rails 的 remote 相關 helper 都會產生一個 data-remote="true" 的 UJS 綁定 data-* ,也就是這個,讓我們在 Rails 中可以簡單的做 Ajax 網頁部分更新!

那四個呢?就這四個!

  1. form_with:設定就是 remote: true,不用再寫了
  2. link_to:如果要變成 Ajax 動作,就要加上 remote: true,不然就是單純的 link_to <a>
  3. button_to: 跟上面的 link_to 一樣。
  4. 其他 (哈哈哈哈哈)

這四個中,form_with 是個新玩意,Rails 5.1 以後才有,Rails 的想法是要用 form_with 取代以前的 form_for and form_tag,也就是說,Rails 希望以後所有的 form 都是 Ajax partial DOM 更新,這很有趣,為什麼?因為這意味著 Rails 未來希望更結合 UJS + Turbolinks,這兩個東西都是在 Rails 中有人愛,有人恨的東西,我想要取代的難度不低,不過,我是覺得就適應吧,時代在進步的,以前,網頁一頁一頁換,沒人嫌,現在,好多都是 single page app (SPA) 單頁式網站,說真的,SPA 的使用者體驗真的比較好,只是,開發維護都難多了。想要知道更多的 form_with?  這篇有說:

Rails 5.1's form_with vs. form_tag vs. form_for – Patrik on Rails — form_tag and form_for are soft deprecated and they will be replaced by form_with in the future. If you want to know more about form_with… Patrik on Rails

說到 SPA,Rails 的對應(戰鬥)法就是用 Server-generated JavaScript Responses (SJR) 反擊!DHH 的這篇大作,如果你還未讀,必讀啊!SJR benifits

如果您已經很熟悉 Rails 的 Ajax,恭喜,如果你還不熟,要說明它可是一個大工程,實在沒辦法在這篇寫清楚,不過它的工作方式大致如下:

  1. Client 端送了一個 Ajax 要求到 server ( = link_to <action> remote: true )
  2. Server 端產生一段新的 HTML,再把它包在 JavaScript 裡,送回 client 端 ( = format.js + <action>.js.erb 中的 escape_javascript(render @xxx) )
  3. clinet 端就執行這段 server 送回來的 JavaScript 碼,通常就是修改一些 DOM 的內容。( 執行 <action>.js.erb )

如果是新手,我這樣寫一定也有看沒懂,我在網上找到《捷姆斯》寫的這一篇還不錯:Rails with AJAX 範例 (使用 render js方法),寫得很詳盡,Rails 的美麗就是很棒互助的社群,幾乎各種問題都可以在網上找到解答。

等等,那第四個「其他」呢?哈!差點忘掉,哈,就是直接用 data-remote="true" + data-url + data params 來呼叫 Ajax 啦,所以是「其他」,例如下面這個例子就是用來做 checkbox Ajax 呼叫:

<input type="checkbox" data-remote="true" data-url="/update" data-params="id=10" data-method="put">

Form_with 這個新玩意,就是預設 data-remote="true",要關掉可以用 local: true,哇哈!又一個新玩意,以前的 form_for/form_tag 預設不是 data-remote="true",現在改成預設就是,要關掉還要另外寫,可見時代在轉變啊,真的不喜歡,可以在 config 中關掉:

# config/initializers/action_view.rb

Rails.application.config.action_view.form_with_generates_remote_forms = false

如果,你還需要根據 AJAX 活動狀況做不同的動作,UJS AJAX 有提供很不錯的事件 (events) 驅動,比較會用到的是: ajax:success/complete/error,詳細就請看文件吧,不過重點是,5.1 版前與後,也就是 jQuery 與原生 JavaScript 的兩個版本的 events 是不同的,要注意! 我把這兩個版本的官網連結列在下面了:

4.防止 form submit 被「雙點擊」

在 Rails 中做一個 form 要 submit ,真是再簡單不過了:

<%= f.submit class: "btn btn-default" %>
<input type="submit" name="commit" value="更新文章" class="btn btn-default" data-disable-with="更新文章">

就簡單的寫個第一行,Rails 就會產生第二行的 HTML 碼,這就是 Rails 美麗的地方,我想大部分的人都沒有真的去看產稱的 HTML 碼,但是,這次,注意一下,如果你沒看到,第二行後面 UJS 有幫你自動加了:data-disable-with="更新文章"  咦?這是什麼東東啊?

這個就是 UJS 很棒的「防雙點擊」保護,我想很多人都會在很多網路論壇上看到,同一則留言被發佈了兩次,這並不是使用者有意的,在實務上,雙點擊真的很容易產生,你如果要為每一個 submit 個別寫一個防雙點擊 JavaScript 真是很麻煩的,再說,你可以試試,要寫一個好的「防雙點擊」還不算容易的事呢。

好日子會一直到,某一天,你有一個 form 要用到 JavaScript 來驗證內容,而你又不幸的用了 onsubmit 這個最簡單的 form 驗證方法,咦?為什麼我的 form 不能 submit 了?哈,被咬到了,原來你要加入 data: { disable_with: false },但是為什麼?說明如下:

form_for(@blog, html: { multipart: true, :onsubmit => "return Valid(this);" }) do |f| %>
<%= f.submit class: "btn btn-default", data: { disable_with: false } %>
<input type="submit" name="commit" value="更新文章" class="btn btn-default">
  • 第一行::onsubmit => "return Valid(this);" 簡單的指到那一個 JS function 去做form 驗證,傳回 true 就是沒錯,繼續 submit 動作,傳回 false 就是有錯,submit 停止,留在這個 form 上。
  • 第二行:就是這,你要加入 data: { disable_with: false } ,這就是跟 UJS 說,不要給我產生「防雙點擊」保護,不加入這個,使用者只要按一次 submit,submit 就 disabled 了,也就是說,如果你的 Valid()傳會 false 說輸入有誤,使用者也不能再按 submit 了,別怕,加入 data: { disable_with: false } ,就不會鎖「防雙點擊」了。
  • 第三行:你也可以看到產生的 HTML 碼, data-disable-with="更新文章" 沒了,還記得“UJS 概觀”中提到的,UJS 就是用 data-* 來運作的嗎?

你還可以用 UJS 來改變按下去後,Submit 按鈕的文字: 

<%= f.submit class: "btn btn-default", data: { disable_with: "更新文章中..." } %>

這樣,使用者按下去後,就會顯示:"更新文章中...",酷吧。

UJS 真的不錯用,當然偶而會被咬到,這也是我為什麼寫這篇文章的原因,因為我被咬很多次了啦⋯⋯ 所以這篇算是送禮自用兩相宜~

附上jQuery UJS官方網址:

rails/jquery-ujs — Ruby on Rails unobtrusive scripting adapter for jQuery - rails/jquery-ujs
GitHub

 


WriterShelf™ is a unique multiple pen name blogging and forum platform. Protect relationships and your privacy. Take your writing in new directions. ** Join WriterShelf**
WriterShelf™ is an open writing platform. The views, information and opinions in this article are those of the author.


Article info

This article is part of:
分類於:
標籤:
日期:
創作於:2018/12/21,最後更新於:2020/03/13。
合計:2710字


Share this article:
About the Author

很久以前就是個「寫程式的」,其實,什麼程式都不熟⋯⋯
就,這會一點點,那會一點點⋯⋯




Comments
六小編
1
Join to follow...
Follow/Unfollow Writer: 六小編
By following, you’ll receive notifications when this author publishes new articles.
Don't wait! Sign up to follow this writer.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.
六小編就是六小編,六小編不是七小編。
2019/09/29

謝謝你的這篇文章,想請問一下,其它框架有哪些類似 UJS 的套件嗎?或是純前端但是也具備類似 UJS 的框架?Vue.js 也能作到資料綁定,但是我不太確定它是否有本文提到的這些功能?

紅寶鐵軌客
2
Join to follow...
Follow/Unfollow Writer: 紅寶鐵軌客
By following, you’ll receive notifications when this author publishes new articles.
Don't wait! Sign up to follow this writer.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.
寫程式中、折磨中、享受中 ......
2019/09/29

其他的 web framework 我真的不熟,很抱歉不能回答你的問題。

我個人認為 UJS 的發明,主要就是因為 Rails 本身是一個網站的後台開發平台,為了要讓 Rails 所開發的網頁,盡量不要寫 JavaScript 而誕生的,畢竟很多後台工程師對前端不熟,我就算一個啦,不熟前端又碰到 UJS,說實話,我剛開始真是很朦朧的。

我對前端的了解還停留在 JQuery 跟 JavaScript,前端的世界變得好快,學得好累啊。

Join the discussion now!
Don't wait! Sign up to join the discussion.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.