v7.0.0
更多資訊請前往 rubyonrails.org: 更多在 Ruby on Rails

在 Rails 中使用 JavaScript

本指南包含了 Rails 的內建 Ajax/JavaScript 功能(和 更多的);它將使您能夠建立豐富的動態 Ajax 應用程式 舒適!

閱讀本指南後,您將瞭解:

1 Ajax 簡介

為了瞭解 Ajax,您必須首先了解 Web 瀏覽器的作用 一般。

當您在瀏覽器的位址列中鍵入 http://localhost:3000 並點選 “Go”,瀏覽器(您的“客戶端”)向伺服器發出請求。它解析 回應,然後獲取所有關聯的資產,例如 JavaScript 檔案, 樣式表和影象。然後它組裝頁面。如果你點選一個連結,它 執行相同的過程:獲取頁面,獲取資產,將它們放在一起, 向你展示結果。這稱為“請求回應週期”。

JavaScript 還可以向伺服器發出請求,並解析回應。它 還可以更新頁面上的資訊。結合這兩個 權力,JavaScript 編寫者可以製作一個網頁,可以只更新部分 本身,而無需從伺服器獲取整頁資料。這是一個 我們稱之為 Ajax 的強大技術。

例如,下面是一些發出 Ajax 請求的 JavaScript 程式碼:

fetch("/test")
  .then((data) => data.text())
  .then((html) => {
    const results = document.querySelector("#results");
    results.insertAdjacentHTML("beforeend", html);
  });

此程式碼從“/test”獲取資料,然後將結果附加到元素 id 為 results

Rails 為使用此構建網頁提供了相當多的內建支援 技術。您很少需要自己編寫此程式碼。本指南的其餘部分 將向您展示 Rails 如何幫助您以這種方式編寫網站,但它是 所有這些都建立在這個相當簡單的技術之上。

2 不顯眼的 JavaScript

Rails 使用一種稱為“Unobtrusive JavaScript”的技術來處理附加 JavaScript 到 DOM。這通常被認為是最佳實踐 在前端社群中,但您可能偶爾會閱讀教程 演示其他方式。

這是編寫 JavaScript 的最簡單方法。你可能會看到它被稱為 '內聯 JavaScript':

<a href="#" onclick="this.style.backgroundColor='#990000';event.preventDefault();">Paint it red</a>

單擊時,連結背景將變為紅色。問題來了:什麼 當我們有很多想要在點選時執行的 JavaScript 時會發生什麼?

<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';event.preventDefault();">Paint it green</a>

很彆扭吧?我們可以從點選處理程式中提取函式定義, 並將其變成一個函式:

window.paintIt = function(event, backgroundColor, textColor) {
  event.preventDefault();
  event.target.style.backgroundColor = backgroundColor;
  if (textColor) {
    event.target.style.color = textColor;
  }
}

然後在我們的頁面上:

<a href="#" onclick="paintIt(event, '#990000')">Paint it red</a>

那好一點,但是具有相同的多個連結呢? 影響?

<a href="#" onclick="paintIt(event, '#990000')">Paint it red</a>
<a href="#" onclick="paintIt(event, '#009900', '#FFFFFF')">Paint it green</a>
<a href="#" onclick="paintIt(event, '#000099', '#FFFFFF')">Paint it blue</a>

不是很乾,嗯?我們可以通過使用事件來解決這個問題。我們將新增一個 data-* 屬性給我們的連結,然後繫結一個處理程式到每個連結的點選事件 具有該屬性:

function paintIt(element, backgroundColor, textColor) {
  element.style.backgroundColor = backgroundColor;
  if (textColor) {
    element.style.color = textColor;
  }
}

window.addEventListener("load", () => {
  const links = document.querySelectorAll(
    "a[data-background-color]"
  );
  links.forEach((element) => {
    element.addEventListener("click", (event) => {
      event.preventDefault();

      const {backgroundColor, textColor} = element.dataset;
      paintIt(element, 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>

我們稱其為“不引人注目”的 JavaScript,因為我們不再混合我們的 JavaScript 到我們的 HTML 中。我們已經正確地分離我們的關注點,讓未來 改變容易。我們可以通過新增資料輕鬆地向任何連結新增行為 屬性。我們可以通過最小化程式執行我們所有的 JavaScript 並且 聯結器。我們可以在每個頁面上提供整個 JavaScript 包,這 意味著它將在第一個頁面載入時下載,然後快取在 之後的每一頁。許多小好處真的加起來了。

3 內建Helpers

3.1 遠端元素

Rails 提供了一堆用 Ruby 編寫的 view helper 方法來幫助你 在產生 HTML 時。有時,您想為這些元素新增一點 Ajax, 在這些情況下,Rails 得到了您的支援。

由於 Unobtrusive JavaScript,Rails "Ajax helpers" 實際上分為兩個 部分:JavaScript 部分和 Ruby 部分。

除非你禁用了 Asset Pipeline, rails-ujs 提供了 JavaScript 的一半,正常的 Ruby view helpers 新增適當的 標籤到你的 DOM。

您可以在下面閱讀有關觸發處理的不同事件的資訊 應用程式中的遠端元素。

3.1.1 form_with

form_with 是幫助書寫表單的 helper。要在表單中使用 Ajax,您可以 將 :local 選項傳遞給 form_with

<%= form_with(model: @article, id: "new-article", local: false) do |form| %>
  ...
<% end %>

這將產生以下 HTML:

<form id="new-article" action="/articles" accept-charset="UTF-8" method="post" data-remote="true">
  ...
</form>

注意 data-remote="true"。現在,表單將由 Ajax 提交而不是 而不是通過瀏覽器的正常提交機制。

不過,您可能不想坐在那裡填滿 <form>。 您可能想在成功提交後做一些事情。要做到這一點, 繫結到 ajax:success 事件。失敗時,使用 ajax:error。看看這個:

window.addEventListener("load", () => {
  const element = document.querySelector("#new-article");
  element.addEventListener("ajax:success", (event) => {
    const [_data, _status, xhr] = event.detail;
    element.insertAdjacentHTML("beforeend", xhr.responseText);
  });
  element.addEventListener("ajax:error", () => {
    element.insertAdjacentHTML("beforeend", "<p>ERROR</p>");
  });
});

顯然,你會想要比這更復雜一點,但這是一個 開始。

link_to 是幫助產生連結的 helper。它有一個 :remote 選項,你 可以這樣使用:

<%= link_to "an article", @article, remote: true %>

產生

<a href="/articles/1" data-remote="true">an article</a>

您可以繫結到與 form_with 相同的 Ajax 事件。這是一個例子。讓我們 假設我們有一個可以刪除的文章列表 點選。我們會像這樣產生一些 HTML:

<%= link_to "Delete article", @article, remote: true, method: :delete %>

並像這樣編寫一些 JavaScript:

window.addEventListener("load", () => {
  const links = document.querySelectorAll("a[data-remote]");
  links.forEach((element) => {
    element.addEventListener("ajax:success", () => {
      alert("The article was deleted.");
    });
  });
});
3.1.3 button_to

button_to 是幫助您建立按鈕的 helper。它有一個 :remote 選項,你可以這樣呼叫:

<%= button_to "An article", @article, remote: true %>

這會產生

<form action="/articles/1" class="button_to" data-remote="true" method="post">
  <input type="submit" value="An article" />
</form>

因為它只是一個 <form>,所以 form_with 上的所有資訊也適用。

3.2 自定義遠端元素

可以使用 data-remote 自定義元素的行為 屬性而無需編寫一行 JavaScript。您可以指定額外的 data- 屬性來實現這一點。

3.2.1 data-method

啟用超連結總是會導致 HTTP GET 請求。但是,如果您的 應用程式是 RESTful, 一些連結實際上是 actions 更改伺服器上的資料,並且必須 使用非 GET 請求執行。此屬性允許標記此類連結 使用顯式方法,例如“釋出”、“放置”或“刪除”。

它的工作方式是,當連結被啟用時,它會構造一個隱藏的表單 在具有“action”屬性對應的“href”value的文件中 連結,以及對應於 data-method value 的方法,並提交該表單。

因為使用 GET 和 POST 以外的 HTTP 方法提交表單不是 跨瀏覽器廣泛支援,所有其他 HTTP 方法實際上都是通過 使用 _method 引數中指示的預期方法進行 POST。 Rails 自動檢測並對此進行補償。

3.2.2 data-urldata-params

您頁面的某些元素實際上並未引用任何 URL,但您可能希望 它們觸發 Ajax 呼叫。指定 data-url 屬性以及 data-remote 將觸發對給定 URL 的 Ajax 呼叫。你也可以 通過 data-params 屬性指定額外的引數。

這對於在複選框上觸發 action 很有用,例如:

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

也可以在執行時顯式定義 Ajax dataType 通過 data-type 屬性請求 data-remote 元素。

3.3 確認

您可以通過新增 data-confirm 來要求使用者額外確認 連結和表單上的屬性。使用者將看到一個 JavaScript confirm() 包含屬性文字的對話方塊。如果使用者選擇取消,則 action 不會發生。

在連結上新增此屬性將在單擊時觸發對話方塊,並新增它 在表單上將在提交時觸發它。例如:

<%= link_to "Dangerous zone", dangerous_zone_path,
  data: { confirm: 'Are you sure?' } %>

這會產生:

<a href="..." data-confirm="Are you sure?">Dangerous zone</a>

表單提交按鈕上也允許使用該屬性。這允許您自定義 警告訊息取決於被啟用的按鈕。在這種情況下, 你應該在表單本身上有 data-confirm

3.4 自動禁用

還可以在提交表單時自動禁用輸入 通過使用 data-disable-with 屬性。這是為了防止意外 來自使用者的雙擊,這可能會導致重複的 HTTP 請求 後端可能不會檢測到這樣。屬性的 value 是將要顯示的文字 成為處於禁用狀態的按鈕的新 value。

這也適用於具有 data-method 屬性的連結。

例如:

<%= form_with(model: Article.new) do |form| %>
  <%= form.submit data: { disable_with: "Saving..." } %>
<% end %>

這將產生一個表單:

<input data-disable-with="Saving..." type="submit">

3.5 Rails-ujs 事件處理程式

Rails 5.1 引入了 rails-ujs 並刪除了 jQuery 作為依賴項。 因此,Unobtrusive JavaScript (UJS) 驅動程式已被重寫以在沒有 jQuery 的情況下執行。 這些引入會導致請求期間觸發的 custom events 發生小的變化:

對 UJS 事件處理程式的呼叫簽名已更改。 與使用 jQuery 的版本不同,所有自定義事件僅返回一個引數:event。 在此引數中,有一個附加屬性 detail,其中包含一組額外引數。 有關之前在 Rails 5 及更早版本中使用的 jquery-ujs 的資訊,請閱讀 jquery-ujs wiki

活動名稱 額外引數 (event.detail) 解僱
ajax:before 在整個ajax業務之前。
ajax:beforeSend [xhr,選項] 在傳送請求之前。
ajax:send [xhr] 傳送請求時。
ajax:stopped 當請求停止時。
ajax:success [回應,狀態,xhr] 完成後,如果回應成功。
ajax:error [回應,狀態,xhr] 完成後,如果回應是錯誤的。
ajax:complete [xhr,狀態] 請求完成後,無論結果如何。

用法示例:

document.body.addEventListener("ajax:success", (event) => {
  const [data, status, xhr] = event.detail;
});

3.6 可停止事件

您可以通過執行 event.preventDefault() 來停止執行 Ajax 請求 來自處理程式方法 ajax:beforeajax:beforeSendajax:before 事件可以在序列化和序列化之前操作表單資料 ajax:beforeSend 事件對於新增自定義請求標頭很有用。

如果停止 ajax:aborted:file 事件,則預設行為允許 瀏覽器通過正常方式提交表單(即非 Ajax 提交)將是 取消,並且根本不會提交表格。這對 實現您自己的 Ajax 檔案上傳解決方法。

請注意,您應該使用 return false 來防止 jquery-ujsevent.preventDefault()rails-ujs

4 伺服器端問題

Ajax 不僅僅是客戶端,您還需要在伺服器上做一些工作 一邊支援它。通常,人們喜歡他們的 Ajax 請求返回 JSON 而不是 HTML。讓我們討論實現這一目標需要什麼。

4.1 一個簡單的例子

假設您有一系列使用者想要顯示並提供一個 在同一頁面上建立一個新使用者。您的索引 action controller 看起來像這樣:

class UsersController < ApplicationController
  def index
    @users = User.all
    @user = User.new
  end
  # ...

索引 view (app/views/users/index.html.erb) 包含:

<b>Users</b>

<ul id="users">
<%= render @users %>
</ul>

<br>

<%= form_with model: @user do |form| %>
  <%= form.label :name %><br>
  <%= form.text_field :name %>
  <%= form.submit %>
<% end %>

app/views/users/_user.html.erb 部分包含以下內容:

<li><%= user.name %></li>

索引頁的頂部顯示使用者。底部 提供一個表單來建立一個新使用者。

底部表單將呼叫 UsersController 上的 create action。因為 表單的遠端選項設定為 true,請求將被髮布到 UsersController 作為 Ajax 請求,尋找 JavaScript。為了 為該請求提供服務,您的 controller 的 create action 看起來像 這:

  # 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

注意 respond_to 塊中的 format.js:它允許 controller 回應您的 Ajax 請求。然後你就有了相應的 app/views/users/create.js.erb view 產生實際 JavaScript 的檔案 將在客戶端傳送和執行的程式碼。

var users = document.querySelector("#users");
users.insertAdjacentHTML("beforeend", "<%= j render(@user) %>");

JavaScript view 渲染不做任何預處理,所以你不應該在這裡使用 ES6 語法。

5 渦輪連結

Rails 隨附 Turbolinks 庫, 它使用 Ajax 來加速大多數應用程式中的頁面渲染。

Turbolinks 將點選處理程式附加到頁面上的所有 <a> 標籤。如果你的瀏覽器 支援 PushState, Turbolinks 會向頁面發出 Ajax 請求,解析回應,然後 用回應的 <body> 替換頁面的整個 <body>。它 然後將使用 PushState 將 URL 更改為正確的 URL,保留 重新整理語義併為您提供漂亮的 URL。

如果要禁用某些連結的 Turbolinks,請新增 data-turbolinks="false" 標籤的屬性:

<a href="..." data-turbolinks="false">No turbolinks here</a>.

5.2 頁面更改事件

你經常想對它進行某種處理 頁面載入。使用 DOM,您可以編寫如下內容:

window.addEventListener("load", () => {
  alert("page has loaded!");
});

但是,由於 Turbolinks 覆蓋了正常的頁面載入過程, 這個依賴的事件不會被觸發。如果你的程式碼看起來像 為此,您必須更改程式碼才能執行此操作:

document.addEventListener("turbolinks:load", () => {
  alert("page has loaded!");
});

有關更多詳細資訊,包括您可以繫結的其他事件,請檢視 the 渦輪連結 自述檔案

6 Ajax 中的跨站請求偽造 (CSRF) token

使用其他庫進行ajax呼叫時,需要新增 安全性 token 作為庫中 Ajax 呼叫的預設標頭。要得到 token:

const token = document.getElementsByName(
  "csrf-token"
)[0].content;

然後,您可以將此 token 作為 X-CSRF-Token 標題提交給您的 Ajax 請求。您不需要為 GET 請求新增 CSRF token, 只有非 GET 的。

您可以在 安全指南 中閱讀有關跨站點請求偽造的更多資訊。

7 其他資源

以下是一些有用的連結,可幫助您瞭解更多資訊:

回饋

我們鼓勵您幫助提高本指南的品質。

如果您發現任何拼寫錯誤或資訊錯誤,請提供回饋。 要開始回饋,您可以閱讀我們的 回饋 部分。

您還可能會發現不完整的內容或不是最新的內容。 請務必為 main 新增任何遺漏的文件。假設是 非穩定版指南(edge guides) 請先驗證問題是否已經在主分支上解決。 請前往 Ruby on Rails 指南寫作準則 查看寫作風格和慣例。

如果由於某種原因您發現要修復的內容但無法自行修補,請您 提出 issue

關於 Ruby on Rails 的任何類型的討論歡迎提供任何文件至 rubyonrails-docs 討論區