版型(Layout)


樣版是回應中動態的部份,版型是回應中共用的靜態內容。Rails的預設的回應樣版,是位於app/views/layouts資料夾下的application.html.erb,例如用 RESTful 與 Rails 中的範例來說:
  • application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>Bookmark</title>
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>

其中<%= yield %>的部份,就是各個樣版檔案的部份,以上版型中stylesheet_link_tag輔助方法的呼叫方式,預設會產生以下內容:

<link href="/assets/application.css?body=1" media="screen" rel="stylesheet" type="text/css" />
<link href="/assets/bookmarks.css?body=1" media="screen" rel="stylesheet" type="text/css" />

如果你有其它樣式表,可以如下自行加入:

<%= stylesheet_link_tag "main1" %>
<%= stylesheet_link_tag "print/main2" %>
<%= stylesheet_link_tag "/print/main3" %>

這會產生以下的內容:

<link href="/assets/main1.css" media="screen" rel="stylesheet" type="text/css" />
<link href="/assets/print/main2.css" media="screen" rel="stylesheet" type="text/css" />
<link href="/print/main3.css" media="screen" rel="stylesheet" type="text/css" />

stylesheet_link_tag指定相對路徑時,產生的路徑會以/assets開始,以範例前兩個路徑來說,可以在public資料夾下擺放assets/main1.css與assets/print/main2.css,如果stylesheet_link_tag指定絕對路徑,產生的路徑就以你指定的為主,以範例最後一個路徑來說,可以在public資料夾下擺放print/main3.css。public資料夾是Rails應用程式中,可以使用HTTP方法直接取得資源的地方,簡單地說,public資料夾可作為文件根目錄。

(實際上,預設還可以將圖片、JavaScript檔案、CSS檔案擺放在app/assets、lib/assets或vender/assets三個資料夾下對應的images、javascripts與stylesheets資料夾中,這會進行一些額外處理動作,資產(Assets) 中會再說明)。

以上版型中javascript_include_tag的呼叫方式,預設會產生以下內容:

<script src="/assets/jquery.js?body=1" type="text/javascript"></script>
<script src="/assets/jquery_ujs.js?body=1" type="text/javascript"></script>
<script src="/assets/bookmarks.js?body=1" type="text/javascript"></script>
<script src="/assets/application.js?body=1" type="text/javascript"></script>

類似地,如果你有一些JavaScript檔案要載入,可以如下自行加入:

<%= javascript_include_tag "test1" %>
<%= javascript_include_tag "print/test2" %>
<%= javascript_include_tag "/print/test3" %>

產生的內容是:

<script src="/assets/test1.js" type="text/javascript"></script>
<script src="/assets/print/test2.js" type="text/javascript"></script>
<script src="/print/test3.js" type="text/javascript"></script>

可擺放.js的位置,可參考方才對的stylesheet_link_tag說明。

csrf_meta_tags的原始碼是:

# File actionpack/lib/action_view/helpers/csrf_helper.rb, line 19
def csrf_meta_tags
  if protect_against_forgery?
    [
      tag('meta', :name => 'csrf-param', :content => request_forgery_protection_token),
      tag('meta', :name => 'csrf-token', :content => form_authenticity_token)
    ].join("\n").html_safe
  end
end

也就是若頁面有防範跨站請求攻擊,預設會產生以下內容:

<meta content="authenticity_token" name="csrf-param" />
<meta content="qy/SsxqyQVDKSTjE2dWIeBjZa5eYXmZpq3G1cxGVYq4=" name="csrf-token" />

當使用JavaScript發送請求,若為非GET資料,會讀取這段meta,將csrf-param與csrf-token的content分別作為請求參數名稱與值發送,以link_to輔助方法為例,在按下其產生的鏈結時,會執行jquery_ujs.js中的程式碼,動態建立表單並發送:

    // Handles "data-method" on links such as:
    // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
    handleMethod: function(link) {
      var href = link.attr('href'),
        method = link.data('method'),
        target = link.attr('target'),
        csrf_token = \$('meta[name=csrf-token]').attr('content'),
        csrf_param = \$('meta[name=csrf-param]').attr('content'),
        form = \$('<form method="post" action="' + href + '"></form>'),
        metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';

      if (csrf_param !== undefined && csrf_token !== undefined) {
        metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
      }

      if (target) { form.attr('target', target); }

      form.hide().append(metadata_input).appendTo('body');
      form.submit();
    },

application.html.erb是預設的版型檔案,如果在app/views/layouts下有個與控制器相同名稱的.html.erb,則該控制器預設會使用它,例如若app/views/layouts下有個bookmarks.html.erb,則控制器bookmarks會使用它作個版型檔案。

也可以在控制器中指定想使用的版型檔案。例如:

class BookmarksController < ApplicationController
    layout "test"
end

如上指定之後,BookmarksController的畫面呈現,預設會使用app/views/layouts下的test.html.erb作為版型。可以指定方法傳回的字串值,動態決定使用哪個版型。例如:

class BookmarksController < ApplicationController
    layout :random_layout

    private
    def random_layout
        ["layout1", "layout2", "layout3"][rand(3)]
    end
end

可以使用:only指定特定動作套用版型,或是使用:except指定特定動作不套用版型,例如若只有index動作要套用test.html.erb版型,可以如下:

class BookmarksController < ApplicationController
    layout "test", :only => :index
end

show、delete不套用test.html.erb版型,可如下指定:

class BookmarksController < ApplicationController
    layout "test", :except => [:show, :delete]
end