Desafío Latam
Uncategorized

Aceptar atributos anidados con ruby on rails

300px Russian Matroshka no bg

300px Russian Matroshka no bg

Un truco muy simple en ruby on rails 4 (también aplica a rails 3) que puede ahorrarnos mucho trabajo es la capacidad de guardar un objeto junto con sus objetos hijos simultáneamente en el mismo formulario, de esta forma podemos guardar una pregunta con sus respuestas, o un post con su categoría, simúltaneamente sin necesidad de llamados ajax adicionales, o sea únicamente utilizando los atributos anidados de Ruby on Rails.

Atributos anidados en el modelo con Ruby on Rails

Para lograr esto necesitamos al menos dos modelos, el padre (father) y el hijo (child), donde un padre puede tener n hijos y el hijo tiene un padre.

#models/father.rb 
class Father < ActiveRecord::Base
  has_many :childs
  accepts_nested_attributes_for :childs
end
#models/child.rb
class Father < ActiveRecord::Base
 belongs_to :father
end

La línea que marca la diferencia es accepts_nested_attributes_for :childs, la cual es justamente la que permite aceptar atributos anidados, o sea que permite guardar el modelo junto con el modelo hijo.

Agregando los atributos anidados al white list de los strong parameters

otra modificación que tenemos que hacer, en el controller de la clase padre tenemos que agregar a los strong_parameters.

 def father_params
   params.require(:father).permit(:name, childs_attributes: [:id, :name]) 
 end

Dentro del arreglo child_attributes tenemos que incorporar todos los campos del objeto hijo que queremos pasar, en este ejemplo únicamente estoy pasando el id y el nombre pero más campos podrían llegar a ser necesarios.

La vista del formulario con atributos anidados

Ahora nos está faltando agregar al formulario para crear un objeto del tipo padre los campos del hijo, este es un ejemplo de un formulario tipo padre creado con scaffold y modificado para aceptar el campo nombre del hijo.

<%= form_for(@father) do |f| %>
  <% if @father.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@father.errors.count, "error") %> prohibited this father from being saved:</h2>

      <ul>
      <% @father.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>

  <%= f.fields_for :childs, @childs do |c| %>
    <%= c.text_field :name %>
  <% end %>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Como ven, la única diferencia radica en el f.field_for

Agregando hijos al formulario

ahora esto va a tirar un error, puesto que @childs no está definido en ninguna parte, para eso tenemos que volver al controlador de la clase padre y dejarla de la siguiente forma:

def new
  @father = Father.new
  @childs = @father.childs.build
end

Y con eso ya tendríamos un formulario junto a su formulario hijo funcionando.

Desafío propuesto

Si quisiéramos que tuviese más hijos por defecto simplemente podemos agregar más hijos al arreglo @childs.

Un ejercicio interesante es crear más de estos formularios por javascript (o coffeescript) y aceptar sólo los que no están en blanco.

Artículos relacionados

La gran alternativa educacional para estudiar Carreras Digitales

Fernando
7 años ago

¡Mejora tu concentración al estudiar con estos increíbles recursos!

marcos
2 años ago

Conversando de UX/UI

Trinidad Swinburn
6 años ago
Salir de la versión móvil