niveles dependientes anidados javascript ruby-on-rails nested-forms formtastic

javascript - dependientes - Cómo agregar y eliminar campos de modelos anidados dinámicamente usando Haml y Formtastic



select anidados jquery (4)

En Rails 3, no hay necesidad de usar la función h () en el ayudante. Simplemente omitir y debería funcionar.

Todos hemos visto las formas complejas y brillantes de railscast donde Ryan Bates explica cómo agregar dinámicamente o eliminar objetos anidados dentro de la forma del objeto principal usando Javascript.

¿Alguien tiene alguna idea sobre cómo se deben modificar estos métodos para trabajar con Haml Formtastic?

Para agregar algo de contexto, aquí hay una versión simplificada del problema al que me enfrento actualmente:

# Formulario de profesor (que tiene formularios de materia anidados) [de mi solicitud]

- semantic_form_for(@teacher) do |form| - form.inputs do = form.input :first_name = form.input :surname = form.input :city = render ''subject_fields'', :form => form = link_to_add_fields "Add Subject", form, :subjects

# Asunto individual formulario parcial [de mi solicitud]

- form.fields_for :subjects do |ff| #subject_field = ff.input :name = ff.input :exam = ff.input :level = ff.hidden_field :_destroy = link_to_remove_fields "Remove Subject", ff

# Asistente de aplicación (directamente de Railscasts)

def link_to_remove_fields(name, f) f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)") end def link_to_add_fields(name, f, association) new_object = f.object.class.reflect_on_association(association).klass.new fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder| render(association.to_s.singularize + "_fields", :f => builder) end link_to_function(name, h("add_fields(this, /"#{association}/", /"#{escape_javascript(fields)} /")")) end

# Application.js (directamente de Railscasts)

function remove_fields(link) { $(link).previous("input[type=hidden]").value = "1"; $(link).up(".fields").hide(); } function add_fields(link, association, content) { var new_id = new Date().getTime(); var regexp = new RegExp("new_" + association, "g") $(link).up().insert({ before: content.replace(regexp, new_id) }); }

El problema con la implementación parece estar en los métodos javascript: el árbol DOM de una forma Formtastic difiere mucho de una forma de rieles regular.

He visto esta pregunta en línea varias veces, pero todavía no he encontrado una respuesta. ¡Ahora sabe que la ayuda será apreciada por algo más que solo yo!

Jack


Estás en el camino correcto:

... el árbol DOM de una forma Formtastic difiere mucho de una forma de rieles regular.

Para adaptar el ejemplo de Ryan para formtastic, es útil recordar que semantic_fields_for helper es similar a semantic_form_for helper, que genera las entradas en forma de lista.

Para mantener las cosas lo más cerca posible del código de Railscast, deberás:

  • encierre la colección de campos anidados en un contenedor (uso un div con el ID de CSS de los subjects ).
  • encierre los campos anidados en un contenedor ul / ol (apliqué una clase CSS de nested-fields ).

Esto es lo que deberían gustarle sus archivos.

Formulario de profesor (con campos de materia anidados):

- semantic_form_for(@teacher) do |form| - form.inputs do = form.input :first_name = form.input :surname = form.input :city %h2 Subjects #subjects - form.semantic_fields_for :subjects do |builder| = render :partial => "subject_fields", :locals => { :f => builder } .links = link_to_add_fields "Add Subject", form, :subjects

Campos de asunto parciales (para Asunto anidado):

%ul.nested-fields = f.input :name = f.input :exam = f.input :level = link_to_remove_fields "Remove Subject", f

ApplicationHelper:

def link_to_remove_fields(name, f) f.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)") end def link_to_add_fields(name, f, association) new_object = f.object.class.reflect_on_association(association).klass.new fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder| render(association.to_s.singularize + "_fields", :f => builder) end link_to_function(name, h("add_fields(this, /"#{association}/", /"#{escape_javascript(fields)}/")")) end

Aplicación.js:

function remove_fields(link) { $(link).previous("input[type=hidden]").value = "1"; $(link).up(".nested-fields").hide(); } function add_fields(link, association, content) { var new_id = new Date().getTime(); var regexp = new RegExp("new_" + association, "g") $(link).up().insert({ before: content.replace(regexp, new_id) }); }

Usando las siguientes gemas:

  • formtastic (0.9.10)
  • haml (3.0.2)
  • pepinillo (1.0.26)
  • rieles (2.3.5)

He estado golpeando mi cabeza contra esta una y otra vez durante años, y hoy se me ocurrió algo de lo que estoy orgulloso: elegante, discreto, y puedes hacerlo solo con dos o tres (realmente largas) líneas de código .

ParentFoo has_many NestedBars, con accept_nested_parameters apropiados y todas esas golosinas. Usted representa un conjunto de campos para una barra anidada en un atributo de datos agregados anidados en el enlace, utilizando: child_index para establecer una cadena que puede reemplazar más adelante. Pasas esa cadena en un segundo parámetro, data-add-nested-replace

parent_foos / _form.html.haml

- semantic_form_for @parent_foo do |f| -# render existing records - f.semantic_fields_for :nested_bars do |i| = render ''nested_bar_fields'', :f => i # magic! - reuse_fields = f.semantic_fields_for :nested_bars, @parent_foo.nested_bars.build, :child_index => ''new_nested_bar_fields'' do |n| render(''nested_bar_fields'', :f => n) end = link_to ''Add nested bar'', ''#'', ''data-add-nested'' => reuse_fields, ''data-add-nested-replace'' => ''new_nested_bar_fields''

Los campos anidados parciales no son nada lujosos.

parent_foos / _nested_bar_fields.html.haml

- f.inputs do = f.input :name = f.input :_delete, :as => :boolean

En su jQuery, vincula el clic de los elementos con un campo de datos agregados (he usado live () aquí para que funcionen los formularios cargados con ajax) para insertar los campos en el DOM, reemplazando la cadena con una nueva ID. Aquí estoy haciendo lo sencillo de insertar los nuevos campos antes de () el enlace, pero también podría proporcionar un atributo add-nested-replace-target en el enlace que indique dónde en el DOM desea que los nuevos campos terminen.

aplicacion.js

$(''a[data-add-nested]'').live(''click'', function(){ var regexp = new RegExp($(this).data(''add-nested-replace''), "g") $($(this).data(''add-nested'').replace(regexp, (new Date).getTime())).insertBefore($(this)) })

Podría poner esto en un ayudante, por supuesto; Lo presento aquí directamente en la vista, por lo que el principio es claro sin cometer errores en la metaprogramación. Si le preocupa que los nombres entren en conflicto, podría generar una ID única en ese ayudante y pasarla en reemplazo anidado.

adquirir fantasía con el comportamiento de eliminación se deja como un ejercicio para el lector.

(mostrado para Rails 2 porque ese es el sitio en el que estoy trabajando hoy, casi el mismo en Rails 3)


application.js para jQuery:

function remove_fields(link) { $(link).prev("input[type=hidden]").val("1"); $(link).parent(".nested-fields").hide(); } function add_fields(link, association, content) { var new_id = new Date().getTime(); var regexp = new RegExp("new_" + association, "g") $(link).parent().before(content.replace(regexp, new_id)); }