#269 Template Inheritance
- Download:
- source codeProject Files in Zip (106 KB)
- mp4Full Size H.264 Video (14.1 MB)
- m4vSmaller H.264 Video (8.77 MB)
- webmFull Size VP8 Video (10.3 MB)
- ogvFull Size Theora Video (19.7 MB)
Una delle nuove features di Rails 3.1 è l’eredietarità dei template. Questa non rivoluzionerà il modo in cui scrivi le tue views, ma può risultare molto utile e in questo episodio ti mostreremo come funziona.
In questo episodio abbiamo una applicazione con lo stesso menu di navigazione in ogni pagina.
Il menu di navigazione è al momento definito nel file di layout dell’applicazione dentro un elemento div
con id
uguale a side
ed è statico.
<!DOCTYPE html> <html> <head> <title>Store</title> <%= stylesheet_link_tag "application" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> </head> <body> <div id="container"> <% flash.each do |name, msg| %> <%= content_tag :div, msg, :id => "flash_#{name}" %> <% end %> <div id="side"> <strong>Pages</strong> <ul> <li><%= link_to "Home", root_path %></li> <li><%= link_to "Products", products_path %></li> </ul> </div> <%= yield %> <div class="clear"></div> </div> </body> </html>
Vogliamo personalizzare il menù in maniera tale cha cambi in base all’attuale controller . Esistono diversi modi per fare questo e noi useremo l’ereditarietà dei template. Per prima cosa spostiamo il menù in un partial chiamato “side”.
<div id="side"> <%= render "side" %> </div>
Dove dovremmo mettere questo partial in modo tale che qualunque controller possa usarlo? Potremmo metterlo dentro la cartella views/shared
oppure dentro layouts
ma in Rails 3.1 possiamo salvarlo invece dentro views/application
. Questa cartella non esiste di default con Rails 3, dobbiamo quindi crearla.
<strong>Pages</strong> <ul> <li><%= link_to "Home", root_path %></li> <li><%= link_to "Products", products_path %></li> </ul>
Il partial side
può ora essere utilizzato da tutti i controller perchè l’ereditarietà delle views funziona allo stesso modo di quella dei controller. Tutti i controller ereditano da ApplicationController
quindi tutti i template che si trovano nella cartella application
saranno ereditati.
Se adesso carichiamo una delle pagine della nostra applicazione il menu di navigazione funziona come prima.
Abbiamo adesso un partial che possiamo facilmente sovrascrivere in qualunque controller. Per fare questo bisogna creare un file chiamato _side.html.erb
nella cartella views
del controller in questione. Faremo questo per ProductsController
, creando un partial contenente un link in più.
<strong>Pages</strong> <ul> <li><%= link_to "Home", root_path %></li> <li><%= link_to "Products", products_path %></li> <li><%= link_to "Manage Products", admin_products_path %></li> </ul>
Caricando qualunque pagina del ProductsController
il nuovo link sarà visibile, mentre tutti gli altri controller continueranno ad usare il menù di default.
Abbiamo adesso un posto dove salvare i templates che saranno condivisi da tutti i controller e possiamo sovrascrivere ciascuno di essi creando un template con lo stesso nome nella cartella views
del controller desiderato.
Questo approccio funziona anche con controller annidati. La nostra applicazione ha diversi controller sotto il namespace Admin
, ciascuno dei quali eredita da BaseController
.
module Admin class BaseController < ApplicationController end end
Possiamo sovrascrivere anche i templates che si trovano in Base
view. Se clicchiamo il link “Manage Products” nella pagina di sopra ci sposteremo nella pagina /admin/products
e il menù di navigazione sarà di nuovo quello di default. Cosa dovremmo fare se volessimo sovrascrivere il menù per tutte le pagine di amministrazione?
Dentro /app/views/admin
troviamo la cartella base
. Possiamo salvare dei templates qui e questi saranno automaticamente ereditati dagli altri controller. Faremo questo aggiungendo un link per distinguere questo template dagli altri.
<strong>Pages</strong> <ul> <li><%= link_to "Home", root_path %></li> <li><%= link_to "Products", products_path %></li> <li><%= link_to "Manage Products", admin_products_path %></li> <li><%= link_to "Manage Categories", admin_categories_path %></li> </ul>
Se ricarichiamo la pagina admin/products
vedremo applicato il template dalla cartella base
.
Questo template sarà utilizzato in tutte le pagine del namespace Admin
poichè queste ereditano dal BaseController
del namespace Admin
.
L’ereditarietà dei template funziona non solo per i partial ma per qualunque template view. Nella cartella app/views/admin
c’è un template edit.html.erb
per entrambe le cartelle categories
e products
. Entrambe contengono lo stesso codice.
<h1>Edit</h1> <%= render 'form' %>
Se spostiamo uno di questi template nella cartella base
e rimuoviamo l’altro, entrambi i controller erediteranno dallo stesso. Entrambe le pagine funzionano nella stessa maniera di prima. Questo è molto utile nel caso in cui la nostra applicazione contiene molte pagine di amministrazione. Di solito queste pagine hanno molte cose in comune e usando questa tecnica possiamo astrarre i templates in un controller di base e sovrascriverli se necessario.
E siccome stiamo parlando di come è possibile sovrascrivere templates, ti mostreremo un altro trucco che ti permette di personalizzare templates in base a dei parametri. Ammettiamo che vogliamo fornire una versione mobile del sito usando il sottodominio mobile
. Potremmo cambiare le views in base al sottodominio e analizzando l’url. Questa feature non è nuova in Rails 3.1 e funziona anche in Rails 2 ma siccome si sposa bene con l’argomento ne parleremo adesso.
Nei controller dell’applicazione abbiamo qualcosa chiamato view paths. Se chiamiamo controller.view_paths
in una view possiamo vedere quale sia il cammino per le views di quel controller.
<h1>Edit</h1> <%= render 'form' %> <%= controller.view_paths %>
Se ricarichiamo la pagina troveremo la lista delle view_paths
. (Per facilitare l’uso di sottodomini stiamo usando Pow web server durante lo sviluppo della nostra applicazione.)
Questo controller ha una view path di default ma possiamo aggiungerne altre. Un modo per aggiungerne una è usare un before_filter
nel nostro ApplicationController
.
class ApplicationController < ActionController::Base protect_from_forgery before_filter :subdomain_view_path def subdomain_view_path if request.subdomain.present? prepend_view_path "app/views/#{request.subdomain}_subdomain" end end end
Nel metodo before_filter
abbiamo usato prepend_view_path
per aggiungere una nuova view path in base al sottodominio della richiesta, se questo è presente. Possiamo vedere la nuova view path ricaricando la pagina.
Il sottodominio mobile viene adesso incluso nella lista delle view_paths
. Se rimuoviamo il sottodominio mobile
dall’ URL di sopra e ricarichiamo la pagina, questo scomparirà anche dalla lista delle view_paths. Questo trucco può essere usato per personalizzare le views in base al sottodominio. Se creiamo una nuova cartella sotto views
e la chiamiamo mobile_subdomain
possiamo aggiungervi view templates che sovrascrivono i template di default. Creeremo un nuovo index.html.erb
template dentro la nuova cartella products
e questo template sarà usato solo in questa pagina quando il sottodominio mobile viene visitato.
<h1>mobile version</h1>
Se rimuoviamo il sottodominio vedremo di nuovo la version di default.
Tuttavia quando aggiungiamo il sottodominio il nuovo template viene utilizzato sovrascrivendo quello di default.
Questo è tutto per questo episodio su come sovrascrivere i template, sia nel caso in cui sia fatto con l’ereditarietà che in quello in cui il cammino della richiesta venga analizzato. Entrambi i metodi forniscono molte possibilitè di personalizzazione e diversi metodi per astrarre views e sovrascriverle. Se hai bisogno di ancora più personalizzazione puoi creare un view resolver ma parleremo di questo in un altro episodio.