#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)
Ogni volta che si gestiscono delle date in un’applicazione è spesso utile mettere a disposizione un calendario nell’interfaccia utente. Se l’utente deve infatti scegliere una data per un certo campo, sarà sicuramente più semplice per lui farlo da una popup che mostra un calendario, piuttosto che doverne inserire una scegliendo da una serie di tendine (giorno, mese e anno) o peggio ancora inserendo testualmente la data in un campo di testo. Può anche capitare che si abbia una tabella di modello nell’applicazione che abbia un campo data e che quindi serva un calendario per permettere di scegliere per data fra i record di tale tabella. Vi mostreremo in questo episodio come poter fare entrembe le cose.
L’applicazione con cui lavoreremo è una semplice applicazione di blogging che ha un certo numero di articoli a database, ma quasi tutte le applicazioni si aspettano che i campi di input per le date beneficino di questa funzionalità di selezione. Quando creiamo o modifichiamo un articolo, possiamo scegliere una data di pubblicazione usando un classico date picker di Rails. Cambieremo questo sistema per poter usare un calendario in popup come metodo alternativo per scegliere tale data.
I calendari sono complicati da creare e gestire da zero, ma per fortuna ci sono parecchie soluzioni di terze parti disponibili, che ci permetteranno di fare praticamente tutto ciò che vorremmo fare con i calendari e le date. Uno di questi è il plugin calendar_date_select. Questo plugin ci fornisce un metodo helper che funziona con le librerie Prototype e che invoca un calendario attraverso del JavaScript. Non useremo questo plugin, tuttavia, ma useremo piuttosto una soluzione diversa che fa uso di unobtrusive JavaScript e che si basa su jQuery. Non si tratta di una soluzione specifica per Rails, ma l’abbiamo comunque scelta in quanto fornisce un metodo facile da capire per aggiungere un calendario ad un campo di data.
La libreria jQuery UI fornisce un Datepicker che permette l’inclusione pulita di un calendario ad un qualunque campo di testo di una pagina. Quando il campo di testo ottiene il focus, o perchè viene selezionato con click, o perchè viene selezionato mediante tab da tastiera, una popup mostrante un calendario appare per consentire la selezione di una data. Una volta scelta in questo modo la data, il campo di testo si ritrova il valore selezionato impostato. Questo calendario può essere personalizzato mediante temi, usando il Themeroller di jQuery: in questo modo ci viene data la possibilità di scegliere l’aspetto che più ci piace. Se vogliamo ottenere rapidamente ciò che vogliamo, possiamo usare i file JavaScript e gli stili presenti sui server di Google per includere il codice jQueryUI ed uno dei temi di default. Dobbiamo solo referenziare i corretti file nella sezione HEAD della pagina di 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>
Si noti che l’applicazione con cui lavoriamo qui è scritta in Rails 2. Se stessimo usando Rails 3, avremmo bisogno di aggiungere una versione compatibile con jQuery del file rails.js, come già trattato nell’episodio 205 [guardalo, leggilo].
Ora che abbiamo incluso le librerie jQuery e jQueryUI, possiamo cminciare a modificare la nostra applicazione per farla funzionare con il Datepicker. La prima cosa da fare è modificare il campo published_on nella form dell’articolo, in modo tale che usi un text_field
al posto di un date_select
:
<%= f.label :published_on %> <%= f.text_field :published_on %>
Poi dobbiamo aggiungere un calendario in maniera non invasiva e lo possiamo fare aggiungendo del JavaScript al file application.js
:
$(function (){ $('#article_published_on').datepicker(); });
Questo codice eseguirà al termine del caricamento del DOM, aggiungendo un date-picker ad ogni elemento che abbia id
ugauale a article_published_at
. Se ricarichiamo la pagina di modifica dell’articoloe clicchiamo nel campo di testo published_on
, vedremo la popup del calendario e potremo scegliere una data da quella popup:
Al submit della form, Rails interpreterà la data dal valore presente nel campo di testo e aggiornerà la data relativa al campo published_on
nella tabella articles sul database.
Una vista calendario
Ora che abbiamo il nostro selettore di date funzionante, vediamo gli altri usi dei calendari di cui abbiamo discusso: fornire una vista basata su calendario degli articoli nella nostra appicazione. Questo potrebbe non essere il modo più efficiente di elencare gli articoli in un blog, ma lo forniremo comunque come modo alternativo per sfogliare gli articoli presenti.
Ci sono molteplici soluzioni a questo problema e trovare quella corretta dipende dalle necessità dell’applicazione. Se c’è bisogno di mostrare dei record che si sviluppano su più giorni, allora ha senso dare un’occhiata al plugin event_calendar che può mostrare eventi che spaziano su di un range di date. Se tale funzionalità non è richiesta, una buona alternativa è il plugin table_builder. Questo plugin offre un metodo helper chiamato calendar_for
che rende semplice raggruppare un dato insieme di record in giorni su di un calendario. Quest’ultimo corrisponde meglio ai nostri bisogni, per cui lo useremo nella nostra applicazione.
Possiamo installare table_builder lanciando il seguente comando:
script/plugin install git://github.com/p8/table_builder.git
Dopo che si sarà installato, potremo cominciare a lavorare sulla nostra vista a calendario cambiando la vista index
nel nostro controller degli articoli. Attualmente appare così:
<% 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>
Sostituiremo il codice che itera su ogni articolo e lo mostreremo con una chiamata al metodo calendar_for
per mostrare gli articoli in una vista a 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>
Chiamiamo il calendar_for
passandogli la nostra collezione di articoli. Il blocco viene eseguito una volta e accetta un oggetto di tipo calendar
, per cui possiamo chiamare il calendar.day
per iterare su ogni giorno. Dobbiamo specificare una property dell’articolo che restituisca una data, per poter raggruppare gli articoli per giorno, per cui gli passiamo un parametro :day_method
col valore di :published_on
. Il blocco in questione ha due parameteri: una data e una lista di articoli che sono stati pubblicati per giorno. Possiamo mettere il codice che ci pare dentro a questo blocco, ma per ora restituiremo semplicemente in output il giorno del mese:
Se ricarichiamo la pagina ora, non vedremo una lista di articoli, quanto piuttosto una lista di date che assomigliano un po’ ad un calendario:
Non sembra ancora molto carino, ma possiamo aggiungere un po’ di CSS per migliorarne l’aspetto:
Possiamo migliorare l’usabilità del calendario, aggiungendo i nomi dei giorni in testata, usando il metodo 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>
Quando ricarichiamo la pagina nuovamente, i giorni della settimana saranno stati aggiunti in cima al calendario:
Così com’è, il nostro calendario può solo mostrare delle date del mese corrente, per cui il prossimo passo sarà di aggiungere un seletore del mese che mostri il mese corrente e che abbia dei link su entrambi i lati per permetterci di cambiare il mese mostrato.
Per fare questo, partiamo dalla action index
del controller degli articoli, dove creeremo una variabile che manterrà la data del mese che vogliamo sia mostrata. Per ora impostiamo semplicemente la data di oggi:
def index @articles = Article.all @date = Date.today end
Poi modifichiamo la vista in modo tale che siano mostrati il mese e i link:
<% 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>
I link puntano entrambi alla action index
che è mostrata attualmente, ma con l’aggiunta di un parametro alla stringa di query, chiamato month
, che indica il mese precedente o il mese successivo. Ora possiamo ritornare al codice del controller, controllare l’esistenza di quel parametro month
e usarlo per impostare il valore della variabile @date
se esiste:
def index @articles = Article.all @date = params[:month] ? Date.parse(params[:month]) : Date.today end
Al ricaricamento della pagina, vedremo il nome del mese sopra il calendario e le freccie su entrambi i lati di questo, che ci permetteranno di scorrere fra i vari mesi:
La nostra vista a calendario ora appare carina, ma non mostra gli articoli che ci sono per un dato giorno. Abbiamo già accesso agli articoli di un determinato giorno nel blocco calendar.head
, per cui possiamo iterare attraverso questa collection e mettere un link per ciascun articolo di quel giorno in 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 %>
Quando ricarichiamo la pagina un’ultima volta, vedremo elencati gli articoli per ciascun giorno e potremo cliccare su ciascuno per essere portati al dettaglio di quell’articolo:
Per questo episodio è tutto. Come abbiamo mostrato, è semplice usare i calendari per scegliere date o per mostrare una lista di record a seconda di una determinata property di tipo data. Siete invitati a considerare l’utilizzo di una vista a calendario tipo questa ogni volta che possa rivelarsi utile rispetto ai dati delle vostre applicazioni.