← Volver a la lista de posts

Modales de Bootstrap en Ruby on Rails

Un requerimiento muy frecuente en las aplicaciones Web que usan Bootstrap es que los formularios de crear y editar se abran en ventanas modales, y no en otra página, como se muestra en la siguiente animación:

Products

Los formularios de crear y editar usando vistas de JavaScript. Una ventaja es que las vistas se renderizan en el servidor lo que nos permite hacer uso de los helpers de Rails.

Existen varias formas de hacerlo. Sin embargo, Rails incluye una funcionalidad para trabajar con AJAX y JavaScript sin necesidad de utilizar un framework como Angular, Ember, Backbone, etc., que muchas veces agregan complejidad innecesaria a los proyectos.

El truco está en utilizar vistas de JavaScript. Así como generamos vistas de HTML, es posible generar vistas con código JavaScript que se ejecuta en el navegador.

Las vistas de JavaScript son archivos con código JavaScript que tienen la extensión .js, por ejemplo new.js.erb que, en este caso, abre el modal con el formulario:

$('<%= j render "form", title: "Publicar Producto" %>').modal();

Dentro de esta vista, estamos incluyendo una vista parcial que es la que contiene el modal y el formulario. El prefijo j sirve para escapar el código HTML y que sea una cadena de JavaScript válida.

La vista parcial con el modal y el formulario se encuentra en el archivo _form.html.erb dentro de la misma carpeta:

<div id="product-modal" class="modal fade">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title"><%= title %></h4>
      </div>
      <%= form_for @product, remote: true do |f| %>
        <div class="modal-body">
          <div class="form-group">
            <%= f.text_field :name, class: "form-control input-lg", placeholder: "Nombre del producto" %>
          </div>
          <div class="form-group">
            <%= f.text_field :price, class: "form-control input-lg", placeholder: "Precio del producto" %>
          </div>
        </div>

        <div class="modal-footer">
          <%= f.submit class: "btn btn-primary btn-lg btn-block" %>
        </div>
      <% end %>
    </div>
  </div>
</div>

En el controlador inicializamos la variable @product:

def new
  @product = Product.new
end

Por último, al enlace que abre el modal en la lista de productos debemos agregarle remote: true para indicarle a Rails que haga un llamado AJAX en vez de una petición normal:

<%= link_to "Nuevo Producto", new_product_path, remote: true, class: "btn btn-primary" %>

Enviar el formulario con AJAX

Si revisas el código de _form.html.erb (arriba) te vas a dar cuenta que la definición del formulario incluye remote: true:

<%= form_for @product, remote: true do |f| %>
   ...
 <% end %>

De nuevo, remote: true le indica a Rails que haga un llamado AJAX en vez de una petición normal. En el controlador, el método create crea el producto en la Base de Datos:

def create
  @product = Product.create(product_params)
end

Para cerrar el modal o mostrar los errores usamos una vista JavaScript llamada create.js.erb:

<% if @product.errors.empty? %>
  // add the row to the table, show alert and hide modal
  $('table tbody').append(' <%= j render "row", product: @product %>');
  $('.alert-success').html("El producto ha sido creado con éxito").show();
  $('#product-modal').modal('hide');
<% else %>
  // show first error
  alert('<%= @product.errors.full_messages[0] %>');
<% end %>

Esta vista verifica que el producto no tenga errores, agrega la fila a la tabla, muestra el mensaje de alerta y oculta el modal. Para mostrar la fila utiliza una vista parcial que se encuentra en row.html.erb:

<tr id="<%= product.id %>">
  <td><%= product.name %></td>
  <td><%= number_to_currency(product.price, precision: 2) %></td>
  <td>
    <%= link_to "Editar", edit_product_path(product), remote: true %>
  </td>
</tr>

Lo interesante de esta estrategia es que la página nunca se refresca y el código HTML del modal solo se descarga si el usuario abre el modal. La desventaja es que si la red está lenta, la modal se puede demorar en ser desplegado.


Aunque actualmente existe una moda por frameworks como Angular, Ember, etc., la realidad es que la mayoría de aplicaciones no necesitan ese nivel de complejidad. Es increíble lo que se puede lograr usando Rails “puro” y vistas de JavaScript.

Las ventanas modales son solo una muestra de lo que es posible, pero cualquier tipo de interacción se puede mejorar con vistas de JavaScript. Lo importante es no exagerar. Si ves que tu aplicación tiene muchas interacciones que necesitan llamados AJAX y muchas vistas de JavaScript, es mejor utilizar un framework front-end como Angular, Ember, React, etc.

Nota: El código de este post lo puedes encontrar en este repositorio de Github.

comments powered by Disqus