When I add a model in my index action, the create action is invoked that adds an instance of the model to the database. This is the following create action:
tracks_controller.rb
def create
#track = Track.new(params[:track])
if #track.save
redirect_to(root_url) //Want to change this!
else
#tracks = Track.all
render :action=>"index"
end
end
Where you can see that I am redirected to my root url (where I want to be) everytime create is invoked. However, how can I carry this out without refreshing the page? Since tracks are being played, I do not want the page to be refreshed whenever something is added to the database.
If I change this line to render :action=>"index", then I receive the following error in my index.html.erb file
undefined methodeach' for nil:NilClass`
15: <p>Database is empty!</p>
16: <%else%>
17: <br>
18: <% #tracks.each do |track| %>
19: <div id="list_container">
20: <ul>
21: <li class="list_container">
How do I go about achieving this?
form
<%= form_for #track, remote: true %>
form fields
<%= f.submit %>
<% end %>
app/views/tracks/create.js.erb
<% if #track.valid? %>
$(".tracks").prepend('<%= j(render(#track)) %>'); // make sure you have _track.html.erb
$("ID OR CLASS OF YOUR FORM")[0].reset(); // this will clear your form inputs
<% else %>
alert('Something Went Wrong');
<% end %>
app/views/tracks/index.html.erb
<div class="tracks">
<% #tracks.each do |track| %>
<%= render :partial => 'track' %>
<% end %>
</div>
app/views/tracks/_track.html.erb
some code here to show track:
<div>track.id</div>
<div>track.name</div>
index.html.erb needs the #tracks variable. Therefore, you have to set it before rendering the page.
For example:
def create
#track = Track.create(params[:track])
#tracks = Track.all
render :action => "index"
end
The simplest, quickest way to get it is to add :remote => true to you form_for!
If you are posting track from a form then make the remote to true
like
<%=form_for #track, :remote => true do |f| %>
your input fields
<%end %>
index.html.erb
<div id="track_list">
<%= render :partial => 'tracks_record' %>
</div>
_tracks_record.html.erb
<% #tracks.each do |track|%>
your code
<% end %>
in controller
def create
#track = Track.create(params[:track])
#tracks = Track.all
respond_to do |format|
format.js
end
end
Create one js.erb file for create.js.erb
<% if #track.valid? %>
('#track_list').html('<%= escape_javascript( render :partial => "tracks_record" ) %>');
<% else %>
alert('Could not save');
<% end %>
This will refresh the data and populate the new entries without refreshing the page.
Unfortunately I can't comment on Debadatt's suggestion but I found it much better than the top post. My only notes are to change this to
def create
#track = Track.create(params[:track])
#tracks = Track.all
respond_to do |format|
format.js
end
end
to this:
def create
#track = Track.create(params[:track])
#tracks = Track.all
respond_to do |format|
format.js { render layout: false, content_type: 'text/javascript' }
end
end
And this:
('#track_list').html('<%= escape_javascript( render :partial => "tracks_record" ) %>');
to this:
$('#track_list').html('<%= escape_javascript( render :partial => "tracks_record" ) %>');
Great suggestion thanks Debadatt
Related
I am using Hotwire Turbo Frame to create new record. My understanding was that when we create new record using Turbo Frame then only that single record will be replaced in index page rather than fetching all records from server. But when I look into Network tab, it returns all records from database. Only thing that it doesn't return is <head></head> content as expected. If it will return all record from database then it's not going to be fast. I most be doing something wrong. Below are my code snippets.
Controller
class QuotesController < ApplicationController
def index
#quotes = Quote.all
end
def new
#quote = Quote.new
end
def create
#quote = Quote.new(quote_params)
if #quote.save
respond_to do |format|
format.html { redirect_to root_path, notice: 'Success' }
format.turbo_stream
end
else
render :new, status: :unprocessable_entity
end
end
end
(more code here ....)
View (index.html.erb)
<h1>Quotes</h1>
<%= link_to 'New Quote', new_quote_path, data: { turbo_frame: dom_id(Quote.new) } %><br>
<%= turbo_frame_tag Quote.new %>
<%= turbo_frame_tag 'quotes' do %>
<%= render #quotes %>
<% end %>
View (new.html.erb)
<h1>New Quote</h1>
<%= turbo_frame_tag #quote do %>
<%= render "form" %>
<% end %>
create.turbo_stream.erb
<%= turbo_stream.prepend 'quotes', #quote %>
<%= turbo_stream.update Quote.new, '' %>
I have action index:
def index
if params['type'] == 'random'
#objects = Object.order("RANDOM()").limit(1)
else
#objects = Object.all.limit(1)
end
end
and create action:
def create
object = Object.find(params[:object_id])
comment = object.comments.create(params[:comment].permit(:body))
respond_to do |format|
format.html
format.js #ajax
end
if comment.save
redirect_to root_path(params[:object_id]) #doesn't work
else
flash[:error] = comment.errors.full_messages[0]
redirect_to root_path(params[:object_id]) #doesn't work
end
end
I can comment an object in my index page. When I put a comment, I want to redirect to the object that was commented.
With my code, the page is reloaded, but the next object is displayed, and I cannot see the comment. How can I redirect to the same object?
My root_path
<span class="random-icon"><%= link_to icon('random'), "http://localhost:3000/?type=random" %></span>
<div class="inner-container">
<% #objects.each do |object| %>
<h1 class="title"><%= object.title %></h1>
<p class="obj"><%= object.body %></p>
<h3 class="comments-title">Comments:</h3>
<div id="comments">
<% object.comments.each do |comment| %>
<div class="comments"> <%= comment.body %>
<span class="time-to-now"><%= distance_of_time_in_words_to_now(comment.created_at) %> ago</span>
</div>
<% end %>
</div>
<div id="error"><%= flash[:error] %></div>
<%= form_for([object, object.comments.build], remote: true) do |f| %>
<%= f.text_area :body, class: "text-area" %>
<p class="char-limit">255 characters limit</p>
<%= f.submit "Comment", class: 'button' %>
<% end %>
<% end %>
</div>
If params['type'] is true, Object.order("RANDOM()").limit(1) will always br reevaluated and usually return a new object. To ensure you return to the same object, you might want to store it in session and then check first in your index if there is a liked comment in your sessions, if so, #objects = Object.find(session[:comment_object_id])
def index
if session[:comment_object_id]
#objects = Object.find(session[:comment_object_id])
session.delete(:comment_object_id) # delete the session after use
elsif params['type'] == 'random'
#objects = Object.order("RANDOM()").limit(1)
else
#objects = Object.all.limit(1)
end
end
def create
object = Object.find(params[:id])
comment = object.comments.create(params[:comment].permit(:body))
respond_to do |format|
format.html
format.js #ajax
end
if comment.save
session[:comment_object_id] = :object_id # set the session here
redirect_to root_path # should work now
else
flash[:error] = comment.errors.full_messages[0]
redirect_to root_path #should work now
end
end
This is pretty easy, and you're very close.
Inside your create method, you've got the object you want to redirect to. So just use it directly inside the if comment.save like:
redirect_to object_path(object)
You can get a list of all these path "helpers" via the command:
rake routes
And in that listing, you should see, by the way, that root_path does not accept any arguments ... for future reference.
I have a form and a div for showing the message which is sent by form. I have to refresh the page after sending a message by form to see the messages in my div. However, I want to see the messages in div without refreshing the page, like a chatroom. I tried to use Ajax but I couldn't make it.
Here is my form:
<%= form_for #message, remote:true do |f| %>
<%= f.text_area :body, class: "form-control", placeholder: "Enter Message" %>
<%= f.submit "Send", class: 'btn btn-primary btn-lg' %>
<% end %>
And here is my controller
class MessagesController < ApplicationController
def index
#messagesAll = Message.all
#message = Message.new
end
def new
#message = Message.all
end
def create
#message = Message.new(params.require(:message).permit(:user, :body))
if #message.save
respond_to do |format|
format.js {}
format.html {redirect_to messages_url}
end
else
render 'new'
end
end
end
And here is my div:
<div class="panel-body" id="messages-div">
<ul class="media-list">
<% #messagesAll.each do |post| %>
<li class="media">
<%= post.body %>
</li>
<% end %>
</ul>
</div>
Could anyone help me? Thank you.
Assuming you have a div with an id of messages that you're rendering the messages in e.g.
<div id="messages">
<%= render #messages %>
</div>
On your create action rails will look for a create.js.erb file. So what you want is this in your controller:
respond_to do |format|
format.js {}
format.html {redirect_to messages_url}
end
Then have a create.js.erb file:
$('#messages').append('<%= j render #message %>');
Edit: In your specific case you would need to target the ul. So your create.js.erb would be
$('#messages-div ul.media-list').append('<li class="media"><%= j #message.body %></li>');
j is just a shortcut for escape_javascript. Docs
That should get you started, but you'll want to do something else via ajax if the message isn't saved.
I used this tutorial for making polymorphic comments
https://gorails.com/episodes/comments-with-polymorphic-associations
It works fine but when I set remote true to my comments form I got 505 error(the new comment is not appended to the list) when trying to add a new comment.
But when I reload the page the new comment appears in the list.
What can be wrong here?
I got this files in views/comments folder:
create.js
<% unless #comment.errors.any? %>
$("ul.comments").append('<%= j render "comments/comment" %>')
$("#new_comment")[0].reset();
<% end %>
_form.html.erb
<%= simple_form_for([commentable, Comment.new], remote: true) do |f| %>
<%= f.input :body, label: false %>
<%= f.button :submit, "Submit" %>
<% end %>
comments_controller.rb
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
#comment.save
respond_to do |format|
format.html { redirect_to #commentable, notice: "Your comment was successfully added."}
format.js
end
end
_comment.html.erb
<li class="comment">
<b><%= comment.user.try(:username) %>:</b>
<%= comment.body%>
</li>
console
UP
I got this routes for post
resources :posts do
resources :comments, module: :posts
end
+
controllers/posts/comments_controller
class Posts::CommentsController < CommentsController
before_action :set_commentable
private
def set_commentable
#commentable = Post.friendly.find(params[:post_id])
end
end
The folder structure looks almost the same as here
https://github.com/excid3/gorails-episode-36/tree/master/app/controllers
the response tab shows this
undefined local variable or method `comment' for #<#<Class:0x007f993d3c5450>:0x007f99450a6190>
Trace of template inclusion: app/views/comments/create.js.erb
and when I replace
<%= j render #comment %> with some text it appends to the list on submit
ok the problem is with _comment.html erb
updated create.js with instance variable for comment and it works now.
<% unless #comment.errors.any? %>
$("ul.comments").append('<%= j render partial: "comments/comment", locals:{comment: #comment} %>')
$("#new_comment")[0].reset();
<% end %>
I am able to update the like and unlike button via ajax, but I am not able to update the like count.
Post has_many likes
likes_controller.rb
def create
#like = Like.new(:post_id => params[:post_to_be_liked])
if #like.save
respond_to do |format|
format.html {redirect_to :back, notice: "Liked!"}
format.js
end
end
end
view.html.erb
<% #post = user.posts.first %>
<div id="send_like_for_<%= #post.id %>">
<% if #post.is_liked?(#post, current_user)%>
<%= render 'unlike'%>
<%else%>
<%= render 'like'%>
<%end%>
</div>
<div id="liked_<%= #post.id %>" %>
<%= render 'liked_users'%>
</div>
create.js.erb
$("#send_like_for_<%= params[:post_to_be_liked]%>").html("<%= escape_javascript(render('users/unlike')) %>")
$("#liked_<%= params[:post_to_be_liked]%>").html("<%= escape_javascript(render('users/liked_users')) %>")
_like.html.erb
<%= link_to "LIKE", likes_path(:post_to_be_liked => #post.id), remote: true, :method=>:post %>
_unlike.html.erb
<span> UNLIKE </span>
_liked_users.html.erb
<%= "#{#post.likes.length} like this" %>
The like/unlike functionality works just fine and when someone likes the post the button changes to Unlike. But the like count doesn't increase.
But why are you doing this in your view?
<% #post = user.posts.first %>
It will update only your first post always. I can't understand.
Try with this:
_liked_users.html.erb
<%= #updated_post = Post.where(id: #post.id) %>
<%= "#{#updated_post.likes.length} like this" %>
or
<%= #post.reload %>
<%= "#{#post.likes.length} like this" %>
Edit (Try to build your like this):
def create
#post = Post.where(id: params[:post_to_be_liked])
#like = #post.likes.build(:post_id => #post.id)
if #like.save
respond_to do |format|
format.html {redirect_to :back, notice: "Liked!"}
format.js
end
end
end
I think you should try to do this
#post = user.posts.first
after save in your controller and remove <% #post = user.posts.first %> from erb file and if it is not possible then initialize #post in your controller#create
#post = Post.find(params[:post_to_be_liked])