WhiteTiger
WhiteTiger

Reputation: 776

Rails - Use nested form to create new records

I have a nested form for a user, portfolios, and photos. Basically a user can create a portfolio by uploading photos in ANOTHER FORM. However, then I want to give them a chance to create a new portfolio, by selecting some photos from the current portfolio they are viewing, and have the method resubmit in PortfolioController create a new portfolio for them. The models are:

class User < ActiveRecord::Base
    has_many: portfolios
end

class Portfolio < ActiveRecord::Base
    has_many: photos
    belongs_to: user
end


class Photo < ActiveRecord::Base
    belongs_to: portfolio

    attr_accessor :move
end

The controller is:

class PortfolioController < ApplicationController
    //... some generic code

    def resubmit
        // This is where I need help
    end

    def display
        @userPortfolio = Portfolio.where(:id => params[:uid]).first
    end
end

and the view is:

<%= simple_form_for @userPortfolio, :url => {:action => resubmit} do |f|%>
    <%= f.label current_user.name %>
    <% @images = @userPortfolio.photos %>
    <% @images.each do |anImage| %>
        <%= f.fields_for :anImage do |ff| %>
            <%= ff.checkbox :move, :id => "move_#{anImage.id}" %><%=ff.label :move, "Move image #{anImage.name} to new portfolio?" %>

        <% end %>
    <% end %>

    <%= f.submit "Create new portfolio" %>
<% end %>

Basically once the user hits submit I want the method resubmit to create a new portfolio with a collection of new photos that are the same as the photos selected. That is, I want to create 1 new record of portfolio and several new records photo, as many as the user has selected, based on the properties of the photos the user selected, so I need to access the records that represent the selected photos. How do I access all the photos the user has selected? I can't simply create a finite set of checkbox controllers on the form since the number displayed depends on the number of photos in the current portfolio.

Upvotes: 0

Views: 96

Answers (2)

WhiteTiger
WhiteTiger

Reputation: 776

Okay, so it seems what I was trying to do was fairly simple after all. No need for nested_form, the simple_form does just as well. But I'm sure nested_form (as Tory SK suggested) does the trick as well. The major point was adding attr_accessor: to the code.

Basically the code is as follows:

class User < ActiveRecord::Base
    has_many: portfolios
end

class Portfolio < ActiveRecord::Base
    has_many: photos
    belongs_to: user

    attr_accessor: photos_attributes // This is an important piece of code
end


class Photo < ActiveRecord::Base
    belongs_to: portfolio

    attr_accessor :move
end

Then I simply changed the form as follows (notice the additional :pid => params[:pid] so that I can figure out in resubmit which portfolio I need to access IF I need to access it):

<%= simple_form_for @userPortfolio, :url => {:action => "resubmit", :pid => params[:pid]} do |f|%>
    <%= f.label current_user.name %>
    <% @images = @userPortfolio.photos 
       i = 0
    %>
    <%= f.fields_for :images do |ff| %>
            <% anImage = @images[i] %>
            <%= ff.checkbox :move, :id => "move_#{anImage.id}" %><%=ff.label :move, "Move image #{anImage.name} to new portfolio?" %>
            <% i = i+1%>
    <% end %>

    <%= f.submit "Create new portfolio" %>
<% end %>

And when you hit submit and send the form to resubmit, the params dictionary contains the following:

{"utf8"=>"✓",
 ... more parameters
 "portfolio"=>{"photos_attributes"=>{"0"=>{"move"=>"0",
                                           "id"=>"1"},
                                     "1"=>{"move"=>"0",
                                           "id"=>"2"},
                                     ...

                                     "4"=>{"move"=>"0",
                                           "id"=>"5"}}},
 "commit"=>"Submit Job",
 "pid"=>"1"}

Now I can just iterate through the checkboxes and compare them to all the photos that belong to the current portfolio in resubmit

def resubmit
    @myImages = Portfolio.where(:id => params[:pid]).photos
    // iterate through each image and use a counter to access 
    // the dictionary elements to see if they were checked
end

Upvotes: 0

Troy SK
Troy SK

Reputation: 1289

Use the gem nested_form or cocoon to make the elements dynamic. On the backend use the selected items to create entries for the new portfolio.

Upvotes: 1

Related Questions