Cómo paginar, ordenar y hacer búsquedas en una tabla con Ajax y Rails

Introducción y observaciones

(Nota: este documento es una traducción del tutorial How to paginate, sort and search a table with Ajax and Rails, escrito por Julien Barnier, y publicado en su web http://nozav.org)

En este tutorial intentaremos utilizar Ajax con Rails para mostrar una tabla de elementos con diversas funcionalidades:

  1. paginación: separar la tabla en páginas distintas, si el contenido no puede mostrarse de una sola vez
  2. ordenación: ordenar la tabla por alguna de las columnas, en orden ascendente o descendente
  3. búsqueda: escoger qué elementos van a ser mostrados con una consulta

Esta es una tarea muy común en el desarrollo de aplicaciones web. El interés de usar Ajax para esto es proporcionar una interfaz dinámica que no hace necesario recargar toda la página cuando la tabla cambia. El interés de usar Rails es, bueno, si estás leyendo esto ya deberías saberlo, pero Ajax y Rails se llevan bastante bien, de forma que es muy fácil implementar funcionalidades Ajax con nuestro framework favorito. Además, el código de este tutorial debería funcionar también de la manera tradicional (recargando la página entera) si JAvascript no está disponible en la parte del cliente. Esto es muy importante para la accesibilidad.

El código que sigue a continuación esta muy inspirado en diferentes páginas del wiki de Rails, en especial “How to make a real-time search box with the Ajax helpers” y “How to paginate with Ajax”. Simplemente puse las cosas juntas y adapté el código ligeramente.

Ahora, las observaciones. No soy un gurú de Rails, de hecho estoy bien lejos de ser un experto en Ajax. Soy un principiante en ambas áreas, así que tómense este documento como una introducción para principiantes escrita por un principiante: el código podría ser más claro, las explicaciones más acertadas y algunas cosas podrían haberse diseñado de manera más eficaz y elegante. Además, como el inglés no es mi lengua nativa, es posible que se encuentren muchos errores e inexactitudes durante el texto. Les pido excusas por anticipado (N. del T: ¡esto va también por mí!)

En todo caso no duden en enviarme sus comentarios a la siguiente dirección:

julien (at) nozav (dot) (org)

Instalación y configuración de la aplicación

Lo primero que hemos de hacer es instalar y configurar la aplicación. Si ya lo has hecho, te puedes saltar lo que viene a continuación.

Requisitos

En este tutorial se supone que tienes una versión reciente e Rails instalado (al menos la versión 1.0) y un sistema de bases de datos funcional. Aquí usaremos SQLite, pero se puede reemplazar por cualquier otro sin el menor problema.

Archivos

Primero, tenemos que crear el esqueleto de la aplicación en un directorio de nuestra elección

$ rails ajaxtable
$ cd ajaxtable

Como se trata de una aplicación muy básica solamente utilizaremos un modelo que representará a los elementos de nuestra tabla, así como un controlador para estos elementos. Generamos todo el código correspondiente usando Rails:

$ ruby script/generate model Item
$ ruby script/generate controller Item

La base de datos

Una vez que tengamos el código, debemos configurar una fuente de datos. En aras de la simplicidad utilizaremos SQLite junto a un esquema Rails. Esto significa que tendremos que describir nuestra base de datos dentro de Rails y dejar que sea el framework quien gestione la creación de la base de datos.

Tenemos que modificar la fuente de datos para el entorno de desarrollo en config/database.yml:

1
2
3
development:
        adapter: sqlite3
        database: db/development.db

A continuación, crearemos la base de datos utilizando las herramientas de Rails

$ rake db_schema_dump

Esto debería crear un archivo llamado db/schema.rb en el que definiremos nuestro esquema de base de datos de la siguiente manera:

1
2
3
4
5
6
7
8
ActiveRecord::Schema.define() do

        create_table "items" do |t|
                t.column "name", :string, :limit => 30
                t.column "quantity", :integer, :null => false, :default => 0
                t.column "price", :integer, :null => false, :default => 0
        end
end

Esto crea una tabla llamada items con tres columnas: un campo de cadena llamado nombre, y dos columnas numéricas llamadas cantidad y precio (no es nada demasiado original, ciertamente)

Luego tan sólo hemos de hacer

$ rake db_schema_import

Y ya deberíamos tener un fichero development.db en nuestro directorio db que es la base de datos creada por Rails con la tabla items en su interior.

Después podemos introducir algunos ítems en la tabla para tener algo que mirar mientras desarrollamos el código. Se puede hacer manualmente o escribiendo las siguientes instrucciones SQL en un fichero db/dump.sql:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BEGIN TRANSACTION;
INSERT INTO "items" VALUES(1, 'caja', 3, 10);
INSERT INTO "items" VALUES(2, 'carretilla', 2, 60);
INSERT INTO "items" VALUES(3, 'tenazas', 15, 3);
INSERT INTO "items" VALUES(4, 'batman', 1, 3000);
INSERT INTO "items" VALUES(5, 'morcilla de pescado', 2, 8);
INSERT INTO "items" VALUES(6, 'sauerkraut', 9, 9);
INSERT INTO "items" VALUES(7, 'regadera', 4, 13);
INSERT INTO "items" VALUES(8, 'dandiliones', 78, 1);
INSERT INTO "items" VALUES(9, 'nevera', 12, 250);
INSERT INTO "items" VALUES(10, 'cerillas voladoras', 8, 145);
INSERT INTO "items" VALUES(11, 'acordeón roto', 1, 18);
INSERT INTO "items" VALUES(12, 'silbido salvaje', 5, 7);
INSERT INTO "items" VALUES(13, 'caracol histérico', 8, 13);
COMMIT;

Podremos insertar todas estas tablas en la base de datos ejecutando

$ sqlite3 db/development.db < db/dump.sql

(Nota, si se utiliza una base de datos MySQL, la sintaxis adecuada es @INSERT INTO items VALUES (1, ‘hoe’, 3, 10);@, etc)

Creación del modelo

Como seguramente sabrán, una aplicación Rails se divide en tres tipos de componentes: modelos, vistas y controladores. Los iremos creando uno por uno.

El modelo de nuestra aplicación será muy sencillo. De hecho, como no tenemos ninguna consulta compleja que hacer a la base de datos, será exactamente el mismo que ha generado Rails para nostros en nuestra instalación de la aplicación. Vamos, que irá vacío. Así que no tocaremos el fichero app/models/item.rb

¡Espero que este paso no haya sido demasiado difícil!

Creación de la vista

La vista de nuestra aplicación estará dividida en dos componentes: un layout, una vista y un parcial.

Layout

El layout es una plantilla de página que se utilizará para interpretar diferentes vistas. En él se pondrán los elementos que no varían, tales como los encabezamientos y pies de página en HTML, menús, elementos de diseño, etc. La utilidad de un layout en nuestro ejemplo es muy limitada, porque sólo tendremos una página. Pero a fin de cuentas esto no es más que un tutorial…

En nuestro caso guardaremos el layout en app/views/layouts/item.rhtml, que contendrá algo parecido a

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
  <title>Ajax table manipulation attempt</title>
  <%= stylesheet_link_tag "style" %>
  <%= javascript_include_tag :defaults %>
</head>
<body>

<div id="content">
<%= @content_for_layout %>
</div>

</body> </html>

Es muy importante no olvidar la sentencia javascript_include_tag, que será reemplazada por las librerías que utiliza Rails para proporcionar toda la funcionalidad Ajax: sin esto no funcionará. También es de señalar la instrucción content_for_layout, que será reemplazada por el contenido generado convenientemente por…

La vista

El componente de la vista se utiliza para mostrar acciones particulares de nuestros controladores. Como es una lista de nuestro controlador de items, lógicamente se hallará en app/views/item/list.rhtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<h1>Welcome to my wonderful items list</h1>

<p>This list is updated real-time from the world largest database of
items by using bleeding-edge technology associated to full-featured
Web 2.0 eye candy goodies.</p>

<p>But be careful, there are plenty of bugs.</p>

<h2>And here is the list...</h2>

<p>
<form name="sform" action="" style="display:inline;">
<label for="item_name">Filter on item name  : </label>
<%= text_field_tag("query", @params['query'], :size => 10 ) %>
</form>

<%= image_tag("spinner.gif",
              :align => 'absmiddle',
              :border=> 0,
              :id => "spinner",
              :style=>"display: none;" ) %>
</p>

<%= observe_field 'query',  :frequency => 2,
         :update => 'table',
         :before => "Element.show('spinner')",
         :success => "Element.hide('spinner')",
         :url => {:action => 'list'},
         :with => 'query' %>

<div id="table"> <%= render :partial => "items_list" %> </div> 

En principo no hay nada muy complicado. Un texto estúpido de presentación y un formulario de búsqueda que utilizaremos para filtrar los items por su nombre.

Después, tendremos una imagen cuyo id es spinner y que está oculto por defecto. Esta imagen se mostrará brevemente durante las operaciones Ajax en la página, y luego se ocultará otra vez cuando todo haya acabado. Se pueden conseguir algunas imágenes de domino público de este estilo en http://mentalized.net/activity-indicators/

Hay que poner la imagen escogdia en el directorio public/images de nuestra aplicación.

La instrucción observe_field es algo más especial. Lo que hace esta instrucción es añadir un nuevo observer Ajax al campo de consulta. Este observador periódicamente comprobará (aquí es cada dos segundos, pero hay un parámero que permite establecer la frecuencia) los contenidos de este campo y se activará si cambian sus contenidos. La acción a tomar viene descrita por los parámetros,

  • update determina el id del elemento de la página que será actualizado. En este caso es la tabla que rodea a nuestra llamada al parcial (la tabla, por supuesto, tendrá el id ‘table’).
  • url determina la acción que mostrará el nuevo contenido HTML a insertar. Aquí cada petición será tratada por la acción list de nuestro controlador
  • with indica la manera en al que el campo del contenido será pasadao a la URL de la acción. Aquí añadiremos un parámetro query a nuestra petición Ajax con un valor igual a lo que se haya introducido en el campo observado.
  • before y success indican, respectivamente, una acción a realizar durante el tiempo que se trata la acción Ajax, y una vez que haya sido completada satisfactoriamente.

El efecto concreto de todo esto es muy sencillo. Cuando el usuario escribe algo en el campo de consulta, el observador detectará los nuevos contenidos del campo y a continuación generará una petición Ajax al servidor con la URL y los parámetros especificados. En cuanto que se envíe la petición, la acción :before cambia la visibilidad del elmento spinner, y el usuario puede ver la imagen animada. Cuando se recibe la respuesta, el elemento de la tabla XHTML se actualiza y la acción :success oculta la imagen otra vez.

Por curiosidad, aquí está el código que devuelve la función observe_field con los parámetros mostrados:

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
//<![CDATA[
new Form.Element.Observer('query', 2, function(element, value) {Element.show('spinner'); 
new Ajax.Updater('table', '/item/list', {
        asynchronous:true, 
        evalScripts:true, 
        onSuccess:function(request){Element.hide('spinner')}, parameters:'query=' + value})})
//]]>
</script>

Hemos detallado las diferentes opciones de los métodos Ajax porque volveremos a verlas en casi todos los métodos Ajax que veamos en este tutorial.

Para concluir con esta descripción de la vista, tenemos una llamada a un parcial llamado items_list. Describiremos este concepto en detalle después del controlador.

Creando el controlador

El controlador gestionará diferentes tipos de peticiones para actualizar la vista, llamando al modelo según convenga dependiendo de la petición y sus parámetros. Nuestro controlador Item será muy sencillo, y tan sólo incluirá una acción llamada list. No implementaremos ninguna otra acción CRUD (Create, Read, Update, Delete) en el tutorial.

Aquí están los contenidos de app/controllers/item_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ItemController < ApplicationController

  def list

    items_per_page = 10

    sort = case @params['sort']
           when "name"  then "name"
           when "qty"   then "quantity"
           when "price" then "price"
           when "name_reverse"  then "name DESC"
           when "qty_reverse"   then "quantity DESC"
           when "price_reverse" then "price DESC"
           end

    conditions = ["name LIKE ?", "%#{@params[:query]}%"] unless @params[:query].nil?

    @total = Item.count(:conditions => conditions)
    @items_pages, @items = paginate :items, :order => sort, 
             :conditions => conditions, 
             :per_page => items_per_page

    if request.xml_http_request?
      render :partial => "items_list", :layout => false
    end

  end

end 

Nuestro controlador sólo define una acción, llamada list, que gestionará todas las peticiones. Primero, definimos una variable llamada items_per_page que representará el número de líneas que nuestra tabla mostrará en cada página.

A continuación, definiremos otra variable llamada sort, que depende del parámetro de la petición que tiene el mismo nombre. Con la sentencia case construimos la sentencia a enviar a la base de datos para ordenar los elementos de la tabla. La cadena reverse es el parámetro que indica que la ordenación debe hacerse por orden inverso (en el helper veremos cómo se envía este parámetro al controlador).

Luego se construye una variable conditions si viene un parámetro de consulta, armando una sentencia SQL que filtrará los resultados.

Tras esto, asignamos el número total de elementos en nuestra base de datos que se corresponden con las condiciones en la variable total

Y, por fin, encontramos una llamada a la función paginate de Rails. Le damos a esta instrucción el modelo al que deberia asociarse (:items), un campo de ordenación, las condiciones para aplicar a la consulta y el número delementos que queremos en cada página. Y automágicamente devuelve un objeto paginador llamado items_pages y los items para la página actual (este número de pagina se pasa como un parámetro en la petición de página, que veremos más adelante) El objeto paginador se usará después para mostrar enlaces de paginación.

Todo lo que hemos visto para el controlador hasta ahora era aplicable a cada petición que se pasa a la aplicación, sea cual sea su tipo. Los tipos de peticiones HTTP más frecuentes son los tradicionales GET y POST, pero Ajax y Rails pueden hacer uso de un tercer tipo, cuyo nombre es *XmlHttpRequest* (en realidad, la peticón XmlHttpRequest no es otro tipo de petición HTTP, es tan sólo una petición GET o POST tradicional que es enviada y tratada por el intérprete Javascript de manera asíncrona)

Como hemos dicho, este tipo de petición es invocada desde Javascript, que la enviará en segundo plano por HTTP hacia el sevidor. Un uso posible (y que es el que usaremos) de este tipo de petición es el de obtener un fragmento de una página XHTML para actualizar parte del contenido que muestra el navegador sin volver a cargar la página entera, lo cual proporciona una experiencia de usuario más rápida y agil.

De esto es de lo que va la parte final de nuestro controlador: comprueba si la petición que tiene es de tipo xml_http_request (también podría haberse escrito abreviadamente xhr). En este caso no mostrará la lista completa sino tan sólo un fragmento de ella el parcial cuyo nombre es items_list

Así que, como puede verse, esta comprobación xml_http_request es el único código “realmente Ajax” con que nos topamos en el controlador. Esto es así porque en Rails los asuntos de Ajax están enlazados al interfaz de nuestra aplicación (es decir, a la vista) y más específicamente a la parte de ella que será gestionada por Ajax (es decir, el parcial).

Así que, ¿adivinan qué es lo que vamos a hacer ahora?

Creación del parcial

Un componente parcial de la vista se utiliza para mostrar una parte de la página. Es muy útil separar diferentes partes de la vista para poder reutilizarlas y seguir el principio Rails de _No te Repitas_ (DRY, Don’t Repeat Yourself) Pero además también es muy útil con Ajax.

De hecho el efecto de nuestras acciones Ajax en este tutorial siempre consistirá en volver a dibujar la tabla de elementos, ya cambie la página, el filtro por nombre o el orden de búsqueda. Así que tendremos que refrescar la tabla pero no toda la página: por eso separaremos todos los elementos que tendrán que ser actualizados en un parcial. (Esto podríamos haberlo hecho de alguna otra manera. Por ejemplo, creando otra acción de nombre ajax_list que gestionaría las peticiones xml_http_request, con una vista asociada pero sin ningún parcial. El problema que tiene este enfoque es que habríamos duplicado buena parte del código en las acciones list y ajax_list, así que.. ¡DRY!)

Los nombres de fichero de los parciales siempre empiezan con un subrayado. Así que nuestro parcial será app/views/item/_items_list.rhtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<% if @total == 0 %>

<p>No items found...</p>

<% else %>

<p>Number of items found : <b><%= @total %></b></p>

<p>
<% if @items_pages.page_count > 1 %>
Page&nbsp;:
<%= pagination_links_remote @items_pages %>
<% end %>
</p>


<table>
  <thead>
    <tr>
      <td <%= sort_td_class_helper "name" %>>
        <%= sort_link_helper "Name", "name" %>
      </td>
      <td <%= sort_td_class_helper "qty" %>>
        <%= sort_link_helper "Quantity", "qty" %>
      </td>
      <td <%= sort_td_class_helper "price" %>>
        <%= sort_link_helper "Price", "price" %>
      </td>
    </tr>
  </thead>
  <tbody>
    <% @items.each do |i| %>
    <tr class="<%= cycle("even","odd") %>">
      <td><%= i.name %></td>
      <td><%= i.quantity %></td>
      <td><%= i.price %></td>
    </tr>
    <% end %>
  </tbody>
</table>

<% end %>

El parcial contiene la paginación de la tabla y la gestión de la ordenación. Vamos a verlo con más detalle.

Helpers de paginación

Al principio tenemos una comprobación para ver si el número total de elementos que se ha encontrado es mayor que cero. En este caso, mostramos este número y después un párrafo que estará vacío si sólo hay una página en nuestro objeto de paginación.

Si tenemos más de una página de resultados está claro que tendremos que mostrar los enlaces de paginación. Rails por supuesto ya tiene métodos de ayuda pero tendremos que personalizarlos un poco. Para hacer esto, crearemos un helper. Un helper es una función Ruby que se utiliza para ayudar a generar la vista. La idea es separar el código en estas funciones de la propia vista, favoreciendo la reusabilidad.

Nuestros helpers estarán todos en app/helpers/item_helper.rb. Cada uno de los métodos de este archivo estarán disponibles para usarse en el código de nuestra vista. Opcionalmente, y si quisiéramos que estuviera disponible en todas las vistas de la aplicación, lo añadiríamos a application_helper.rb

Bien, ya basta de charla, he aquí el código de nuestro helper,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def pagination_links_remote(paginator)
  page_options = {:window_size => 1}
  pagination_links_each(paginator, page_options) do |n|
    options = {
      :url => {:action => 'list', :params => @params.merge({:page => n})},
      :update => 'table',
      :before => "Element.show('spinner')",
      :success => "Element.hide('spinner')"
    }
    html_options = {:href => url_for(:action => 'list', 
                    :params => @params.merge({:page => n}))}
    link_to_remote(n.to_s, options, html_options)
  end
end

A este método le pasaremos un objeto paginador como argumento. El paginador, como vimos en el controlador, es un objeto de Rails que guarda información sobre el estado de paginación (número de páginas, la página que estamos viendo, etc.)

Después definiremos un hash llamado page_options que contiene tan sólo un item llamado window_size Este será un parámetro que le dice a Rails cuántas páginas tiene que mostrar alrededor de la actual en la línea a los enlaces de paginación. Por ejemplo, si window_size vale uno, tendremos algo como

1 ... 5 6 7 ... 13

Mientras que si window_size es 2:

1 … 4 5 6 7 8 … 13

Podríamos entonces hacer una llamada a la función pagination_links, que generaría el código XHTML necesario para dibujar nuestros enlaces. El problema es que esto generaría enlaces XHTML “clásicos”, que no realizarían llamadas Ajax. Así que tendremos que generar por nuestra cuenta el código de los enlaces. Esto lo haremos con el método pagination_links_each Este método de Rails se recorre las páginas a mostrar y después aplica el bloque que se le pasa como argumento.

Nuestro bloque en primer lugar define dos tipos de hashes de opciones

  • options se define para la generación del enlace Ajax. Son muy similares a las que se definieron en el elemento observe_field. La única novedad es la invocación de params.merge, que añadirá los parámetros de la petición al enlace reemplazando cualquier parámetro que ya hubiera en la página por el número del bloque .
  • html_options simplemente se definen para generar el enlace “clásico” XHTML, para que funcione la paginación si Javascript no está disponible o el navegador no lo soporta.

Después hay una llamada a la función link_to_remote que generará el código XHTML para nuestro enlace, incluyendo el Javascript que hará la invocación Ajax y la parte “clásica” con su href.

Por ejemplo, esto es lo que devuelve el helper si hay dos páginas, y estamos ya mostrando la primera:

1
2
3
4
5
6
7
8
1 <a href="/item/list?page=2" 
     onclick="Element.show('spinner'); 
              new Ajax.Updater('table', '/item/list?page=2', {
                                asynchronous:true, 
                                evalScripts:true, 
                                onSuccess:function(request){Element.hide('spinner')}
                           }); 
                          return false;">2</a>

Helpers de ordenación

Volvamos a nuestro parcial. Después de los enlaces de paginación empezamos a definir la tabla propiamente dicha. La definición de la cabecera es algo complicada porque ahí es donde vamos a definir los enlaces que ordenarán los datos por una u otra columna. Cada celda del encabezamiento de la tabla hace uso de dos helpers.

El primer helper no es del todo necesario. Se llama sort_td_class_helper, y su único objetivo es el de añadir una clase “sortup” si la columna que va a dibujar es la que se está usando para ordenar la tabla, o la clase “sortdown” si se utiliza para ordenar en orden inverso. La única utilidad de esta función es que, usando CSS, podremos indicar al usuario que función se está empleando como campo de ordenación

El código no es precisamente interesante:

1
2
3
4
5
def sort_td_class_helper(param)
  result = 'class="sortup"' if @params[:sort] == param
  result = 'class="sortdown"' if @params[:sort] == param + "_reverse"
  return result
end

Luego tenemos el segundo helper, llamado sort_link_helper.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def sort_link_helper(text, param)
  key = param
  key += "_reverse" if @params[:sort] == param
  options = {
      :url => {:action => 'list', 
               :params => @params.merge({:sort => key, :page => nil})},
      :update => 'table',
      :before => "Element.show('spinner')",
      :success => "Element.hide('spinner')"
  }
  html_options = {
    :title => "Sort by this field",
    :href => url_for(
        :action => 'list', 
        :params => @params.merge({:sort => key, :page => nil}))
  }
  link_to_remote(text, options, html_options)
end

Este helper, de hecho, es muy similar al que hemos visto más arriba, pagination_links_remote, y recoge dos argumentos:

  • text es tan sólo el texto a mostrar como cabecera de la columna y el enlace para ordenar * param es el nombre del parámetro de la petición asociado con la columna que luego el controlador usará para generar la consulta SQL

Las primeras dos líneas definen una nueva variable, llamada key, que recibe el valor del argumento param, es decir, la clave de búsqueda. Se le añadirá el sufijo _reverse si el parámetro ya está siendo para ordenar. Esto se utiliza para implementar la búsqueda tanto con criterio ascendente como descendente: si el usuario pulsa un enlace de ordenación, se ordenará por orden ascendente y si escoge otra vez el mismo enlace ordenará por criterio descendiente y así sucesivamente. Viendo el código del controlador probablemente todo esto quedará más claro.

El resto del helper define las opciones para la llamada final a la función link_to_remote, que son muy similares a los de pagination_links_remote

  • options como ya sabemos se utiliza para el enlace Ajax y contiene la URL a invocar para generar la nueva tabla, el id del elemento a actualizar y las acciones before y success para mostrar y ocultar el spinner.
  • html_options se utiliza para el enlace HTML. Genera el contenido del atributo href con la función url_for de Rails.

Esto es lo que devuelve sort_link_helper para una llamada con las cadenas Quantity y qty como texto y parámetro, respectivamente:

1
2
3
4
5
6
7
8
<a href="/item/list?sort=qty" 
        onclick="Element.show('spinner'); 
            new Ajax.Updater('table', '/item/list?sort=qty', {
                                asynchronous:true, 
                                evalScripts:true, 
                                onSuccess:function(request){Element.hide('spinner')}});
                         return false;" 
        title="Sort by this field">Quantity</a>

Cuerpo de la tabla

Por último, el cuerpo de la tabla simplemente muestra el contenido de la misma: un elemento en cada fila. Lo único relevante aquí es el uso de la función cycle de Rails, que automática y alternativamente añadirá la clase odd o even al estilo de nuestras filas de la tabla, lo que puede resultar útil a la hora de mostrarlas de forma más vistosa.

¡Y eso es todo amigos!

Ya hemos visto todos los trozos de nuestra aplicación con más menos detalle. En teoría, uno podría ver los resultados arrancando del servidor WEBrick y entrando a la dirección

http://localhost:3000/item/list

Espero que este documento les haya sido útil. Repito que pueden enviarme cualquier comentario, etc. a la direccón de correo dada en la introducción.

Acerca de este documento.

Este documento se publica bajo licencia Creative Commons Attribution.

Agradecimientos a Nicolas St-Laurent y Rachel McConnel por sus observaciones.

He traducido al castellano el tutorial How to paginate, sort and search a table with Ajax and Rails, escrito por Julien Barnier.

Con bastante soltura y enfrentándose al problema de paginar una tabla mediante Ajax, Julien nos da un repaso de principio a fin de las técnicas más frecuentes, desde el uso de parciales que modifican sólo ciertas partes del DOM, hasta la creación de observadores que disparan eventos como respuesta a la actualización de un campo (pasando, cómo no, por los inevitables spinners)

Podeis leer (y enlazar) la traducción aquí.

Una de las características del Diccionario Malacitano es su interfaz basada en Ajax. La consultas de las distintas letras del diccionario se hacen invocando un código Javascript que actualiza el contenido de una parte del DOM de la página (más exactamente el <div id="contenido"> usando una plantilla RJS, como ya vimos aquí)

La navegación básica en nuestra aplicación depende, por tanto, de que el usuario tenga habilitado Javascript (la J de AJAX) en su navegador. ¿Qué ocurre si un usuario llega a nuestra a web con Javascript inhabilitado, o, peor aún, con un navegador sin Javascript?

Pues simplemente verá que los enlaces del índice apuntan a URLs un tanto sospechosas (http://diccionariomalacitano.sobrerailes.com/#). Y no sólo eso, sino que además, si hace clic en una letra no ocurre nada. En conclusión, gracias al uso de AJAX hemos conseguido unos atractivos efectos visuales pero a cambio hemos convertido nuestra aplicación en un desastre de usabilidad que necesita arreglo.

Read the rest of this entry

Una de las novedades de la última versión de Rails ha sido la introducción de las nuevas plantillas RJS que nos dan mayor control sobre los resultados que devolvemos ante peticiones Ajax. Según la descripción, RJS consiste en poder generar Javascript usando código Ruby de nuestra aplicación.

Read the rest of this entry

La edición de datos in situ usando Javascript es uno de los galones que debe exhibir cualquier web moderna que se precie de ser Web 2.0 (sea esto lo que sea) En este entrada vamos a ver cómo podemos implementar un efecto parecido al que describió el otro día Fernando usando, cómo no, Ruby on Rails.

Read the rest of this entry