Ask A Question

Notifications

You’re not receiving notifications from this thread.

How do I submit multiple checked items?

Alan Reid asked in Rails

Hi all,
can someone guide me in the right direction please?

Basically i have dynamic options a user can select 1 or many of these items. I thought checkboxes would be good for this.

The user then submits the form and an array is then passed over and a separate row for each selected item is added to the Db

cheers

Reply

I could have sworn Chris had a video that talked about this but I'm not able to find it...

If you search for rails multiple checkboxes you'll get quite a few results and there's a Railscast episode that covers it really well. I'm not sure if it's kosher to post another video so I'll let you handle the Googling :)

But the basics are in your form you'll need something like

<% @invoices.each do |invoice| %>
  <%= check_box_tag "invoice_ids[]", invoice.id %>
<% end %>

which submits to a method in your controller that then, in this case would:

def some_function
  invoices = params[:invoices]
end

From there, do whatever you want with the array of invoice id's

Hope it helps!

Reply

Cheers buddy, yeah i did a google and came across the videos you mention. They didn't really cover what i was trying to do. Most of them store the array within a string field in the DB. I want to create a new row for each venue and variant.

Example:

venue_ids => ["1","2"], variant_ids=>["10"]

So i would want to add in a row which has a venue_id of 1, with variant_id of 10. And a venue_id of 2, with variant_id of 10

I got this kind of working and its now passing in my 2 arrays. I think I am almost there i am not sure the .each is the right way to do it but i think i am on the right track haha. So i have it submitting however where would i put my @back_bar.save? cause this might cause issues as it wont redirect

@back_bar = BackBar.new
@venues = params[:venue_ids]
@variants = params[:variant_ids]

# For each venue we have in the array, grab the ID.
@venues.each do |v|
  @back_bar.venue_id = v

  # Then for each variant we associate the variant ID with that venue.
  @variants.each do |pv|
    @back_bar.product_variant_id = pv

    # Add in our product_id
    @back_bar.product_id = params[:product_id]

    # Save the venue and variant to the DB.
    if @back_bar.save
      # Redirect to the back bar page
      redirect_to back_bars_path
    else
      # Redirect to the product page
      redirect_to discoveries_product_path(@back_bar.product_id)
    end 
  end
end

private

  def back_bar_params
    params.require(:back_bar).permit(:venue_id, :product_id,  :product_variant_id)
  end
Reply

Oops, I just noticed I had an error in my original post:

def some_function
  invoices = params[:invoices]
end

should be

def some_function
  invoices = params[:invoice_ids]
end

So when you say "I want to create a new row for each venue and variant.", in your example you'd end up with a total of 2 records, but if you had 2 variant_id's, then you'd have a total of 4 records?

If this is the case then as far as I am aware your way is fine, there might be a sleeker or more "Railsy" way of doing it that someone else could share. You may consider building a hash/array of new BackBar objects then do a mass save in a transaction. This would allow you to handle any errors during the transaction more elegantly.

Reply

Yeah, so a separate row per products variant associated to a venue. It seems to work, but its the save part which messes up so i think i need to move it out of the loops haha

i like the idea of a mass save, so thats basically creating an object and saving?

Reply

Yeah, basically after each iteration, @back_bar is a new BackBar object that has values based on what you passed to it. So you can now do something like:

to_save_array = []

@venues.each do |v|
  @back_bar.venue_id = v

  @variants.each do |pv|
    @back_bar.product_variant_id = pv
    @back_bar.product_id = params[:product_id]
    to_save_array << @back_bar
  end
end

BackBar.save!(to_save_array)

You may need to tinker with the save part some, I didn't test this but this should be what's needed if I recall correctly since save is able to take a single object or an array of objects. Also note I didn't do this in a transaction, while not required it can help, but I'd suggest getting this working first before adding the complexity of a transaction. Let me know if you have further problems and I'll take a look at what I did in one of my projects.

Reply

Awesome, thanks. I'll give it a try in the morning. And see what i can get working.

Finally got my Rails books here so i can start reading up and learning more haha

Just got Agile web development with rails 5. I heard good things so fingers crossed i pick up a fee things haha

Reply

Hey guys!
I think this might be the episode you're looking for...https://gorails.com/episodes/clean-javascript-with-data-behavior
Hope it helps! :)

Reply

Hey Brittany, Thanks but that looks to just be relating to the front end but very handy :)

Jacob, that kind of works, however as it works through the venues its simply overwriting the previous one. lol :)

Reply

Hard to diagnose without seeing exactly what you have, but you may want to byebug in a few places to see what the data looks like at each step and do kind of a "duh" check to see if it makes sense...

to_save_array = []

@venues.each do |v|
  @back_bar.venue_id = v

  @variants.each do |pv|
    @back_bar.product_variant_id = pv
    @back_bar.product_id = params[:product_id]
    to_save_array << @back_bar
    byebug
  end
  byebug
end

byebug
BackBar.save!(to_save_array)

For reference, check out this gist => https://gist.github.com/nanosplit/a42d1c18b5eca01654563cfb3bb8c22f
It's setup a bit different than what I showed above since I can't do a mass save (model has validations) but shows how I'm making my own set of params to then create a new object from them. So you may try mimicking this setup to see if you can at least get it to property save each unique record, then worry about making it more efficient later.

Reply

So i have spoken to a couple of people and have had this suggestion...

In the back_bar.rb model...

  def self.add_set(vanue_ids, product_id, variant_ids)
    values = vanue_ids.map{|ven|
      variant_ids.map{|var|
        "(#{ven},#{product_id}, #{var})"
      }
    }.flatten.join(",")

    ActiveRecord::Base.connection.execute("INSERT INTO back_bars (venue_id, product_id, product_variant_id) VALUES #{values}")
  end

Which means i can now use this in my controller...

  def create
    if BackBar.add_set(params[:venue_ids], params[:product_id], params[:variant_ids])
      flash[:success] = "Product has been added to the selected back bars."
      # Redirect to the back bar page
      redirect_to back_bars_path
    else
      flash[:alert] = "A selected variant for is already in your back bar."
       # Redirect to the product page
      redirect_to discoveries_product_path(params[:product_id])
    end
  end

This is pretty interesting and similar to how you and Chris said to do the articles stuff a few threads back.

My only issue is validation, how would i validate that the params are valid so i can move on with the save. Or would you say your way is the way to go?

Reply

In regards to the code you gave, it works up to the save, where i get the following error...

#<NoMethodError: undefined methodsave!' for #Class:0x007fbac0e53288>`

Thats just for when i save 1 item. When i add in multiple it duplicates the entries

Reply

Hard for me to say which way is better - I prefer my way just because I understand it and I like service objects :D but their suggestion may be valid or better. Although I am a little thrown off by their methodology... doesn't feel very "Railsy"

I should have refreshed myself on your original question and subsequent posts... so I'm going to back track a little. Using your original .each way (below) - did it work as expected?

@back_bar = BackBar.new
@venues = params[:venue_ids]
@variants = params[:variant_ids]

@venues.each do |v|
  @back_bar.venue_id = v

  @variants.each do |pv|
    @back_bar.product_variant_id = pv
    @back_bar.product_id = params[:product_id]
    @backbar.save!
  end
end

If not, then I bet your problem is calling BackBar.new outside of your loops, so it's only be instantiated once. You'd need to do this instead:

@venues = params[:venue_ids]
@variants = params[:variant_ids]

@venues.each do |v|
  @back_bar.venue_id = v

  @variants.each do |pv|
    @back_bar = BackBar.new
    @back_bar.product_variant_id = pv
    @back_bar.product_id = params[:product_id]
    @back_bar.save!
  end
end

Let me know if this works now. If so, then you can look into making it more robust / efficient.

Reply

Ack, wait - this isn't right either. This is why it's so hard to trouble shoot stuff without being able to run the code - simple stuff is easy to overlook =D

@venues = params[:venue_ids]
@variants = params[:variant_ids]

@venues.each do |v|
  @variants.each do |pv|
    @back_bar = BackBar.new
    @back_bar.venue_id = v
    @back_bar.product_variant_id = pv
    @back_bar.product_id = params[:product_id]
    @back_bar.save!
  end
end

Have to move @back_bar.venue_id = v down into the @variants loop since we moved @back_bar = BackBar.new down as well.

Reply

To make it shorter, I believe you can do this:

@venues = params[:venue_ids]
@variants = params[:variant_ids]

@venues.each do |v|
  @variants.each do |pv|
    @back_bar = BackBar.new(venue_id: v, product_variant_id: pv, product_id: params[:product_id])
    @back_bar.save!
  end
end

If you have validations, then as far as I'm aware you can't do the bulk saving (mass insert) since it still has to check each object on save. If anyone has knowledge how to do this mass insert with validation I'd love to know!

Reply

Mate that last one worked perfectly! Just need to do some checks to make sure there are items in the params and then redirect if the save is completed.

I personally prefer your method, it makes more sense to me. Thanks again, maybe i need to get you involved in this project too haha.

Reply

Sweet! That was a bit of a roller coaster! :)

If you're going to be working on this for awhile and think you'll need help often, it would help to make a repo that could be cloned and ran locally... if that's an option of course. If so let me know, I'd love to take a deeper look at what you're doing with this project.

Reply

Sounds like a plan Jacob, i could do with someone with expertise in Rails helping me out haha feel free to drop me an email

Reply

Sorry was just testing something out :)

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 86,946+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.