Layout 的基本使用

Posted by Anthony Chao on 2019-09-09

如何使用 Layout

今天從到這篇的 2.2.13 繼續看下去,這裡講到 layout 的使用方法

我們先來看看一開始 rails 的 layout 長什麼模樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
<title>titile</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>

<%= yield %>

</body>
</html>

看到這裡,不知道大家有沒有注意到在平常使用 rails view 的時候,怎麼都不用寫上面這些寫 html 的時候一定要寫的東西?就是因為它藏在 layout 裡面,平常他直接會跟著 controller ,基本上在同一個 controller 中的 layout 是一樣的,然後在 body 中才把內容 “讓” 給 view 裡面的內容渲染

那這些 layout 該放在哪裡呢?

如果你的 controller 名字叫做 CandidatesController,那麼他會去找 app/views/layouts/candidates.html.erb 這個檔案當作 layout , 如果沒有這個檔案,他會去找 app/views/layouts/application.html.erb 當作 layout,這些都是 “慣例” ,當然我們也是可以自己設定

1
2
3
4
class CandidatesController < ApplicationController
layout "qoo"
#...
end

利用 layout 這個方法,我們可以指定 layout 是哪個檔案,上面這個例子中就會去找 app/views/layouts/qoo.html.erb 這檔案

另外,layout 後面也可以接方法,來判斷是要使用哪一份 layout

1
2
3
4
5
6
7
8
class CandidatesController < ApplicationController
layout :which_layout

private
def which_layout
@current_user.login? ? "admin" : "visit"
end
end

在上面的例子中,會去判斷 @current_user 是否登入,登入的話會使用 admin layout,否則使用 visit layout


跳回前一個來源: redirect_back

有時候我們會希望使用者回到他在進到這個頁面的前一個來源,這時候可以用 redirect_back 這個方法,在 rails 5 以前是 redirect_to :back

不過要注意的是,使用這個方法的前提是瀏覽器會回報 HTTP_REFERER 這個 error ,但有時候瀏覽器並不是丟這個 error 而是丟一個例外,這時候畫面就會爆掉,接下來說明解決方法

在 rails 5 以前解決這個問題有點麻煩,必須去抓這個例外錯誤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CandidatesController < ApplicationController
rescue_from ActionController::RedirectBackError, with: :redirect_to_default

def vote
@candidate = Candidate.find params[:id]
@candidate.vote
redirect_to :back
end

private

def redirect_to_default
redirect_to root_path
end
end

上面這個例子是說,當例外發生的時候,我就把瀏覽器導至首頁

現在變得比較簡單了,可以用 fallback_location 這個方法

1
2
3
4
5
6
7
8
class CandidatesController < ApplicationController

def vote
@candidate = Candidate.find params[:id]
@candidate.vote
redirect_back(fallback_location: root_path)
end
end

Render 跟 direct_to 的差異?

2.3 中提到,我們會在某個條件成功或失敗的情況下,使用 render 或者 redirect_to 讓使用者看到跳轉的畫面,那redirect_torender 有什麼不同?

redirect_to 是叫瀏覽器送出新的 request 給這個目的地的 url
render 則是請瀏覽器借用哪個畫面做為回應

在 2.3.2 中, Rails guide 給了一個講解的很清楚的例子,我們這裡直接借用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# redirect_to 版本
def index
@books = Book.all
end
def show
@book = Book.find_by(id: params[:id])
if @book.nil?
redirect_to action: :index
end
end

# render 版本
def index
@books = Book.all
end
def show
@book = Book.find_by(id: params[:id])
if @book.nil?
@books = Book.all
flash.now[:alert] = "Your book was not found"
render "index"
end
end

乍看之下 redirect_to 的程式碼比較簡短,但我們透過瀏覽器的角度來看看這件事情

redirect_to 版本中,我們嘗試去找這本 @book ,但發現並沒有這本書,這時候 controller 要求瀏覽器去 index 的頁面,我們轉到這個網址來,發現需要 index 的 view 跟 資料庫中的 @books 資訊,因此再去抓這些東西回來到瀏覽器上

render 版本中,我們同樣沒找到這本書,這時候 controller 請我們先抓資料庫中的 @bookd 資料,並請瀏覽器渲染出 index 的畫面,需要 @books 的地方就把資料套上去

從上面兩個說明中,應該可以知道在這個例子中 redirect_to 多繞了一圈,因此效能較差,這種情況下建議使用 render

參考資料
rails guide

Rails 5 improves redirect_to :back with new redirect_back method





prevent_hack