Phoenix Framework: Is inputs_for available for the has_many associations? - phoenix-framework

Using the Phoenix Framework, I want to implement the nested form like the fields_for of the RoR.
Today I tried to implement it with inputs_for, but I got the error.
Referring the official document of Phoenix Framework, I could found the sample with inputs_for. It describes only the sample with the embed_one association. And I met the error with the has_many association.
How can I implement the nested form for the has_many association correctly?
The schemata are as below:
defmodule AnApp.User do
use PhoenixBlog.Web, :model
schema "users" do
field :handle, :string
field :password_digest, :string
has_many :emails
end
end
defmodule AnApp.Email do
use PhoenixBlog.Web, :model
schema "emails" do
field :address, :string
end
end
And I implemented the form.html.eex as below:
<%= form_for #changeset, #action, fn f -> %>
<%= inputs_for f, :emails, fn ef -> %>
<% end %>
<% end %>
This cause the error Argument Error: unknown field :emails.

No, right now it supports only embeds. We are working on supporting associations next. The error messages in the newly released Phoenix.Ecto should also be clearer.

Related

Rails 4 many to many relationship

I have an app that has 2 models that need a many to many relationship. I originally tried using has_and_belongs_to_many but it wasn't working. After scrummaging through the internet I was convinced that this was not what I needed anyhow. So, I switched to a has_many through method. However, this isn't working either. I am now at a major loss on what the issue is. I have ripped it out many times and tried tutorial after tutorial but to no avail.
The models I am trying to connect are Message and Author. In my most recent attempt, I created a joining model (with corresponding table) called Authoring. Here is my code:
message.rb
has_many :authorings
has_many :authors, through: :authorings
author.rb
has_many :authorings
has_many :messages, through: :authorings
authoring.rb
belongs_to :message
belongs_to :author
messages_controller.rb
def messages_params
params.require(:message).permit(:title, :summary, :date, :duration, :youversion, :hours, :minutes, :seconds, :authors)
end
My Messages form:
<%= f.label :speakers %><br />
<%= collection_select(:authors, :authors, Author.all, :id, :name, {}, {:multiple => true, :size => 10}) %>
When I created a new message, this showed in the log:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"OcLeke/eRRzwApoMSgQF81kBCm7TaGxW1PoVAUgEcvo=", "message"=>{"title"=>"Test1", "summary"=>"This is my test summary.", "date(2i)"=>"8", "date(3i)"=>"25", "date(1i)"=>"2014", "date(4i)"=>"01", "date(5i)"=>"00", "hours"=>"0", "minutes"=>"58", "seconds"=>"52", "youversion"=>""}, "authors"=>{"authors"=>["", "1"]}, "commit"=>"I'm Finished", "id"=>"1"}
The form obviously recognizes the authors in the form but it doesn't save them in the database. What is my disconnect? Please help!!!
Thanks!
authors records are not getting created because the controller is expecting authors to be present within message key's value in params hash BUT if you notice the params hash in the log
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"OcLeke/eRRzwApoMSgQF81kBCm7TaGxW1PoVAUgEcvo=",
"message"=>{...}, "authors"=>{...}, "commit"=>"I'm Finished",
"id"=>"1"}
you would see that authors is coming as an altogether separate key in params hash which is why it is being ignored.
What you need to do is associate the authors with the form object, instead of using collection_select you need to use f.collection_select as shown below. Specify the first argument as :author_ids in f.collection_select.
<%= f.collection_select(:author_ids, Author.all, :id, :name, {}, {:multiple => true, :size => 10}) %>
As you have :multiple => true option specified, you would receive authors as an Array in the params hash. You need to permit author_ids as an Array in message_params method
def messages_params
params.require(:message).permit(:title, :summary, :date, :duration, :youversion, :hours, :minutes, :seconds, :author_ids => [])
end

Dropdown Filter for Polymorphic association nested_attribute

I'm building an application in which the user can create a Company and add Investments made to that company. The investments can come from two sources user's Funds or companies' Coinvestors. Funds are big deal in the application as the user can do a bunch of stuff in them. The Coinvestors are not that important, but I want to have control over few aspects of so I created a Model just for them. For that I created a Polymorphic association for which I gave the [terrible] name of Investables. I'm running Rails 3.2.15 and Ruby 2.0.0. Models are below:
class Company < ActiveRecord::Base
has_many :investments
accepts_nested_attributes_for :investments
end
class Investment < ActiveRecord::Base
belongs_to :fund, :class_name => "Fund", :foreign_key => 'investable_id'
belongs_to :company, inverse_of: :investments
belongs_to :coinvestor, :class_name => "Coinvestor", :foreign_key => 'investable_id'
end
class Fund < ActiveRecord::Base
has_many :investments, :as => :investable, :dependent => :destroy
end
class Coinvestor < ActiveRecord::Base
has_many :investments, :as => :investable, :dependent => :destroy
end
When editing the company I want to be able to add investments and I want to dynamically add form lines for each new investment. I was able to achieve that following the awesome 165-Edit Multiple Revised.
To make it more complex, I also want to add a Dropdown for choosing the Polymorphic Type so it filters the next Dropdown to show only the names of either the Funds or the Coinvestors.
For that I mostly adapted code from Railscast 88-Dynamic Select Menus(Thanks Ryan!!)
/views/company/edit.html.erb
<%= form_for(#company) do |f| %>
...
<%= f.fields_for :investments do |builder| %>
<%= render 'investment_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Investment", f, :investments, 'table' %>
/views/company/_investment_fields.html.erb
<%= f.select :investable_type , [ "Fund", "Coinvestor" ], {prompt: "Investor Type"} %>
<%= f.grouped_collection_select( :investable_id, investables_to_collection, :investables, :name, :id, :name, {prompt: "Investor"} ) %>
The "investable_to_collection" is a helper I built to aggregate objects from both Funds and Coinvestors models.
module CompaniesHelper
InvestableCollection = Struct.new(:name, :investables)
CollectionItem = Struct.new(:name, :id)
def investables_to_collection
a = Array.new
a << InvestableCollection.new('Fund', Fund.all.map { |item| CollectionItem.new(item.name, item.id )})
a << InvestableCollection.new('Coinvestor', Coinvestor.all.map { |item| CollectionItem.new(item.name, item.id )})
a
end
end
I didn't add any JavaScript yet to filter the dropdown which will be another challenge. But I've got my beautiful view to show data I've already got in the DB. But the dropdown that should show the Fund's or Coinvestor's name is mixing up things: it will show the name of Coinvestor with ID == 1 even if the investment was made by a Fund.
I thought of making one of the models to have a custom ID such as f1, f2, f3 ... instead of 1, 2, 3... so the system wouldn't mix them. But it seems that it would generate other big compatibility issues.
Do you guys have any other idea?
I wouldn't alter the id column (i.e. I wouldn't change it from an auto-incrementing integer), but because you're mixing two lists in the DB into a single list in the UI you will need some way to note which table each item in the UI came from. For that, sure, use whatever ID scheme you want and then use that differentiating ID as the way to lookup the relevant item.
Also, if you're not going to use the auto-incrementing id columns in the UI (for links or whatever), then you could also just remove them and replace them with your custom identification scheme. There's no reason, for example, that you couldn't assign them all a random 8-digit number, ensuring that number is unique across the different types you're going to put into the list, and then using that id in the UI. It seems that the real issue you're running into is how to combine the lists where ids might overlap, and perhaps it may be feasible for you to devise a way to assign a non-overlapping ID.
Another possibility for assigning non-overlapping IDs, without having to come up with your own scheme or checking for uniqueness across multiple tables is to use a UUID for the lookup ID in the UI drop-down.

Can't see why I'm getting undefined method form_for

I don't get why I'm getting this error
undefined method `sector_id' for #<Portfolio:0x007fe17c2e3848>
I have a Portfolio Model and a Sector model, they look like so
class Portfolio < ActiveRecord::Base
belongs_to :sector
attr_accessible :overview, :title, :sector_id
end
class Sector < ActiveRecord::Base
has_many :portfolios
attr_accessible :name
end
My routes
resources :portfolios do
resources :sectors
end
So within my form to create a new portfolio I have this collection_select
<%= f.label :sector_id, "Choose Sector", :class => 'title_label' %><br>
<%= f.collection_select(:sector_id, Sector.all, :id, :name, :prompt => "Please Select a Sector") %>
This is something I've done many times before and it has worked, can anyone see why I would be getting this error?
The only thing I can think of is that I have called my controller for portfolio as Portfolios, I always get mixed up with plural and singular controller names, would this make a difference in my case?
Maybe you have not run the migration yet that adds the column "sector_id" in your table "portfolios". If you are using MySQL connect to your database and check the table (show create table portfolios;). Use appropriate method to get this info from your database server if you are using other rdbms. Alternatively, in your rails console (rails c) type in Portofolio and see what attributes it prints out. Does it include sector_id?

ActiveModel::MassAssignmentSecurity Error - concerning time_select in formtastic?

I'm using the following code to create some timeslect and date select objects using activeadmin and formtastic. The result generates two timeselects and a date select object as the correspoinding data field types are time and date. The problem is that when I submit the form I get a ActiveModel::MassAssignmentSecurity error
<%= f.inputs :class=>'inputs', :for=>Schedule.new do |fu| %>
<%= fu.input :eta, :label=>"ETA", :ignore_date=>true %>
<%= fu.input :etd, :label=>"ETD", :ignore_date=>true %>
<%= fu.input :date, :ignore_time=>true %>
<% end %>
When posting the etd and eta fields are posted as arrays - is there a way to get the actual values of them cleanly?
I can't know for sure what your problem is without seeing the models in question, but my best guess is that you've got an issue with accepts_nested_attributes_for and attr_accessible.
On the model represented by the parent form, you will need:
has_one :schedule #assuming it's has_one, could be something else...
accepts_nested_attributes_for :schedule
attr_accessible :schedule_details
Note that the association name in attr_accessible is appended with _details. If you just have the association name, Rails will throw an exception.
Does this look like it could be the issue?

Rails populate collection_select

I am trying to auto populate a select box in rails with a relational table.
Venture_Users has a list of Users and Venture ID's so I want to find all Users under a specific Venture and then display the users via user_id, and user.name.
I tried the following
<%= f.collection_select(:user_id, VentureUser.find_all_by_venture_id(#venture.id), :user_id, :name) %>
However the last attribute :name doesn't work because it is not directly in my results and I need to run a query on my user table to get the name of the user.'
Essentially what I need to but don't know how to do is modify my VentureUsers.find_all statement to join attributes from my user table.
Thanks,
Mike
This should be quite straight forward as long as you have your associations set correctly. If I have understood your scenario correctly then you should probably have it set up like this:
class Venture < ActiveRecord::Base
has_many :venture_users
has_many :users, :through => :venture_users
end
class VentureUser < ActiveRecord::Base
belongs_to :venture
belongs_to :user
end
If your associations indeed look like this, then you should be able to create the select like this:
<%= f.collection_select(:user_id, #venture.users, :id, :name) %>

Resources