2

Issue: I have a nested fields_for text_field not appearing, I am not sure what I have been done wrong.

Goal: While creating a record, iterate through a model with preset variables, and save a file (testing with text_field) to a join table which saves both the preset variables and the forms record ID

Models:

class PrintLocation < ApplicationRecord
  has_many :shop_products, through: :shop_product_print_files
  has_many :shop_product_print_files
  accepts_nested_attributes_for :shop_product_print_files
end

class ShopProductPrintFile < ApplicationRecord
  belongs_to :shop_products
  belongs_to :print_locations
end

class ShopProduct < ApplicationRecord
    ...
  has_many :shop_product_print_files
  has_many :print_locations, through: :shop_product_print_files
  accepts_nested_attributes_for :print_locations
  accepts_nested_attributes_for :shop_product_print_files
    ...
end

Form:

<%= form_for @shop_product do |f| %>
   <%= f.collection_select :product_id, @products, :id, :sku %>
   <% PrintLocation.all.each do |print_location| %>
     <%= print_location.title %>
        <%= f.fields_for :shop_product_print_files do |a| %>
           <%= a.text_field :print_file %>
        <% end %>
     <% end %>
   <%= f.submit %>
<% end %>

With this, the text_field doesn't appear but the print_location.title's do appear. There are no errors with this.

While saving the @shop_product, I want to be able to iterate through the possible print_location variables, which are defined, and then for each possible print_location, to then be able to upload a file (text_field for testing), and then save that to the ShopProductPrintFile model which has shop_product_id and print_location_id and print_file attributes.

Is there something I am misunderstanding for how to use fields_for?

Shop Product Controller:

Create:

@shop_product = ShopProduct.new(shop_product_params)
shop = Shop.find(params["shop_product"]["shop_id"])
product = Product.find(params["shop_product"]["product_id"])    @shop_product.product_id = product.id
@shop_product.shop_id = shop.id
respond_to do |format|
   if @shop_product.save!
...

Update:

@shop_product = ShopProduct.find_by(store_variant_id: params["shop_product"]["store_variant_id"])
@product = Product.find(params["shop_product"]["product_id"])

Strong Params:

def shop_product_params
   params.require(:shop_product).permit(:product_id, :store_product_id, :shop_id, :store_variant_id, :sync, :shop_product_print_file_attributes[:id, :print_files, :print_location_ids => [], :shop_product_ids => []], {print_location_ids: []})
end

UPDATE 2:

Update and Create Method:

@shop_product.shop_product_print_files.build

form:

<% PrintLocation.all.each do |print_location| %>
  <%= print_location.title %>
    <%= f.fields_for :shop_product_print_files_attributes do |a| %>
       <%= a.text_field :print_file %>
       <%= a.hidden_field :print_location_id, value: print_location.id %>
       <%= a.hidden_field :shop_product_id, value: shop_product.id %>
    <% end %>
<% end %>

params:

def shop_product_params
   params.require(:shop_product).permit(:shop_product_print_files_attributes => [:ids => [], :print_files => [], :print_location_ids => [], :shop_product_ids => []])
end

error: Shop product print files shop products must exist Shop product print files print locations must exist

params that pass:

Parameters: {"utf8"=>"✓", "authenticity_token"=>"u/c103465uNCjF/trYrMleqxJ8b9wyLbU/vjPK4llYtCg/ODj92q5MN24==", "shop_product"=>{"sync"=>"1", "product_id"=>"3", "shop_product_print_files_attributes"=>{"print_file"=>"", "print_location_id"=>"6", "shop_product_id"=>"42"}, "store_product_id"=>"191234345", "store_variant_id"=>"15341234273", "id"=>"42"}, "commit"=>"Sync", "id"=>"42"}

The models haven't changed.

Print file in params still blank?

UPDATE 3:

**using this form: thanks to @arieljuod **

<%= f.fields_for :shop_product_print_files do |ff| %>
    <%= ff.object.print_location.title # get the print location from the association %> 
    <%= ff.hidden_field :print_location_id # save the print_location_id as a hidden field %>
    <%= ff.file_field :print_file # file input %>
  <% end %>

with this in the new and method housing the view:

@shop_product = ShopProduct.new
PrintLocation.all.each{|p| @shop_product.shop_product_print_files.build(print_location: p)}

works on create.

Issue still arises due to not knowing the ID of the ShopProduct until the page loads due to API and there is a possibility of being multiple IDs on one page.

I use:

<% if @shop_products.find_by(store_variant_id: variant.id)  %>
<% shop_product = @shop_products.find_by(store_variant_id: variant.id)  %>
   <%= form_for shop_product do |f| %>
   ...

Which, variant comes from a loop defined by an API:

<% @in_store_variants.each do |variant| %>

Now when using shop_products (from when shop_product already exists from finding by the variant.id), the fields_for won't appear. Assuming this is because no records exist in relation. Only if a shop_product.shop_product_print_files exist, will they appear.

The only work around, at this time to my knowledge, is to save all print_locations but use a boolean for which are actually active, or search for which print_locations have an ID attached. But i would rather not do it that way and just save which print_locations are chosen on create (chosen by uploading a print_file).

To "fix" this issue, I:

  1. added accepts_nested_attributes_for reject_if: proc { |attributes| attributes['print_file'].blank? } which doesn't save ShopProductPrintFile's unless the print_file field is submitted with something...

  2. use this form (2 forms depending on if exists or not)

    <% if @shop_products.find_by(store_variant_id: variant.id)  %>
       <%= form_for shop_product do |f| %>
       <% PrintLocation.all.each{|p| shop_product.shop_product_print_files.build(print_location: p)} %>
       <%= f.fields_for :shop_product_print_files do |ff| %>
           <%= ff.object.print_location.title %>
          <%= ff.hidden_field :print_location_id %>
            <%= ff.text_field :print_file %>
       <% end %>
      <%= f.submit "Sync" %>
    <% end %>
    <% else %>
       <%= form_for @shop_product do |f| %>
       <% PrintLocation.all.each{|p| @shop_product.shop_product_print_files.build(print_location: p)} %>
       <%= f.fields_for :shop_product_print_files do |ff| %>
           <%= ff.object.print_location.title %>
           <%= ff.hidden_field :print_location_id %>
           <%= ff.text_field :print_file %>
       <% end %>
        ...
    

The issue with 2 is i have have PrintLocation 1,2,3 associated, it will show 9 fields, the 1,2,3 ready for update, and the 6 ready for create.

is it possible to call the PrintLocation.all.each{|p| @shop_product.shop_product_print_files.build(print_location: p)} on already created ShopProducts's for where a shop_product_print_file doesn't exist in relation to the possible print location.

So for example... Created ShopProduct with print location, 1,2,3 (out of 6 possible)

Now, shop_product_print_location where print_location exists will show for updating in the form, so thats 1,2, and 3. How can I have it so the other 3 that weren't created now show to update the ShopProduct and create new ShopProductPrintFile's? so it is possible to update the ShopProduct to have more print_locations to the shop_product_print_file model.

6
  • 1
    Nice, maybe try to add "_attributes" like so f.fields_for :shop_product_print_files_attributes, but I'm not sure Commented Jun 14, 2019 at 4:49
  • 1
    you're godly, however, I still have issues with the print_file param not passing. It goes through blank as "shop_product_print_file_attributes"=>{"print_file"=>"", "print_location_id"=>"6", "shop_product_id"=>"42"} and in my strong params I have ` :shop_product_print_file_attributes [ :print_files, :shop_product_ids, :print_location_ids ]` but receiving error: too many arguments Commented Jun 14, 2019 at 5:24
  • I had this problem also, but in my case it was not nested. So I think it's because print_location_ids and shop_product_ids are arrays. So, in the strong params would be something like product_ids:[] and shop_product_ids:[] I don't know about this error though, maybe is missing the arrow ? :shop_product_print_file_attributes => [ ... ] I hope it works ... Commented Jun 14, 2019 at 5:38
  • Also... check in the console if says something like "unpermited parameters" when you submit the form, sometimes it happens.. Commented Jun 14, 2019 at 5:40
  • 1
    @GuilhermeNunes ended up being becauseof my strong_params but still haven't issues.. I updated my post above at the bottom if you can have a look Commented Jun 14, 2019 at 22:02

2 Answers 2

1

I have a nested fields_for text_field not appearing, I am not sure what I have been done wrong.

You should add this line in your create action

@shop_product = ShopProduct.new(shop_product_params)
@shop_product.shop_product_print_files.build #this one

Also change shop_product_print_file_attributes to shop_product_print_files_attributes to avoid any further errors.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks... @shop_product.shop_product_print_files.build should go in both update and create? (users can update)
I updated my OP with new form, params, and new error i am receiving.
I am using Ruby 2.4.5 btw if that helps... Rails 5.2.2
So doing this: @shop_product.shop_product_print_files.build(shop_product_id: @shop_product.id, print_location_id: params["shop_product"]["shop_product_print_files_attributes"]["print_location_id"]) in the update works. but the print_file still appears blank on the form submit... Also having issues on Creating a ShopProduct because in order to Save to the ShopProductPrintFile mode, I need a ShopProduct and it's not yet created so the @shop_product in the create method doesn't pass through?
I also changed both of the belongs_to in the ShopProductPrintFile model to singular instead of plural
|
0

You have to tell rails which PrintLocation to use on each iteration since your object does not have any

<%= f.fields_for :shop_product_print_files, print_location do |a| %>

I'm not really sure if that's what you want, but the field will appear.

EDIT: so, I think you need something like this:

On the controller

@shop_product = something_to_get_the_product
PrintLocation.all.each{|p| @shop_product.shop_product_print_files.build(print_location: p)}

I prefer to do this here, I don't like that logic on the view

Now you have all the possible print location prebuilt on the shop product object

On the form

# note here the multipart option to allow files
<%= form_for @shop_product, multipart: true do |f| %>
  <%= f.collection_select :product_id, @products, :id, :sku %>

  <%= f.fields_for :shop_product_print_files do |ff| %>
    <%= ff.object.print_location.title # get the print location from the association %> 
    <%= ff.hidden_field :print_location_id # save the print_location_id as a hidden field %>
    <%= ff.file_field :print_file # file input %>
  <% end %>

  <%= f.submit %>
<% end %>

9 Comments

Hm maybe ill try that but i made an update at the bottom of the post
Using that makes the page not load with: ActionView::Template::Error (undefined method print_file' for #<PrintLocation:0x000055ace1e55e50>):` .. the goal is to save to he shop_products_print_file model based on each possible print_location
Any ideas? . . . . . . . . . .
I'm not sure I understand what you are trying to do. You want a form to display all possible print locations with a field to add a file on each of those print locations? Or a selector of the avialable print locations to upload a file? I don't really get it
Yes (to the first 1)!, so i seeded 6 possible PrintLocations into PrintLocation. This model database will never be added to, it has title and cost. I then have a ShopProductPrintFile model that has print_location_id, shop_product_id, print_file... In the form while creating a ShopProduct, I want to display each possible print_location, and have a print_file field to each print_location. For now, I am using text_field for print_location for testing. Then on submit, it will save a ShopProduct and ShopProductPrintFile(s)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.