Routing Error passing an ID on a link_to in Rails - ruby-on-rails

I have the following set of routes in my rails app:
resources :customers do
resources :jobs
end
I also have the following two models - job.rb and customer.rb:
class Job < ActiveRecord::Base
belongs_to :customer
end
class Customer < ActiveRecord::Base
has_many :jobs
end
I have the following New method in my jobs_controller.rb:
def new
#customer = Customer.find(params[:customer_id])
#job = #customer.jobs.build
end
I am trying to pass the customer ID directly to a new job upon creation, as every job should be associated with a customer (no rogue jobs with no customer_id associated).
In order to do that, I have the following link on the customer view page:
<%= link_to "Add New Job", new_customer_job_path(#customer) %>
When clicked, the ID is passing, but I am getting a no method error as follows:
NoMethodError in Jobs#new
undefined method `jobs_path' for #<#<Class:0x007f89899d9030>:0x007f898a7ab0f0>
<%= form_for(#job) do |f| %>
So it seems like some conflict with my routes as it does not like my jobs path - anyone know where this is coming from?
Thanks in advance for the help.

form_for #job will look for a jobs controller. Because you've nested it it won't work.
Switch it to
<%= form_for([#customer, #job]) do |f| %>
Assuming you have a before filter to find the customer in your controller.

Just replace
<%= form_for(#job) do |f| %>
to
<%= form_for[#customer, #job]) do |f| %>

Related

Creating multiple children at the same time as I create the parent

I'm working on a simple rails task list app for learning purposes, and one of the things I would like to have on the app is to be able to create a new list at the same time as I can add in the tasks within that list. I have finalized the basic CRUD actions for creating lists, and now I want to add the capability for creating tasks at the same time as the creation of lists.
I have done some of the initial associations like so:
My List model:
class List < ApplicationRecord
has_many :tasks
accepts_nested_attributes_for :tasks
end
My Task model:
class Task < ApplicationRecord
belongs_to :list
end
Also I've changed my list_params to return the tasks aswell:
def list_params
params.require(:list).permit(:title, :public, task_attributes: [:text])
end
Now my problem is with how to write the form for my list with the possibility to add a dynamic number of tasks within it, then send those tasks over to my create action in order to save it.
My new action is as simple as it gets:
def new
#list = List.new
end
My current form is like so:
<%= form_with scope: :list, url: lists_path, local: true do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :public %><br>
<%= form.check_box :public %>
</p>
<h2>Tasks</h2>
<%= form.fields_for :tasks do |task_form| %>
<p>
<%= task_form.label :text %><br>
<%= task_form.text_field :text %><br>
</p>
<% end %>
<p>
<%= form.submit %>
</p>
<% end %>
I intend to use this for testing purposes, to first create a list with one task, then one with two tasks, and then finally create some code to be able to add new fields via javascript so I can create an indefinite number of tasks. The problem I am arriving at however, is that when I submit this form, and call params at my create action, I can see it contains my task:
params
{\"utf8\"=>\"✓\", \"authenticity_token\"=>\"...\", \"list\"=>{\"title\"=>\"list\", \"public\"=>\"0\", \"tasks\"=>{\"text\"=>\"task\"}}, \"commit\"=>\"Save List\", \"controller\"=>\"lists\", \"action\"=>\"create\"}"
But when I try to see what's contained within my list_params what I get omits the tasks:
list_params
{\"title\"=>\"list\", \"public\"=>\"0\"}"
And beyond that, if I add two text fields in my tasks form, say filled with "task1" and "task2", what I get in the params is only "task2", seemingly overwriting the previous task.
So my problems are
1) Am I doing my form correctly? How should I change it so it allows for multiple tasks?
2) Why doesn't my list_params return any data from the task?
and I guess as a bonus, is there anything else that I am missing to be able to save a list at the same time as it's tasks?
EDIT: Here's the github link for my project if anyone wants to try it: https://github.com/bpromas/task-list
Maybe this can help you.
Take a look at this gem: https://github.com/nathanvda/cocoon
I created a new rails app and tried to follow the code you provided.
rails new a
rails generate scaffold List title public:boolean
rails generate scaffold Task text list:references
rails db:migrate
Then I edited the models like yours
app/models/list.rb
class List < ApplicationRecord
has_many :tasks
accepts_nested_attributes_for :tasks
end
app/models/task.rb
class Task < ApplicationRecord
belongs_to :list
end
Now I looked your code and I did not understand how you initialized the tasks to be displayed in form.fields_for. I am going to print two possibilities that I am aware.
First possibility is creating a new instance of Task in _form.html.erb
<%= form.fields_for :tasks, Task.new do |task_form| %>
<p>
<%= task_form.label :text %><br>
<%= task_form.text_field :text %>
</p>
<% end %>
Second possibility is building new instances of Task in lists_controller.rb
def new
#list = List.new
#list.tasks.build
end
My list_params method is the same
def list_params
params.require(:list).permit(:title, :public, tasks_attributes: [:text])
end
For me with all the steps above the app is working properly saving the tasks for the respective list. Check out if the console is displaying a red message like that "Unpermitted parameter: :tasks_attributes", if so there is some missing step you need to look at.
The time you make this work then to change the code to display more task fields is easy, just pass an array of new Task in _form.html.erb or create more builds in lists_controller.rb
First alternative
<%= form.fields_for :tasks, [Task.new, Task.new] do |task_form| %>
<p>
<%= task_form.label :text %><br>
<%= task_form.text_field :text %>
</p>
<% end %>
Second alternative
def new
#list = List.new
2.times { #list.tasks.build }
end
Good luck !!

Associating two models together in Rails 4+

I have a User model which is working under Devise with no problems (using devise sanitizer to update fields, so no UsersController)
I am working on creating a Quiz model, which belongs_to the User model, and the User model has_one Quiz. In my routes, I have: resources :users, :quizzes (is this supposed to be quizzes or quizs? I know that Rails pluralizes but couldn't seem to find which it'd be in this case).
In my views, I'm trying to open up a modal (which works) and inside have it populated with fields that a User can enter in questions they want (q1 through q5 being the database fields).
Inside the modal content area, I have the code:
<%= form_for #quiz, url: {action: "new"} do |f| %>
<%= f.submit "Create" %>
<% end %>
and I get the error "First argument in form cannot contain nil or be empty"
Inside my quizzes controller, I have defined new as
def new
#quiz = Quiz.new
end
I would greatly appreciate some assistance here! Thank you.
In your WelcomeController action: index add this line to initialized #quiz
def index
#quiz = Quiz.new
end
hope you made a good progress in your project.
shoudn't it be like following
<%= form_for #quiz do |f| %>
<%= f.submit "Create" %>
<% end %>

Why is this form not working?

I'm making a mailer so that I have a business directory, each business listing has a form and when the form is filled out and submitted it is sent to the relative business email with the lead information.
Here's the mailer folder file:
class Enquiry < ActionMailer::Base
default from: "admin#uk-franchise.co.uk"
attr_accessible :name
def lead(listing)
#listing = listing
mail(to: #listing.leadrecepient, subject: "test")
end
end
Here's the listing controller where I'm calling it:
def lead
listing = Listing.find(params[:id])
Enquiry.lead(listing).deliver
redirect_to listing
end
The view:
<%= form_for #listing, url: lead_listing_path(#listing), method: :put do |listing| %>
<%= listing.text_field :name %>
<%= listing.submit %>
<% end %>
The routes:
resources :listings do
member do
put :lead
end
end
And the error :(
undefined method `name' for #<Listing:0x4d7c6d8>
On the view line:
<%= listing.text_field :name %>
Any help greatly appreciated, mailers are a nightmare!
When you use form_for helper, it actually rely on a model object. Doing this, when you write listing.text_field :name it will actually look for a field name in the listing model. You get that error because your model (connected to a DB table? we would like to see the listing class) doesn't have this field. I can see it in the Enquiry class.
I would say you have to move it in the model, but is hard to say without seeing the Model or knowing for what you need that field...
Show us the model and explain a little better what are you trying to do, so we can help you :)

Rails - nested forms to link has_one's where the child object has already been created

I have two models which are linked in a has_one / belongs_to association; Computer and Ipv6Address respectively.
I have pre-populated the Ipv6 table with all the entries that I want it to have, and I now need to have a drop-down list on the Computer new/edit forms to select an item from Ipv6 to associate it with.
Everything I've seen so far on this only seems to work when you are creating both objects at the same time on the new form and then subsequently editing them.
I've tried to set up my MVC's as per the examples I've found online, but I keep getting errors, as underneath these code excerpts:
Computer model:
class Computer < ActiveRecord::Base
accepts_nested_attributes_for :ipv6_address
has_one :ipv6_address
...
Ipv6Address model:
class Ipv6Address < ActiveRecord::Base
attr_accessible :computer_id, :ip_address
belongs_to :computer
...
Computer controller:
class ComputersController < ApplicationController
def new
#computer = Computer.new
#ipv6s = Ipv6Address.where('computer_id IS NULL').limit(5)
end
def edit
#computer = Computer.find(params[:id])
#ipv6s = Ipv6Address.where('computer_id = #{#computer.id} OR computer_id IS NULL').order('computer_id DESC').limit(5)
end
Computer new form:
<%= simple_form_for( #computer ) do |f| %>
<%= f.fields_for :ipv6_addresses do |v6| %>
<%= v6.input :ipv6_address, :collection => #ipv6s %>
<% end %>
<% f.button :submit %>
<% end %>
Error when browsing to computer new form:
NoMethodError in ComputersController#new
private method `key?' called for nil:NilClass
No line of code reference is given for this error, but it only appears when I have the nested form included in the computer new form.
Any ideas as to what is causing it or how I can better go about what I'm doing?
EDIT:
As it turns out, I needed to have accepts_nested_attributes_for :ipv6_address after the line has_one :ipv6_address in the Computer model.
That fixed the issue with the form loading.
As per Yarden's answer, I then also singularized all instances of "ipv6_address" so as to reflect the has_one relationship.
Once doing that in the new form, however, the ipv6 field completely disappeared. I'll open a new question with this one if I can't get it sorted out shortly.
Try adding ':', I think that may be the problem :
<%= f.fields_for :ipv6_addresses do |v6| %>
Also you forgot to add a '.' here:
#ipv6s = Ipv6Address.where('computer_id IS NULL').limit(5)
Edit after change:
The problem is that its only a has_one relationship so it doesnt know the plural of :ipv6_address as you stated in your model... you need to change it to :ipv6_address instead of :ipv6_addresses...
Also in your form change the :ipv6_address to the actual field which is :ip_address.
So overall your form should look like this:
<%= simple_form_for( #computer ) do |f| %>
<%= f.fields_for :ipv6_address do |v6| %>
<%= v6.input :ip_address, :collection => #ipv6s %>
<% end %>
<% f.button :submit %>
<% end %>

Rails Nested Object Form *_attributes

I'm using Rails 2.3.2, and trying to get a nested object form to work properly. I've narrowed my problem to the issue that Rails is not setting my nested form elements with the *_attributes required to initiate the accepts_nested_attributes_for processing.
My model code is:
class Person < Party
has_one :name, :class_name => "PersonName"
accepts_nested_attributes_for :name, :allow_destroy => true
end
class PersonName < ActiveRecord::Base
belongs_to :person
end
My view code looks like this (I'm using HAML):
%h3 New customer
= error_messages_for :person, :person_name, :name, :country
- form_for :person, :url => collection_url, :html => {:class => 'MainForm'} do |person_form|
- #person.build_name unless #person.name
- person_form.fields_for :name do |name_form|
= name_form.label :given_name, "First Name:"
= name_form.text_field :given_name
= name_form.label :family_name, "Last Name:"
= name_form.text_field :family_name
= hidden_field_tag :inviter_id, params[:inviter_id]
= hidden_field_tag :inviter_code, params[:inviter_code]
%p= submit_tag "Create"
= link_to 'Back', collection_url
Instead of params being:
{"person"=>{"name_attributes"=>{"given_name"=>"Fred", "family_name"=>"Flintstone"}}, ...}
I get:
{"person"=>{"name"=>{"given_name"=>"Fred", "family_name"=>"Flintstone"}}, ...}
As a result, I get a TypeMismatch exception. I've followed the documentation from Ryan Daigle. I've also followed the advice from this blog and the complex-forms-example.
Using Firebug, I went through my form and adjusted the name attribute of the input tags from name to name_attributes. This produced the params with name_attributes, and the create worked fine.
I'm stuck as I cannot figure out why my form is not producing the *_attributes form of the name.
Another thing I tried is I got the complex_form_example working in my environment. I've gone through every inch of the controller, models and views and compared it to my code. I cannot find what is different. I know this is something small, and would appreciate any help!
Thanks!
Post backs do not get routed to the right place
def new
#person = Person.new
end
<% form_for #person do |f| %>
<% f.fields_for :name_attributes do |p| %>
...
<% end %>
<% end %>
Post backs get routed to the right place
def new
#person = Person.new
#person.name = PersonName.new # << this allows fields_for :relation vs :relation_attributes
end
<% form_for #person do |f| %>
<% f.fields_for :name do |p| %>
...
<% end %>
<% end %>
No need to #person.name again in #create
Try to use an actual object for form_for:
form_for :person => form_for #person
I have just been struggling for about an hour with exactly the same problem!
Follow nowk's pattern for the new method in the controller, then put this in your view
<% form.fields_for :name, #person.name do |name_form| %>
<% end %>
Good luck if you try it, that's what worked for me.
Not sure why this isn't working for, but as a workaround, you could just use params[:name] in your controller#create method to update the person record.
person = Person.new(params[:person])
person.name << PersonName.new(params[:name])
Unfortunately, I still have not been able to figure out why this form wouldn't work with nested object forms. I stripped it down to the simplest data, started over using the complex-form-example as a start. I ended using the active_presenter to gather x-object data from the form. I'll revisit nested object forms sometime in the form. Thanks for your help.
Thought I would share my solution as it's slightly different - in my case I want the nested attributes to be dynamic.
In new action:
case params[:type]
when "clubber"
#account = resource.send "build_#{params[:type]}"
when "promoter"
#account = resource.send "build_#{params[:type]}"
when "company"
#account = resource.send "build_#{params[:type]}"
when "venue_owner"
flash[:notice] = 'We ask that venue owners register via the web. Thanks.'
redirect_to root_path and return
end
In my view:
= f.fields_for #account.class.name.downcase+'_attributes' do |form|
Pretty ghetto, but it works.

Resources