#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 de las novedades más importantes que trae Rails 3.1 es la herencia de plantillas, que si bien no va a revolucionar cómo escribimos nuestras vistas sí que es bastante útil y en este episodio veremos cómo funciona.
En este episodio veremos una aplicación que tiene la misma navegación en cada página.
La navigación está definida en el fichero de layout de la aplicación dentro de un elemento div
que tiene un id
con valor side
y que es estático.
<!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>
Queremos personalizar la navegación de forma que cambie dependiendo del controlador en cada momento. Hay varias maneras posibles de hacer esto, pero nosotros usaremos la herencia de plantillas. Para ellos moveremos la navegación a su propio parcial llamado side
.
<div id="side"> <%= render "side" %> </div>
¿Dónde poner este parcial para que quede accesible a todos los controladores de la aplicación? Podríamos crear un directorio llamado shared
bajo app/views
y ponerlo ahí o podríamos ponerlo bajo el directorio layouts
. Sin embargo en Rails 3.1 podemos ponerlo en un directorio app/views/application
que por defecto no se crea con la aplicación Rails 3.1, así que tendremos que hacerlo nosotros.
<strong>Pages</strong> <ul> <li><%= link_to "Home", root_path %></li> <li><%= link_to "Products", products_path %></li> </ul>
Este parcial estará disponible para todos los controladores, porque la herencia de vistas funciona de forma paralela a la herencia de controladores. Todos los controladores heredan de ApplicationController
por lo que todas las plantillas herederán del directorio application
.
Si ahora cargamos una de las páginas de nuestra aplicación veremos que la navegación funciona igual que antes.
Con esto tenemos un parcial que puede ser fácilmente redefinido para cualquier controlador de la aplicación. Tan sólo tenemos que crear un fichero parcial _side.html.erb
en el directorio normal de vistas del controlador bajo bajo app/views/
. Hagamos esto en ProductsController
, creando un parcial que tenga un enlace adicional.
<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>
Si recargamos cualquier página debajo de ProductsController
veremos este enlace. En cualquier otro controlador seguiremos viendo la navegación por defecto.
Con esto ya tenemos un lugar estándar donde dejar las plantillas que se tengan que compartir entre controladores, pudiéndose redefinir cualquiera de estas plantillas creando una plantilla con idéntico nombre en el directorio de vistas correspondiente al controlador que necesite redefinirlas.
Este enfoque también funciona con controladores anidados. Nuestra aplicación tiene varios controladores bajo el espacio de nombres Admin
, y todos ellos heredan de BaseController
.
module Admin class BaseController < ApplicationController end end
También podemos redefinir plantillas en esta vista Base
. Si hacemos clic en el enlace “Manage Products” de la página anterior iremos a la página /admin/products
y la navegación volverá a ser la que se muestra por defecto. ¿Qué hacer si queremos redefinir la navegación en todas las vistas de administración?
Bajo el directorio app/views/admin
tenemos un directorio base
. Las plantillas que pongamos ahí serán heredadas automáticamente por los otros controladores. Hagámoslo y añadamos otro enlace para poder distinguir esta plantilla de las otras.
<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>
Si recargamos la página admin/products
veremos aplicada la plantilla del directorio base
.
Esta plantilla se mostrará para cualquier página del espacio de nombres Admin
porque todas estas páginas heredan del controlador BaseController
que pertenece al espacio de nombres Admin
.
La herencia de plantillas no sólo funciona para los parciales sino también para cualquier plantilla de vista. En la carpeta app/views/admin
existe una plantilla edit.html.erb
tanto en los directorios categories
como products
. Ambas contienen el mismo código.
<h1>Edit</h1> <%= render 'form' %>
Si movemos una de estas plantillas al directorio base
y borramos la otra ambos controaldores heredarán la misma plantilla, y ambas páginas funcionarán igual que antes. Se trata de una posibilidad muy útil si tenemos que crear muchas páginas de administración para nuestra aplicación. Estas páginas por lo general tienen mucha repetición en las vistas y con esta técnica podemos abstraer dichas plantillas a un controlador base y redefinirlas cuando se necesaria para controladores concretos.
Y ya que estamos hablando de redefinir plantillas veamos a continuación otro truco que nos permite personalizar las plantillas basándonos en algo que no sea el controlador. Supongamos que queremos dar una versión móvil del sitio bajo el subdominio mobile
. Podemos cambiar las visitas a usar dependiendo del subdominio definiendo rutas de vistas distintas. Esta funcionalidad no es nueva en Rails 3 y funciona con Rails 2 pero la hemos incluido en este episodio porque encaja muy bien.
Los controladores de nuestra aplicación tienen rutas de vistas. Si invocamos a controller.view_paths
en una vista veremos cuáles son las rutas de vistas de un controlador.
<h1>Edit</h1> <%= render 'form' %> <%= controller.view_paths %>
Si recargamos la página de edición de un producto veremos listado view_paths
(para que sea más cómodo utilizar subdominios con nuestra aplicación en desarrollo utilizamos el servidor web Pow.)
El controlador tiene su ruta de vistas por defecto, pero podemos añadir más. Una forma de hacerlo es usando before_filter
en 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
En el método before_filter
utilizamos prepend_view_path
para añadir una nueva ruta de vistas basándonos en el subdominio de la petición, si es que hay tal subdominio. Podemos ver la nueva ruta de vistas cargando otra vez la página.
Vemos que ya aparece el subdominio en view_paths
. Si elminamos el subdominio mobile
de la URL y recargamos la página, éste desaparecerá. Podemos utilizar esto para pesonalizar nuestras vistas dependiendo del subdominio actual. Si creamos un nuevo directorio bajo views
llamado mobile_subdomain
podemos añadir plantillas de vistas que tendrán precedencia sobre sus equivalentes por defecto. Podemos crear un nuevo index.html.erb
bajo el directorio products
y esta plantila sólo se usará cuando se acceda desde el subdominio mobile
.
<h1>mobile version</h1>
Si vemos la página sin el subdominio veremos la versión por defecto.
Si ponemos el subdominio, se utiliza la nueva plantilla que redefine la plantilla por defecto.
Con esto concluimos este episodio que trata sobre la redefinición de plantillas, bien mediante la herencia o por la manipulación de la ruta de vistas. Ambas técnicas permiten personalizar, abstraer y redefinir todas nuestras vistas. Si necesitamos personalizar todavía más las vistas podemos hacer un decisor de vistas, pero eso es algo que dejamos para otro episodio.