Rails: Adding a post without refreshing the page - ruby-on-rails

I'm new on ruby on rails.
I'm currently working on a facebook clone application and having trouble when adding a post without refreshing the page. Tried to create a post but the post did not show upon submitting and encountered Internal Server Error. When the page is refreshed, the newly created post is finally displayed.
Error in rails server
Completed 500 Internal Server Error in 50ms (ActiveRecord: 15.9ms | Allocations: 10085)
ActionView::Template::Error (First argument in form cannot contain nil or be empty):
1: = form_for([post, #comment]) do |f|
2: .form-bottom-button-container
3: .d-flex.comment-text-button
4: .p-2.flex-grow-1
Here are my codes. Please help me out. Thank you very much!
app/controllers/posts_controller.rb
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
format.js
format.html { redirect_to posts_path, notice: 'Successfully posted.'}
else
format.html { redirect_to posts_path, status: :unprocessable_entity }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
app/views/posts/create.js.haml
:plain
$("#post-display").append("#{escape_javascript(render 'post_display', post: #post)}");
app/views/posts/index.html.haml
-# All Posts
.col-8
.post-form
= render "post_form"
- if #posts.empty?
.post-card
.post-text
.no-post-text No posts to show
- else
#post-display
= render partial: "post_display", collection: #posts, as: :post
app/views/posts/_post_display.html.haml
.post-card
.post-text
.d-flex.justify-content-between
.d-flex
.post-user-image
- if post.user.profile_pic.present?
= image_tag post.user.profile_pic.url, class: "img img-fluid img-thumb"
- else
= image_tag "/default-user.jpg", class: "img img-fluid img-thumb"
.d-flex.flex-column
.username-bold= link_to post.user.username, profile_path(post.user.username)
.distance-date= distance_of_time_posted_in_words(post.created_at)
.post-edit-delete
- if post.user.username == current_user.username
= link_to edit_post_path(post), class: "text-space", remote: true, "data-bs-toggle" => "modal", "data-bs-target" => "#edit-post-#{post.id}-modal" do
%i.fa.fa-edit
.modal.fade(id="edit-post-#{post.id}-modal" tabindex="-1" aria-labelledby="edit-post-title" aria-hidden="true")
.modal-dialog.modal-dialog-centered.modal-dialog-scrollable
.modal-content
= link_to post, method: :delete, class: "text-space" do
%i.fa.fa-trash
.caption= post.caption
- if post.image.present?
= image_tag post.image.url, class: "img img-fluid img-fill"
-# Like Button
.row.container-margin-top
.col-6
- if liker(post)
= button_to post_like_path(post, liker(post)), method: :delete, class: "btn btn-maroon btn-wide btn-like-comment" do
%i.far.fa-thumbs-up
Unlike
- else
= button_to post_likes_path(post), method: :post, class: "btn btn-plain btn-wide btn-like-comment" do
%i.far.fa-thumbs-up
Like
.col-6
.btn.btn-plain.btn-wide.btn-like-comment.btn-comment
%i.far.fa-comment
%span Comment
-# Comment Section
.comment-box
.d-flex.justify-content-between
- unless post.likes.count == 0
.post-likes(type="button" data-bs-toggle="modal" data-bs-target="#likers-#{post.id}-modal")= "#{pluralize(post.likes.count, "like")}"
= render "layouts/likers_modal", post: post, index: post.id
.comment-toggle View Comments
.comment-display.hidden
= render "posts/comments", post: post
.comment-form
= render "comments/comment_form", post: post
app/views/comments/_comment_form.html.haml
= form_for([post, #comment]) do |f|
.form-bottom-button-container
.d-flex.comment-text-button
.p-2.flex-grow-1
= f.text_area :content, class: "comment-text-area comment", placeholder: "Write a comment..."
.p-2
= f.submit :Comment, class: "btn btn-maroon create-comment-button"

Check your application logs - it'll probably tell you the format.js method does not exist.
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
format.js # <----
format.html { redirect_to posts_path, notice: 'Successfully posted.'}
else
format.html { redirect_to posts_path, status: :unprocessable_entity }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
Just remove that line or use the format.json method and you're good to go.

To make the page not refresh you must submit the form using ajax request... you simply add remote: true to the form.
form_for([post, #comment], remote: true) do |f|
Also the error you are getting is because #comment is not being set.

Related

link_to in Rails 7

I am new to Ruby on Rails and I am making a view page showing a table from the database. I want to add a link_to to delete this record. I tried the code written below and nothing worked for me.
The browser keeps refreshing and does nothing.
ruby 3.1.2
Rails 7.0.4
<%= link_to 'Delete', #friend, data: {turbo_method: :delete, turbo_confirm: 'Sure?'}, class: "btn btn-danger" %>
also with this
<%= link_to 'Delete', #friend, method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-danger" %>
html.erb code
<p style="color: green"><%= notice %></p>
<%= render #friend %>
<div>
<%= link_to "Edit this friend", edit_friend_path(#friend) %> |
<%= link_to "Back to friends", friends_path %>
<%#= button_to "Destroy this friend", #friend, method: :delete,data: {turbo_confirm: 'Are you sure?'} %>
<%#= button_to "Destroy this friend", #friend, method: :delete,data: {turbo_confirm: 'Are you sure?'} %>
<%= link_to 'Delete', #friend, method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-danger" %>
</div>
Controller code
class FriendsController < ApplicationController
before_action :set_friend, only: %i[ show edit update destroy ]
# GET /friends or /friends.json
def index
#friends = Friend.all
end
# GET /friends/1 or /friends/1.json
def show
end
# GET /friends/new
def new
#friend = Friend.new
end
# GET /friends/1/edit
def edit
end
# POST /friends or /friends.json
def create
#friend = Friend.new(friend_params)
respond_to do |format|
if #friend.save
format.html { redirect_to friend_url(#friend), notice: "Friend was successfully created." }
format.json { render :show, status: :created, location: #friend }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #friend.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /friends/1 or /friends/1.json
def update
respond_to do |format|
if #friend.update(friend_params)
format.html { redirect_to friend_url(#friend), notice: "Friend was successfully updated." }
format.json { render :show, status: :ok, location: #friend }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #friend.errors, status: :unprocessable_entity }
end
end
end
# DELETE /friends/1 or /friends/1.json
def destroy
#friend.destroy
respond_to do |format|
format.html { redirect_to friends_url, notice: "Friend was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_friend
#friend = Friend.find(params[:id])
end
# Only allow a list of trusted parameters through.
def friend_params
params.require(:friend).permit(:first_name, :last_name, :email, :phone, :twitter)
end
end
Your code should work
<%= link_to 'Delete', #friend,
data: {turbo_method: :delete, turbo_confirm: 'Sure?'}, class: "btn btn-danger" %>
But more reliable to use button_to that generate form tag with button
<%= button_to 'Delete', #friend,
method: :delete, form: {data: {turbo_confirm: 'Sure?'}}, class: "btn btn-danger" %>

Rails Remote True Ajax ActionController::UnknownFormat

I have a mini-update method in my model that only updates one field, set up in a chart (so I see the form for each row). It works properly, but want it to update using AJAX to be able to do multiple updates quickly without waiting for a full page reload.
The cell looks like this:
<td style="min-width: 150px" id="goody_cell<%= blog.id %>">
<%= render partial: "admin/goody_cell", locals: { blog: blog } %>
</td>
With this as the partial:
<% if blog.variation_id %>
<%= Variation.find(blog.variation_id).name %>
<% end %>
<%= simple_form_for(blog, url: update_goody_path(blog), remote: true, method: :patch, authenticity_token: true) do |f| %>
<% variation_collection = [] %>
<% Variation.all.each do |lm| %>
<% variation_collection << ["#{lm.name}", "#{lm.id}"] %>
<% end %>
<div class="row text-left">
<div class="form-group col-12 mb-0 pb-0">
<%= f.input :variation_id, label: false, prompt: "Choose a Related Goody", input_html: { id: "lmFor" + blog.id.to_s, class: 'browser-default', onchange: "this.form.submit();" }, collection: variation_collection, required: false %>
</div>
</div> <!-- row -->
<% end %>
My blogs/update_goody.js.erb looks like this:
$('#goody_cell<%= blog.id %>').html('<%= j render partial: "admin/goody_cell", locals: { blog: #blog } %>');
And my controller method is like this:
def update_goody
#blog = Blog.friendly.find(params[:id])
if #blog.update!(blog_params)
respond_to do |format|
format.js
end
else
format.html { render :edit }
format.json { render json: #blog.errors, status: :unprocessable_entity }
end
end
I have the route like this:
patch "blogs/:id/update_goody" => "blogs#update_goody", as: "update_goody"
When I try to update it, it does update the value, but before it renders the partial, I get an error saying:
ActionController::UnknownFormat - ActionController::UnknownFormat:
app/controllers/blogs_controller.rb:178:in `update_goody'
I've looked at SO posts like this but they all say that the error was fixed by adding remote: true, which I already have.
Can anyone help me get this working?
instead of remote: true, use local: false (Rails 6)
make sure you have both update_goody.html.erb and update_goody.js.erb in your views/blogs folder.
you dont have to add anything to update_goody.html.erb as its not rendering at all.
in your controller
def update_goody
#blog = Blog.friendly.find(params[:id])
if #blog.update!(blog_params)
respond_to do |format|
format.html
format.js
end
else
format.html { render :edit }
format.json { render json: #blog.errors, status: :unprocessable_entity }
end
end
note that format.html in respond_to block. it dosent matter you add it or not as you are set remote: true in your form
refer this
In my case i had to put redirect_to to render html
respond_to do |format|
format.js
format.html { redirect_to update_goody_path }
end

Correct way to pass local variables from a controller to a js.erb which renders a partial

I'm trying to implement an edit function for comments using ajax. The function must load a _form.html.erb partial to edit the comment on which the user clicks but I am not able to figure out how to implement the variables used.
def edit
#comment = Comment.find(params[:id])
#post = Post.find_by_id(params[:post_id])
respond_to do |format|
format.html {}
format.json { head :no_content }
format.js { render "edit", :locals => {:#comment => Comment.find(params[:id]), :#post => #post} }
end
end
def update
#comment = Comment.find(params[:id])
#post = Post.find_by_id(params[:post_id])
if #comment.update_attributes(comment_params)
respond_to do |format|
format.html {
flash[:notice] = "Successfully updated comment"
redirect_to post_path(#comment)
}
format.json { head :no_content }
format.js
end
else
respond_to do |format|
format.html {flash[:alert] = "Error updating coment"
render :edit
flash[:alert] = "Error updating coment"
render :edit}
format.json { head :no_content }
format.js
end
end
end
Here are my js files, edit and update:
$('.edit_comment').bind('ajax:success', function() {
$(this).closest('.comment').html("<%= escape_javascript(render partial: 'comments/form', :locals => {:post => #post,:comment => #comment}) %>");});
$('.edit_comment').bind('ajax:success', function() {
$(this).closest('.comment').replaceWith('<%=j render 'comments/comment', post: #post, comment: #comment%>');
});
There must be some problem with the variables I'm using in my controller and in my js files because when I try and edit more than one comment without refreshing I am always editing the first one I clicked on.
In my console the right comment ids are given when I click on edit, however when the form is loaded on the actual page it is using always the first comment that I tried to edit.
EDIT
adding my form partial:
<%= form_for [post, comment], remote: true do |f| %>
<%= f.text_area :body, placeholder: "Add a Comment" %><br/>
<%= f.submit "Post Comment" %>
<% end %>

I'm receiving a 406 error on ajax return

I'm new to rails so I'm probably messing this up somehow (obviously). I have a form that is sending the submission back to the controller via ajax. The data is getting there and getting into the db. The goal is that on successful submission the data will be returned in an html format that I can then prepend to page without causing a page refresh.
Here's my controller:
# POST /projects
# POST /projects.json
def create
#project = Project.new(params[:project])
#project.user_id = current_user.id
respond_to do |format|
if #project.save
#format.html { redirect_to #project, notice: 'Project was successfully created.' }
#format.json { render json: #project, status: :created, location: #project }
render :partial => "projects/project", :locals => { :project => #project }
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
My ajax function looks like this:
$('#new_project').submit(function(){
$.ajax({
type: 'post',
beforeSend: setHeader,
url:'/projects',
dataType: 'json',
data: $('#new_project').serialize(),
success: function(json) {
console.log(json);
parent.jQuery('#cboxClose').click();
}
});
return false;
});
And finally, my partial. The partial is part of a loop. The loop works great and there's no problem there. The loop looks like this:
<% #projects.each do |project| %>
<%= render :partial => "project", :object => project %>
<% end %>
And the partial looks like this:
<section class="project">
<p class="name"><span>Name: </span> <%= project.name %></p>
<p class="title"><span>Title: </span><%= project.title %></p>
<p class="title"><span>User ID: </span><%= project.user_id %></p>
<article>
<span>Details:</span> <%= project.details %>
</article>
<article>
<p>Meta:</p>
<%= link_to 'Show', project %>
<%= link_to 'Edit', edit_project_path(project), :data => { :colorbox => true,:colorbox_height => '500px', :colorbox_width => '500px', :colorbox_iframe => true } %>
<%= link_to 'Destroy', project, method: :delete, data: { confirm: 'Are you sure?' } %>
</article>
</section>
So that's it. I thought I was on the right track but the 406 error is throwing me for a loop.
You send ajax request and your application should return javascript (or json) back to browser. But you call "render" and response has html type. So 406 error is.
If you want to render partial after success ajax call you should slightly change your controller (consider using format.js against format.json).
Controller:
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.js #render create.js.erb
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
And you should create file create.js.erb in directory with views of this controller with something like that:
$("div#that_you_wat_to_update").html("<%= escape_javascript render(:partial => 'projects/project', :locals => { :project => #project }) %>");
P.S. If you really want to respond with json, than you should update your ajax call to process the json response, but it's another story.

ActiveRecord::RecordNotFound

I am trying to submit a form and getting this error: Couldn't find Event without an ID
Here is the controller:
def create
#event = Event.find(params[:event_id])
#event_sponsorship = EventSponsorship.new(params[:event_sponsorship])
#event_sponsorship.sponsor_id = current_user.id
#event_sponsorship.event_id = #event
respond_to do |format|
if #event_sponsorship.save
format.html { redirect_to #event_sponsorship, notice: 'Event sponsorship was successfully created.' }
format.json { render json: #event_sponsorship, status: :created, location: #event_sponsorship }
else
format.html { render action: "new" }
format.json { render json: #event_sponsorship.errors, status: :unprocessable_entity }
end
end
end
Here is the form:
<%= simple_form_for(#event_sponsorship) do |f| %>
<%= f.error_notification %>
<div class="signin">
<%= f.hidden_field :event_id, value: #event %>
<%= f.hidden_field :sponsor_id, value: current_user.id %>
<%= f.button :submit, :class => "btn btn-success btn-block", value: "Yes" %>
</div>
<% end %>
In the create method the event_id should be found from the URL. Where am I going wrong?
Post your error as seen in your server console, you'll see that your param 'event_id' is a sub key of (I guess) 'event_sponsorship'
Try this:
#event = Event.find(params[:event_sponsorship][:event_id])
And it's even possible that you'll need to use this code:
#event = Event.find(params[:event_sponsorship].delete(:event_id))
That would remove the 'event_id' fron your params, and allow EventSponsorship.new to work without error

Resources