I am hoping someone can help me here. Been at this for a few hours, but can't seem to figure out why my Javascript is not working on my simple form. I'm simply trying to display comments using the ajax call. I have gem 'jquery-rails', and required jquery, and jquery_ujs in my application.js file. Here's the code I have so far:
I have a Joy model that has_many comments.
<% #joys.each do |joy| %>
<ul id="comments-<%= joy.id %>">
<% joy.comments.where(void:'f').each do |comment| %>
<li><%= comment.body %></li>
<% end %>
</ul>
<%= form_with(model: [ joy.user, joy, joy.comments.build ], data:{ "js-comments-#{joy.id}" => true}) do |f| %>
<%= f.text_area :body, :class=>'form-control', :required=>'true' %>
<%= f.submit "Post Comment" %>
<% end %>
<script>
$(document).ready(function() {
$('[data-js-comments-<%= joy.id %>]').on("ajax:success", function(event, data, status, xhr){
$('#comments-<%=joy.id%>').append(xhr.responseText);
});
});
</script>
<% end %>
In my Comment controller, in my 'create' action, I have:
if #comment.save
render partial: "comment", locals: {comment: #comment}
end
In my Comment views, I created a partial, called _comment.html.erb:
<li>
<%= comment.body %>
</li>
So, now when I enter a comment, and click 'Post Comment', it correctly saves my Comment, but it does not ajax load the comment into list. Furthermore, the form does not refresh either. The text remains in the text box. It is not only until after I refresh the page when my comment shows up in the list. Anyone with any thoughts on what I'm missing or doing wrong???
Your help is greatly appreciated in advance.
form_with it's a remote form, you dont need that block <script>..</script>.
Just use create.js.erb then use jquery to append new comment.
And if you want to check if your message was created successfully, you can add a #status value in your controller.
//comments_controller.rb
def create
...
if #comment.save
#status = "success"
else
#status = "failed"
end
...
end
//create.js.erb - for example
$("#yourCommentList").append('<%= j( render partial: 'comments/comment', locals: #comment ) %>');
<% if #status == "success" %>
$("body").append(...your notification...)
...
<% else %>
...
<% end %>
Related
So I'm trying to create a load more button (not infinite scroll although some simple js tweaks could make it so). I'm using the will_paginate gem in Rails 5.
Its currently all working as expected except when I click load more it loads the next page posts twice e.g:
Load more >
Post 4,
Post 5,
Post 6,
Post 4,
Post 5,
Post 6
FYI my 'posts' are #links:
links_controller.erb
def index
#links = Link.order('created_at DESC').paginate(:page => params[:page], :per_page => 3)
respond_to do |format|
format.html
format.js
end
end
views/links/index.html.erb
<div class="link-wrap">
<%= render #links %>
</div>
<% if #links.next_page %>
<div class="link more">
<%= link_to 'Load More', links_path(:page => #links.next_page), :class => 'next_page', :remote => true if #links.next_page %>
</div>
<% end %>
pagination.js.coffee
$ ->
$('.next_page').on 'click', (e) ->
e.preventDefault()
url = $(this).attr('href')
$.getScript(url)
index.js.erb
$('.link-wrap').append('<%= j render(#links) %>');
<% if #links.next_page %>
$('.next_page').attr('href','<%= links_path(:page => #links.next_page) %>');
<% else %>
$('.more').remove();
<% end %>
For the life of me I can't figure out why it would be rendering the links twice when 'load more' is clicked. This seems like a really simple thing but I'm struggling.
So it was a stupid error of course.
Remove the line on index.html.erb:
:remote => true if #links.next_page
So now it looks like this:
<div class="link-wrap">
<%= render #links %>
</div>
<% if #links.next_page %>
<div class="link more">
<%= link_to 'Load More', links_path(:page => #links.next_page), :class => 'next_page' %>
</div>
<% end %>
All works fine now!
If you check the server console, you can actually see two requests coming in, one triggered by your :remote => true enabled link and other from your custom ajax. Actually you don't need any js to make this work. Just remove that click function from your pagination.js.coffee or remove :remote => true from the link. After all both are doing the same thing. Hope this will help.
I have a rails app and when user clicks to login button on header a bootstrap popup modal opens with a form, asks for user email and password. When user types and presses enter, I use window.location.reload(); and the button login turns in to a button with a text says "Welcome <%= current_user.name %>"
What I want to do is, instead of using window location reload, can I update this dynamically?
Here is my sessions#create action
def create
user = User.find_by(email: params[:session][:email].downcase)
respond_to do |format|
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
format.html { redirect_back_or user }
flash[:notice] = t('flash.sessions.create.success.html')
format.js #here I should do smth
else
format.html { redirect_to root_url }
format.json { render json: {email:['Account not activated. Check your email for the activation link.']} , status: :unprocessable_entity}
format.js { render json: {email:['Account not activated. Check your email for the activation link.']}, status: :unprocessable_entity }
end
then the create.js.erb
// close modal
$('#login-dialog').fadeToggle();
// clear form input elements
// todo/note: handle textarea, select, etc
$('form input[type="text"]').val('');
//Clear previous errors
$('.form-group.has-error').each(function(){
$('.help-block').html('');
$('.form-group').removeClass('has-error');
});
window.location.reload(); #here I am reloading the page then I can see login button disappears and new button saying Welcome Billy appears.
So how can I do that without reloading the window.
Thank you
EDIT
The thing is, I have also signup modal which user can click to open and these modal codes are in header.html.erb, when I render the page as you suggested it gives an error for sign up form;
<% modal ||= false %>
<% remote = modal ? true : false %>
<%= form_for(#user, remote: modal, :html => {role: :form, 'data-model' => 'user'}) do |f| %>
<div class="form-group">
<%= f.label :name, t('header.nameSurname') %>
<span class="help"></span>
<%= f.text_field :name, class: 'form-control' %>
<span class="help-block"></span>
</div>
<div class="form-group">
<%= f.label :username, t('header.username') %>
<span class="help"></span>
<%= f.text_field :username, class: 'form-control' %>
<span class="help-block"></span>
</div>
....
because of #user variable, if I change it to User.new is it ok?, would it create problem?.
I also have 3 different header partials. I normally render them in application.html.erb as;
<div id="render_main">
<% if #header_main %>
<%= render 'layouts/header_main' %> <!--Header comes here-->
<% elsif #header_listing %>
<%= render 'layouts/header_listing' %> <!--Header comes here-->
<% else %>
<%= render 'layouts/header' %>
<% end %>
</div>
But then in create.js.erb;
// close modal
$('#login-dialog').fadeToggle();
// clear form input elements
// todo/note: handle textarea, select, etc
$('form input[type="text"]').val('');
//Clear previous errors
$('.form-group.has-error').each(function(){
$('.help-block').html('');
$('.form-group').removeClass('has-error');
});
//window.location.reload();
<% if #header_main %>
$('#render_main').html('<%= j render "layouts/header_main"%>')
<% elsif #header_listing %>
$('#render_main').html('<%= j render "layouts/header_listing"%>')
<% else %>
$('#render_main').html('<%= j render "layouts/header"%>')
<% end %>
as I render it can not find #header_main variable I believe, so It does not work as it should be. How can I fix this?.
Main controller;
before_action :show_main_header, only: [:home]
def show_main_header
#header_main = true
end
Considering user login, from main controller home action. But it is probably because I actually run from session#create action. how can I fix it?
EDIT
firstly, thank you Rodrigo,
I have written a function to hold last action;
def location_action_name
if !logged_in?
url = Rails.application.routes.recognize_path(request.referrer)
#last_action = url[:action]
end
end
Then I write to create.js.erb;
<% if (#last_action == "home") %>
$('#render_main').html('<%= j render "layouts/header_main"%>')
<% elsif (#last_action == "listings") %>
$('#render_main').html('<%= j render "layouts/header_listing"%>')
<% else %>
$('#render_main').html('<%= j render "layouts/header"%>')
<% end %>
and worked! in case anyone wonders..
Let's say that you have an partial view for render the header (app/views/shared/_header.html.erb), what you need to do is rerender this partial and replace the header html:
create.js.erb
// window.location.reload();
$('#header-container').html('<%= j render "shared/header"%>')
EDIT:
If the #header_main and #header_listing variables are used to render the header partial, you'll need to instantiate them in your sessions#create action.
To do this, add the show_main_header filter to SessionsController too.
I have a rails 4 app where I'm following basically this railscast:
_form.html.erb:
<%= form_for #store, do |f| %>
<%= f.fields_for :products do |builder| %>
<%= render "product_fields", f: builder %>
<% end %>
<%= link_to_add_fields "Add Product", f, :products %>
<% end %>
_product_fields.html.erb
<%= f.select :the_product_type, %w[Shelves, Tools, Wires]%>
<div>
<%= f.fields_for :product_fields do |builder| %>
<%= builder.text_area :name_of_product_field %>
<% end %>
</div>
My JS looks like:
$('form').on('click', '.add_fields', function(e) {
var regexp, time;
time = new Date().getTime();
regexp = new RegExp($(this).data('id'), 'g');
$(this).before($(this).data('fields').replace(regexp, time));
return e.preventDefault();
});
My issue is that when I click the Add Product button, I can only see a select. I can't see the name_of_product_field textarea. But I can't figure out why I can see the select if I can't see the textarea?
product_fields is a nested attribute which you have not build anywhere in your code which is why you are not seeing it.
Assuming that a product has_many product_fields, you can resolve this issue in two ways, choose one that suits you:
1. Build it at Controller level
Build the product_fields in the Controller#action which is rendering the problematic view:
def action_name
#store = Store.new
product = #store.products.build
product.product_fields.build
end
2.Build it at View level
Update the fields_for in _product_fields.html.erb as below:
<%= f.fields_for :product_fields, f.object.product_fields.build do |builder| %>
I have the following action on my controller for a Comment:
def create
#comment = comment.new(params[:comment])
#comment.replyable_type = params[:replyable_type]
#comment.replyable_id = params[:replyable_id]
#comment.user = current_user
respond_to do |format|
format.html do
# Removed
end
format.js do
#comment.save
end
end
end
and the following create.js.erb file for when it gets called by a remote form:
console.log('It did run');
<% if #comment.errors.any? %>
<% #comment.errors.full_messages.each do |msg| %>
console.log('<%= javascript_escape(msg) %>');
<% end %>
<% else %>
comment = "<%= escape_javascript render #comment %>";
<% if #comment.replyable_type == 'Comment' %>
$('#comment-<%= #comment.id %> > .replies').prepend(comment));
<% else %>
$('.comments').prepend(comment);
<% end %>
// removed
<% end %>
Along with the following form partial:
<%= form_for(Comment.new, url: comments_path, remote: true) do |f| %>
<div class="comment-form" id="comment-<%= replyable_type %>-<%= replyable_id %>">
<%= f.text_area :content, cols: 60, rows: 5 %>
<%= hidden_field_tag :replyable_id, replyable_id %>
<%= hidden_field_tag :replyable_type, replyable_type %>
<%= f.submit 'Send Comment', class: 'btn btn-primary' %>
</div>
<% end %>
replyable is a polymorphic association, which can be either a Post or a Comment object. When I submit a copy of the form on a page for a post, where replyable_type is Post and replyable_id is a post's ID, it works fine. When I submit a copy of the form on the same page, with the replyable_type set to Comment, and the replyable_id set to a comment's ID, the view the javascript does not run, at all. The action on the controller runs, using Firebug I can see the rendered javascript being sent back in response to the post request, and the comment is added to the database.
There are no errors in either the Rails console for generating the javascript or in the Firebug console to do with running the generated javascript. There are also no errors in the #comment.errors array. Even the first console.log in the Javascript is not run when the replyable object is a comment, despite being present in the rendered javascript.
What could cause this? I am using Rails 3.2.13 on Ruby 1.9.3
On the user's page there are microposts and each of them have it's own comment form and comments. Using "Endless Page" railscast i'm trying to create "show more comments" button, which will load comments by AJAX. But it's not working.
The problem is in show.js.erb file because:
1) common pagination of comments (without AJAX) is working well
2) "show more button" is working well too. I tested it on the users list page
I think "show more comments" not working because it don'understand <%= j render(comments) %> , <%= j will_paginate(comments) %> and i should have here variables like <%= j render(#comments) %> , <%= j will_paginate(#comments) %>.
But when i try to write in my users_controller.rb
def show
#micropost = Micropost.find(params[:micropost_id])
#comments = #micropost.comments
end
it's not working because on my user's page there are many microposts and i have an error "Couldn't find micropost without an id". So in my microposts/_micropost.html.erb i had to use this
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<%= render comments %>
Can anyone please help? How should i change my show.js.erb?
users/show.html.erb
<%= render #microposts %>
microposts/_micropost.html.erb
...
micropost's content
...
<%= render 'comments/form', micropost: micropost %>
<% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
<div class="commentaries">
<%= render comments %>
</div>
<div id="append_and_paginate">
<%= will_paginate comments, :class =>"pagination", :page_links => false %>
</div>
javascripts/users.js.coffee
jQuery ->
if $('.pagination').length
$('#append_and_paginate').prepend('<a id="append_more_results" href="javascript:void(0);">Show more</a>');
$('#append_more_results').click ->
url = $('.pagination .next_page').attr('href')
if url
$('.pagination').text('Fetching more...')
$.getScript(url)
users/show.js.erb
$('.commentaries').append('<%= j render(comments) %>');
<% if comments.next_page %>
$('.pagination').replaceWith('<%= j will_paginate(comments) %>');
<% else %>
$('.pagination').remove();
<% end %>
<% sleep 0.3 %>
Bennington, in users/show.html.erb you have
But #microposts is not defined in your controller. What I think you want is to define #microposts as all the microposts associated with what, a User?
If so, you'd want something like `#microposts = Micropost.where(:user_id => current_user.id) or something.