Introduction about Pjax and Tubolinks in rails (Part II)

Sau phần đầu mang tính chất giới thiệu tổng quan về pjax và tubolinks, trong bài viết này tôi xin giới thiệu chi tiết thêm về pjax cũng như đưa ra một vài ví dụ nhỏ giúp mọi người có thể hình dung dễ dàng về Pjax.

Pjax sử dung Ajax để load lại một phần trang web. ĐIều này không có gì mới mẻ, nhưng điều đặc biệt chính là ở chữ cái "P" viết tắt của PushState. PushState là một chức năng mới của Html5 cho phép chúng ta có thể sửa đổi lịch sử trình duyệt mà không gây xáo trộn các giá trị của bảng hash. Do đó PushState cung cấp cho chúng ta một cách hiểu quả để tăng tốc độ sử dụng ajax để update chỉ một phần của trang web. Hiện nay, đa số các trình duyệt như, Firefox 4+, Safari 5+, Chrome 8+ cũng như hệ điều hành iOS 4+, and Android 2.2+ đã hỗ trợ PushState. PJAX là kĩ thuật giúp chúng ta có thể update chính xác một phần của trang web và hỗ trợ để thay đổi địa chỉ trên thanh URL của trình duyệt khi yêu cầu kết thúc.

Tổng quát lại, PJAX = AJAX + PushState

Sử dụng Pjax, ta có thể chuyển đường link http://mysite.com/#!/books/5 thành http://mysite.com/books/5 trong khi vẫn giữ nguyên single-page design.

Một lợi ích to lớn nhất của việc này chính là request mới sẽ không phải reload lại những elements dùng chung (ví dụ như header và footer của trang) và nó cũng sẽ không phải load lại các file css, scss, js (stylesheets, javascripts) - những thành phần gây load chậm và từ đó làm trang web load nhanh hơn.

Chúng ta có thể bắt đầu bằng việc sử dụng jQuery PJAX plugin nếu bạn dùng jQuery: https://github.com/defunkt/jquery-pjax

app/assets/javascripts/application.js (nhớ bundle trước khi sử dụng)

//=require jquery.pjax

Chọn loại link để sử dụng với PJAX:

// app/assets/javascripts/application.js

$(function() {
  $(document).pjax('a:not([data-remote]):not([data-behavior]):not([data-skip-pjax])', '[data-pjax-container]')
});

Trong ví dụ này, phần nội dung áp dụng pjax được đánh dấu bởi data-pjax-container attribute:

<body>
  <div>
    <!-- This will not be touched on PJAX updates -->
    <%= Time.now %>
  </div>

  <div data-pjax-container>
    <!-- PJAX updates will go here -->
    <%= content_tag :h3, 'My site' %>
    <%= link_to 'About me', about_me_path %>
    <!-- The following link will not be pjax'd -->
    <%= link_to 'Google', 'http://google.com', 'data-skip-pjax' => true %>
  </div>
</body>
Layouts

Mặc định, pjax_rails gem không render application layout file và chỉ render thay thể duy nhất phần yielded view. Nhưng nếu bạn muốn thêm nội dung luôn trả lại mỗi khi gửi request pjax, bạn có thể override pjax_layout trong controller và xác nhận layout cần render (mặc định là false).

class ApplicationController < ActionController::Base
  def pjax_layout
    'pjax'
  end
end

Setting up rack-pjax

Gemfile

gem 'rack-pjax'

config/application.rb

config.middleware.use Rack::Pjax

bash

mkdir -p vendor/assets/javascripts

curl https://raw.github.com/defunkt/jquery-pjax/master/jquery.pjax.js > vendor/assets/javascripts/jquery.pjax.js

app/assets/javascripts/application.js

//= require jquery.pjax

products.js.coffee

jQuery ->
  $('.product a').pjax('[data-pjax-container]')

index.html.erb

<div data-pjax-container>
  ...
</div>

Sau đây là một ví dụ đơn giản về sử dụng pjax:

Trong application.js, tất cả các thẻ <a> sẽ được load bằng sử dụng Pjax.

application.js

$('a').pjax('#main')

Dòng trên thể hiện tất cả cách dòng links sẽ được load bằng pjax. Page sẽ được load lần đầu tiền và thay thế tất các các phần tử với id="main" bằng nội dung được load từ pjax request .

Tiếp theo, browser URL bar được update thành new URL.

Dưới đây là ví dụ code trong application.html.erb layout.

application.html.erb

<html>
<head>
  <title>Boek uit? Vind je volgende fantasy boek</title>
  <%= stylesheet_link_tag    "application", :media => "all" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>
  <div class="header">
    <div class="logo">
      <%= link_to root_path do %>
        volgendboek<span>.nl</span>
      <% end %>
    </div>
  </div>

  <div class="wrapper">
    <div id="main">
      <%= yield %>
    </div>
  </div>
</body>
</html>

Tất cả các trang mới sẽ render tất cả cùng với layout và đưa vào trong view thông qua thẻ

. Khi pjax load trang nó sẽ load nó sẽ đứa html vào lại div.

app/views/series/show.html.erb

<title>Serie: <%= @series.title %></title>
<div class="container">
  <h1><%= @series.title %></h1>
  <div class="books">
    <%= render @series.books %>
  </div>
</div>

Khi tạo view, bạn cần chú ý 2 việc:

Đầu tiên, chắc chắn rằng đưa thẻ <title> vào trong view. Từ đó jQuery Pjax update đồng thời browser's title bar

Thứ hai, điều quan trọng là đóng gói tất cả những thứ bạn muốn load trong 1 container element. Trong ví dụ tôi sử dụng

nhưng bạn có thể sử dụng các elements khác nếu muốn. Đây là điều quan trọng bởi vì jQuery PJAX sẽ tìm một container element để response việc load tới trang web.

Cùng với đó bạn cũng cần làm 1 vài việc ở trong controller. Cần chắc chắn rằng layout không render bởi pjax request Như trong ví dụ sau:

class BooksController < ApplicationController
  def show
    @book = Book.find(params[:id])
    @series = @book.series
    @offset = params[:offset]

    if request.xhr?
      render :layout => false
    end
  end
end

Bạn có thể sử dụng request.xhr? để kiểm tra xem PJAX request bởi vì mỗi PJAX request là một AJAX request.

Bạn có thể sử dụng https://github.com/rails/pjax_rails plugin, để cung cấp cho bạn helper và các tính năng hữu dụng để sử dụng PJAX in Rails:

if pjax_request?
  render :layout => false
end

Trên đây là một vài giới thiệu cũng như ví dụ về việc sử dụng pjax trong rails. Cảm ơn các bạn đã đọc, rất mong bài viết giúp ích cho bạn. Trong kỳ tới chúng ta sẽ đi tìm hiểu về tubolinks.