mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Rewrite JavaScript guide to use ES6
Co-Authored-By: guledali <guled.ali01@gmail.com> Closes #37529
This commit is contained in:
parent
145299315a
commit
1e7f867a7a
1 changed files with 99 additions and 86 deletions
|
@ -37,16 +37,15 @@ powers, a JavaScript writer can make a web page that can update just parts of
|
||||||
itself, without needing to get the full page data from the server. This is a
|
itself, without needing to get the full page data from the server. This is a
|
||||||
powerful technique that we call Ajax.
|
powerful technique that we call Ajax.
|
||||||
|
|
||||||
Rails ships with CoffeeScript by default, and so the rest of the examples
|
As an example, here's some JavaScript code that makes an Ajax request:
|
||||||
in this guide will be in CoffeeScript. All of these lessons, of course, apply
|
|
||||||
to vanilla JavaScript as well.
|
|
||||||
|
|
||||||
As an example, here's some CoffeeScript code that makes an Ajax request using
|
```js
|
||||||
the jQuery library:
|
fetch("/test")
|
||||||
|
.then((data) => data.text())
|
||||||
```coffeescript
|
.then((html) => {
|
||||||
$.ajax(url: "/test").done (html) ->
|
const results = document.querySelector("#results");
|
||||||
$("#results").append html
|
results.insertAdjacentHTML("beforeend", data);
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
This code fetches data from "/test", and then appends the result to the `div`
|
This code fetches data from "/test", and then appends the result to the `div`
|
||||||
|
@ -69,57 +68,68 @@ Here's the simplest way to write JavaScript. You may see it referred to as
|
||||||
'inline JavaScript':
|
'inline JavaScript':
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a>
|
<a href="#" onclick="this.style.backgroundColor='#990000';event.preventDefault();">Paint it red</a>
|
||||||
```
|
```
|
||||||
When clicked, the link background will become red. Here's the problem: what
|
When clicked, the link background will become red. Here's the problem: what
|
||||||
happens when we have lots of JavaScript we want to execute on a click?
|
happens when we have lots of JavaScript we want to execute on a click?
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a>
|
<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';event.preventDefault();">Paint it green</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
Awkward, right? We could pull the function definition out of the click handler,
|
Awkward, right? We could pull the function definition out of the click handler,
|
||||||
and turn it into CoffeeScript:
|
and turn it a function:
|
||||||
|
|
||||||
```coffeescript
|
```js
|
||||||
@paintIt = (element, backgroundColor, textColor) ->
|
window.paintIt = function(event, backgroundColor, textColor) {
|
||||||
element.style.backgroundColor = backgroundColor
|
event.preventDefault();
|
||||||
if textColor?
|
event.target.style.backgroundColor = backgroundColor;
|
||||||
element.style.color = textColor
|
if (textColor) {
|
||||||
|
event.target.style.color = textColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And then on our page:
|
And then on our page:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
|
<a href="#" onclick="paintIt(event, '#990000')">Paint it red</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
That's a little bit better, but what about multiple links that have the same
|
That's a little bit better, but what about multiple links that have the same
|
||||||
effect?
|
effect?
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a>
|
<a href="#" onclick="paintIt(event, '#990000')">Paint it red</a>
|
||||||
<a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a>
|
<a href="#" onclick="paintIt(event, '#009900', '#FFFFFF')">Paint it green</a>
|
||||||
<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a>
|
<a href="#" onclick="paintIt(event, '#000099', '#FFFFFF')">Paint it blue</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
Not very DRY, eh? We can fix this by using events instead. We'll add a `data-*`
|
Not very DRY, eh? We can fix this by using events instead. We'll add a `data-*`
|
||||||
attribute to our link, and then bind a handler to the click event of every link
|
attribute to our link, and then bind a handler to the click event of every link
|
||||||
that has that attribute:
|
that has that attribute:
|
||||||
|
|
||||||
```coffeescript
|
```js
|
||||||
@paintIt = (element, backgroundColor, textColor) ->
|
function paintIt(element, backgroundColor, textColor) {
|
||||||
element.style.backgroundColor = backgroundColor
|
element.style.backgroundColor = backgroundColor;
|
||||||
if textColor?
|
if (textColor) {
|
||||||
element.style.color = textColor
|
element.style.color = textColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$ ->
|
window.addEventListener("load", () => {
|
||||||
$("a[data-background-color]").click (e) ->
|
const links = document.querySelectorAll(
|
||||||
e.preventDefault()
|
"a[data-background-color]"
|
||||||
|
);
|
||||||
|
links.forEach((element) => {
|
||||||
|
element.addEventListener("click", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
backgroundColor = $(this).data("background-color")
|
const {backgroundColor, textColor} = element.dataset;
|
||||||
textColor = $(this).data("text-color")
|
paintIt(element, backgroundColor, textColor);
|
||||||
paintIt(this, backgroundColor, textColor)
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
```
|
```
|
||||||
```html
|
```html
|
||||||
<a href="#" data-background-color="#990000">Paint it red</a>
|
<a href="#" data-background-color="#990000">Paint it red</a>
|
||||||
|
@ -135,10 +145,6 @@ concatenator. We can serve our entire JavaScript bundle on every page, which
|
||||||
means that it'll get downloaded on the first page load and then be cached on
|
means that it'll get downloaded on the first page load and then be cached on
|
||||||
every page after that. Lots of little benefits really add up.
|
every page after that. Lots of little benefits really add up.
|
||||||
|
|
||||||
The Rails team strongly encourages you to write your CoffeeScript (and
|
|
||||||
JavaScript) in this style, and you can expect that many libraries will also
|
|
||||||
follow this pattern.
|
|
||||||
|
|
||||||
Built-in Helpers
|
Built-in Helpers
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
@ -164,10 +170,10 @@ remote elements inside your application.
|
||||||
[`form_with`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with)
|
[`form_with`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with)
|
||||||
is a helper that assists with writing forms. By default, `form_with` assumes that
|
is a helper that assists with writing forms. By default, `form_with` assumes that
|
||||||
your form will be using Ajax. You can opt out of this behavior by
|
your form will be using Ajax. You can opt out of this behavior by
|
||||||
passing the `:local` option `form_with`.
|
passing the `:local` option to `form_with`.
|
||||||
|
|
||||||
```erb
|
```erb
|
||||||
<%= form_with model: @article do |form| %>
|
<%= form_with(model: @article, id: "new-article") do |form| %>
|
||||||
...
|
...
|
||||||
<% end %>
|
<% end %>
|
||||||
```
|
```
|
||||||
|
@ -175,7 +181,7 @@ passing the `:local` option `form_with`.
|
||||||
This will generate the following HTML:
|
This will generate the following HTML:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<form action="/articles" accept-charset="UTF-8" method="post" data-remote="true">
|
<form id="new-article" action="/articles" accept-charset="UTF-8" method="post" data-remote="true">
|
||||||
...
|
...
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
@ -187,22 +193,22 @@ You probably don't want to just sit there with a filled out `<form>`, though.
|
||||||
You probably want to do something upon a successful submission. To do that,
|
You probably want to do something upon a successful submission. To do that,
|
||||||
bind to the `ajax:success` event. On failure, use `ajax:error`. Check it out:
|
bind to the `ajax:success` event. On failure, use `ajax:error`. Check it out:
|
||||||
|
|
||||||
```coffeescript
|
```js
|
||||||
$(document).ready ->
|
window.addEventListener("load", () => {
|
||||||
$("#new_article").on("ajax:success", (event) ->
|
const element = document.querySelector("#new-article");
|
||||||
[data, status, xhr] = event.detail
|
element.addEventListener("ajax:success", (event) => {
|
||||||
$("#new_article").append xhr.responseText
|
const [_data, _status, xhr] = event.detail;
|
||||||
).on "ajax:error", (event) ->
|
element.insertAdjacentHTML("beforeend", xhr.responseText);
|
||||||
$("#new_article").append "<p>ERROR</p>"
|
});
|
||||||
|
element.addEventListener("ajax:error", () => {
|
||||||
|
element.insertAdjacentHTML("beforeend", "<p>ERROR</p>");
|
||||||
|
});
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Obviously, you'll want to be a bit more sophisticated than that, but it's a
|
Obviously, you'll want to be a bit more sophisticated than that, but it's a
|
||||||
start.
|
start.
|
||||||
|
|
||||||
NOTE: As of Rails 5.1 and the new `rails-ujs`, the parameters `data, status, xhr`
|
|
||||||
have been bundled into `event.detail`. For information about the previously used
|
|
||||||
`jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
|
|
||||||
|
|
||||||
#### link_to
|
#### link_to
|
||||||
|
|
||||||
[`link_to`](https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to)
|
[`link_to`](https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to)
|
||||||
|
@ -227,12 +233,17 @@ click. We would generate some HTML like this:
|
||||||
<%= link_to "Delete article", @article, remote: true, method: :delete %>
|
<%= link_to "Delete article", @article, remote: true, method: :delete %>
|
||||||
```
|
```
|
||||||
|
|
||||||
and write some CoffeeScript like this:
|
and write some JavaScript like this:
|
||||||
|
|
||||||
```coffeescript
|
```js
|
||||||
$ ->
|
window.addEventListener("load", () => {
|
||||||
$("a[data-remote]").on "ajax:success", (event) ->
|
const links = document.querySelectorAll("a[data-remote]");
|
||||||
alert "The article was deleted."
|
links.forEach((element) => {
|
||||||
|
element.addEventListener("ajax:success", () => {
|
||||||
|
alert("The article was deleted.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
#### button_to
|
#### button_to
|
||||||
|
@ -298,7 +309,7 @@ requests for `data-remote` elements, by way of the `data-type` attribute.
|
||||||
### Confirmations
|
### Confirmations
|
||||||
|
|
||||||
You can ask for an extra confirmation of the user by adding a `data-confirm`
|
You can ask for an extra confirmation of the user by adding a `data-confirm`
|
||||||
attribute on links and forms. The user will be presented a JavaScript `confirm()`
|
attribute on links and forms. The user will be presented with a JavaScript `confirm()`
|
||||||
dialog containing the attribute's text. If the user chooses to cancel, the action
|
dialog containing the attribute's text. If the user chooses to cancel, the action
|
||||||
doesn't take place.
|
doesn't take place.
|
||||||
|
|
||||||
|
@ -323,7 +334,7 @@ you should **not** have `data-confirm` on the form itself.
|
||||||
The default confirmation uses a JavaScript confirm dialog, but you can customize
|
The default confirmation uses a JavaScript confirm dialog, but you can customize
|
||||||
this by listening to the `confirm` event, which is fired just before the confirmation
|
this by listening to the `confirm` event, which is fired just before the confirmation
|
||||||
window appears to the user. To cancel this default confirmation, have the confirm
|
window appears to the user. To cancel this default confirmation, have the confirm
|
||||||
handler to return `false`.
|
handler return `false`.
|
||||||
|
|
||||||
### Automatic disabling
|
### Automatic disabling
|
||||||
|
|
||||||
|
@ -338,9 +349,9 @@ This also works for links with `data-method` attribute.
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```erb
|
```erb
|
||||||
<%= form_with model: @article.new do |form| %>
|
<%= form_with(model: Article.new) do |form| %>
|
||||||
<%= form.submit data: { disable_with: "Saving..." } %>
|
<%= form.submit data: { disable_with: "Saving..." } %>
|
||||||
<%= end %>
|
<% end %>
|
||||||
```
|
```
|
||||||
|
|
||||||
This generates a form with:
|
This generates a form with:
|
||||||
|
@ -358,6 +369,7 @@ These introductions cause small changes to `custom events` fired during the requ
|
||||||
NOTE: Signature of calls to UJS's event handlers has changed.
|
NOTE: Signature of calls to UJS's event handlers has changed.
|
||||||
Unlike the version with jQuery, all custom events return only one parameter: `event`.
|
Unlike the version with jQuery, all custom events return only one parameter: `event`.
|
||||||
In this parameter, there is an additional attribute `detail` which contains an array of extra parameters.
|
In this parameter, there is an additional attribute `detail` which contains an array of extra parameters.
|
||||||
|
For information about the previously used `jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
|
||||||
|
|
||||||
| Event name | Extra parameters (event.detail) | Fired |
|
| Event name | Extra parameters (event.detail) | Fired |
|
||||||
|---------------------|---------------------------------|-------------------------------------------------------------|
|
|---------------------|---------------------------------|-------------------------------------------------------------|
|
||||||
|
@ -371,18 +383,14 @@ In this parameter, there is an additional attribute `detail` which contains an a
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
```html
|
```js
|
||||||
document.body.addEventListener('ajax:success', function(event) {
|
document.body.addEventListener("ajax:success", (event) => {
|
||||||
var detail = event.detail;
|
const [data, status, xhr] = event.detail;
|
||||||
var data = detail[0], status = detail[1], xhr = detail[2];
|
});
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: As of Rails 5.1 and the new `rails-ujs`, the parameters `data, status, xhr`
|
|
||||||
have been bundled into `event.detail`. For information about the previously used
|
|
||||||
`jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
|
|
||||||
|
|
||||||
### Stoppable events
|
### Stoppable events
|
||||||
|
|
||||||
You can stop execution of the Ajax request by running `event.preventDefault()`
|
You can stop execution of the Ajax request by running `event.preventDefault()`
|
||||||
from the handlers methods `ajax:before` or `ajax:beforeSend`.
|
from the handlers methods `ajax:before` or `ajax:beforeSend`.
|
||||||
The `ajax:before` event can manipulate form data before serialization and the
|
The `ajax:before` event can manipulate form data before serialization and the
|
||||||
|
@ -393,8 +401,8 @@ browser to submit the form via normal means (i.e. non-Ajax submission) will be
|
||||||
canceled and the form will not be submitted at all. This is useful for
|
canceled and the form will not be submitted at all. This is useful for
|
||||||
implementing your own Ajax file upload workaround.
|
implementing your own Ajax file upload workaround.
|
||||||
|
|
||||||
Note, you should use `return false` to prevent event for `jquery-ujs` and
|
Note, you should use `return false` to prevent an event for `jquery-ujs` and
|
||||||
`e.preventDefault()` for `rails-ujs`
|
`event.preventDefault()` for `rails-ujs`.
|
||||||
|
|
||||||
Server-Side Concerns
|
Server-Side Concerns
|
||||||
--------------------
|
--------------------
|
||||||
|
@ -475,10 +483,13 @@ respond to your Ajax request. You then have a corresponding
|
||||||
`app/views/users/create.js.erb` view file that generates the actual JavaScript
|
`app/views/users/create.js.erb` view file that generates the actual JavaScript
|
||||||
code that will be sent and executed on the client side.
|
code that will be sent and executed on the client side.
|
||||||
|
|
||||||
```erb
|
```js
|
||||||
$("<%= escape_javascript(render @user) %>").appendTo("#users");
|
var users = document.querySelector("#users");
|
||||||
|
users.insertAdjacentHTML("beforeend", "<%= j render(@user) %>");
|
||||||
```
|
```
|
||||||
|
|
||||||
|
NOTE: JavaScript view rendering doesn't do any preprocessing, so you shouldn't use ES6 syntax here.
|
||||||
|
|
||||||
Turbolinks
|
Turbolinks
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -504,21 +515,23 @@ attribute to the tag:
|
||||||
|
|
||||||
### Page Change Events
|
### Page Change Events
|
||||||
|
|
||||||
When writing CoffeeScript, you'll often want to do some sort of processing upon
|
You'll often want to do some sort of processing upon
|
||||||
page load. With jQuery, you'd write something like this:
|
page load. Using the DOM, you'd write something like this:
|
||||||
|
|
||||||
```coffeescript
|
```js
|
||||||
$(document).ready ->
|
window.addEventListener("load", () => {
|
||||||
alert "page has loaded!"
|
alert("page has loaded!");
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
However, because Turbolinks overrides the normal page loading process, the
|
However, because Turbolinks overrides the normal page loading process, the
|
||||||
event that this relies upon will not be fired. If you have code that looks like
|
event that this relies upon will not be fired. If you have code that looks like
|
||||||
this, you must change your code to do this instead:
|
this, you must change your code to do this instead:
|
||||||
|
|
||||||
```coffeescript
|
```js
|
||||||
$(document).on "turbolinks:load", ->
|
document.addEventListener("turbolinks:load", () => {
|
||||||
alert "page has loaded!"
|
alert("page has loaded!");
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
For more details, including other events you can bind to, check out [the
|
For more details, including other events you can bind to, check out [the
|
||||||
|
@ -532,23 +545,23 @@ When using another library to make Ajax calls, it is necessary to add
|
||||||
the security token as a default header for Ajax calls in your library. To get
|
the security token as a default header for Ajax calls in your library. To get
|
||||||
the token:
|
the token:
|
||||||
|
|
||||||
```javascript
|
```js
|
||||||
var token = document.getElementsByName('csrf-token')[0].content
|
const token = document.getElementsByName(
|
||||||
|
"csrf-token"
|
||||||
|
)[0].content;
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then submit this token as a `X-CSRF-Token` header for your
|
You can then submit this token as a `X-CSRF-Token` header for your
|
||||||
Ajax request. You do not need to add a CSRF token for GET requests,
|
Ajax request. You do not need to add a CSRF token for GET requests,
|
||||||
only non-GET ones.
|
only non-GET ones.
|
||||||
|
|
||||||
You can read more about about Cross-Site Request Forgery in [Security](https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf)
|
You can read more about about Cross-Site Request Forgery in the [Security guide](https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf).
|
||||||
|
|
||||||
Other Resources
|
Other Resources
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Here are some helpful links to help you learn even more:
|
Here are some helpful links to help you learn even more:
|
||||||
|
|
||||||
* [jquery-ujs wiki](https://github.com/rails/jquery-ujs/wiki)
|
* [rails-ujs wiki](https://github.com/rails/rails/tree/master/actionview/app/assets/javascripts)
|
||||||
* [jquery-ujs list of external articles](https://github.com/rails/jquery-ujs/wiki/External-articles)
|
|
||||||
* [Rails 3 Remote Links and Forms: A Definitive Guide](http://www.alfajango.com/blog/rails-3-remote-links-and-forms/)
|
|
||||||
* [Railscasts: Unobtrusive JavaScript](http://railscasts.com/episodes/205-unobtrusive-javascript)
|
* [Railscasts: Unobtrusive JavaScript](http://railscasts.com/episodes/205-unobtrusive-javascript)
|
||||||
* [Railscasts: Turbolinks](http://railscasts.com/episodes/390-turbolinks)
|
* [Railscasts: Turbolinks](http://railscasts.com/episodes/390-turbolinks)
|
||||||
|
|
Loading…
Reference in a new issue