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?

o inicia sesi贸n.

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.

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.

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!

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.

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 鈥淎gregar鈥
  • partial: 'participating_user_fields' -> El parcial que se agregar谩 cada vez que el usuario haga click en 鈥淎gregar鈥 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.

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

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.