I am trying to get the rails flash notice to appear within my application by using JavaScript only. I have a form which leverages remote: true to make an AJAX request to the controller when submitting. The form looks like the example shown below:
# app/views/_email_register.html.erb
<%= simple_form_for :email, url: emails_path, remote: true do |f| %>
<div class="modal-body">
<div class="form-group">
<%= f.input :address, as: :email, placeholder: 'user#domain.com', label: false %>
</div>
</div>
<div class="modal-footer">
<%= f.submit 'Subscribe', class: 'btn btn-primary btn-block' %>
</div>
<% end %>
The rails application was generated using rails-composer which also installed bootstrap css.
# app/controllers/emails_controller.rb
def create
#email = Email.new(email_params)
respond_to do |format|
if #email.save
format.html { redirect_to :back, notice: 'Email was successfully registerd' }
format.js
else
format.html { render :new }
format.js
end
end
end
The controller then invokes the create.js.erb as part of the respond_to block.
# app/views/emails/create.js.erb
// flash to user it was successful
$(".alert").html("Email was successfully registered");
The _messages.html.erb partial was automatically generated from rails-composer. Not sure if this partial can be invoked from the JavaScript
# app/views/layouts/_messages.html.erb
<% flash.each do |name, msg| %>
<% if msg.is_a?(String) %>
<div class="alert alert-<%= name.to_s == 'notice' ? 'success' : 'danger' %>">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
</div>
<% end %>
<% end %>
Any assistance on how I can get the flash notice to show up via JavaScript would be much appreciated.
You just need to re-render the partial which renders the flash messages in your js.erb
# app/views/emails/create.js.erb
$('.flash_wrapper').html("<%= escape_javascript(render 'layouts/messages') %>");
In your controller set the flash
format.js { flash.now[:notice] = 'Email was successfully registered' }
This will show you flash
NOTE: .flash_wrapper is your wrapper where you are rendering the _message partial.
Related
My application contains a very simple login and two separate AJAX calls. I have a loading spinner that is hidden automatically whenever a page is ready it is contained in index.js
$(function() {
$("loading").hide();
$("#scan_button").on("ajax:success", function(e, data, status, xhr) {
$("#standard_results").hide();
$("#results").show();
$("#results").html(data);
});
$(document).ajaxStop(function() {
$("#loading").hide();
});
$(document).ajaxStart(function() {
$('#loading').show();
});
});
When a user logs in the following happens
class SessionController < ApplicationController
def create
user = User.find_by(username: params[:session][:username])
if(user.password == params[:session][:password])
log_in user
redirect_to root_path
end
end
def destroy
logout
redirect_to root_path
end
end
The route path takes me back to 'home#index' for which the index.js file exists. When the user logs in the redirect happens and the loading spinner is hidden and all JavaScript executes as expected.
When the user hits log out however the redirect occurs, but the loading spinner is shown and no JavaScript ever gets executed. Here is my index.html.erb file that contains the JavaScript.
<% content_for :title, "Network Monitor" %>
<div class="container-fluid">
<nav class="navbar narvbar-dark bg-inverse">
<span class="navbar-brand">Network Monitor</span>
<%= link_to "Scan", scan_path, remote: true, class: "btn btn-secondary", id: "scan_button" %>
<% if logged_in? %>
<span class="pull-xs-right"><%= link_to "Logout", logout_path, class: "btn btn-secondary" %></span>
<% else %>
<%= form_for(:session, url: login_path, :html => {:class => 'form-inline pull-xs-right'}) do |f| %>
<%= f.text_field :username, class: 'form-control', placeholder: "Username" %>
<%= f.password_field :password, class: 'form-control', placeholder: "Password" %>
<%= f.submit "Log in", class: 'btn btn-primary', id: 'login_button' %>
<% end %>
<% end %>
</nav>
<div id="loading">
<%= image_tag "loading.gif", class: "loading_gif" %>
</div>
<div class="row">
<div class="col-md-12">
<div id="scan_bar">
<h3>Welcome to your network monitor!</h3> <br />
<p>To get started hit the scan button in the top right corner, this will detect the computers that are alive on your network and show them below.</p>
</div>
</div>
</div>
<div class="row">
<div id="results">
</div>
<div id="standard_results">
</div>
Solved. It was as simple as using
$(document).on('turbolinks:load', function() {});
Instead of what I had posted, this seems to be the way that Rails 5 and turbolinks want you to use, and it worked.
According to the
turbolinks document, I use the event page:change to solve the problem:
$(document).on("page:change", function(){...});
It works for me.
In case others run into this. If you are using an href or link_to try doing something like this:
<li><%= link_to "Home", jobs_path, method: :get %></li>
...where you specify the get method in the request.
I've got a hand rolled registration form where I am using the Ambethia Recaptcha gem. Everything works fine when the form is valid. However, when it is not, I need the ReCaptcha to be reset. Here is the create method:
def create
#users = User.all
#user = User.create(user_params)
respond_to do |format|
if verify_recaptcha(model: #user, timeout: 300, message: "Problem with ReCAPTCHA, please try again.") && #user.save
session[:user_id] = #user.id
flash[:notice] = "Thank you for signing up!"
format.html
format.js {render js: "window.location = '#{root_path}';"}
format.json { render :show, status: :created, location: #user }
if Rails.env.production?
UserMailer.welcome_email(#user).deliver
end
else
flash[:alert] = "Sign Up Failed!"
format.html { redirect_back(fallback_location: root_path) }
format.js
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
You see, when the form is correct, the ReCaptcha works, but when there is a validation failure, like passwords not matching, The Recaptcha shows up already checked. Then when you correct the validation errors, you can't redo the ReCaptcha and you get the "Problem with ReCAPTCHA..." error. This is the form that contains my recaptcha_tags:
<div class="modal-dialog">
<div class="spinner">
<%= image_tag "loading.gif" %>
</div>
<div class="modal-content">
<div id="registration-form">
<h1>Sign Up</h1>
<%= form_for #user, remote: true, html: { style: "display:inline;" } do |f| %>
<div class="modal-body">
<ul class="errors"></ul>
<div class="field">
<%= f.label :email %><br>
<%= f.email_field :email, required: true, autofocus: true, placeholder: "Enter a valid email" %>
</div>
<div class="field">
<%= f.label :username, 'Username' %><br>
<%= f.text_field :username, required: true, placeholder: "Pick a username" %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.password_field :password, required: true, placeholder: "Create a password" %>
<p id="form-tip">Passwords must be at least <strong>8 characters</strong> in length and contain at least <strong>one upper case letter</strong>, <strong>one lower case letter</strong>, and <strong>one number or special character</strong>.</p>
</div>
<div class="field">
<%= f.label :password_confirmation %><br>
<%= f.password_field :password_confirmation, required: true, placeholder: "Password again" %>
</div>
<%= render 'recaptcha' %>
<div class="modal-footer actions">
<%= f.submit "Sign Up", class: "btn btn-l btn-success"%>
<%= link_to "Cancel", "#", class: "btn btn-l btn-danger", data: {dismiss: "modal"} %>
</div>
<% end %>
</div>
</div>
</div>
</div>
This form is rendered with an AJAX request from a navbar button. Form submission triggers the following _save.js.erb:
$("ul.errors").html("")
<% if #user.errors.any? %>
<% #user.errors.full_messages.each do |message| %>
$("ul.errors").append($("<li />").html("<%= message.html_safe %>"))
<% end %>
<% else %>
$("#new_user_modal").modal("hide")
<% end %>
I have tried to add $(".g-recaptcha").grecaptcha.reset(); to make the recaptcha reload. I've tried lots of things. Nothing seems to work. When my form is reloaded with the validation errors, a new ReCAPTCHA box should appear. Either that or somehow allow the user to fix their errors and continue without redoing the recaptcha.
Thanks
Try something like the following code, which will bind (via jquery) to the ajax:error thrown by your JSON return / 422.
$('#registration-form form').on("ajax:error", function(e, xhr, status, error) {
if (window.grecaptcha) grecaptcha.reset();
})
The simplest way, using something like jQuery would be to bind to your form element, and handle it on error. When your remote call returns, you can simply reset the form, but only on error. This way you can ensure your users will always get a fresh reCaptcha form and once again validate. This seems to be part of the RAILS gem as I hit the same piece tonight and solved it using that.
So nobody has responded, but I have found a way around the problem for now. I still can't seem to "reset" the ReCaptcha, but by removing the "optional" parameters from the verify_recaptcha method in the controller it no longer expects the user to resubmit a new recaptcha. Mainly it's the un-binding of ReCaptcha to the User model that allows it to "bypass" the model validation. I updated this line in users_controller.rb:
...
if verify_recaptcha(model: #user, timeout: 300, message: "Problem with ReCAPTCHA, please try again.") && #user.save
session[:user_id] = #user.id
...
to this:
...
if verify_recaptcha && #user.save
session[:user_id] = #user.id
...
Still interested to know how to go about actually resetting the ReCaptcha (preferably via the gem itself or a jQuery call). I've thrown everything at it (clearing the div, toggling classes, g-recaptcha.reset() function, messing with the timeout attribute, etc...) and nothing has worked.
Big thanks to anyone who can help!
I'm trying to make a simple example on a new form to send the notice of success when creating a new item using ajax.
I'm reciving a response from the server with 500 internal error.
new.js.erb
$("#flash-messages").html("<%= notice %>")
new.html.erb
<h1>New Ajax</h1>
<%= form_for(#ajax, remote: true) do |f| %>
<div id="flash-messages" >
</div>
<% if #ajax.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#ajax.errors.count, "error") %> prohibited this ajax from being saved:</h2>
<ul>
<% #ajax.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
controller
def create
#ajax = Ajax.new(ajax_params)
respond_to do |format|
if #ajax.save
format.html { redirect_to #ajax, notice: 'Ajax was successfully created.' }
format.json { render :show, status: :created, location: #ajax }
format.js { redirect_to new_ajax_path, flash[:notice] = "Fudeu" }
else
format.html { render :new }
format.json { render json: #ajax.errors, status: :unprocessable_entity }
end
end
end
I have found why was bugging my code.
Just nee do remove the flash[:notice] to notice:
Now I`m looking into giving a lifetime to this flash, like 10s and it vanishs
When I have it working I post it here for other people that may strugle as me
I have a view that has a form to create a new post using Ajax, and it also renders all the posts and adds the newly added post to them. The problem is when I post the very first post, the page does not show it unless I refresh, then if I refresh it shows up and when I add more posts Ajax works fine.
Here is the view index.html.erb :
<%= link_to('Logout', destroy_user_session_path, :method => :delete) %>
<%= link_to 'Edit my account', edit_user_registration_path %>
<%= form_tag search_posts_path, method: :get do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", name: nil %>
<% end %>
<b> Study requests </b>
<%= form_for(#post, remote: true) do |f| %>
<%=f.label :Course_name %>
<%=f.select :course_name, options_for_select(course_names) %><br/>
<%=f.label :Course_number %>
<%=f.select :course_number, options_for_select(course_numbers) %> <br/>
<%=f.submit %>
<% end %>
<%= render #posts %>
The partial is _post.html.erb :
div id="post">
<%= post.user_name%>
</div>
Controller :
def index
#posts = Post.all.order('id DESC')
#post = Post.new
end
def create
#post = current_user.posts.build(post_params)
#post.save
respond_to do |format|
format.html { redirect_to #post, notice: "Study request successfully added" }
format.js {}
format.json { render json: #post, status: :created, location: #post }
end
end
And finally my create.js.erb :
$("<%= escape_javascript(render #post) %>").prependTo("#post");
I got it, I only needed to add a new div around my partial rendering like this :
<ul id="container">
<%= render #posts %>
</ul>
And change the create.js to append to this div
I don't have any idea why code works on development machine, but doesn't work on production server. I am trying to create live chat between operator and user. In development machine the user's interface and operator's works fluently. I am using pusher as web-sockets. Actually every js.erb view does not work at all in production server. There is some parts of my view.
_show.html.erb:
<%= content_tag :div, id: "chat-heading", class: %w(mpreview migedit r).include?(action_name) ? 'panel-heading2' : 'panel-heading', style: "font-size:14px;" do %>
<%= t :chat_heading %>
<i class="fa fa-arrow-up" aria-hidden="true" style="float: right;"></i>
<% end %>
<div class="panel panel-primary" style="display: none" id="chat-primary" tabindex="0">
<%= content_tag :div, class: 'panel-body', id: 'messages', data: {url: "/#{params[:locale]}/chats/#{#chat.id}/messages/"} do %>
<% unless #messages.total_pages < 2 %>
<div class="row" id="show-more-aligment">
<div class="col-md-4 col-md-offset-5" style="margin-bottom: 20px;">
<%= link_to 'Show more', url_for(:controller => 'home', :action => 'index', :page => 2, :locale => params[:locale]), :remote => true, :id => 'show_more_link' %>
</div>
</div>
<% end %>
<ul class="chat" id="chat-ul">
<% #messages.reverse_each do |message| %>
<%= render partial: 'messages/message', locals: {message: message} %>
<% end %>
</ul>
</div>
<div class="panel panel-footer">
<div class="row">
<%= simple_form_for #chat.messages.build, url: "/#{params[:locale]}/chats/#{#chat.id}/messages/", :user_id => current_user, remote: true, :html => {:autocomplete => "off"} do |f| %>
<div class="col-md-10">
<%= f.input :text, required: true, as: :string, :class => 'btn-input form-control input-sm', :placeholder => "Type your message here...", :label => false %>
</div>
<div class="col-md-2">
<%= f.submit 'Send', :class => 'btn-chat btn btn-warning btn-sm form-control msg-submit' %>
</div>
<% end %>
<% end %>
</div>
</div>
render of _show.html.erb:
<% if user_signed_in? %>
<%= content_tag :div, class: %w(mpreview migedit r).include?(action_name) ? 'welcome_chat2' : 'welcome_chat' do %>
<%= render 'chats/show' %>
<% end %>
<% end %>
controller for create method:
def create
#message = #chat.messages.new(params[:message])
#message.user = current_user
respond_to do |format|
if #message.save
format.html {redirect_to #chat}
format.js { Pusher.trigger("live_chat_#{#chat.id}", 'chat', {message: #message}) }
else
format.html {render 'new'}
format.js
end
end
end
create.js.erb:
$('#message_text').val('');
index.js.erb(adding new sent message to chat div):
if ($('#chat-ul li').length == 0){
$('ul.chat').html("<%= escape_javascript(render partial: 'messages/message', locals: {message: #message}) %>");
}
else {
$('ul.chat li:last').after("<%= escape_javascript(render partial: 'messages/message', locals: {message: #message}) %>");
}
That's it if I didn't miss something. Basically this code should add new message to database and render it on form send. Actually it saves to database but do not append it to the messages div. It gives 500 internal error. And what is most important in development machine the same exact code works like a charm, but in production it does not. This must be something with server or environment config I guess. Maybe someone has some ideas? Thanks in an advance.
ERROR IS HERE