text_field rails form_with form_for fields ruby-on-rails checkbox database-design models

ruby-on-rails - form_for - form_with ruby on rails



Rails 4 Cómo modelar un formulario con una colección de casillas de verificación con otro text_field (3)

Permítanme comenzar diciendo que esto también podría ser un problema de modelado y estoy abierto a sugerencias modelo.

Caso de uso: tengo un formulario y debo permitir que un usuario seleccione una casilla de verificación en la categoría de su publicación. Si no hay ninguna categoría que se ajuste a su verificación posterior, la otra categoría mostrará un campo de texto para que el usuario agregue una categoría personalizada. Esto debería ser para crear y actualizar módulos anidados

Modelado de bases de datos

class CreateCategories < ActiveRecord::Migration def change create_table :categories do |t| t.string :name, null: false t.timestamps null: false end reversible do |dir| dir.up { Category.create(name: ''Hats'') Category.create(name: ''Shirts'') Category.create(name: ''Pants'') Category.create(name: ''Shoes'') Category.create(name: ''Other'') } end create_table :categorizations, id: false do |t| t.belongs_to :post, index: true, null: false t.belongs_to :category, index: true, null: false t.string :value end end end

Modelos de aplicaciones

class Post < ActiveRecord::Base has_many :categorizations accepts_nested_attributes_for :categorizations, allow_destroy: true has_many :categories, through: :categorizations accepts_nested_attributes_for :categories end class Category < ActiveRecord::Base has_many :posts end

Controlador:

def update if @post.update(post_params) flash.now[:success] = ''success'' else flash.now[:alert] = @post.errors.full_messages.to_sentence end render :edit end private def set_post @post = Post.find(params[:id]) (Category.all - @post.categories).each do |category| @post.categorizations.build(category: category) end @post.categorizations.to_a.sort_by! {|x| x.category.id } end def post_params params.require(:post).permit(:name, :description, categorizations_attributes: [ :category_id, :value, :_destroy], ) end

Ver:

= f.fields_for :categorizations do |ff| = ff.check_box :_destroy, { checked: ff.object.persisted? }, ''0'', ''1'' = ff.label :_destroy, ff.object.category.name = ff.hidden_field :category_id = ff.text_field :value if ff.object.category.other?

Sin embargo, con la solución anterior sigo corriendo para duplicar los errores de registro al guardar. ¿No estoy seguro de por qué ocurre esto? ¿Hay una mejor manera de hacer esto?


¡No guarde el otro en su modelo, ni su nombre! Si está utilizando form_for para sus posts , simplemente agregue un campo no relacionado.

ej .: f.text_field :other_name to text_field_tag :other_name

Agregue manualmente su opción Other a la colección desplegable.

Puede agregar JS para ocultar y mostrar un campo de texto oculto si se selecciona otro.

En tu posts_controller do:

def create ... if params[:other_name] post.categories.create(name: param[:other_name]) end ... end


Yo preferiría algo como esto:

Modelos

post.rb

class Post < ActiveRecord::Base has_many :categorizations has_many :categories, through: :categorizations accepts_nested_attributes_for :categorizations, allow_destroy: true accepts_nested_attributes_for :categories end

category.rb

class Category < ActiveRecord::Base has_many :categorizations has_many :posts, through: :categorizations end

Controlador

... def update if @post.update(post_params) flash.now[:success] = ''success'' else flash.now[:alert] = @post.errors.full_messages.to_sentence end render :edit end private def set_post @post = Post.find(params[:id]) end def post_params params.require(:post).permit(:name, :description, category_ids: []) end ...

Vistas Siempre prefiero plain .erb entonces, con la ayuda de simple_form .

<%= simple_form_for(@post) do |f| %> <%= f.error_notification %> <div class="form-inputs"> <%= f.input :content -%> ... </div> <div class="form-inputs"> <%= f.association :categories, as: :check_boxes -%> </div> <div class="form-actions"> <%= f.button :submit %> </div> <% end %>

Puede tener estados marcados / desmarcados y destruir de forma fácil y limpia de esta manera. Además, puedes agregar

<%= f.simple_fields_for :category do |category_fields| %> <%= category_fields.input :name -%> <% end %>

para obtener campos anidados para asociaciones, pero no olvides agregar params relacionados a strong_params cuando hagas esto.

... def post_params params.require(:post).permit(:name, :description, category_attributes: [:name]) end ....


En lugar de hacer que el usuario seleccione la Categoría "Otros" y luego almacenar el campo de texto en otro lugar, debe crear una nueva instancia de Categoría en su lugar. Estás en el camino correcto con accepts_nested_attributes_for .

El próximo paso sería:

# app/controllers/posts_controller.rb def new @post = Post.new @post.build_category end private # don''t forget strong parameters! def post_params params.require(:post).permit( ... category_attributes: [:name] ... ) end

Vistas (usando gemas simple_form y nested_form )

# app/views/new.html.haml = f.simple_nested_form_for @job do |f| = f.simple_fields_for :category do |g| = g.input :name

También puedes hacerlo más limpio utilizando Objetos de formulario.

Editar: si necesita separar las preocupaciones de las categorías Otras de las categorías Originales, puede usar la herencia OO para hacerlo. La forma de Rails de hacer esto es la herencia de tabla única .

# app/models/category.rb class Category < ActiveRecord::Base end # app/models/other_category.rb class OtherCategory < Category end # app/models/original_category.rb class OriginalCategory < Category end