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
Related
I have a div in a parent view that renders a show partial. In this show partial, I have a button that should change the parent's partial to render the 'form' partial to edit the object, but I keep getting jammed up because it seems that the 'form' partial's form_for is missing the #project object.
How can I pass this object to the 'form' partial? Am I missing something else?
I am rather new to using AJAX. Happy to provide more information if you need.
The error I am getting in the server terminal is
ActionView::Template::Error (First argument in form cannot contain nil or be empty):
routes.rb
get 'switchProjectView', to: 'projects#switch_main_view'
resources :projects
projects_controller.rb
class ProjectsController < ApplicationController
def new
#project = Project.new
end
def create
#project = Project.new(project_params)
if #project.save
flash[:success] = "New Project Added"
redirect_to #project
else
flash[:danger] = "Project Not Added. Please Try Again"
end
end
def show
#project = Project.find(params[:id])
end
def index
#projects = Project.all
end
def update
#project = Project.find(params[:id])
if #project.update_attributes(project_params)
redirect_to #project
else
render 'edit'
end
end
def edit
end
def switch_main_view
respond_to do |format|
format.html
format.js
end
end
def project_params
params.require(:project).permit(:main_contact_name, :id, :main_contact_phone, :main_contact_email, :project_name)
end
end
show.html.erb
<div class="body">
<div class="center jumbotron heading-jumbo">
<h1><%= #project.project_name %></h1>
</div>
<div class="body-jumbo jumbotron" id='project-details'>
<%= render "projects/show" %>
</div>
</div>
switch_main_view.js.erb
$('#project-details').html("<%= j render :partial => 'projects/form' %>");
_form.html.erb
<%= form_for(#project) do |f| %>
<%= f.label :project_name %>
<%= f.text_field :project_name, class: 'form-control'%>
<%= f.label :main_contact_name %>
<%= f.text_field :main_contact_name, class: 'form-control'%>
<%= f.label :main_contact_phone %>
<%= f.text_field :main_contact_phone, class: 'form-control'%>
<%= f.label :main_contact_email %>
<%= f.text_field :main_contact_email, class: 'form-control'%>
<%= f.submit 'Save Project', class: 'btn btn-primary'%>
<% end %>
_show.html.erb
<div class='text-center center'>
<h3><strong>Project Information</strong></h2>
</div>
<h2>Start Date: Launch Date:</h1>
<span class='pull-right jumbo-btn-span'><%= link_to "Edit Project Information", switchProjectView_path(#project), remote: true, class:'btn btn-primary jumbo-btn' %></span>
<h2>Primary Conact's Name: <%= #project.main_contact_name %></h2>
<h2>Primary Conact's Phone: <%= #project.main_contact_phone %></h2>
<h2>Primary Conact's Email: <%= #project.main_contact_email %></h2>
Well, with each request, you need to re-initialize the variable in your controller method. In your case, you need to do the following:
def switch_main_view
#project = Project.new
respond_to do |format|
format.html
format.js
end
end
Doing so will enable you to get rid of the error: First argument in form cannot contain nil or be empty, but that won't enable you to do what you are actually trying to do.
When you use link_to, by default it goes for an HTML request as you can see in Rails logs, something like following will appear:
Processing by ProjectsController#switch_main_view as HTML
In order to get the JS response, you need to tell link_to that you are up for a JS response, not for an HTML response, and you can do by passing format: :js in link_to like:
<%= link_to "Edit Project Information", switchProjectView_path(#project, format: :js), remote: true, class:'btn btn-primary jumbo-btn' %></span>
remote: true will be there to make sure that you aren't reloading the contents of the page, rather you are trying to fetch something from the server in the background.
Your request "switchProjectView_path(#project)" only send object id to server, so you need to load the object to present it in view for response.
1. First the route should look like this so that, the url could contain object id:
get 'switchProjectView/:id', to: 'projects#switch_main_view', as:"switchProjectView"
2. You have to load the object in controller:
def switch_main_view
#project = Project.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
Try passing the variable when calling the partial:
$('#project-details').html("<%= j render :partial => 'projects/form', locals: {project: #project} %>");
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.
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?
I'm trying to get the activities when the user clicks on a button called display
Controller:
def display
#activities = Activity.all
#activities = Activity.order("created_at desc")
#we will need to find the Post id Through Users relation to the Comments
#And this is because Activity Track, tracks Users actions, it breaks down from there.
#posts = Post.all
#user_comments = UserComment.all
respond_to do |format|
format.js { #activities = Activity.order("created_at desc") }
end
View:
<%= link_to "Display", activities_path, :remote => true %>
<div id="notify" class="section-link">
</div>
display.js.erb
$('#notify').append("<%= escape_javascript(render :partial => 'layouts/activity') %>");
_activity.html.erb
<% #activities.each do |activity| %>
<%= ActivityPresenter.new(activity, self).render_activity %>
<% end %>
Try this in display.js.erb:
$('#notify').append('<%= escape_javascript(render :partial => 'layouts/activity', :collection => #activities) %>');
Then, try this in _activity.html.erb:
<%= ActivityPresenter.new(activity, self).render_activity %>
To ensure that whenever the user clicks on "Display' the display action is invoked, you will need to adjust your routes.rb file, which is located in the config directory. You could do something like this in routes.rb:
get '/activities/display', 'activities#display', as: :activity_display
Once you make that change in routes.rb, you can edit your link_to like this:
<%= link_to "Display", activity_display_path, :remote => true %>
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 %>