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.
Related
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 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])
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
I have 2 controllers: DocumentsController and DashboardController
After the user login successful, he is redirected to dashboard_path, which has a form to create a 'fast document' like this
<%= form_for #document, :html => {:class => 'well'} do |f| %>
<% if #document.errors.any? %>
<div id="alert alert-block">
<div class="alert alert-error">
<h2>Couldn't create your doc. :(</h2>
<ul>
<% #document.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<label>A lot of fields</label>
<%= f.text_field :fields %>
<div class="form-actions">
<%= f.submit 'Create document', :class => 'btn btn-large' %>
</div>
<% end %>
but when an exception happen (like the user forgot to fill a field), I would like to show these exceptions, not just an alert saying 'Error'...actually, I didn't found a way to do this
here's my DashboarController
class DashboardController < ApplicationController
before_filter :authenticate
def index
#document = Document.new
end
end
and my DocumentsController
class DocumentsController < ApplicationController
respond_to :json, :html
def show
end
def create
#document = Document.new(params[:document])
#document.user = current_user
if #document.save
redirect_to dashboard_path, notice: 'Created!'
else
flash[:error] = 'Error!'
redirect_to dashboard_path
end
end
end
any help is appreciated :)
You are correctly redirecting on success; on failure, though, should not redirect; you need to render the template where the form was filled.
if #document.save
redirect_to dashboard_path, notice: 'Created!'
else
render 'dashboard/index'
end
You'll have to make sure that any variables needed by the index template are available in the create action of the documents_controller (you're just rendering the index template; you're not running the code from the dashboard controller's index action). Here's a excerpt from the relevant Rails Guide to clarify:
Using render with :action is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does not run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling render.
More at http://guides.rubyonrails.org/layouts_and_rendering.html#using-render