So I'm trying to update my comments section with AJAX without the full page refresh for a college project. However I can't seem to get this working.
In the console it gives me s
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
My show.html.erb file:
<h1>Title: <%= #post.title %></h1>
<h2>Body: <%= #post.body %></h2>
<hr />
<h1>Your comments</h1>
<%= link_to "View comments", "#", id: "comments-link" %>
<ol id="comments">
<%= render 'comments' %>
<hr />
<h1>Create comment</h1>
<%= form_for(#comment, :html => {class: "form", role: "forms"}, :url => post_comments_path(#post), remote: true) do |comm| %>
<div class="container">
<div class="input-group input-group-md">
<%= comm.label :body %>
<%= comm.text_area :body, class: "form-control", placeholder: "Enter a comment" %>
</div>
<%= comm.submit "custom", id: "button" %>
</div>
<% end %>
</ol>
My comments.coffee:
$(document).on "page:change", ->
$('#comments-link').click ->
$('#comments').fadeToggle()
$('#comments_body').focus()
My create.js.erb:
$('#comments').append("<%= j render #comment %>");
and my Comments controller:
class CommentsController < ApplicationController
def index
end
def new
end
def new
#comment = Comment.new
end
def create
#comment = Comment.new(comment_params)
#comment.post_id = params[:post_id]
if #comment.save
flash[:success] = "Successfully created comment"
respond_to do |format|
format.html { redirect_to post_path(#comment.post_id) }
format.js
end
else
flash[:danger] = "Failed to create comment"
redirect_to root_path
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
I may have missed some files so just let me know, it is basic as it's just a post and comment system - no styling needed for the project, so yeah. I have been trying this for the last 4 hours and other places just don't work. I've looked on here, Youtube - everywhere however no one else's code works for me so I have come here! Thank's for you're help.
EDIT:
I noticed it said to create a view in the error response, however I made that view and rendered the comment's body onto the create.html.erb however I just need to display the form now.
I notice you are posting to the url post_comments_path(#post). For nested routes, it might be cleaner to do the following:
1) Post to the nested route directly:
<%= form_for([#post, #comment], html: {class: "form", role: "forms"}, remote: true) do |comm| %>
2) Make sure your routes are nested properly, in routes.rb:
resources :posts do
resources :comments
end
3) Refactor your CommentsController, to build through the #post instance:
class CommentsController < ApplicationController
before_action :get_post
def index
#comments = #post.comments
end
def new
#comment = #post.comments.build
end
def create
#comment = #post.comments.build(comment_params)
if #comment.save
flash[:success] = "Successfully created comment"
respond_to do |format|
format.html { redirect_to post_path(#post) }
format.js
end
else
respond_to do |format|
format.html { render :new }
format.js { render :error }
end
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
def get_post
#post = Post.find(params[:post_id])
end
end
4) Render the validation errors in app/views/comments/error.js.erb. I'll let you decide how best to do that, but here's a quick dump to the console:
<% #comment.errors.full_messages.each do |message| %>
console.log("<%= message %>");
<% end %>
This file is not to be confused with your app/views/comments/create.js.erb which is handling successful save of the comment. That should look something like this:
$('#comments').append("<%= j render #comment %>");
$(".form")[0].reset();
5) Tweak your view a little bit. I notice you need to output the comments differently:
<ol id="comments">
<%= render #post.comments %>
</ol>
which should correspond to a partial in app/views/comments/_comment.html.erb so make sure this is there.
Related
There is some problem with using ajax in rails. I want to create new post in my wellcome/inex page. But when I push the botton it show
No route matches [POST] "/welcome/index"
wellcome/index.html
<b>Projects</b>
<ul id="projects">
<% #projects.each do |project| %>
<h2><%= project.title %></h2>
<% end %>
</ul>
<br>
<%= form_for :project do |f| %>
<%= f.text_field :title, :placeholder => ' Enter new project here..' %>
<but><%= f.submit 'Add Project'%></but>
<% end %>
welcome_controller.rb
class WelcomeController < ApplicationController
def index
#projects = Project.all
#project = Project.new
end
def create
#project = Project.new(project_params)
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'project was successfully created.' }
format.js
format.json { render json: #project, status: :created, location: #project }
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
private
def project_params
params.require(:project).permit(:title)
end
end
create.js.erb
$('#projects').html("<%= escape_javascript (render 'projects') %>");
routes project
Prefix Verb URI Pattern Controller#Action
project_index GET /project(.:format) project#index
POST /project(.:format) project#create
new_project GET /project/new(.:format) project#new
edit_project GET /project/:id/edit(.:format) project#edit
project GET /project/:id(.:format) project#show
PATCH /project/:id(.:format) project#update
PUT /project/:id(.:format) project#update
DELETE /project/:id(.:format) project#destroy
Spent a lot of time updating this with AJAX but failed everytime. Any there to help? Thanks in advance
You have to add remote: true into form.
<%= form_for #project, url: project_index_path, remote: true do |f| %>
<%= f.text_field :title, :placeholder => ' Enter new project here..' %>
<but><%= f.submit 'Add Project'%></but>
<% end %>
If you want integrate Ajax and Ruby you need to add the line below inside your link action
remote:true
also you can check this about Ajax and Ruby
How Ajax works with ruby on rails
I'm working on an dynamic edit of an article using rails. On each articles can be added paragraphs. I wanted to use Ajax with Jquery following the 136-jquery-ajax-revised tutorial from railscast. I created the template for the form and the new.js.erb response but I keep geting same error:
ParagraphsController#new is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: []
This is the link that request the new form from view/article/show
<%= link_to 'New Paragraph', new_article_paragraph_path(#article), remote: true, class: 'uk-button uk-button-primary' %>
view/paragraph/_form
<div class="article-form-container">
<%= form_with scope: :paragraph, url: article_paragraphs, remote: true do |f| %>
<%= f.text_field :title, class: 'uk-input', placeholder: 'Title of paragraph (optional, can be a subtitle of the article)' %>
<%= f.text_area :text, class: 'uk-textarea', placeholder: 'Content of paragraph' %>
<%= f.hidden_field :position, :value => 3 %>
<div class="submit-button">
<%= f.submit class: 'uk-button uk-button-primary' %>
</div>
<% end %>
</div>
routes
resources :articles, only: %i[show update destroy create]
resources :articles do
resources :paragraphs, only: %i[new create update destroy]
end
view/paragraphs/new.js.erb
$('div#article-control-panel').hide("fast", function () {
$('div#article-control-panel').after('<%= j render 'form' %>')
})
controllers/paragraphs_controller
class ParagraphsController < ApplicationController
def new
#paragraph = Paragraph.new
end
def create
#paragraph = Paragraph.new(paragraph_params)
#article = Article.find(params[:article_id])
if #article.user == current_user
#paragraph.article = #article
#paragraph.save
end
respond_to do |format|
format.html { redirect_to #article }
format.js
end
end
def paragraph_params
params.require(:paragraph).permit(:title, :text, :position)
end
end
Can't figure out what the problem is. The error happens in the article page, after I press the link button.
Edit
Strange thing is that if i try to change the url of orm in something like article_path it will work...
Your controller is missing a respond_to. Your new function should look like this:
def new
#paragraph = Paragraph.new
respond_to { |format| format.js }
end
This respond_to allows rails to call the relevant .js.erb file, in this instance your new.js.erb file.
Just keep in mind that when you want to perform an ajax call, you require these few elements:
A respond_to { |format| format.js } in your controller action
Your js.erb file needs to be the same name as your controller action (foo.js.erb)
A partial to render through your js.erb file, typically named the same as your js file. (_foo.js.erb)
If it is a link, you need remote: true. If it is a form and you're using rails 5, you could use form_with or simply include remote: true too.
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} %>");
I'm having trouble passing locals to a partial shared by three different views, each related to different actions in different controllers. The partial and the locals passed to it work without a problem when working with html requests, but I cannot get them to work when issuing xhr requests.
Let me show you my code to explain myself better.
So, I have this partial
# app/views/shared/_vote_form.html.erb
<div id="vote_form">
<% if post.votes.find_by(user_id: current_user.id).nil? %>
<%= render partial: "votes/vote", locals: { postv: post,
vote: post.votes.build } %>
<% else %>
<%= render partial: "votes/unvote", locals: { postu: post,
vote: post.votes.find_by(user_id: current_user.id) } %>
<% end %>
</div>
As you can see, this partial renders one of two partials depending on the outcome of an if statement:
# app/views/votes/_vote.html.erb
<%= form_for([postv, vote], remote: true) do |f| %>
<%= hidden_field_tag 'vote[vote]', 1 %>
<%= f.submit "Vote", class: "btn" %>
<% end %>
# app/views/votes/_unvote.html.erb
<%= form_for([postu, vote], html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Unvote", class: "btn btn-primary" %>
<% end %>
As I mentioned, this partials are shared by three different views associated to different actions in different controllers.
# app/views/posts/show.html.erb
<div class="post-vote-form">
<%= render partial: "shared/vote_form", locals: { post: #post } %>
</div>
Which is associated to the following action in the following controller:
class PostsController < ApplicationController
def show
#post = Post.find(params[:id])
end
end
Another view
# app/views/users/feed.html.erb
<% #feed_items.each do |f| %>
<div class="vote-button">
<%= render partial: "shared/vote_form", locals: { post: f } %>
</div>
<% end %>
Associated to the following controller#action
class UsersController < ApplicationController
def feed
#user = User.find_by(id: current_user.id)
#feed_items = #user.feed
end
end
And finally
# app/views/categories/other.html.erb
<% #gal_items.each do |f| %>
...
<%= render partial: "shared/vote_form", locals: { post: f } %>
<% end %>
Associated to controller#action
class CategoriesController < ApplicationController
def other
#other = Category.find(7)
#gal_items = #other.posts
end
end
As you can see, the forms send an xhr request to create/destroy an instance of Vote (I have routes for Vote nested in Post. That's why the form_for takes two arguments).
These requests are handled by the following actions in the VotesController
class VotesController < ApplicationController
def create
#post = Post.find_by(id: params[:post_id])
#vote = #post.votes.build(vote_params)
#vote.user_id = current_user.id
#vote.save
respond_to do |format|
format.html { redirect_to #post }
format.js
end
end
def destroy
#vote.destroy
respond_to do |format|
format.html { redirect_to #post }
format.js
end
end
end
And these two js.erb files come into play:
# app/views/votes/create.js.erb
$("#vote_form").html("<%= escape_javascript(render :partial => 'votes/unvote', locals: { postu: #post,
vote: #post.votes.find_by(user_id: current_user.id)}) %>");
And
# app/views/votes/destroy.js.erb
$("#vote_form").html("<%= escape_javascript(render :partial => 'votes/vote', locals: { postv: #post.each,
vote: #post.votes.build }) %>");
The way I am presenting these last two js.erb files work for the view # app/views/posts/show.html.erb as the values for the locals are taken directly from the VotesController actions, but I have not been able to find a way to make it work for the other two views (which are #something.each do |f|) that render these partials, as I cannot pass the appropriate values to the locals for the form_for arguments to work.
I have tried with a helper to pass values to the locals depending on the url, but without success.
It seems obvious that I cannot get these js.erb files to render the partials with appropriate values for the locals because I cannot retrieve the variables from their respective controllers.
So, bottomline, is there a way to make it work through these js.erb files, or will I have to sort this out using pure JQuery?
Has anyone faced something like this?
I am sorry that I cannot make a question that requires a more specific answer.
Hope you guys can help.
Did you try this?
class UsersController < ApplicationController
def feed
#post = Post.find(params[:id])
#user = User.find_by(id: current_user.id)
#feed_items = #user.feed
end
end
class CategoriesController < ApplicationController
def other
#post = Post.find(params[:id])
#other = Category.find(7)
#gal_items = #other.posts
end
end
I have 2 model Post and Comment, where Post has-many Comments and Comment belongs_to Post.
Every thing working fine with creation of posts and comments for that posts.
Now I have a requirement where when I click on "create new comment" in posts/show page, I want to show the comments/_form in modal.
comments_controller.rb:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
# GET /comments
# GET /comments.json
def index
#comments = Comment.all
respond_with(#comments)
end
# GET /comments/1
# GET /comments/1.json
def show
respond_with(#comments)
end
# GET /comments/new
def new
#post = Post.find(params[:post_id])
#comment = #post.comments.build
end
# GET /comments/1/edit
def edit
#post = Post.find(params[:post_id])
end
# POST /comments
# POST /comments.json
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to(#comment, :notice => 'Article was successfully created.') }
format.xml { render :xml => #comment, :status => :created, :location => #comment }
format.js
else
format.html { render :action => "new" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
format.js
end
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
#post = Post.find(params[:post_id])
#comment.update(comment_params)
redirect_to post_path(#post)
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
#comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:commenter, :description)
end
end
posts/show.html.erb:
<%= link_to 'New Coment', new_post_comment_path(#post), :id => 'create_comment' %>
comments/new.html.erb:
<div id="content">
<h1>New Comment</h1>
<%= render 'form' %>
</div>
comments/_form.html.erb:
<%= form_for([#post, #comment], :remote => true) do |f| %>
<div class="field">
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
comments/create.js.erb:
<%- if #comment.errors.any? %>
console.log('Error');
$('#dialog-form').html('<%= escape_javascript(render('form')) %>');
<%- else %>
console.log('Created');
$('#dialog-form').dialog('close');
$('#dialog-form').remove();
$('table').append('<%= escape_javascript(render(#comment)) %>');
<%- end %>
I don't know where I went wrong. When I click on "create comment" it is redirecting to new page instead of pop-up model and even couldn't create comment.
Please help.
Create a partial for showing comment form.
Render this partial to post show page as hidden.
show hidden partial.
Example:
<%= link_to 'Show Modal', "#", data: {toggle: "modal", target: "#modal"} %>
hidden partial
<div class="modal hide fade" id="modal" title="My modal">
/* your comment form */
/* render comment form */
</div>
Just make sure target is your hidden partial id.