No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Creando formularios anidados

26/36
Recursos

Aportes 12

Preguntas 7

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Les comparto un tutorial que seguí hace poco para crear un formulario anidado desde cero en Rails 6 sin necesidad de usar ninguna gema ni jQuery por si quieren explorar otras formas de hacerlo, ya que la gema cocoon necesita jQuery para funcionar, en el tutorial se usa en cambio Javascript vanilla (JS puro).

Create a nested form in Rails from scratch

Quien tenga problemas con zsh tienen que añadir ’ ’ asi:

yarn add 'github:nathanvda/cocoon#c24ba53'

Es un conflicto con las strings,pero eso lo debe solucionar.

Si usas Rails 7 y no te funciona - Así es como resolvi el problema de cocoon y jquery

Primero agregar la gema de jquery-rails y cacoon en el GEMFILE

gem 'jquery-rails'
gem 'cocoon'

instalalas: $bundle install

Después, cargar los jquery en los assets de rails cuando inicializa la app en el archivo config/initializers/assets.rb

Rails.application.config.assets.precompile += %w( jquery.min.js jquery_ujs.js )

Agregar cacoon y jquery dentro de config/importmap.rb

pin "jquery", to: "https://ga.jspm.io/npm:[email protected]/dist/jquery.js"
pin "@nathanvda/cocoon", to: "https://ga.jspm.io/npm:@nathanvda/[email protected]/cocoon.js"

Agregar cacoon en el archivo app/javascript/applications.js

import "@nathanvda/cocoon"

Importar jquery y agregar $ y jQuery a window dentro de app/javascript/controller/application.js para que quede algo así:

import { Application } from "@hotwired/stimulus"

const application = Application.start()

import jQuery from "jquery"

// Configure Stimulus development experience
application.debug = false
window.Stimulus   = application
window.jQuery = jQuery
window.$ = jQuery

export { application }

Reinicializar el server y listo!

Al utilizar Ruby on Rails versión 7 parece ser que esta clase con la siguiente se complica su seguimiento. Por lo menos para mi fue imposible realizar todo lo que se necesita para poder user cocoon propiamente. No lo pude hacer funcionar.
En la documentación tampoco indica que sea compatible con esta versión de Rails.
Parece ser que implementarlo por nuestra cuenta es la mejor opción con la que se cuenta por el momento.

Los formularios anidados quieren decir que en un solo formulario haya interacción con dos modelos. En este caso será con Task y Participant, con la ayuda de la gema Cocoon.

Para instalar esta gema hay que agregarla en el Gemfile, lo siguiente es agregar una dependencia de Cocoon para que pueda trabajar de forma correcta, esta dependencia se agrega con yarn desde el siguiente repositorio: github:nathanvda/cocoon#c24ba53.

Configuración de cocoon

En el archivo eviroment.js:

const webpack = require('webpack')

environment.plugins.prepend('Provide',
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery',
        Popper: ['popper.js', 'default'],
    })
)

En el archivo application.js:

import 'cocoon'

Esta configuración es necesaria ya que cocoon tiene como dependencia a jquery.

Una vez hecho esto hacemos la migración, para migrar el modelo Participant (rails db:migrate).


Formulario de tasks

En el formulario de tasks es donde se requiere asociar con los participantes, para esto se usará simple_fields_for, un método que permite asociar un modelo anidado que exista en la estructura de modelos.

= simple_form_for(@task) do |f|
  = f.error_notification
  = f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?

  .form-inputs
    = f.input :name, label: t('tasks.name')
    = f.input :description, label: t('tasks.description')
    = f.input :due_date, label: t('tasks.due_date')
    = f.association :category, label: t('tasks.category')
    
    .participants
      = f.simple_fields_for :paticipating_users

Con el método simple_fields_for se habilita la comunicación con participating_users, que es como se nombró la relación entre tareas y participantes.

Para el formulario principal el constructor es f (primera línea) y para el formulario anidado el constructor es g.

Para presentar el formulario anidado se usará un parcial, que es un archivo creado en la carpeta views>tasks con el nombre _participating_user_fields.html.haml. Y se llamará al formulario principal con la función render dentro del formulario anidado.

.participants
      = f.simple_fields_for :paticipating_users
        = render 'participating_users_fields', f: g

Parcial:

.nested-fields 
    = f.input :user_id, as: :select, collections: User.all - [current_user]
    = f.input :role

En el primer input se inserta un elemento select, para poder elegir entre todos los usuarios, pero le restamos el usuario actual para que no aparezca en la lista. Era necesario poner current_user en un arreglo para que Ruby pudiera realizar las operaciones entre arreglos con User.all (también es un arreglo) sin problemas.

que raro hasta ahora eh seguido al pie de la letra todo el curso todo iba bien pero no tengo en mi app ni app/javascript/packs ni environment.js

Posible solución para nested forms en Rails 7 utilizando esbuild como bundler de JS

No he sido capaz de utilizar cocoon con rails 7 (aunque seguramente sea posible, he acabado desistiendo).

En su lugar he decidido utilizar la gema vanila-nested https://github.com/arielj/vanilla-nested.

La cual es una alternativa al uso de cocoon sin necesidad de utilizar jQuery

Pasos a seguir:

  1. bundle add vanilla-nested
  2. yarn add vanilla-nested
  3. Añadir la siguiente línea al archivo app/assets/config/manifest.js
    //= require vanilla_nested
  4. Añadir la siguiente línea al archivo app/javascript/application.js
import "vanilla-nested"
  1. Permitir atributos anidados en el modelo Task
accepts_nested_attributes_for :participating_users, reject_if: :all_blank, allow_destroy: true
  1. Asignar los valores relaccionales a cada modelo, como indica Johan en el video (has_many, belongs_to, etc), y filtrar los parámetros permitidos en el controlador tasks_controller, como se muestra en el video.

  2. app/views/tasks/_form.html.haml:

-# frozen_string_literal: true
= simple_form_for(@task) do |form|
  = form.error_notification
  = form.error_notification message: form.object.errors[:base].to_sentence if form.object.errors[:base].present?

  .form-inputs
    = form.input :name, label: t('.name')
    = form.input :description, label: t('.description')
    = form.input :due_date, label: t('.due_date')
    = form.association :category, label: t('.category')
    = link_to_add_nested(form , :participating_users, '#participants', partial:'participating_user_fields', link_text:"Agregar")
    #participants 
    
  .form-actions
    = form.button :submit, t('common.submit')

Si nos fijamos en el código, veremos que estamos utilizando un método propio de la gema vanilla-nested: link_to_add_nested.

El cuál recibe varios parámetros (ampliables si se quiere personalizar o configurar):

  • form -> el constructor del formulario padre (que aplica a @task)
  • :participating_users -> nombre de la relación de User con Task.
# relacion del modelo Task. Fíjate en el nombre de la relacion
has_many :participating_users, class_name: 'Participant'
  • #participants -> El id del DOM que será afectado por el click del usuario en “Agregar”
  • partial: 'participating_user_fields' -> El parcial que se agregará cada vez que el usuario haga click en “Agregar” y que contiene los campos del formulario asociados al participante.
  • link_text:"Agergar" -> El texto del link

_participating_user_fields.html.haml:

.nested-fields
  = form.input :user_id, as: :select, collection: User.all.map {|u| [u.email, u.id]}, label: t('.user')
  = form.input :role
  = link_to_remove_nested form

Los campos anidados se irán añadiendo al div con id participants que hemos definido en el formulario.

los que estan en mac con todas las comillas simples:

yarn add 'github:nathanvda/cocoon#c24ba53'

y en javascript/packs/aplication.js

import('cocoon')

y en config/webpack/environment.js

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    'window.jQuery': 'jquery',
    Popper: ['popper.js', 'default']
  })
)
module.exports = environment

Yo también tuve problemas con la instalación con el comando:

yarn add github:nathanvda/cocoon#c24ba53

Pero lo resolví dandole permisos de administrador al comando yarn, uso Ubuntu 20 y solo agregue sudo al inicio, quedando:

sudo yarn add github:nathanvda/cocoon#c24ba53

Me da error al ejecutar

yarn add github:nathanvda/cocoon#c24ba53

tal pareciera que ese commit ya no existe o algo así.

Intenté también de esta forma:

yarn add https://github.com/nathanvda/cocoon\#c24ba53

y me apareció lo siguiente:

Couldn't find match for "c24ba53" in "refs/heads/fix_rbx_build,refs/heads/fix_travis_rbx,refs/heads/master,refs/heads/unfreezing,refs/tags/1.0.12,refs/tags/v1.0.0,refs/tags/v1.0.1,refs/tags/v1.0.10,refs/tags/v1.0.11,refs/tags/v1.0.12,refs/tags/v1.0.13,refs/tags/v1.0.14,refs/tags/v1.0.15,refs/tags/v1.0.16,refs/tags/v1.0.17,refs/tags/v1.0.18,refs/tags/v1.0.19,refs/tags/v1.0.2,refs/tags/v1.0.20,refs/tags/v1.0.21,refs/tags/v1.0.22,refs/tags/v1.0.3,refs/tags/v1.0.4,refs/tags/v1.0.5,refs/tags/v1.0.6,refs/tags/v1.0.7,refs/tags/v1.0.8,refs/tags/v1.0.9,refs/tags/v1.1.0,refs/tags/v1.1.1,refs/tags/v1.1.2,refs/tags/v1.2.0,refs/tags/v1.2.1,refs/tags/v1.2.10,refs/tags/v1.2.11,refs/tags/v1.2.12,refs/tags/v1.2.13,refs/tags/v1.2.14,refs/tags/v1.2.2,refs/tags/v1.2.3,refs/tags/v1.2.4,refs/tags/v1.2.5,refs/tags/v1.2.6,refs/tags/v1.2.7,refs/tags/v1.2.8,refs/tags/v1.2.9" for "https://github.com/nathanvda/cocoon".
info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.

Me da error al ejecutar el siguiente comando en Mac:

yarn add github:nathanvda/cocoon#c24ba53.

Me devuelve el siguiente mensaje:

zsh: no matches found: github:nathanvda/cocoon#c24ba53

Probe con todas las formas que pude encontrar pero no logro solucionarlo.