Working with Javascript

in Rails

제 70회 Biweekly Lecture

2014-09-16

SeungKyun Nam

- ROR Lab. Season 3 -

Agenda

  • Ajax: Introduction
  • Unobtrusive Javascript
  • Built-in Helpers
  • Ajax: Server-Side Concerns
  • Turbolinks

Ajax: Introduction

What is Ajax:

  • Asynchronous
  • Javascript
  • and
  • XML

Ajax: Introduction

The origin of the term called 'Ajax'

Ajax: Introduction

Understanding Request-Response Cycle

Ajax: Introduction

Classic Model vs AJax Model

Ajax: Introduction

Synchronous Model vs Asynchronous Model

Ajax: Introduction

Defining Ajax

  • Presentation: XHTML and CSS -> HTML5 and CSS3
  • Document Object Model
  • Data: XML and XSLT -> (mostly JSON)
  • Carrier: XMLHttpRequest
  • Binding everything together: Javascript

Ajax: Introduction

Sample Code

// COFFEESCRIPT
$.ajax(url: "/test/lorem").done (html) ->
    $("#results").append html

// Generated JAVASCRIPT
(function() {
    $.ajax({
        url: "/test/lorem"
    }).done(function(html) {
        return $("#results").append(html);
    });

}).call(this);

Ajax: Introduction

Demo

Unobtrusive Javascript

Three main goals

  • To separate JavaScript from HTML markup, as well as keeping modules of JavaScript independent of other modules.
  • Unobtrusive JavaScript should degrade gracefully - all content should be available without all or any of the JavaScript running successfully.
  • Unobtrusive JavaScript should not degrade the accessibility of the HTML, and ideally should improve it, whether the user has personal disabilities or are using an unusual, or unusually configured, browser.

Tip:

To complie CoffeeScript

without top-level function safety wrapper

# Compile the Javascript without the top-level function safety wrapper
#    CoffeeScript Option -b or --bare
Tilt::CoffeeScriptTemplate.default_bare = true

config/environment.rb

Traditional Way

Inline Javascript

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

Traditional (Better) Way

Using Functions

paintIt = (element, backgroundColor, textColor) ->
    element.style.backgroundColor = backgroundColor
    if textColor?
        element.style.color = textColor

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

Unobtrusive Way

paintIt = (element, backgroundColor, textColor) ->
    element.style.backgroundColor = backgroundColor
    if textColor?
        element.style.color = textColor

$ ->
    $("a[data-background-color]").click ->
        backgroundColor = $(this).data("background-color")
        textColor = $(this).data["text-color"]
        paintIt(this, 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

Demo

Built-in Helpers

Rails Ajax helpers are in two parts

  • Javascript half -> rails.js
  • Ruby half -> add appropriate tags to DOM

form_for

<%= form_for(@post, remote: true) do |f| %>
    ... 
<% end %>
<!-- Generated HTML -->
<form accept-charset="UTF-8" 
    action="/posts" 
    class="new_post" 
    data-remote="true" 
    id="new_post" method="post">
  ...
</form>

form_for

Adding Ajax Event

$(document).ready -> 
    $("#new_post").on("ajax:success"), (e, data, status, xhr) ->
        $("#new_post").append xhr.responseText
    ).bind "ajax:error", (e, data, status, error) ->
        $("#new_post").append "<p>ERROR</p>"

form_tag

<%= form_tag('/posts', remote: true) %>

link_to

<%= link_to "a post", @post, remote: true %>
<!-- Generated HTML -->
<a href="/posts/1" data-remote="true">a post</a>

link_to

Adding Ajax Event

<%= link_to "Delete Post", @post, remote: true, method: :delete %>
$ ->
    $("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
        alert "The post was deleted."

button_to

<%= button_to "A post", @post, remote: true %>
<!-- Generated HTML -->
<form action="/posts/1" class="button_to" data-remote="true" method="post">
    <div><input type="submit" value="A post"></div>
</form>

Built-in Helpers

Demo

Server-Side Concerns

Server-Side Concerns

by Example

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

    # ...
end
<b>Users</b>

<ul id="users">
    <% @users.each do |user| %>
        <li><%= user.name %></li>
    <% end %>
</ul>

<br>

<%= form_for(@user, remote: true) do |f| %>
    <%= f.label :name %><br>
    <%= f.text_field :name %>
    <%= f.submit %>
<% end %>

app/views/users/index.html.erb

app/controllers/users_controller.rb

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

app/views/users/_user.html.erb

Server-Side Concerns

by Example

# ...
    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
# ... 

app/controllers/users_controller.rb

Server-Side Concerns

by Example

$("<%= escape_javascript(render @user) %>").appendTo("#users")

app/views/users/create.js.erb

Server-Side Concerns

Demo

Turbolinks

  • Rails 4 Default
  • Turbolinks Gem
  • Uses Ajax to speed up page rendering
  • PushState

Turbolinks

How it works

  • attaches a click handler to all <a> tags
  • check if browser supports PushState
  • if so,
    • make an Ajax request
    • parse the response
    • replace the entire <body>
    • change the URL using PushState

PushState

a part of HTML5 History-API

<!doctype html>
<html>
	<head>
		<title>PushState Example</title>
		<script language="javascript">
			function update_history() {
				var stateObj = { 'Website' : "localhost" };
				window.history.pushState(stateObj, "page 2", "bar.html");
				//	Usage: histor.pushState(state_object, title, url)
				//	title -> will be currently ignored
			}
		</script>
	</head>
	<body>
		<a href="#" onclick="update_history()">pushState example</a>
	</body>
</html>

Using Turbolinks

# Turbolinks makes following links in your web application faster. 
# Read more: https://github.com/rails/turbolinks
gem 'turbolinks'

Gemfile

//= require turbolinks

app/assets/javascripts/applications.js

Turbolinks

To disable Turbolinks for certain links

<a href="..." data-no-turbolinks>No turbo links here</a>.

using data-no-turbolinks attribute

Turbolinks

Troubleshooting

$(document).ready ->
    alert "page has loaded!"

# This event will not work because of Turbolinks
$(document).on "page:change", ->
    alert "page has loaded!"

# This will work!

Turbolinks

Demo

Thank you

Copy of Working with Javascript in Rails

By Hyoseong Choi

Copy of Working with Javascript in Rails

레일스 가이드 "Working with Javascript in Rails" 강의록

  • 1,302