I am developing a rails application for a client. In a nutshell, it is for monitoring the progression of PhD uni students. One of the main functional requirements was for the user to be able to fill out reports online. The issue I am having is with one of the forms.
I am allowing the number of questions per form to be completely unrestricted, so I am using dynamic symbols to store the answers to the questions, and pass those answers to the controller in a hash. Here is a snippet of the code from the form, and the controller method:
<% #questions.each do |q| %>
<div class="field">
<!--Question Label -->
<%= s.send(:label, "Question #{q.question_number}") %>
<!--Link to delete question -->
(<%= link_to 'Remove', {:controller => "report_details", :action => "delete_question", :question_to_delete => q.id}, data: { confirm: 'Are you sure?' }, :class => 'form-link', :method => "post" %>)
<!--Question text box -->
<%= s.send(:text_area, "qu_hash[question#{q.question_number}]", :rows => 3, :cols => 60, :value => q.question) %>
<!--New Question Button -->
<%= link_to '>> Insert Question Here <<', {:controller => "report_details", :action => "add_question", :position_to_add => q.id}, :method => "post" %>
</div>
<% end %>
My issue is with with the line below I think. I will explain why below, but here is my controller method (accessed by submitting the form).
def save
questions = params[:report_detail][:qu_hash]
ReportDetailQuestion.where(:report_detail_id => #report_detail.id).each do |q|
q.update_attributes(:question => questions["question#{q.question_number}".to_sym])
end
redirect_to #report, notice: 'Report was successfully updated.'
end
The error I am getting is this:
undefined method `[]' for nil:NilClass
questions = params[:report_detail][:qu_hash]
ReportDetailQuestion.where(:report_detail_id => #report_detail.id).each do |q|
#Error occurs on the below line
q.update_attributes(:question => questions["question#{q.question_number}".to_sym])
end
The hash is not a part of the database, it is being used to update entries in the table of report questions (seperate to the report table). This error is being caused because the hash is not being submitted from the form (on the error screen - where it shows the params passed to the controller, the hash is not present).
Also, I am unsure how to permit this hash through the "white list" of the object, so if someone could help with that it would also be helpful (for now I am just permitting everything, so that won't be the issue. The field just isn't being submitted for some reason).
If you need more information let me know!
Cheers
For this problem I will advise you to use nested forms which is more suitable for this case mainly for user experience. For that, you need to create a question model first.
you can follow this railscast which explains well how to do...
Not sure it is what you are looking for...
Related
The following works great for carrying forward data from one page to another:
<%= link_to 'New Work Order', new_workorder_path, :class => 'btn btn-primary', :onclick => session[:worequest_id] %>
How would I add a 2nd field? The following doesn't work:
<%= link_to 'New Work Order', new_workorder_path, :class => 'btn btn-primary', :onclick => session[:worequest_id] = #worequest.id, [:client_id] = #worequest.client_id %>
Thanks!
UPDATED
This is the code I'm using in the new work order form. It picks up the worequest_id field from the session
<% if session[:worequest_id] != nil %>
<%= f.hidden_field :worequest_id, :value => session[:worequest_id] %>
onclick doesn't really work this way – it's an html attribute used to store JavaScript code to be executed when the element is clicked. While you can use it to evaluate Ruby code in the context of a Ruby method call (in this case as part of the options hash given to link_to), it doesn't really make sense to do so.
In your first example, it doesn't actually do anything. If you check your rendered html on the page where that link appears, I expect it evaluates to something like New Work Order. You can, however, store data in session (which is persistent for as long as the user remains logged in), which is why you're seeing this data carrying forward from page to page.
If you're trying to fill in default values for the new workorder, you could pass them as params to the path method:
link_to 'New Work Order',
new_workorder_path('workorder[worequest_id]' => #worequest.id,
'workorder[client_id]' => #worequest.client_id),
:class => 'btn btn-primary'
In your workorders#new action, your model instantiation would need to include the params:
def new
#workorder = Workorder.new(params[:workorder])
end
However, this might not be the best way to proceed. If there will always be a client or worequest associated with a workorder, you might want to look into nested routes.
Okay so I'm building a really simple list with items app, pretty much exactly the same as your standard to-do list application. I've managed to ajax-ify the creation of new 'points' within a list (point belongs_to :list and list has_many :points) but I'm having trouble with the 'destroy' action.
When I click on the destroy link in the browser, nothing visibly occurs, and I get the error Error: Syntax error, unrecognized expression: /lists/10/points/125 obviously with different values depending on the id of the list and point.
If I refresh the page or look at the db, it's clear that the entry has indeed been deleted. Without ajax, my destroy action works just fine. I feel like I must be missing something obvious, any ideas?
fyi the 'pro' attribute is just a boolean associated with every point.
points_controller.rb
def destroy
#point = #list.points.find(params[:id])
#point.destroy
respond_to do |format|
format.html { redirect_to list_url(#list) }
format.js
end
end
lists/show.html.erb
<% #list.points.each do |point| %>
<% if point.pro == true and point.valid? == true %>
<li class="weight-<%= point.weight %>"><%= point.content %>
<%= link_to "×".html_safe, [#list, point],
:remote => true,
:method => :delete,
:class=> "close",
:data => {:dismiss => 'alert'} %>
</li>
And it doesn't seem to matter what I put in views/points/destroy.js.erb, because the code doesn't seem to be getting executed.
Update
I figured it out, I had to change the path in the delete link to list_point_url(#list, point). The other problem was that my invalid javascript was causing a server error, so I didn't realize what the problem was (turns out #<%= dom_id(#point) %> needed to be wrapped in quotes).
Thanks all!
Maybe check if the delete link routes to the destroy controller action, because list_point_path doesn't really seem like a delete route.
Edit
Sorry for the lake of knowledge but I'm not sure what [#list, point] will produce as a route. This is what I have for a view of my own, just for your reference:
link_to "Delete", admin_photo_path(photo), :method => :delete, :confirm => "Delete this image?", :class => "btn-trash"
My admin_photo_path is a singular path that route to a single Photo instance; not a collection.
Edit
Simple way could be sending delete to the point object, maybe this could help?
link_to "×".html_safe, point,
:remote => true,
:method => :delete,
:class=> "close",
:data => {:dismiss => 'alert'}
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Ruby on Rails: How to have multiple submit buttons going to different methods (maybe with with_action?)
In a form I have some submit_tags and on server side I have to detect which one was clicked.
This is that I've tried, not working: on server side I only get an action name in params:
<%= form_tag controller_action_path(:id => #project.id), :method => :post do %>
<% if #project_is_synced %>
<%= submit_tag 'Update synchronization', :name => 'update' %>
<%= submit_tag 'Stop synchronization', :name => 'stop' %>
<% else %>
<%= submit_tag 'Start synchronization', :name => 'start' %>
<% end %>
<% end %>
I have only params[:action] with a current action name which is always the same
The easiest way to debug this is to run it locally and look at the parameters as they come through, or log parameters in your action:
if request.post?
logger.warn "POST #{params}"
end
You have named the submit_tags, so instead of the default name 'commit', each button has a different name, and you'll have to check for params named 'start', 'stop', or 'update'.
Simplest is just to remove the names and check params[:commit] for a varying value, however if you don't want to change the view, use this (replacing the render code obviously):
if params[:start]
render :text => "start"
elsif params[:update]
render :text => "update"
else
render :text => "stop"
end
The comments to the answer in the linked post do deal with this, but I can see why you missed it.
I've got the following form:
<%= form_for(#subscription = #task.subscriptions.build(:user_id => subscribers.id)) do |f| %>
<%= f.check_box :subscribed, :class => 'submittable' %>
<%= f.label :subscribed, subscribers.full_name %>
<%= f.hidden_field :user_id, :value => subscribers.id %>
<%= f.hidden_field :task_id, :value => #task.id %>
<% end %>
The 'submittable' class on the checkbox causes the form to be submitted (via jQuery) on update.
:susbcribed is returned via a method in model that returns whether a user is subscribed or not - it cannot be modified directly.
The controller is available here: http://pastebin.com/zZy6KcXz - it is the standard scaffold.
When I click the checkbox, the subcription is successfully created, but I cannot work out how to get it to delete the subscription when unticked.
cjm, in follow up to jimworm's answer, the controller's destroy method is called when you DELETE (HTTP verb, it's actually a POST with a _method=DELETE field passed since some browsers don't support the DELETE verb.
as he said:
<%= link_to 'Delete', #model, :confirm=> 'Are you sure?', :method=> :delete %>
The route is the same as your show or GET /models/1 but the verb DELETE is used instead,
DELETE /models/1
which is actually
POST /models/1 with a hidden field _method=DELETE passed in order to support all browsers.
As he also mentioned, Rails automatically figures out which action to use when using form_for by checking to see if the #model is a new_record? (no id yet) or an existing one. It will then pick
POST /models for create
or
PUT /models/1 for update
It's a form_for #subscription, so it'll probably be submitting to the create or update actions, which don't destroy models. You could hack the action or the model to make it destroy on create(!)/update. Probably the action... updating the model for this hack gives me the heeby-jeebies.
The Rails way™ to the DELETE method and get to the destroy route is a link like this:
<%= link_to 'Delete', #model, :confirm=> 'Are you sure?', :method=> :delete %>
Watch out for IE9 though. If you let your redirects go to another "deleteable" it'll potentially go up the chain and delete your entire database. http://techno-weenie.net/2011/8/19/ie9-deletes-stuff/
The correct redirect after successful POST, PUT and DELETE is:
redirect_to path, :status => 303
I am still kind of fuzzy on controllers in rails, especially so because a lot of things seem to happen magically behind the scenes, and that's not happening in this case.
So say I have a person model, which has many photos (using paperclip) and has many favorite quotes. The quotes can have the text, the attributed author, etc. In both of those models, they are set as belonging to my person model.
Within a new person form, I used some code elsewhere to create a new photo:
<% form.fields_for :screenshots, :html => { :multipart => true } do |screen_form| %>
<%= render :partial => 'screenshot', :locals => { :form => screen_form } %>
<% end %>
The partial for that is very simple, like this (minus some ajax javascript stuff I put in for nested models):
<%= form.label :photo, "Screenshot:" %>
<%= form.file_field :photo %>
This all works fine and magically the ID of the person is associated with a screenshot upon creation in person_id. I don't even have a controller for screenshots and it still works.
However, it's not working for my quotes.
<% remote_form_for :quote, :html => { :method => :put }, :url => {:controller => "quote", :action => "create", :person_id => #person.id} do |quote_form| %>
<%= render :partial => 'quote', :locals => { :form => quote_form } %>
<% end %>
The partial for this is also very simple.
<%= form.label :quote_text %>
<%= form.text_field :quote_text %>
.........
<%= form.submit 'Create' %>
I am not really sure if I can put person ID in there, but it didn't complain. However it didn't work, either. The quotes controller is very simple.
def create
#quote = Quote.create(params[:quote])
end
Currently it gets put in the DB but person_id is not populated so I can't pull up the quotes associated with a particular person. Sorry if this is a silly question, but I'm kind of learning Rails by tweaking tutorials and mashing them together so bear with me :) It's just kind of mysterious how the photo thing works with NO controllers or special stuff and this doesn't.
The first form is a person form mainly that has snapshots fields associated to it, so looking at your HTML you will find something like person[snapshots][photo], this form will be submitted to person controller.
Passing person id to second form the is key to make it work, however it's a bit weird that it's not working, the form will submit to quote controller. Did you make sure(watch the log) that the params hash has person_id attribute?