Passing Form object when using Ajax in rails - javascript

I have a complex form for a survey. A survey
belongs_to :template
has_many :questions, :through => :template
has_many :answers, :through => :questions
I.e. User creates a new survey, and he must select a survey template. The survey template will create some default questions and answer fields.
<%= form_for #survey do |f| %>
<p>Select a template:</p>
<%= render #templates, :locals => {:patient => #patient }, :f => f %>
<% end %>
Then I render the _templates partial, and I still have access to the form object. From here, the idea is that a user can click on a template name, and the template questions and answers will be rendered via Ajax.
<% #templates.each do |template| %>
<div class="thumbnail">
<%= link_to template.name,
{ :controller => "surveys",
:action => "new",
:template_id => template.id,
:patient_id => nil,
:f => f,
:remote=> true },
:class=> "template", :id=> template.id %>
</div>
<% end %>
surveys#new:
respond_to :js, :html
def new
#template = Template.find(params[:template_id])
#patient = Patient.find(params[:patient_id])
end
new.js.erb:
$('#assessment').append("<%= j (render new_survey_path, :locals => {:f => f} ) %>");
new_survey_path:
<%= f.fields_for :questions do |builder| %>
<% #template.questions.where(category:"S").each do |question| %>
<p><%= question.content %></p>
<%= render 'answer_fields', :question=> question %>
<% end %>
<% end %>
But I'm having trouble passing the original |f| object from #survey to new_survey_path. The last place I can access the form object is in the templates partial.
How can I fix this?

Related

How to avoid sending params of fields deleted using JQuery remove()

In this code I was trying to remove fileds for nested_attributes using ajax :remote => ture to avoid reloading
the whole page in browser. Although fileds in fields_for was removed from DOM and association was removed from database, the fields of the nested attributes
still exist in page source and raise ActiveRecord::RecordNotFound error when trying to send params to update action of parent model
consider the following code:
_artist_form.html.erb
<%= form_for #artist do |f| %>
<%= f.label :name %>
<%= f.text_field :name %><br/>
<%= f.label :style %>
<%= f.text_field :style %><br/>
<%= f.fields_for :songs do |song_builder|%>
<div id = 'song_<%= song_builder.object.id %>_div'>
<%= song_builder.label :title %>
<%= song_builder.text_field :title %><br/>
<%= song_builder.label :lyrics %>
<%= song_builder.text_area :lyrics %><br/>
<%= link_to 'Remove song', delete_song_path(:a_id => #artist.id, :s_id => song_builder.object.id),
:method => :delete , :remote => true %>
</div>
<% end %>
<%= f.submit 'Save' %>
<% end %>
routes.rb
Rails.application.routes.draw do
...
delete '/artists/remove_song', :to => 'artists#delete_song', :as => :delete_song
end
application_controller.rb
class ArtistsController < ApplicationController
def edit
...
end
def update
#artist = Artist.find(params[:id])
if #artist.update(artist_params) #=> error Couldn't find Song with ID=2 for Artist with ID=2
redirect_to artist_path(#artist)
else
flash[:errors] = #artist.errors.full_messages
render :edit
end
end
...
def delete_song
#song_id = params[s_id]
aritst = Artist.find(:params[a_id])
song = artist.songs.find(#song_id)
song.delete
respond_to do |format|
format.js {render 'delete_song.js.erb'}
end
end
end
delete_song.js.erb
$('#song_<%= #song_id %>_div').remove() ;
Error
Couldn't find Song with ID=2 for Artist with ID=2
how to prevent sending params of removed fields by $(...).remove() to update action?
I tried to find a solution for this error. So according to charlietfl comment, I tried to store delete status somewhere locally, then rails can delete association later. So I modified the code as following:
deleting all remote script code including delete_song.js.erb file and delete_song action and delete route. then I allowed marking nested attribute for delete in Artist model file:
accepts_nested_attributes_for :songs, :allow_destroy => true
then adding delete button in _artist_form.html.erb file as following:
<%= button_tag 'x' , :class => 'close_sign', :type => 'button', :onclick => "$('#song_#{song_builder.object.id}_destroy').val('true'); $('#song_#{song_builder.object.id}_div').hide()" %><br/>
and a hidden flied to fields_for as below:
<%= song_builder.hidden_field :_destroy, :id => "song_#{song_builder.object.id}_destroy" %>
and allowing :songs_nested_attributes => [:title, :lyrics, :_destroy] in song_params
once user remove the song field, it will be hidden and marked for destroy later

undefined method `item_path' while working with ajax

Im trying to update my create item action to work with Ajax but Im getting an error of undefined methoditem_path` which i wasn't getting before when it was responding in regular html format. The item is created and saved but ajax doesn't seem to work properly though.
Here is my _from partial :
<%= form_for [#user, item], remote: true do |f|%>
<div class="form-group">
<%= f.label :name, class: 'sr-only' %>
<%= f.text_field :name , class: 'form-control', placeholder: "Enter a new item " %>
</div>
<%= f.submit "Submit Item", class: 'btn btn-primary pull-right' %>
<% end %>
item#create:
def create
#item = Item.new(item_params)
#item.user = current_user
if #item.save
flash[:notice] = 'Item saved successfully.'
else
flash[:alert] = 'Item not saved. Title is too short or missing. Please try again.'
end
respond_to do |format|
format.html
format.js
end
end
create.js.erb:
$('.js-items').prepend("<%= escape_javascript(render(#item)) %>");
$('.new-item').html("<%= escape_javascript(render partial: 'items/form', locals: {user: #user , item: #item }) %>");
User#show view
<div class='new_item'>
<%= render :partial => 'items/form', :locals =>{:item => Item.new , :user => #user} %>
</div>
<div class='js-items'>
<%= render #user.items %>
</div>
routes:
user_items GET /users/:user_id/items(.:format) items#index
POST /users/:user_id/items(.:format) items#create
new_user_item GET /users/:user_id/items/new(.:format) items#new
edit_user_item GET /users/:user_id/items/:id/edit(.:format) items#edit
user_item GET /users/:user_id/items/:id(.:format) items#show
PATCH /users/:user_id/items/:id(.:format) items#update
PUT /users/:user_id/items/:id(.:format) items#update
DELETE /users/:user_id/items/:id(.:format) items#destroy
The error im getting in rails s :
ActionView::Template::Error (undefined method `item_path' for #<#<Class:0x007fa4f0d30cd8>:0x007fa4f31b26b0>):
1: <%= form_for [#user, item], remote: true do |f|%>
2: <div class="form-group">
3: <%= f.label :name, class: 'sr-only' %>
4: <%= f.text_field :name , class: 'form-control', placeholder: "Enter a new item " %>
app/views/items/_form.html.erb:1:in `_app_views_items__form_html_erb__331698480542899910_70173200751480'
app/views/items/create.js.erb:2:in `_app_views_items_create_js_erb___3618987352886002527_70173200313760'
app/controllers/items_controller.rb:17:in `create'
Do something like that.
<%= form_for [#user, item], user_items, remote: true do |f|%>
If it doesn't work then run
rake routes
in terminal see what's your path.

Dynamically adding prepopulated nested forms using cocoon and rails3-jquery-autocomplete

Thanks for your time.
I am working on rails 3.2 and I am using gems simple_form, cocoon, and rails3-jquery-autocomplete.
I have following models Machine, Part, PartUsage, MachineCosting and PartCosting
Machine and Part models with many-to-many association.
class Machine < ActiveRecord::Base
attr_accessible :name
has_many :part_usages
has_many :parts, :through => :part_usage
end
class Part < ActiveRecord::Base
attr_accessible :name
end
class PartUsage < ActiveRecord::Base
attr_accessible :machine_id,
:part_id
belongs_to :part
belongs_to :machine
end
MachineCosting and PartCosting models with one-to-many association.
class MachineCosting < ActiveRecord::Base
attr_accessible :machine_id,
:total_cost,
:machine_name,
:part_costings_attributes
attr_accessor :machine_name
has_many :part_costings, :dependent => :destroy
accepts_nested_attributes_for :part_costings, :allow_destroy => true
def machine_name
self.machine.try(:name)
end
end
class PartCosting < ActiveRecord::Base
attr_accessible :part_id,
:part_name,
:cost
attr_accessor :part_name
belongs_to :machine_costing
belongs_to :part
def part_name
self.part.try(:name)
end
end
Form for MachineCosting
views/machine_costings/_form.html.erb
<%= simple_form_for(#machine_costing, :html => {:multipart => true}) do |f| %>
<%= f.input :machine_id, :url => autocomplete_machine_id_machines_path,
:as => :autocomplete %>
<!-- HERE, WHENEVER I SELECT A MACHINE, I WANT TO ADD NESTED-FORMS FOR
PARTCOSTINGS CORRESPONDING TO ALL THE PARTS OF THE MACHINE I JUST SELECTED.-->
<%= f.simple_fields_for :part_costings do |part_costing|%>
<%= render 'part_costings/part_costing_fields', :f => part_costing %>
<% end %>
<% end %>
Please suggest how can I populate and dynamically add fixed no of fields for PartCostings using javascript after machine_id is selected in the field.
I would be happy to provide any more information.
Thanks again!!
First, summary of my approach.
In MachineCosting form, use 'rails3-jquery-autopopulate' gem to search for Machines by their name.
Hack MachineController to also send 'Parts info' together with matching machines in json form.
When user selects a machine, use javascript to add nested_forms for PartCostings and partially populate them using data recieved in json form.
Now here is how I have done it (code part).
views/machine_costings/_form.html.erb
<%= simple_form_for(#machine_costing, :html => {:multipart => true}) do |f| %>
<%= f.error_notification %>
<%= f.input :machine_name, :url => autocomplete_machine_name_machines_path,
:as => :autocomplete, :input_html => { :id_element => "#machine_costing_machine_id"} %>
<%= f.association :machine, :as => :hidden %>
<div id='part_costings'>
<%= f.simple_fields_for(:part_costings) do |part_costing|%>
<%= render 'part_costings/part_costing_fields', :f => part_costing %>
<% end %>
<div class="links hidden">
<%= link_to_add_association 'Add part costings', f, :part_costings,
:partial => 'part_costings/part_costing_fields' %>
</div>
</div>
<%= f.button :submit%>
<% end %>
views/part_costings/_part_costing_fields.html.erb
<div class= 'nested-fields'>
<!-- DO NOT CHANGE THE ORDER OF ATTRIBUTES ELSE machine_costing.js WILL FAIL -->
<%= f.input :part_id, :as=> :hidden %>
<%= f.text_field :part_name, :disabled => 'disabled' %>
<%= f.input :cost %>
<%= link_to_remove_association '<i class="icon-remove"></i>'.html_safe, f ,:class =>"part_costing_remove" %>
</div>
controllers/machines_controller.rb
class MachinesController < ApplicationController
# OVER RIDING THIS BY OWN METHOD
# autocomplete :machine, :name, :full=> true, :extra_data => [:machine_id]
def autocomplete_machine_name
respond_to do |format|
format.json {
#machines = Machine.where("name ilike ?", "%#{params[:term]}%")
render json: json_for_autocomplete_machines(#machines)
}
end
end
def json_for_autocomplete_machines(machines)
machines.collect do |machine|
parts = Hash.new
unless machine.part_usages.empty?
machine.part_usages.each do |part_usage|
parts[part_usage.part.id] = part_usage.part.name
end
end
hash = {"id" => machine.id.to_s, "value" => machine.name,
"parts" => parts}
hash
end
end
#
#
#
end
And here is the javascript part.
assest/javascripts/machine_costings.js
$(function() {
// Part Costings
var part_id;
var part_name;
$('#machine_costing_machine_name').bind('railsAutocomplete.select', function(event, data){
console.debug("Machine selected...");
parts = data.item.parts;
console.debug("Removing existing part_costings...");
$('.part_costing_remove').click();
console.debug("Adding part_costings...");
for(var id in parts) {
part_id = id;
part_name = parts[id];
$('.add_fields').click();
}
console.debug("Done adding all part_costings...");
});
$('#part_costings').bind('cocoon:after-insert', function(e, part_costing) {
part_costing[0].getElementsByTagName('input')[0].value = part_id;
part_costing[0].getElementsByTagName('input')[1].value = part_name;
console.debug("Added part_costing...");
});
});
routes.rb
resources :machines do
get :autocomplete_machine_name, :on => :collection
end
The critical points in javascript solution are...
Method "bind('railsAutocomplete.select', function(event, data)" from rail3-jquery-autocomplete gem.
Method "bind('cocoon:after-insert', function(e, part_costing)" from cocoon gem.
That's all.
Please let me know if you have some suggestions. Thanks :)

Rails - refreshing page instead of partials when using AJAX [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Rails - AJAX to for new/create action
I have a timeline that has partials for events on it, so you can CRUD events directly on the timeline. I've got the delete part working fine, so that the delete partial and the timeline refresh each time an event gets deleted. However, I can't get it working when a new event is created. Instead of refreshing the form and the timeline, the whole page refreshes.
I'm assuming this is something to do with the controller? But I can't seem to fix it. Any help would be much appreciated, thanks :0)
events controller:
def create
#event = Event.new(params[:event])
respond_to do |format|
if #event.save
format.html { redirect_to #event.timeline, notice: 'Event was successfully created.' }
format.json { render json: #event, status: :created, location: #event }
format.js
else
format.html { render action: "new" }
format.json { render json: #event.errors, status: :unprocessable_entity }
format.js
end
end
end
timeline/show:
<div id="new-event">
<%= render :partial => "new_event", :locals => { :event => Event.new(:timeline_id=>#timeline.id) }, :remote => true %>
</div>
timeline/_new_event:
<br />
<h2>Add an event</h2>
<h4>Fill in the form and click 'Create Event' to add a new event to the timeline.</h4>
<%= form_for(event) do |f| %>
<% if event.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(event.errors.count, "error") %> prohibited this event from being saved:</h2>
<ul>
<% event.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%=f.hidden_field 'timeline_id', :value => current_user.timeline.id %>
<div class="field">
<%= f.label :date %><br />
<%= f.date_select :start_date, :order => [:day, :month, :year], :start_year => 1800 %>
</div>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :headline, :size => 50 %>
</div>
<div class="field">
<%= f.label :event_description %><br />
<%= f.text_area :text, :size => "47x4" %>
</div>
<%= check_box_tag "blockCheck", :value => "1", :checked => false %>
<div class="field" id="media_box">
<%= f.label :media %> <span>Please paste a URL here</span><br />
<%= f.text_field :media, :size => 50 %>
</div>
<div class="field">
<%= f.label :media_description %><br />
<%= f.text_area :caption, :size => "47x3" %>
</div>
<div class="actions">
<%= f.submit 'Create Event', :class => "btn btn-success", :remote => true %>
</div>
<% end %>
events/create.js.erb:
$('#new-event').html('<%= escape_javascript( render :partial => "/timelines/new_event", :locals => { :event => Event.new(:timeline_id=>#timeline.id) } ) %>');
$('.notice').html("<p>Event was successfully created.</p>");
$('.notice').show(300);
$('#my-timeline-box').html('<%= escape_javascript( render :partial => "/timelines/my_timeline" ) %>');
$('#show-timeline').html('<%= escape_javascript( render :partial => "/timelines/show_timeline" ) %>');
(The bottom two lines are to refresh the timeline iteself).
UPDATE:
Here is the error message:
Started POST "/events" for 127.0.0.1 at 2012-10-16 14:52:37 +0100
Processing by EventsController#create as JS
Parameters: {"utf8"=>"V", "authenticity_token"=>"i8oiRI7rOLsfb5o45QCK0te/hAsWv
BMTqpxU9KrbmNA=", "event"=>{"timeline_id"=>"1", "start_date(3i)"=>"16", "start_d
ate(2i)"=>"10", "start_date(1i)"=>"2012", "headline"=>"", "text"=>"", "media"=>"
", "caption"=>""}, "commit"=>"Create Event"}
←[1m←[35m (0.0ms)←[0m begin transaction
←[1m←[36mSQL (5.0ms)←[0m ←[1mINSERT INTO "events" ("caption", "created_at", "
credit", "end_date", "headline", "media", "start_date", "text", "thumbnail", "ti
meline_id", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)←[0m [["capti
on", ""], ["created_at", Tue, 16 Oct 2012 13:52:37 UTC +00:00], ["credit", nil],
["end_date", nil], ["headline", ""], ["media", ""], ["start_date", Tue, 16 Oct
2012], ["text", ""], ["thumbnail", nil], ["timeline_id", 1], ["updated_at", Tue,
16 Oct 2012 13:52:37 UTC +00:00]]
←[1m←[35m (88.0ms)←[0m commit transaction
Rendered events/create.js.erb (99.0ms)
Completed 500 Internal Server Error in 617ms
ActionView::Template::Error (Called id for nil, which would mistakenly be 4 -- i
f you really wanted the id of nil, use object_id):
1: $('#new-event').html('<%= escape_javascript( render :partial => "/timelin
es/new_event", :locals => { :event => Event.new(:timeline_id=>#timeline.id) } )
%>');
2: $('.notice').html("<p>Event was successfully created.</p>");
3: $('.notice').show(300);
4: $('#my-timeline-box').html('<%= escape_javascript( render :partial => "/t
imelines/my_timeline" ) %>');
app/views/events/create.js.erb:1:in `_app_views_events_create_js_erb___8626901
43_20024148'
app/controllers/events_controller.rb:59:in `create'
Rendered C:/Ruby193/lib/ruby/gems/1.9.1/gems/actionpack-3.2.4.rc1/lib/action_d
ispatch/middleware/templates/rescues/_trace.erb (5.0ms)
Rendered C:/Ruby193/lib/ruby/gems/1.9.1/gems/actionpack-3.2.4.rc1/lib/action_d
ispatch/middleware/templates/rescues/_request_and_response.erb (2.0ms)
Rendered C:/Ruby193/lib/ruby/gems/1.9.1/gems/actionpack-3.2.4.rc1/lib/action_d
ispatch/middleware/templates/rescues/template_error.erb within rescues/layout (1
000.1ms)
You have :remote => true on the submit button. That's incorrect. It should be on the form:
<%= form_for(event, :remote => true) do |f| %>
You have not initialized #timeline in your controller code. Thus you get a nil action.
So, put in your Create code:
#timeline = current_user.timeline

Ajax/Rails Nested Comments

I have an application where book_comments belongs_to books.
My routes file :
resources :books do
resources :book_comments
end
I have a book_comment form on show.html.erb for books.
def show
#book = Book.find(params[:id])
#new_book_comment = #book.book_comments.build
end
I'm using ajax to post the book_comments to the book show page via a partial: _book_comment_form.html.erb :
<%= form_for([#book, #new_book_comment], remote: :true) do |f| %>
<div class="field" style="margin-top:60px;">
<%= f.text_area :comment, rows: 3 %>
<%= f.submit "Add Comment" %>
</div>
<% end %>
When #new_book_comment is called it refers to a book_comments_controller create action:
def create
#book = Book.find(params[:book_id])
#book_comment = current_user.book_comments.build(params[:book_comment]) do |f|
f.book_id = #book.id
end
if #book_comment.save
respond_to do |format|
format.html { redirect_to #book }
format.js { render :layout => false }
end
end
end
The book_comment is saved but my problem comes in when trying to render via ajax in the _book_comments partial:
<div id="comments">
<% #book_comments.each do |book_comment| %>
<%= render 'comment', :book_comment => comment %>
</div>
<% end %>
</div>
via: create.js.erb in the book_comments folder
$("div#comments").append("<%= escape_javascript(render('books/comment'))%>")
To sum, I'm having trouble making the create.js.erb in book_comments render to the book show page. Why wouldn't create.js.erb in the book_comments folder know to look at the objects in the and infer the new book comment should go inside the div?
My error message:
undefined method `comment' for nil:NilClass
don't understnd why. Seeing that I'm passing the instance variable to the comment partial.
Your partial's doing it wrong. You're passing a not referenced value (comment) to a not used variable (book_comment).
Instead of:
<% #book_comments.each do |book_comment| %>
<%= render 'comment', :book_comment => comment %> ....
you should do:
<% #book_comments.each do |book_comment| %>
<%= render 'comment', :comment => book_comment %>

Categories

Resources