#213 Calendars
- Download:
- source codeProject Files in Zip (104 KB)
- mp4Full Size H.264 Video (16.5 MB)
- m4vSmaller H.264 Video (10.7 MB)
- webmFull Size VP8 Video (27.3 MB)
- ogvFull Size Theora Video (24.3 MB)
Si nuestra aplicación maneja fechas, con frecuencia tendremos que mostrar un calendario en la interfaz de usuario. A los usuarios les será mucho más fácil introducir fechas en un campo concreto escogiendolas de un calendario en una ventana emergente que de una serie de listas desplegables o escribiéndolas directamente en una campo de texto. Además, si nuestra aplicación tiene muchos registros con un campo de fecha podemos proporcionar una vista de calendario que permita mostrar estos elementos . Veremos como hacer todo esto en este episodio.
La aplicación con la que vamos a trabajar es una aplicación de blog sencilla que tiene varios artículos, pero esto nos podría servir para casi cualquier aplicación que requiera en algún punto introducir fechas. Cuando creemos o editemos un artículo podemos escoger una fecha de publicación utilizando el selector de fechas clásico de Rails. Vamos a cambiar esto para que podamos utilizar un calendario emergente como forma alternativa de escoger la fecha.
Es complicado programar una vista de calendario desde el principio pero hay varias soluciones disponibles que nos permiten hacer casi cualquier cosa que queramos con calendarios y fechas. Una de ellas es calendar_date_select plugin que proporciona un método helper que funciona con la librería Prototype para mostrar un calendario usando JavaScript. Pero no lo vamos a hacer así, en sulugar utilizaremos una solución diferente que utiliza JavaScript no intrusivo y utiliza jQuery. Aunque no se trata de una solución específica de Rails la hemos escogido porque permite mostrarnos de forma sencila cómo añadir el calendario a un campo de fecha.
La librería jQuery UI proporciona un selector de fechas que se puede adjuntar de forma no intrusiva a cualquier campo de texto de una página. Cuando dicho campo recibe el foco (bien porque el usuario hace clic o porque lo selecciona con la tecla de tabulación) muestra una ventana emergente con el calendairo y se puede escoger una fecha tras lo cual su valor queda en el campo de texto. Este calendario se puede personalizar usando Themeroller para darle casi cualquier aspecto que queramos. Si nos interesa ponernos manos a la obra rápidamente podemos utilizar los ficheros JavaScript y las hojas de estilo alojadas en los servidores de Google para incluir el código de jQuery y uno de los temas por defecto. Sólo tenemos que hacer referencia a los archivos apropiados en la cabecera de nuestra vista de layout.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <%= stylesheet_link_tag "http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/redmond/jquery-ui.css", "application" %> <%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.1/jquery-ui.min.js", "application" %> <title><%= yield :title %></title> </head>
Nótese que la aplicación con la que estamos trabajando está escrita en Rails 2. Si estuviésemos utilizando Rails 3 tendríamos que añadir la versión compatible con jQuery del fichero rails.js
tal y como vimos en el episodio 205
[verlo, leerlo].
Ahora que hemos incluido las librerías jQuery y jQueryUI podemos empezar a modificar nuestra aplicación para que funcione con el selector de fechas. Lo primero que tneemos que hacer es modificar el campo published_on
en el formulario del artículo para que utilice un text_field
en lugar de date_select
.
<%= f.label :published_on %> <%= f.text_field :published_on %>
A continuación añadiremos de forma no instrusiva el calendario. Esto lo podemos hacer añadiendo un poco de JavaScript al fichero application.js
.
$(function (){ $('#article_published_on').datepicker(); });
Este código se activará cuando el DOM esté listo y añadirá el selector de fecha a cualquier elemento cuyo id
sea article_published_at
. Si recargamos la página de edición de artículo y hacemos clic en el campo de texto de published_on
veremos el calendario desplegado y podremos escoger una fecha en él.
Si ahora enviamos el formulario, Rails obtendrá la fecha a partir del valor de la caja de texto y actualizará el valor de published_on
en la tabla de artículos de la base de datos.
Una vista de Calendario
Ahora que tenemos funcionando nuestro selector de fechas pasemos al otro uso de calendarios de que hablábamos: mostrar una vista de calendario de todos los artículos de nuestra aplicación. Probablemente no sea la forma más eficiente de mostrar los artículos de un blog pero nos servirá para mostrar una forma alternativa de ver dichos artículo.
Hay varias soluciones posibles para este problema, y encontrar la correcta depende de las necesidades de nuestra aplicación. Si tenemos que mostrar registros que se extienden durante varios días merece la pena ver el plugin event_calendar que puede mostrar eventos que ocupen varios días. Si, por el contrario, no necesitamos esta funcionalidad table_builder es una buena alternativa. Este plugin ofrece un método helper llamado calendar_for
que facilita el agrupamiento de varios registros sobre los días de un calendario. Esto se adapta mejor a nuestras necesidades, así que lo usaremos para nuestro ejemplo.
Podemos instalar table_builder con el siguiente comando:
script/plugin install git://github.com/p8/table_builder.git
Una vez que haya finalizado la instalación podemos empezar a trabajar en nuestra vista de calendario cambiando la vísta index
en el controlador de artículos. Ahora mismo tiene este aspecto:
<% title "Articles" %> <div id="articles"> <% @articles.each do |article| %> <h2> <%= link_to h(article.title), article %> <span class="comments">(<%= pluralize(article.comments.count, 'comment') %>)</span> </h2> <div class="author">from <%=h article.author %> on <%= article.written_date.strftime('%b %d, %Y') %></div> <div class="content"><%= h(article.content) %></div> <% end %> </div> <p><%= link_to "New Article", new_article_path %></p>
Vamos a cambiar el código que itera sobre todos los artículos y los muestra por una llamada a calendar_for
para mostrar la vista de calendario.
<% title "Articles" %> <div id="calendar"> <% calendar_for @articles do |calendar| %> <% calendar.day(:day_method => :published_on) do |date, articles| %> <%= date.day %> <% end %> <% end %> </div>
Invocamos a calendar_for
y le pasamos nuestra colección de artículos. El bloque se ejecuta una vez y recibe un objeto calendar
para que podamos usar calendar.day
para iterar sobre cada día. Tenemos que especificar qué propiedad del artículo devuelve la fecha sobre la que queremos agrupar, así que en nuestro caso pasaremos el parámetro :day_method
con valor :published_on
. El bloque tiene dos parámetros: una fecha y una lista de artículos publicados ese día. Podemos poner el código que queramos dentro de este bloque, pero por ahora simplemente nos limitaremos a mostrar el día del mes de esa fecha.
Si recargamos la página no veremos el listado de artículos pero un su lugar veremos un conjunto de fechas que empiezan a parecerse a un calendario.
Todavía no tiene muy buen aspecto, pero podemos añadir una CSS para mejorar el aspecto.
Podemos mejorar la usabilidad del calendario añadiendo los nombres de los días, empleando el método calendar.head
.
<% title "Articles" %> <div id="calendar"> <% calendar_for @articles do |calendar| %> <%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')%> <% calendar.day(:day_method => :published_on) do |date, articles| %> <%= date.day %> <% end %> <% end %> </div>
Si recargamos la página veremos que se han añadido los días de la semana a la parte superior del calendario.
En su estado actual el calendario tan sólo muestra fechas del mes actual, así que lo siguiente que haremos será añadir un selector de mes que muestre el mes actual con enlaces para navegar al mes anterior o siguiente.
Empezaremos con la acción index
del controlador de artículos donde crearemos una variable que guardará la fecha del mes que queraos mostrar. Por ahora dejaremos la fecha actual.
def index @articles = Article.all @date = Date.today end
A continuación modificaremos la vista para que se muestren el mes y los enlaces.
<% title "Articles" %> <div id="calendar"> <h2 id="month"> <%= link_to "<", :month => (@date.beginning_of_month-1).strftime("%Y-%m-01") %> <%= h @date.strftime("%B %Y") %> <%= link_to ">", :month => (@date.end_of_month+1).strftime("%Y-%m-01") %> </h2> <% calendar_for @articles, :year => @date.year, :month => @date.month do |calendar| %> <%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday') %> <% calendar.day(:day_method => :published_on) do |date, articles| %> <%= date.day %> <% end %> <% end %> </div>
Los enlaces apuntan ambos a la acción actual index
pero añaden un parámetro a la cadena de consulta llamado month
que especifica el mes anterior o el siguiente. Ya podemos volver al código del controlador y comprobar la existencia de dicho parámetro y utilizarlo para establecer el valor de la variable @date
.
def index @articles = Article.all @date = params[:month] ? Date.parse(params[:month]) : Date.today end
Si recargamos la página veremos el mes sobre el calendario y con las flechas a su lado podremos navegar a otros meses.
Nuestra vista de calendario goza de buen aspecto pero aún no muestra los artículos que existen en una determinada fecha. Ya tenemos acceso a los artículos de un día en el bloque calendar.day
por lo que podremos iterar sobre esta colección y mostrar un enlace a cada artículo en una lista.
<% calendar_for @articles, :year => @date.year, :month => @date.month do |calendar| %> <%= calendar.head('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')%> <% calendar.day(:day_method => :published_on) do |date, articles| %> <%= date.day %> <ul> <% for article in articles %> <li><%= link_to h(article.title), article %></li> <% end %> </ul> <% end %> <% end %>
Si recargamos la página por última vez veremos listados los artículos de cada día y podemos hacer clic en los enlaces para visitar los artículos.
Y con esto termina nuestro episodio. Tal y como hemos visto es muy sencilllo tuilizar calendarios para seleccionar fechas o mostrar una lista de registros ordenados por un campo de fecha. Es muy recomendable utilizar una vista de calendario como esta cuando se adapte a los datos de nuestra aplicación.