Tạo Breadcrumb cho web với gem breadcrumbs_on_rails

Gần đây, khi phải tạo Breadcrumb cho 1 trang web được viết bằng Rails, mình có sử dụng gem breadcrumbs_on_rails, và thấy nó khá hiệu quả, dễ dàng cài đặt, nên muốn giới thiệu cho mọi người.

I. Breadcrumb là gì?

Breadcrumb là một tập các đường link phân cấp, được sử dụng để trợ giúp điều hướng trong giao diện web, giúp người dùng biết mình đang ở đâu, và dễ dàng di chuyển sang các trang khác. Với những trang web có cấu trúc phức tạp, việc có breadcrumb giúp người dùng dễ dàng tìm kiếm và truy cập những thông tin họ cần, giảm đi các thao tác quay lại trang cũ.

Breadcrumb thường được đặt đầu trang và có cấu trúc như sau

  • Dựa trên lịch sử xem
Page viewed > Page viewed > Page viewed > Page viewed > Page currently being viewed
  • Dựa trên cấu trúc phân cấp của trang
Home page > Section page > Subsection page

Về phía người dùng, Breadcrumb giúp người dùng tương tác với trang web thuận tiện hơn.

Về phía người chủ trang web, Breadcrumb là 1 phần quan trọng giúp tối ưu SEO. Google Search sử dụng Breadcrumb để phân loại và hiển thị kết quả tìm kiếm. Do đó những trang web có Breadcrumb cũng sẽ được Google Search đánh giá cao hơn.

  • Trang web không có Breadcrumb
  • Trang web có Breadcrumb

Breadcrumb được chia làm 3 loại chính

  • Location breadcrumb: Hiển thị vị trí trang trong hệ thống phân cấp trang. Đây là loại phổ biến nhất
  • Attribute breadcrumb: Cung cấp thuộc tính phân loại trang hiện tại
  • Path breadcrumb: Hiển thị cho người dùng các bước mà họ đã thực hiện để đến trang hiện tại

II. Breadcrumbs On Rails

Breadcrumbs On Rails cho phép tạo và quản lý Breadcrumb trong Rails đơn giản, và mình có thể thay đổi thiết kế Breadcrumb linh hoạt.

Với phiên bản mới nhất hiện nay (3.0.1) thì Breadcrumbs On Rails yêu cầu Rails 4 hoặc Rails 5

1. Cài đặt

Thêm dòng sau vào Gemfile

gem "breadcrumbs_on_rails"

Và thực hiện dòng lệnh

bundle

để cài đặt dependencies

2. Hướng dẫn cơ bản

Để tạo 1 Breadcrumb ở Rails app bằng cách sử dụng Breadcrumbs On Rails thì khá đơn giản.

2.1 Controller

Ở controller, dùng add_breadcrumb để thêm element vào breadcrumb stack. add_breadcrumb cần có 2 tham số: tên của Breadcrumb và đường dẫn của nó

class MyController
  add_breadcrumb "home", :root_path
  add_breadcrumb "my", :my_path

  def index
    add_breadcrumb "index", index_path
    
    # ...
  end
end

Ngoài ra, add_breadcrumb có tham số thứ 3 để customize đường dẫn của breadcrumb.

2.1 View

Ở view, render Breadcrumb bằng cách dùng render_breadcrumbs

<body>
  <%= render_breadcrumbs %>
</body>

Mình có thể thay đổi dấu phân cách mặc định bằng cách sử dụng option :separator

<body>
  <%= render_breadcrumbs :separator => ' / ' %>
</body>

Hiện tại thì mới chỉ support 2 options sau:

  • :separator
  • :tag

3. Chi tiết về add_breadcrumb

3.1 HTML Escaping

Breadcrumb text mặc định sẽ được html-escaped để tránh bị tấn công XSS

class MyController
  add_breadcrumb "This is the <b>Main</b> page".html_safe

  def profile
    add_breadcrumb "#{@user_name} Profile", users_profile
  end
end

Nếu @user_name<script> thì kết quả hiển thị nó sẽ như sau

This is the <b>Main</b> page > &lt;script&gt; Profile
3.2 Breadcrumb Element

Bằng cách sử dụng add_breadcrumb mình sẽ thêm các Element vào danh sách Breadcrumb. Mỗi Element bao gồm: tên Breadcrumb và đường dẫn của nó

Ta có thể sử dụng các Ruby type sau cho tên và đường dẫn của Element

  • Symbol
    Ví dụ ở đây ta set tên của Element bằng Symbol :root_name và sẽ kết quả tên của Element sẽ có giá trị trả về của phương thức đã được định nghĩa ở dưới
class MyController
  add_breadcrumb :root_name, "/"
  
  protected
    def root_name
      "the name"
    end
end
  • Proc
    Proc là 1 cách hiệu quả để truy cập vào 1 phương thức hoặc biến đặc biệt được tạo trong controller
class MyController
  add_breadcrumb Proc.new { |c| c.my_helper_method }, "/"
end
  • String
    Là cách đơn giản nhất, tuy nhiên thực tế ít dùng hơn type kia
class MyController
  add_breadcrumb "homepage", "/"
end
3.3 Phạm vi của breadcrumb

Ta có thể sử dụng các Rails filter option để giới hạn phạm vi của Breadcrumb như sau

class PostsController < ApplicationController
  add_breadcrumb "admin", :admin_path
  # Chỉ hiển thị với các action index và show
  add_breadcrumb "posts", :posts_path, :only => %w(index show)
end

class ApplicationController < ActionController::Base
  # Chỉ hiển thị với điều kiện là admin controller
  add_breadcrumb "admin", :admin_path, :if => :admin_controller?
  
  def admin_controller?
    self.class.name =~ /^Admin(::|Controller)/
  end
end
3.4 Đa ngôn ngữ

Breadcrumbs On Rails tương thích với đa ngôn ngữ của Rails
Nếu muốn ta có thể định nghĩ tên breadcrumb ở file .yml như sau

# config/locales/en.yml
en:
  breadcrumbs:
    homepage: Homepage
    first: First
    second: Second
    third: Third

# config/locales/it.yml
it:
  breadcrumbs:
    homepage: Homepage
    first: Primo
    second: Secondo
    third: Terzo

Và ở controller, ta dùng phương thức I18n.t

class PostsController < ApplicationController
  add_breadcrumb I18n.t("breadcrumbs.first"),  :first_path
  add_breadcrumb I18n.t("breadcrumbs.second"), :second_path, :only => %w(second)
  add_breadcrumb I18n.t("breadcrumbs.third"),  :third_path,  :only => %w(third)
end

class ApplicationController < ActionController::Base
  add_breadcrumb I18n.t("breadcrumbs.homepage"), :root_path
end

III. Một vài case study

Tùy vào yêu cầu của từng trang web, ta có thể cần 1 số xử lý đặc biệt cho Breadcrumb. Dưới đây là 1 số trường hợp mình đã gặp trong dự án. Ngoài ra có thể tham khảo Issues của Breadcrumbs On Rails để biết thêm 1 sô trường hợp khác mà người khác đã gặp.

1. Truyền tham số cho path của Rails

Bình thường ta có thể cài đặt Breadcrumb đơn giản như sau

class MyController
  add_breadcrumb I18n.t('home.title'), :root_path
  add_breadcrumb I18n.t('my.title'), :my_path

  def index
    add_breadcrumb I18n.t('my.index.title'), :my_index_path
    # ...
  end
end

Tuy nhiên với trường hợp path cần truyền tham số thì sao? Ở đây ta có thể dùng Proc để truyền tham số cho path

def edit
  add_breadcrumb(I18n.t('my.edit.title'), proc { edit_my_path(id: @my_id) })
  # ...
end

2. Breadcrumb phụ thuộc vào giá trị thay đổi

Ví dụ trường hợp user bình thường và admin sẽ hiển thị Breadcrumb khác nhau khi vào trang index, ta có thể dùng before_action để xử lý như sau

class MyController
  add_breadcrumb I18n.t('home.title'), :root_path
  before_action only: [:index] do
    @user = User.find(params[:user_id])
    case @user[:type]
    when Settings.user[:admin]
      add_breadcrumb I18n.t('my.admin.title'), :my_admin_path
    when Settings.user[:normal_user]
      add_breadcrumb I18n.t('my.normal_user.title'), :my_user_path
    end
  end

  def index
    add_breadcrumb I18n.t('my.index.title'), :my_index_path
    # ...
  end
end

Hoặc có thể cho xử lý đó vào 1 hàm như sau

class MyController
  add_breadcrumb I18n.t('home.title'), :root_path
  before_action only: [:index] :add_breadcrumb_user_base

  def index
    add_breadcrumb I18n.t('my.index.title'), :my_index_path
    # ...
  end
  
  private
  
  def add_breadcrumb_user_base
    @user = User.find(params[:user_id])
    case @user[:type]
    when Settings.user[:admin]
      add_breadcrumb I18n.t('my.admin.title'), :my_admin_path
    when Settings.user[:normal_user]
      add_breadcrumb I18n.t('my.normal_user.title'), :my_user_path
    end
  end
end

3. Breadcrumb Top/Home cho tất cả các trang

Thường thì ở đầu Breadcrumb ta sẽ muốn set Top/Home cho tất cả các trang web. Ta chỉ cần add Breadcrumb 1 lần ở ApplicationController và tất cả các trang sẽ được hiển thị.

Tuy nhiên, với trường hợp muốn Top/Home khác nhau với từng đối tượng user khác nhau (như admin và user bình thường), hoặc không muốn add Top/Home ở 1 số màn hình đặc biệt (như login, logout) ta có thể cài đặt như sau

class ApplicationController < ActionController::Base
  # Chỉ add breadcrumb ngoại trừ devise_controller (Gem Devise là 1 gem phổ biến để xác thực người dùng)
  before_action unless: :devise_controller? do
    @home_path = home_path
    add_breadcrumb I18n.t('home.title'), @home_path
  end
  
  private
  
  # Set home path khác nhau với những đối tượng user khác nhau
  def home_path
    if current_login_account.admin? && params[:controller].include?('admins')
      admin_home_path
    else
      user_home_path
    end
  end
end

Hi vọng những case study này sẽ hữu ích với mọi người!

IV. Tham khảo

  1. https://en.wikipedia.org/wiki/Breadcrumb_(navigation)
  2. https://developers.google.com/search/docs/data-types/breadcrumb
  3. https://usersnap.com/blog/breadcrumbs/
  4. https://github.com/weppos/breadcrumbs_on_rails