Ajax介绍

为了理解Ajax, 你首先必须理解web浏览器是如何工作的

当你在你的浏览器地址中输入http://localhost:3000并按下enter时,浏览器作为客户端会给服务器端发一个请求,然后浏览器会解析response,搜集所有相关的assets,例如javascript文件、表格和图片。然后它会组合界面。当你点击一个link时,浏览器会重复上面的流程:获取page,获取assets,然后组合在一起,给你展示结果。这称为“request response cycle”。

 javascript也可以给server发送请求,然后解析response。javascirpt还可以更新page上的内容。整合这两个功能,我们可以使用javascript来更新web page的部分内容,而不需要从server获取全部的内容,我们称这个技术为ajax。

下面是一个是我们使用javascript来发送ajax请求的例子:

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

这个代码从"/test"获取数据,然后将结果results添加到一个id为results的element之后。

Rails提供了很多内容的功能去构建web pages。通常我们不需要自己这样写代码。本文将介绍Rails如何帮助我们写这样的website。

javascript脚本

Rails使用"Unobtrusive JavaScript"技术去给dom添加javascript。如:

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

我们可以看到,这有点类似于inline javascript

当我们点击,这个背景将变为红色。当我们希望click触发很多类似操作时,我们则需要将onclick的handler的函数定义真正的转化为一个函数,如下:

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>

则我们可以使用events来进行优化。我们可以给我们的link增加‘data-*’属性,然后给每一个拥有这个属性的link绑定click event的handler。如下:

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>

我们管它叫'unobtrusive' JavaScript是因为我们从此不再需要将javascript和html混合在一起。这样做的好处是:我们很好的维护我们的脚本。我们可以很方面的给link添加data的属性并添加相应的行为。

内置的helpers

远端的元素

Rails提供了一系列的view helper方法帮我们生成html。因此,'unobtrusive' JavaScript分为:javascirpt half 和ruby half。

我们主要讲解rails javascript half。

form_with

form_with是帮助我们生成forms的helper。为了使用ajax,我们需要设置local=false。如下:

<%= 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"时,这个form会异步提交。我们可以绑定ajax:success/ajax:error给这个event。如下:

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是帮助我们生成links的helper。其也有:remote选项,我们可以如下使用:link_to

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

生成为:

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

我们可以绑定向form_with一样的异步操作。我们假设我们有一长串的文件,通过一个link就可以全部删除。我们可以生成如下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.");
    });
  });
});