Ruby on Rails Action View - 樣板設計

轉自https://ihower.tw/rails4/actionview.htmljavascript

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. - Martin Fowlerhtml

在這一章中我們將進入MVC架構中的View,也就是提供介面給用戶操做,與我們的應用程式作互動。java

ActionViewRails中處理View的元件名稱,而提供給用戶的文件,我們會用Template樣板來呈現。本章假設讀者們都對HTML有基本的認識。json

Template樣板

什麼是Template樣板呢?我們知道伺服器最終提供給瀏覽器的格式是HTML文件,而Template樣板就是動態產生HTML的方式。cookie

相對的說,我們用靜態HTML來稱呼不經過程式產生的HTML文件session

Rails預設用來產生Template的方式是Embedded Ruby(ERb),若是你曾經使用過PHPJSPASP,那麼你會很是熟悉這種內嵌程式碼的風格,這是一種最為直覺且容易學習的方法。例如如下是一小段嵌入目前時間的ERb,中間<%= %>的部份即是Ruby程式:app

<h1><%= Time.now.to_s %></h1>

RailsTemplate檔案位置和名稱也是有玄機的,例如app/views/welcome/index.html.erb來說,welcome目錄是它的Controller名稱,檔案第一段index是它的Action名稱,附檔名則是用來指定要用什麼方式來產生什麼格式的文件:index.html.erb表示用ERb產生HTML格式的文件。會有這樣慣例的緣由,你可能已經猜到,那就是使用ERb不表明必定就是用來產生HTML。用什麼Template引擎(在Rails中又叫做Template Handler)產生文件,和文件的Format格式是兩回事情。因此ERb其實能夠用來產生任何文字檔格式,例如CSVXMLJavaScript等等。ide

雖然能夠,但ERb並不是產生XML的最好方式,一般在我們會用Builder來產生XML,例如一個叫作show.xml.builder的檔案:post

people do |p|
  p.person "test"
end

就會產生如下的XMLui

<people>
  <person>test<person>
</people>

如下是常見的樣板引擎與格式組合:

格式 引擎
html、xhtml erb
js erb
json jbuilder
xml、rss、atom builder

擴充Template Handler

Rails預設只有內建ERbBuilder這兩套樣板引擎,但要擴充很是容易。例如在Rails社羣中,也很流行用HAMLSlim這兩套樣板引擎來取代ERb。這兩套都利用縮排的技術簡化HTML撰寫的格式,例如如下的HAML:

%section.container
    %h1= post.title
    %h2= post.subtitle
    .content
        = post.content

等同於如下的ERb

<section class=」container」>
    <h1><%= post.title %></h1>
    <h2><%= post.subtitle %></h2>
    <div class=」content」>
        <%= post.content %>
    </div>
</section>

要安裝使用,只須要在Gemfile檔案中加上gem "haml-rails"然後bundle install便可。不過相較於ERb,使用HAML雖然能夠更為有效率地撰寫HTML樣板,可是還是須要考量團隊中的網頁設計師是否能夠配合使用。

使用RendererController中直接回傳結果

有一些格式的本質不必定須要Template引擎,能夠在Controller中直接render其結果便可,例如JSONCSV或是XMLRailsActiveRecord model提供了to_xmlto_json方法。而CSV則可使用FasterCSV函式庫。範例以下:

require 'csv'
class PeopleController < ApplicationController

def index
    @people = Person.all
    respond_to do |format|
      format.html
      format.json{ render :json => @person.to_json }
      format.xml { render :xml => @person.to_xml }
      format.csv do
        csv_string = CSV.generate do |csv|
            csv << ["Name", "Created At"]
            @people.each do |person|
                csv << [person.name, person.created_at]
            end
        end
        render :text => csv_string
      end
    end
end

ERb標籤

除了上述介紹的ERb標籤<%= %>會輸出中間的Ruby程式執行結果,還有一些其餘用法:

<% %>

這樣就不會輸出任何結果,一般用在if或迴圈條件中,例如:

<% @people.each do |person| %>
    <% if person.name.present? %>
        <p><%= person.name %></p>
    <% end %>
<% end %>

上述的<% %>標籤雖然不會輸出HTML內容,可是還是在HTML原始碼中換行了,為了避免輸出時多餘的換行,能夠改用<%- -%>。不過實際上並沒有不少人在意就是了,畢竟這不影響用戶的頁面。

<%# blah blah %>

這是註解,不會輸出任何內容。不過若是須要整段多行註解,有個小技巧能夠善用:

<% if false %>
    <%= foo %>
    <hr>
    <%= bar %>
<% end %>

Layout版型

Layout能夠用來包裹Template樣板,讓不一樣View能夠共用Layout做為文件的頭尾。所以我們能夠為全站的頁面創建共用的版型。這個檔案預設是app/views/layouts/application.html.erb。若是在app/views/layouts目錄下有跟某Controller同名的Layout檔案,那這個Controller下的全部Views就會使用這個同名的Layout

預設的Layout長得以下:

<!DOCTYPE html>
<html>
<head>
  <title>YourApplicationName</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>

其中的<%= yield %>會被替換成個別Action的樣板。

開頭的<!DOCTYPE html>說明瞭這是一份HTML5文件,這種宣告法向下相容於全部瀏覽器的HTML4

若是須要指定ControllerLayout,能夠這麼作:

class EventsController < ApplicationController
   layout "special"
end

這樣就會指定Events Controller下的Views都使用app/views/layouts/special.html.erb這個Layout,你能夠加上參數:only:except表示只有特定的Action

class EventsController < ApplicationController
   layout "special", :only => :index
end

或是

class EventsController < ApplicationController
   layout "special", :except => [:show, :edit, :new]
end

請注意到使用字串和Symbol是不一樣的。使用Symbol的話,它會透過一個同名的方法來動態決定,例如如下的Layout是透過determine_layout這個方法來決定:

class EventsController < ApplicationController
   layout :determine_layout

	private

	def determine_layout
   	   ( rand(100)%2 == 0 )? "event_open" : "event_closed"
	end
end

除了在Controller層級設定Layout,我們也能夠設定個別的Action使用不一樣的Layout,例如:

def show
   @event = Event.find(params[:id])
	render :layout => "foobar"
end

這樣show Action的樣板就會套用foobar Layout。更常見的情形是關掉Layout,這時候我們能夠寫render :layout => false

自定Layout內容

除了<%= yield %>會載入Template內容以外,我們也能夠預先自定一些其餘的區塊讓Template能夠置入內容。例如,要在Layout中放一個側欄用的區塊,取名叫作:sidebar

<div id="sidebar">
    <%= yield :sidebar %>
</div>
<div id="content">
    <%= yield %>
</div>

那麼在Template樣板中,任意地方放:

<%= content_for :sidebar do %>
   <ul>
       <li>foo</li>
       <li>bar</li>
   </ul>
<% end %>

那麼這段內容就會被置入到Layout<%= yield :sidebar %>之中。

除了側欄以外,也經常使用這招讓每一頁的HTML meta特製化,例如我們能夠放Facebook Open Graph,這樣分享到Facebook時,就會抓取你設定的中介資料:

<head>
  <title>YourApplicationName</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
  <%= yield :head %>
</head>

Template樣板中,加入:

<%= content_for :head do %>
    <%= tag(:meta, :content => @event.name, :property => "og:title") %>
    <%= tag(:meta, :content => @event.description, :property => "og:description") %>
    <%= tag(:meta, :content => "article", :property => "og:type") %>
    <%= tag(:meta, :content => @event.logo.url, :property => "og:image") %>
    <%= tag(:meta, :content => event_url(@event), :property => "og:url") %>
<% end %>

Template中可使用的變數

我們已經認識到,在Controller Action中使用@的物件變數,就會被傳進Template中能夠被存取。除此以外,還包括cookiessessionflashparamsrequestresponse等在Controller中使用的變數也能夠在Template中使用。

比較特別的是,Template中的controller變數,我們能夠用這個變數讓每一頁有不一樣的CSS class,例如

<%= tag(:body, :class => "#{controller.controller_name} #{controller.action_name}") %>

這樣會輸出成

<body class="events show">

局部樣板Partials

局部樣板能夠將Template中重複的程式碼抽出來,例如我們在Part1中示範過的新增和編輯的表單。Partial Template的命名慣例是底線開頭,可是呼叫時不需加上底線,例如:

<%= render :partial => "common/nav" %>

在這個情境下,能夠省略:partial鍵:

<%= render "common/nav" %>

這樣便會使用app/views/common/_nav.html.erb這個樣板。若是使用Partial的樣板和Partial所在的目錄相同,能夠省略第一段的common路徑。

Partial樣板中是能夠直接使用實例變數的(也就是@開頭的變數)。不過好的實務做法是透過:locals明確傳遞區域變數,這樣程式會比較清楚,Partial樣板也比較容易被重複使用:

<%= render :partial => "common/nav", :locals => { :a => 1, :b => 2 } %>

在這個情境下,也能夠進一步把locals鍵也省略:

<%= render "common/nav", :a => 1, :b => 2 %>

這樣在partial樣板中,就能夠存取到區域變數ab

陣列型Collection

若是是陣列的資料,像是trli這類會一直重複的Template元素,我們可使用collection參數來處理,例如像如下的程式:

<ul>
    <% @people.each do |person| %>
        <%= render :partial => "person", :locals => { :person => person } %>
    <% end %>
<ul>

我們能夠改寫成使用collection參數來支援陣列形式:

<ul>
    <%= render :partial => "person", :collection => @people, :as => :person %>
<ul>

_person.html.erb這個partial中,會有一個額外的索引變數person_counter紀錄編號。

使用collection的好處不僅是少打字而已,還有執行效能上的大大改善,Rails內部會針對這種形式作執行效率最佳化。

相關文章
相關標籤/搜索