Więcej na rubyonrails.pl: Start | Pobierz | Wdrożenie | Kod (en) | Screencasty | Dokumentacja | Ekosystem | Forum | IRC

Layouty i Renderowanie w Ruby on Rails

Ten przewodnik opisuje podstawowe własności szablonów (ang. templates) i layoutów (ang. layouts) w modułach Action Controller i Action View. Po jego przeczytaniu powinieneś umieć:

1 Opis zawartości: Jak wszystko razem współgra

Ten poradnik opisuje wzajemne relacje pomiędzy Kontrolerem a Widokiem we wzorcu Model-View-Controller (MVC). Jak wiesz, drogi czytelniku, Kontroler jest odpowiedzialny za nadzorowanie całego procesu obsługi żądań w Railsach, pomimo że zazwyczaj większość kodu znajduje się w warstwie modelu Modelu. Natomiast, gdy dojdzie do wysłania odpowiedzi użytkownikowi, Kontroler przekazuje to zadanie Widokowi. Tematem tego tutoriala jest właśnie to przekazywanie.

Kontroler musi zadecydować co powinno być przesłane jako odpowiedź i wywołać odpowiednią metodę by ją stworzyć. W przypadku gdy jest ona rozwiniętym szablonem, dodatkowo jest opakowywana w layout i ewentualnie są do niej dołączane inne podszablony. Te wszystkie możliwości zobaczysz w dalszej części tego przewodnika.

2 Tworzenie odpowiedzi

Kontroler może stworzyć odpowiedź HTTP dla przeglądarki na trzy sposoby:

  • wywołać render by stworzyć pełną odpowiedź do odesłania przeglądarce
  • wywołać redirect_to by wysłać kod HTTP informujący o przekierowaniu
  • wywołać head by stworzyć odpowiedź zawierającą tylko nagłówki HTTP

Wytłumaczę po kolei każdą z tych metod, ale najpierw kilka słów o najprostszej rzeczy, którą może wykonać kontroler w odpowiedzi na żądanie – nie robić nic :)

2.1 Domyślne renderowanie: strategia Convention Over Configuration w akcji

Słyszałeś zapewne, że Railsy promują strategię „convention over configuration”. Domyślny rendering jest wspaniałym tego przykładem. Standardowo, kontrolery w Railsach automatycznie renderują szablony z nazwami odpowiadającymi akcjom. Na przykład, jeśli masz poniższy kod w klasie BooksController:

def show @book = Book.find(params[:id]) end

Railsy automatycznie po wykonaniu metody wyrenderują plik app/views/books/show.html.erb. Właściwie, jeśli masz domyślną regułę catch-all w pliku config/routes.rb (map.connect ':controller/:action/:id'), Railsy wyrenderują nawet szablony, dla których nie ma żadnego kodu w kontrolerze. Na przykład, jeśli masz ustawioną domyślną drogę routowania i nadejdzie żądanie dla /books/sale_list, Railsy w odpowiedzi wyrenderują app/views/books/sale_list.html.erb.

Właściwe renderowanie jest wykonywane przez podklasę ActionView::TemplateHandlers.

Ten tutorial nie zagłębia się w ten proces, ale należy pamiętać, że rozszerzenie pliku twojego szablonu wpływa na wybór szablonu obsługi. W Railsach 2, standardowymi rozszerzeniami są .erb dla ERB (HTML z osadzonym Rubim), .rjs dla RJS (JavaScript z Rubim) i .builder dla Buildera (generator XML). Dodatkowo znajdziesz również pliki .rhtml używane dla szablonów ERB i .rxml dla szablonów Buildera, jednak obecnie te rozszerzenia są niezalecane i będą usuniętę w przyszłych wersjach Railsów.

2.2 Używanie render

W większości przypadków, metoda ActionController::Base#render wykonuje zdecydowaną większość renderowania w twojej aplikacji zaś sam proces można dostosować do własnych potrzeb. Możesz wyrenderować domyślny szablon, specyficzny szablon, fragment tekstu lub nawet (i tu niespodzianka) nic. Możesz renderować tekst, JSON lub XML. Możesz również ustalić typ zawartości lub stan HTTP wyrenderowanej odpowiedzi.

Jeśli chcesz zobaczyć dokładny wynik wywołania render bez potrzeby sprawdzania tego w przeglądarce, możesz wywołać render_to_string. Ta metoda ma dokładnie takie same opcje jak render, ale zwraca łańcuch znaków zamiast wysyłać odpowiedź bezpośrednio do przeglądarki.

2.2.1 Renderowanie niczego

Prawdopodobnie najprostszą rzeczą jaką możemy zrobić z render jest wyrenderowanie niczego.

render :nothing => true

To wywołanie wyśle pustą odpowiedź do przeglądarki (chociaż będzie ona zawierała nagłówki statusu które ustawisz za pomocą opcji :status, omówionej poniżej).

Prawdopodobnie będziesz chciał używać metody head, omówionej poniżej, zamiast render:nothing. Jest ona bardziej elastyczna i wskazuje, że generujesz tylko i wyłącznie nagłówki HTTP.

2.2.2 Renderowanie szablonu dla akcji

Jeśli chcesz wyrenderować szablon odnoszący się do innej akcji w ramach tego samego kontrolera, możesz użyć funkcji render razem z nazwą akcji:

def update @book = Book.find(params[:id]) if @book.update_attributes(params[:book]) redirect_to(@book) else render "edit" end end end

Jeśli wywołanie update_attributes się nie powiedzie, wywołanie akcji update w kontrolerze wyrenderuje szablon edit.html.erb należący do tego samego kontrolera.

Jeśli wolisz, możesz użyć symbolu zamiast łańcucha znaków, aby określić szablon do wyrenderowania.

def update @book = Book.find(params[:id]) if @book.update_attributes(params[:book]) redirect_to(@book) else render :edit end end end

Uściślając, możesz również użyć render z opcją :action (chociaż nie jest to już konieczne od Railsów 2.3):

def update @book = Book.find(params[:id]) if @book.update_attributes(params[:book]) redirect_to(@book) else render :action => "edit" end end end

Używanie render z :action jest częstym źródłem nieporozumień dla początkujących programistów. Wybrana akcja jest używana do określenia, który szablon należy wyrenderować, ale Railsy nie wykonują żadnego kodu dla tej akcji w kontrolerze. Wszystkie zmienne z których będziesz korzystać w szablonie muszą być ustawione w aktualnej akcji przed wywołaniem render.

2.2.3 Renderowanie szablonu dla akcji z innego kontrolera

Jeżeli nie wiesz co zrobić, aby wyrenderować szablon z zupełnie innego kontrolera, niż ten który zawiera kod akcji, tu znajdziesz odpowiedź. Możesz tego dokonać za pomocą render, które pobiera całą ścieżkę (relatywną do app/views/) kontrolera do wyrenderowania. Na przykład, jeśli uruchamiasz kod w AdminProductsController, który znajduje się w app/controllers/admin, możesz wyrenderować wynik akcji z wykorzystaniem szablonu app/views/products w ten sposób:

render 'products/show'

Railsy wiedzą, że szablon ten należy do innego kontrolera dzięki slashowi pojawiającemu się w łańcuchu znaków. Jeśli chcesz to dodatkowo zaznaczyć w kodzie, możesz użyć opcji :template (która była wymagana w Railsach 2.2 i wcześniejszych):

render :template => 'products/show'
2.2.4 Renderowanie dowolnego pliku

Metoda render może również korzystać z szablonu, który znajduje się zupełnie poza aplikacją (gdy prawdopodobnie dzielisz szablony między dwoma aplikacjami):

render "/u/apps/warehouse_app/current/app/views/products/show"

Railsy stwierdzą, że jest to renderowanie pliku dzięki slashowi na początku łańcucha znaków. Chcąc to dodatkowo zaznaczyć, możesz użyć opcji :file (która była wymagana w Railsach 2.2 i wcześniejszych):

render :file => "/u/apps/warehouse_app/current/app/views/products/show"

Opcja :file pobiera bezwzględną ścieżkę w systemie plików. Oczywiście, musisz mieć odpowiednie prawa odczytu szablonu, którego używasz do wyrenderowania zawartości.

Domyślnie ten plik jest renderowany bez użycia aktualnego layoutu. Jeśli chcesz by Railsy zastosowały aktualny layout musisz dodać opcję :layout => true.

Jeśli korzystasz z systemu Microsoft Windows, powinieneś używać opcji :file do renderowania pliku, ponieważ nazwy plików w Windowsie nie mają tego samego formatu jak w Uniksie.

2.2.5 Używanie render z :inline

Metoda render może obejść się bez jakiegokolwiek szablonu, jeśli tylko użyjesz opcji :inline przekazując ERB kod, który ma wyrenderować. Dlatego następujący kod jest całkowicie poprawny:

render :inline => "<% products.each do |p| %><p><%= p.name %><p><% end %>"

Rzadko jednak istnieje dobry powód by użyć tej opcji. Mieszanie ERB wewnątrz twoich kontrolerów niszczy zorientowanie Railsów na MVC i powoduje, że innym developerom jest trudniej podążać za tokiem twojego myślenia. Zamiast tego lepiej używaj oddzielnych szablonów erb.

Renderowanie typu inline domyślnie używa ERB. Możesz zmusić je by użyło zamiast tego np. Buildera, za pomocą opcji :type:

render :inline => "xml.p {'Horrid coding practice!'}", :type => :builder
2.2.6 Używanie render z :update

Możesz również renderować uaktualnienia stron oparte o javascript używając opcji :update w render:

render :update do |page| page.replace_html 'warning', "Invalid options supplied" end

Umieszczanie javascriptowych uaktualnień w twoim kontrolerze może zdawać się upraszczać małe aktualizacje, ale niszczy orientację MVC Railsów i powoduje, że trudniej jest innym zrozumieć twój kod. Proponuję używać zamiast tego oddzielnych szablonów rjs, nie ważne jak mała byłaby dana aktualizacja.

2.2.7 Renderowanie tekstu

Możesz wysyłać zwykły tekst – bez jakichkolwiek znaczników – z powrotem do przeglądarki za pomocą opcji :text:

render :text => "OK"

Rendering zwykłego tekstu jest najbardziej użyteczny kiedy odpowiadasz na AJAX-owe zapytania lub zapytanie serwisu webowego, które spodziewają się czegoś innego niż kod HTML.

Domyślnie, jeśli korzystasz z opcji :text, plik zostanie wyrenderowany bez użycia aktualnego layoutu. Jeśli chcesz by Railsy umieściły tekst wewnątrz aktualnego layoutu, musisz skorzystać z opcji :layout => true.

2.2.8 Renderowanie JSON

JSON jest formatem danych javascript wykorzystywanym przez wiele bibliotek AJAX. Railsy mają wbudowaną obsługę konwersji obiektów do JSON i wysyłania ich w tej postaci z powrotem do przeglądarki:

render :json => @product

Nie musisz wywoływać funkcji to_json na obiekcie, który chcesz wyrenderować. Jeśli użyjesz opcji :json, render automatycznie wywoła to_json za Ciebie.

2.2.9 Renderowanie XML

Railsy mają także wbudowaną obsługę konwersji obiektów XML i wysyłania ich w tej postaci z powrotem do obiektu wywołującego:

render :xml => @product

Nie musisz wywoływać funkcji to_xml na obiekcie, który chcesz wyrenderować. Jeśli użyjesz opcji :xml, render automatycznie wywoła to_xml za Ciebie.

2.2.10 Renderowanie czystego JavaScript

Railsy mogą renderować czysty JavaScript (jako alternatywę do używania update z plikiem .rjs):

render :js => "alert('Hello Rails');"

Spowoduje to wysłanie przekazanego ciągu do przeglądarki z typem MIME text/javascript.

2.2.11 Opcje dla render

Wywołanie metody render akceptuje cztery opcje:

  • :content_type
  • :layout
  • :status
  • :location
2.2.11.1 Opcja :content_type

Domyślnie Railsy dostarczą wynik operacji renderowania z typem zawartości MIME text/html (application/json jeśli używasz opcji :json lub application/xml dla opcji :xml). Możliwe, że będziesz chciał to zmienić. Możesz to zrobić poprzez ustawienie opcji :content_type:

render :file => filename, :content_type => 'application/rss'
2.2.11.2 Opcja :layout

Większość opcji renderowania wyświetla zawartość jako część aktualnego layoutu. Poznasz dokładniej layouty i jak z nich korzystać w dalszej części tego przewodnika.

Możesz użyć opcji :layout by zakomunikować Railsom, by użyły określonego pliku jako layout dla bieżącej akcji.

render :layout => 'special_layout'

Możesz również powiedzieć Railsom, by renderować bez jakiegokolwiek layoutu:

render :layout => false
2.2.11.3 Opcja :status

Railsy mogą automatycznie generować odpowiedzi z poprawnym kodem HTML (w większości przypadków 200 OK). Aby to zmienić możesz użyć opcji :status:

render :status => 500 render :status => :forbidden

Railsy rozumieją zarówno liczbowe kody stanu jak i odpowiadające im symbole. Listę kodów stanu możesz znaleźć w actionpack/lib/action_controller/status_codes.rb. Możesz również zobaczyć tam jak mapowane są symbole na kody stanu.

2.2.11.4 Opcja :location

Możesz używać opcji :location by ustawić nagłówek HTTP Location:

render :xml => photo, :location => photo_url(photo)
2.2.12 Znajdowanie Layoutów

Aby znaleźć bieżący layout, Railsy najpierw szukają pliku w app/views/layouts z taką samą nazwą bazową jak kontroler. Na przykład, renderowanie akcji z klasy PhotosController użyje /app/views/layouts/photos.html.erb. Jeśli nie ma specyficznego layoutu dla danego kontrolera Railsy użyją /app/views/layouts/application.html.erb. Jeśli nie ma layoutu .erb, Railsy użyją layoutu .builder jeśli taki istnieje. Railsy również dostarczają kilku sposobów na bardziej precyzyjne przypisanie konkretnego layoutu do poszczególnych kontrolerów i akcji.

2.2.12.1 Określenie layoutów na poziomie kontrolera

Możesz zmienić domyślny sposób wyboru layoutów w swoim kontrolerze za pomocą deklaracji layout. Na przykład:

class ProductsController < ApplicationController layout "inventory" #... end

Z tą deklaracją, wszystkie metody w ProductsController będą korzystać z app/views/layouts/inventory.html.erb jako swojego layoutu.

Aby przypisać konkretny layout dla całej aplikacji, użyj deklaracji w klasie ApplicationController:

class ApplicationController < ActionController::Base layout "main" #... end

Z tą deklaracją, wszystkie szablony w całej aplikacji będą korzystać z app/views/layouts/main.html.erb jako swojego layoutu.

2.2.12.2 Wybór layoutu w czasie wykonania

Możesz użyć symbolu dla odroczenia wyboru layoutu, aż do czasu w którym przetwarzane jest zapytanie:

class ProductsController < ApplicationController layout :products_layout def show @product = Product.find(params[:id]) end private def products_layout @current_user.special? ? "special" : "products" end end

Teraz, jeśli bieżący użytkownik jest użytkownikiem specjalnym, to otrzyma specjalny layout podczas przeglądania produktów. Możesz nawet użyć metody inline do wyboru layoutu:

class ProductsController < ApplicationController layout proc{ |controller| controller. # ... end
2.2.12.3 Warunkowe layouty

Layouty określone w kontrolerze obsługują opcje :only i :except, które akceptują albo pojedynczą nazwę akcji albo tablicę nazw:

class ProductsController < ApplicationController layout "inventory", :only => :index layout "product", :except => [:index, :rss] #... end

Z tymi deklaracjami, layout inventory byłby wykorzystywany tylko do akcji index, layout product byłby wykorzystywany do wszystkiego oprócz akcji rss, a akcja rss będzie mieć layout określony poprzez domyślne zasady wyboru.

2.2.12.4 Dziedziczenie layoutów

Layouty są dzielone w dół hierarchii dziedziczenia zaś bardziej konkretne layouty zawsze zasłaniają te bardziej ogólne. Na przykład:

application_controller.rb:

class ApplicationController < ActionController::Base layout "main" #... end

posts_controller.rb:

class PostsController < ApplicationController # ... end

special_posts_controller.rb:

class SpecialPostsController < PostsController layout "special" # ... end

old_posts_controller.rb:

class OldPostsController < SpecialPostsController layout nil def show @post = Post.find(params[:id]) end def index @old_posts = Post.older render :layout => "old" end # ... end

W tej aplikacji:

  • domyślnie szablony będą renderowane z wykorzystaniem layoutu main
  • PostController#index będzie używać layoutu main
  • SpecialPostController#index będzie używać layoutu special
  • 01dPostsController#show nie będzie używać żadnego layoutu
  • 01dPostsController#index będzie używać layoutu old
2.2.13 Unikanie błędów podwójnego renderowania

Prędzej czy później, większość programistów Railsów zobaczy komunikat o błędzie „Can only render or redirect once per action”. Chociaż jest to irytujące, to stosunkowo łatwe do naprawienia. Zwykle dzieje się to z powodu niezrozumienia podstaw działania metody render.

Na przykład, oto fragment kodu, który powoduje ten błąd:

def show @book = Book.find(params[:id]) if @book.special? render :action => "special_show" end end

Jeśli @book.special? ewaluuje do true, Railsy rozpoczną proces renderowania przez wrzucenie zmiennej @book do szablonu special_show. Ale to nie zatrzyma reszty kodu w akcji show przed wykonaniem i jeśli Railsy dojdą do końca akcji to nastąpi próba wyrenderowania szablonu show co spowoduje błąd. Rozwiązanie jest proste: upewnij się, że masz tylko jedno wywołanie render lub redirect w jednej części kodu. Jedną z rzeczy, które mogą pomóc jest and return. Oto poprawiona wersja metody:

def show @book = Book.find(params[:id]) if @book.special? render :action => "special_show" and return end end

2.3 Używanie redirect_to

Innym sposobem obsługi odpowiedzi na żądanie HTTP jest użycie redirect_to. Jak widziałeś, render informuje Railsy, który szablon użyć do konstrukcji odpowiedzi. Metoda redirect_to robi coś zupełnie innego: mówi przeglądarce by wysłać nowe zapytanie pod inny adres URL. Na przykład można przekierować z dowolnego miejsca w kodzie do indeksu zdjęć twojej aplikacji za pomocą tego wywołania:

redirect_to photos_path

Możesz korzystać z redirect_to z wszystkimi argumentami które mogłeś użyć w link_to lub url_for. W dodatku, istnieje specjalne przekierowanie, które wysyła użytkownika z powrotem tam skąd przyszedł:

redirect_to :back
2.3.1 Uzyskanie innego kodu statusu przekierowania

Railsy wykorzystują kod statusu HTTP 302 (stałe przekierowanie), gdy wywołujesz redirect_to. Jeśli chciałbyś użyć innego kodu statusu (prawdopodobnie 301, tymczasowe przekierowanie) możesz to zrobić za pomocą opcji :status:

redirect_to photos_path, :status => 301

Podobnie jak opcja :status dla render, :status dla redirect_to akceptuje zarówno numeryczne jak i symboliczne oznaczenia stanu.

2.3.2 Różnica pomiędzy render a redirect

Czasami niedoświadczony developer traktuje redirect_to jako swego rodzaju polecenie goto, przenosząc wykonanie programu z jednego miejsca do innego w kodzie. To nie jest prawidłowe. Twój kod przestaje się wykonywać i czeka na nowe zapytanie przeglądarki. Po prostu mówisz przeglądarce jakie następne zapytanie powinna zrobić poprzez wysłanie kodu HTTP 302.

Przeanalizuj te działania by zobaczyć różnicę:

def index @books = Book.find(:all) end def show @book = Book.find(params[:id]) if @book.nil? render :action => "index" and return end end

Kod w tej formie prawdopodobnie będzie stanowić problem, jeśli zmienna @book jest ustawiona na nil. Pamiętaj, render :action nie uruchamia żadnego kodu w docelowej akcji, więc nic nie ustawi zmiennej @books na wartość, której oczekuje szablonu index. Jednym sposobem aby to naprawić jest przekierowanie zamiast renderowania:

def index @books = Book.find(:all) end def show @book = Book.find(params[:id]) if @book.nil? redirect_to :action => "index" and return end end

Ten kod spowoduje, że przeglądarka wyśle zapytanie o stronę index. Następnie zostanie uruchomiony odpowiedni kod w metodzie index i wszystko zadziała tak jak tego oczekujemy.

2.4 Używanie head do budowania odpowiedzi tylko z nagłówkami

Metoda head istnieje po to by odesłać do przeglądarki odpowiedzi zawierające tylko nagłówki. Zapewnia to bardziej oczywistą alternatywę dla render :nothing. Metoda head bierze jedną odpowiedź, która jest interpretowana jako hash z nazw nagłówków i wartości. Na przykład możesz zwrócić tylko nagłówek błędu:

head :bad_request

Można też korzystać z innych nagłówków HTTP do przekazania dodatkowych informacji:

head :created, :location => photo_path(@photo)

3 Strukturyzacja layoutów

Kiedy Railsy renderują szablon, łączą go z bieżącym layoutem (stosując zasady wyboru layoutu, które zostały omówione we wcześniejszej części tutoriala). W layoucie masz dostęp do trzech narzędzi służących do łączenia różnych elementów do postaci wynikowej:

  • asset tags
  • yield i content_for
  • podszablony

Omówię każdą z nich po kolei.

3.1 Asset Tags

Helpery asset tags dostarczają metod do generowania kodu HTML, który łączy szablon z elementami zewnętrznymi takimi jak obrazki, javascript, arkusze stylów i kanały. Istnieją cztery takie helpery:

  • auto_discovery_link_tag
  • javascript_include_tag
  • stylesheet_link_tag
  • image_tag

Możesz używać tych tagów w layoutach lub innych szablonach, choć tagi inne niż image_tag są powszechnie używane w sekcji layoutu.

Helpery asset tags nie weryfikują tego czy dany element zewnętrzny istnieje w określonym miejscu. Po prostu zakładają, że wiesz co robisz i generują link.

auto_discovery_link_tag helper buduje HTML, który większość przeglądarek i czytników wiadomości może użyć do sprawdzenia czy istnieją kanały RSS lub ATOM. Wywołanie to akceptuje typ połączenia (:rss or :atom), hash opcji, które są przekazywane do url_for i hash opcji dla tagu:

<%= auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "RSS Feed"}) %>

Istnieją trzy opcje dostępne dla auto_discovery_link_tag:

  • :rel określa wartość rel w połączeniu (domyślnie ustawione na “alternate”)
  • :type określa dokładny typ MIME. Railsy automatycznie wygenerują odpowiedni typ MIME
  • :title określa tytuł kanału
3.1.2 Włączanie plików JavaScript za pomocą javascript_include_tag

Helper javascript_include_tag zwraca tag HTML <script> dla każdego dostarczonego źródła. Railsy szukają tych plików domyślnie w public/javascripts, ale jeśli wolisz można podać pełną ścieżkę w stosunku do głównego katalogu lub adresu URL. Na przykład aby włączyć public/javascripts/main.js:

<%= javascript_include_tag "main" %>

By włączyć public/javascripts/main.js i public/javascripts/columns.js:

<%= javascript_include_tag "main", "columns" %>

By włączyć public/javascripts/main.js i public/photos/columns.js:

<%= javascript_include_tag "main", "/photos/columns" %>

By włączyć http://example.com/main.js:

<%= javascript_include_tag "http://example.com/main.js" %>

Opcja defaults wczytuje biblioteki Prototype i Scriptaculous:

<%= javascript_include_tag :defaults %>

Opcja all wczytuje każdy plik javascript w public/javascripts, zaczynając od bibliotek Prototype i Scriptaculous:

<%= javascript_include_tag :all %>

Możesz dołączyć opcję :recursive by ładować również pliki w podfolderach public/javascripts:

<%= javascript_include_tag :all, :recursive => true %>

Jeśli ładujesz wiele plików javascript, możesz polepszyć efektywność, poprzez połączenie ich w pojedyńczy plik do ściągnięcia. By zrobić to w czasie wykonania, zaznacz :cache => true wewnątrz javascript_include_tag:

<%= javascript_include_tag "main", "columns", :cache => true %>

By default, the combined file will be delivered as javascripts/all.js. You can specify a location for the cached asset file instead:

<%= javascript_include_tag "main", "columns", :cache => 'cache/main/display' %>

>>>>>>> 6398eba2400af004d9db19217f9d43ea7a33d877

Domyślnie połączony plik będzie dostarczony jako javascripts/all.js. Możesz zamiast tego wybrać lokalizację dla załączanego pliku:

<%= javascript_include_tag "main", "columns", :cache => 'cache/main/display' %>

Możesz również używać dynamicznych ścieżek takich jak "cache/#{current_site}/main/display".

Helper stylesheet_link_tag zwraca tag HTML dla każdego dostarczonego źródła. Railsy szukają tych plików domyślnie w public/stylesheets, ale jeśli wolisz, można podać pełną ścieżkę w stosunku do głównego katalogu lub adresu URL. Na przykład aby włączyć public/stylesheets/main.cs:

<%= stylesheet_link_tag "main" %>

By włączyć public/stylesheets/main.css i public/stylesheets/columns.css:

<%= stylesheet_link_tag "main", "columns" %>

By włączyć public/stylesheets/main.css i public/photos/columns.css:

<%= stylesheet_link_tag "main", "/photos/columns" %>

By włączyć http://example.com/main.cs:

<%= stylesheet_link_tag "http://example.com/main.cs" %>

Domyślnie stylesheet_link_tag tworzy połączenia z media=“screen” rel=“stylesheet” type=“text/css”. Możesz nadpisać dowolne z nich poprzez użycie odpowiedniej opcji (:media, :rel, lub :type):

<%= stylesheet_link_tag "main_print", media => "print" %>

Opcja all załącza każdy plik CSS w public/stylesheets:

<%= stylesheet_link_tag :all %>

Możesz wykorzystać opcję :recursive by załączyć wszystkie pliki w podfolderach public/stylesheets:

<%= stylesheet_link_tag :all, :recursive => true %>

Jeśli wczytujesz wiele plików CSS, możesz polepszyć efektywność poprzez połączenie ich w pojedynczy plik do ściągnięcia. By zrobić to w czasie wykonania, zaznacz :cache => true wewnątrz stylesheet_link_tag:

<%= stylesheet_link_tag "main", "columns", :cache => true %>

By default, the combined file will be delivered as stylesheets/all.css. You can specify a location for the cached asset file instead:

<%= stylesheet_link_tag "main", "columns", :cache => 'cache/main/display' %>

>>>>>>> 6398eba2400af004d9db19217f9d43ea7a33d877

Domyślnie połączony plik będzie dostarczony jako stylesheets/all.css. Możesz zamiast tego wybrać lokalizację dla załączanego pliku:

<%= stylesheet_link_tag "main", "columns", :cache => 'cache/main/display' %>

Możesz również użyć dynamicznych ścieżek takich jak: "cache/#{current_site}/main/display".

3.1.4 Załączanie obrazków za pomocą image_tag

Helper image_tag zwraca tag HTML dla każdego dostarczonego źródła. Railsy szukają tych plików domyślnie w public/images. Jeśli nie podasz rozszerzenia domyślnie przyjmowane jest .png:

<%= image_tag "header" %>

Możesz podać ścieżkę do obrazka:

<%= image_tag "icons/delete.gif" %>

Możesz podać hash dodatkowych opcji HTML:

<%= image_tag "icons/delete.gif", :height => 45 %>

Istnieją również trzy specjalne opcje, które można używać z image_tag:

  • :alt określa alternatywny tekst dla obrazka (który domyślnie jest nazwą pliku, napisaną wielkimi literami bez rozszerzenia)
  • :size ustawia zarówno szerokość jak i wysokość, w formacie width x height (na przykład “150×125”)
  • :mouseover ustawia alternatywny obrazek, który ma być użyty w przypadku, gdy wywołane jest zdarzenie onmouseover

3.2 Zrozumienie yield

W ramach kontekstu layoutu, yield identyfikuje sekcję do której powinna być wstawiona zawartość szablonu. Najprostszym sposobem użycia jest posiadanie jednego yield, w którym powinna być wstawiana cała zawartość obecnie renderowanego szablonu.

<html> <head> </head> <body> <%= yield %> <hbody> </html>

Można również tworzyć layout z wieloma miejscami wstawiania kodu.

<html> <head> <%= yield :head %> </head> <body> <%= yield %> <hbody> </html>

Główna część szablonu będzie zawsze renderowana do yield bez argumentu. Aby wyrenderować zawartość w nazwanym yield, należy użyć metody content_for.

3.3 Używanie content_for

Metoda ta pozwala wstawić treść do bloku yield w twoim layoucie. Używa się content_for tylko po to by wstawić zawartość w nazwane yieldy. Dla przykładu, ten szablon będzie współpracował z powyższym layoutem:

<% content_for :head do %> <title>A simple page</title> <% end %> <p>Hello, Rails!</p>

Wynik renderowania tej strony z tym layoutem powinien być taki:

<html> <head> <title>A simple page</title> </head> <body> <p>Hello, Rails!</p> <hbody> </html>

Metoda content_for jest bardzo przydatna kiedy twój layout posiada odrębne regiony, takie jak paski boczne i stopki, które powinny mieć własne bloki zawartości. Jest to także przydatne do wstawiania tagów które wczytują specyficzny dla danej strony plik javascript lub css w nagłówek layoutu.

3.4 Używanie podszablonów

Częściowe szablony (ang. partial templates) – zazwyczaj nazywane po prostu podszablonami lub “parszalami” – są kolejnym narzędziem służącym do dzielenia procesu renderowania na mniejsze kawałki. Za pomocą podszablonów, możesz przenieść kod renderujący odpowiedniej części odpowiedzi do jego własnego pliku.

3.4.1 Nazywanie podszablonów

By wyrenderować podszablon jako część szablonu, musisz użyć metody render wewnątrz tego szablonu i dołączyć opcję :partial:

<%= render :partial => "menu" %>

To wyrenderuje plik nazwany _menu.html.erb w tym momencie wewnątrz aktualnie renderowanego szablonu. Zauważ początkowy znak podkreślenia: podszablony są nazywane w ten sposób by odróżnić je od zwyczajnych szablonów, nawet jeśli odnosi się do nich bez podkreślenia. To działa nawet jeśli włączasz podszablon z zupełnie innego folderu:

<%= render :partial => "shared/menu" %>

Ten kod włączy podszablon z app/views/shared/_menu.html.erb.

3.4.2 Korzystanie z podszablonów do uproszczenia szablonów

Jednym ze sposobów wykorzystania podszablonów jest traktowanie ich jako odpowiedników procedur: jako sposób aby przenieść detale na zewnątrz szablonu, tak by łatwiej zrozumieć co się dzieje wewnątrz. Na przykład, możesz mieć szablon, który wygląda w ten sposób:

<%= render :partial => "shared/ad_banner" %>
<h1>Products</h1> <p>Here are a few of our fine products:</p> ... <%= render :partial => "shared/footer" %>

Tutaj, podszablony _ad_banner.html.erb i _footer.html.erb mogą zawierać kod, który jest dzielona między wiele stron w twojej aplikacji. Nie ma potrzeby aby widzieć szczegóły tych sekcji, kiedy koncentrujesz się na konkretnej stronie.

W przypadku treści, które są dzielone pomiędzy wszystkie strony twojej aplikacji możesz użyć podszablonów bezpośrednio z layoutów.

3.4.3 Layouty podszablonów

Podszablon może użyć swojego własnego pliku layoutu, tak samo jak może to robić zwykły szablon. Na przykład możesz odwołać się do niego w ten sposób:

<%= render :partial => "link_area", :layout => "graybar" %>

Będzie to szukać podszablonu nazwanego _link_area.html.erb i wyrenderuje go używając _graybar.html.erb. Zauważ, że layouty dla podszablonów podlegają tym samym zasadom nazewnictwa co zwykłe podszablony i są umiejscowione w tym samym folderze, w którym znajdują się im odpowiadające podszablony (nie w głównym folderze layouts).

3.4.4 Przekazywanie lokalnych zmiennych

Możesz również przekazać lokalne zmienne do podszablonów, czyniąc z nich bardziej potężne i elastyczne narzędzie. Na przykład możesz użyć tej techniki do zredukowania powtarzania pomiędzy stronami new i edit, wciąż jednak utrzymując w nich nieco odmienną treść:

new.html.erb:

<h1>New zone</h1> <%= error_messages_for :zone %> <%= render :partial => "form", :locals => { :button_label => "Create zone", :zone => @zone } %>

edit.html.erb:

<h1>Editing zone</h1> <%= error_messages_for :zone %> <%= render :partial => "form", :locals => { :button_label => "Update zone", :zone => @zone } %>
_form.html.erb:
<% form_for(zone) do |f| %> <p> <b>Zone name</b><br /> <%= f.text_field :name %> </p> <p> <%= f.submit button_label %> </p> <% end %>

Jakkolwiek ten sam podszablon będzie renderowany w obu szablonach, etykieta na przycisku wysyłającym jest kontrolowana przez lokalną zmienną przekazywaną do podszablonów.

Każdy podszablon ma także lokalną zmienną z taką samą nazwą jak on sam (minus podkreślenie). Możesz przekazać obiekt do tej zmiennej za pomocą opcji :object.

<%= render :partial => "customer", :object => @new_customer %>

Wewnątrz podszablonu customer, zmienna customer będzie odwoływać się do @new_customer przekazanej od rodzica.

W poprzednich wersjach Railsów, domyślna zmienna lokalna szukała w szablonie nadrzędnym zmiennej instancyjnej o takiej samej nazwie jak podszablon. Takie zachowanie jest niezalecane w Railsach 2.2 i będzie usunięte w przyszłych wersjach.

Jeśli masz instancje modelu do wyrenderowania w podszablonie, możesz użyć skróconego zapisu:

<%= render :partial => @customer %>

Zakładając, że zmienna @customer zawiera instancję modelu Customer, do renderowania użyty zostanie _customer.html.erb.

3.4.5 Renderowanie kolekcji

Podszablony są bardzo przydatne w renderowaniu kolekcji. Kiedy przekazujesz kolekcje do podszablonu poprzez opcję :collection, podszablon będzie wstawiony do każdego elementu kolekcji.

index.html.erb:

<h1>Products</h1> <%= render :partial => "product", :collection => @products %>

_product.html.erb:

<p>Product Name: <%= product.name %></p>

Kiedy podszablon jest renderowany z kolekcją, to każda indywidualna instancja podszablonu ma dostęp do renderowanego elementu kolekcji poprzez zmienną nazwaną tak jak podszablon. W tym przypadku, podszablonem jest _product, i wewnątrz niego możesz odwołać się do zmiennej product by uzyskać aktualnie renderowaną instancję. Aby użyć innej nazwy zmiennej wewnątrz podszablonu, użyj opcji :as podczas jego wywołania:

<%= render :partial => "product", :collection => @products, :as => :item %>

Dzięki tej zmianie wewnątrz podszablonu można uzyskać dostęp do instancji kolekcji @product przez lokalną zmienną item.

Railsy tworzą również zmienną licznikową dostępną wewnątrz podszablonu wywołanego przez kolekcję, nazwaną tak jak element kolekcji z dodanym sufiksem _counter. Na przykład, jeśli renderujesz @products wewnątrz podszablonu możesz odwołać się do product_counter by dowiedzieć się ile razy podszablon został wyrenderowany.

Możesz również określić drugi podszablon do wyrenderowania pomiędzy instancjami głównego podszablonu poprzez użycie opcji :spacer_template:

<%= render :partial => "product", :collection => @products, :spacer_template => "product_ruler" %>

Railsy wyrenderują podszablon _product_ruler (bez danych przekazywanych do niego) pomiędzy każdą parą podszablonów _product.

Jest również dostępna skrócona wersja zapisu dla renderowania kolekcji. Na przykład, jeśli @products jest kolekcją produktów to możesz wyrenderować ją w ten sposób:

index.html.erb:

<h1>Products</h1> <%= render :partial => @products %>

_product.html.erb:

<p>Product Name: <%= product.name %></p>

Railsy określają nazwę podszablonu do użycia, bazując na nazwie modelu w kolekcji. W istocie, można również stworzyć różnorodną kolekcję i renderować ją w ten sposób, a Railsy wybierają właściwy podszablon dla każdego członka kolekcji.

index.html.erb:

<h1>Contacts</h1> <%= render :partial => [customer1, employee1, customer2, employee2] %>

_customer.html.erb:

<p>Name: <%= customer.name %></p>

_employee.html.erb:

<p>Name: <%= employee.name %></p>

W tym przypadku, Railsy użyją podszablonu customer lub employee jako odpowiednie dla każdego elementu kolekcji.

3.5 Korzystanie z zagnieżdżonych layoutów

Może się okazać, że twoja aplikacja w jednym kontrolerze wymaga layoutu nieco różniącego się od standardowego layoutu twojej aplikacji. Rozwiązaniem, zamiast edycji kopii głównego layoutu, może być wykorzystanie mechanizmu zagnieżdżonych layoutów. Oto przykład:

Załóżmy, że masz następujący layout dla ApplicationController:

app/views/layouts/application.erb
<html> <head> <title><%= @page_title %><title> <% stylesheet_tag 'layout' %> <style type="text/css"><%= yield :stylesheets %></style> <head> <body> <div id="top_menu">Top menu items here</div> <div id="menu">Menu items here</div> <div id="content"><%= yield :content %></div> <div id="main"><%= yield %></div> </body> </html>

Na stronach generowanych przez NewsController, chciałbyś ukryć górne menu i dodać prawe:

app/views/layouts/news.erb
<% content_for :stylesheets do %> #top_menu {display: none} #right_menu {float: right; background-color: yellow; color: black} <% end -%> <% content_for :content %> <div id="right_menu">Right menu items here</div> <% end -%> <% render :file => 'layouts/application' %>

W wersjach Railsów wcześniejszych niż 2.3, powinieneś użyć render 'layouts/applications' zamiast render :file => 'layouts/applications'

I to wszystko. Szablon news będzie używał nowego layoutu, ukrywającego górne menu i dodającego prawe menu wewnątrz sekcji content.

Istnieje kilka sposobów otrzymania podobnych wyników za pomocą różnych schematów wykorzystania zagnieżdżonych layoutów wykorzystujących tą technikę. Należy pamiętać, że nie ma limitu poziomu zagnieżdżenia. Można wykorzystać metodę ActionView::render poprzez render 'layouts/news' by ulokować nowy layout w layoucie News.

4 Changelog

Lighthouse ticket

  • December 27, 2008: Merge patch from Rodrigo Rosenfeld Rosas covering subtemplates
  • December 27, 2008: Information on new rendering defaults by Mike Gunderloy
  • November 9, 2008: Added partial collection counter by Mike Gunderloy
  • November 1, 2008: Added :js option for render by Mike Gunderloy
  • October 16, 2008: Ready for publication by Mike Gunderloy
  • October 4, 2008: Additional info on partials (:object, :as, and :spacer_template) by Mike Gunderloy (not yet approved for publication)
  • September 28, 2008: First draft by Mike Gunderloy (not yet approved for publication)