#302 In-Place Editing
- Download:
- source codeProject Files in Zip (84.4 KB)
- mp4Full Size H.264 Video (18.5 MB)
- m4vSmaller H.264 Video (9.87 MB)
- webmFull Size VP8 Video (11.7 MB)
- ogvFull Size Theora Video (24.5 MB)
Arriba se muestra la página del perfil de un usuario en una aplicación Rails. Existe un enlace “Edit Profile” al final de la página que nos lleva a otra página donde podemos editar nuestros datos.
Queremos que los usuarios puedan editar sus datos directamente en la página de su perfil haciendo clic en cualquiera de ellos en lugar de utilizar una página específica de edición. Al hacer clic sobre él, el campo debería pasar a ser editable y se guardarían los cambios pulsando la tecla Intro.
Best In Place
Hay varios plugins de Rails que nos pueden ayudar a la hora de añadir esta edición en línea en nuestra aplicación. Hay publicada una lista muy completa de ellos en The Ruby Toolbox. El que vamos a usar esta vez se llama Best In Place, si bien merece la pena echar un vistazo a los otros. Best In Place es una bifurcación de otro proyecto llamado Rest in Place, pero lo que marca la diferencia entre estos dos es el helper best_in_place
con el que se hace muy fácil añadir campos de edición en línea en nuestras aplicaciones Rails.
Best In Place tiene una página de demostración y si la probamos veremos que podemos editar cualquier campo haciendo clic en él, momento en el que se cambia el texto estático por el elemento de formulario apropiado para el tipo de dato del formulario. Al pulsar Intro o hacer clic fuera del campo los datos se enviarán al servidor para que se actualice la base de datos. Es posible validar los campos, por lo que si se introduce una dirección incorrecta (por ejemplo) aparecerá un mensaje de error y el valor volverá al que tenía antes de la edición. También se soportan otros tipos de datos por lo que se puede mostrar un desplegable para editar una asociación o una caja de selección para los campos booleanos. Veamos qué tenemos que hacer para añadir esta funcionalidad a nuestra página de perfil.
Cómo añadir Best In Place a nuestra aplicación
Primero tenemos que añadir la gema de Best In Place al Gemfile de la aplicación y luego ejecutar bundle
.
source 'http://rubygems.org' gem 'rails', '3.1.3' gem 'sqlite3' # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sass-rails', '~> 3.1.5' gem 'coffee-rails', '~> 3.1.1' gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' gem 'best_in_place'
La gema incluye dos ficheros de JavaScript que deberemos incluir en la aplicación, jquery.purr
(que es un plugin de jQuery) y best_in_place
. Dado que se trata de una aplicación Rails 3.1 tenemos que añadir ambos ficheros al manifiesto.
//= require jquery //= require jquery_ujs //= require jquery.purr //= require best_in_place //= require_tree .
Tenemos que definir los campos que serán editables, lo que haremos en el fichero CoffeeScript para el modelo users
. Tan sólo tenemos que invocar a best_in_place()
en aquellos elementos que queramos que se puedan modificar. Se lo aplicaremos a los elementos que tengan la clase best_in_place
.
jQuery -> $('.best_in_place').best_in_place()
Con esto ya podemos empezar a aplicar Rest In Place a nuestra página de perfil de usuario. Este es el aspecto que tiene ahora mismo la plantilla:
<h1>User Profile</h1> <p> <b>Name:</b> <%= @user.name %> </p> <p> <b>Email:</b> <%= @user.email %> </p> <p> <b>Gender:</b> <%= @user.gender %> </p> <p> <b>Public profile:</b> <%= @user.public_profile %> </p> <p> <%= @user.bio %> </p> <%= link_to 'Edit Profile', edit_user_path(@user) %>
La plantilla sencillamente muestra el valor de cada campo. Añadiremos el método helper de Best In Place a los campos name
y email
. El método recibirá dos argumentos, el objeto que estamos viendo y un símbolo para el campo que deseamos editar.
<p> <b>Name:</b> <%= best_in_place @user, :name %> </p> <p> <b>Email:</b> <%= best_in_place @user, :email %> </p>
Al cargar la página del perfil del usaurio y hacer clic en name
o email
, veremos que el campo se hace editable.
Persistencia de los cambios
Al modificar un campo y luego hacer clic fuera de él veremos que el campo vuelve a su valor anterior, lo que no es lo que queremos que ocurra. Si recargamos la página veremos que aparece un mensaje que dice que se ha actualizado el usuario y la página muestra el valor modificado.
Esto ocurre así porque Best In Place envía las actualizaciones mediante JSON al servidor, pero UsersController
no está preparado para responder a peticiones JSON adecuadamente. El controlador utiliza las siete acciones REST habitual, que son las que espera Best In Place, por lo que cuando se cambia el valor en el campo se invoca la acción update
con los parámetros correctos, pero ésta no responde a la petición como debiera.
Para corregir esto tenemos que añadir un bloque respond_to
en update
o (al ser una aplicación Rails 3) utilizar respond_with
que se encargará de todo, siempre qu epongamos una llamada a respond_to
al principio de nuestro controlador. Así que lo añadiremos.
class UsersController < ApplicationController respond_to :html, :json # Se omiten otras acciones def update @user = User.find(params[:id]) @user.update_attributes(params[:user]) respond_with @user end end
Si tenemos que personalizar este comportamiento podemos utilizar un bloque respond_to
en la acción update
, y hay un ejemplo de cómo hacer esto en el README de Best In Place.
Ahora que nuestro controlador responde en JSON, la funcionalidad de edición en línea funciona correctamente. Los cambios que hagamos se guardarán en la base de datos y la interfaz de usuario se actualizará tal y como esperábamos.
Aquí también funcionan las validaciones. Si introducimos una dirección de correo incorrecta aparecerá un mensaje de error, aunque el que se usa por defecto no resultará especialmente visible.
Lo podemos embellecer con un poco de CSS.
.purr { position: fixed; top: 30px; right: 100px; width: 250px; padding: 20px; background-color: #FCC; border: solid 2px #666; &:first-letter { text-transform: uppercase }; }
Todos estos estilos se encuentra en la clase purr
que es lo que utiliza Best In Place. Si volvemos a cargar la página e introducimos una dirección de correo incorrecta el mensaje tendrá un mejor aspecto.
Gestión de otros tipos de datos
Por ahora sólo hemos hecho editables los campos name
y email
. Veamos a continuación los otros atributos de la página del usuario. El campo bio
contiene un fragmento más largo de texto, pero por defecto Best In Place sólo mostrará una caja con una única línea. Podemos cambiar este comportamiento añadiendo el atributo type
.
<%= best_in_place @user, :bio, type: :textarea %>
El campo public_profile
es un campo booleano por lo que deberíamos usar el tipo checkbox
y pasar los valores queramos mostrar en la opción :collection
.
<%= best_in_place @user, :public_profile, type: :checkbox, collection: %w[No Yes] %>
Al hacer clic el valor de este campo cambiará entre las dos opciones. La opción de género podríamos tratarla igual, pero en este caso hemos utilizado :select
en lugar de :checkbox
y hemos pasado una lista de opciones.
<%= best_in_place @user, :gender, type: :select, :collection [["Male", "Male"], ["Female", "Female"], ["", "Unspecified"]] %>
Al hacerlo así, aparecerá un menú desplegable al hacer clic en el género.
Con esto cerramos este episodio. Ya tenemos una opción de edición en línea para todos los campos de nuestra página de perfil de usuario. Habrá veces en las que queramos hacer algo un poco más complicado, por ejemplo mostrar opciones de una asociación, o quizá formatear un campo de precio donde el valor mostrado puede ser distinto del que se muestra al editar. Esto resulta un poco complejo por desgracia en la versión actual de Best In Place. En estas situaciones es mejor volver al formulario tradicional de edición o implementar nuestra propia solución.