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!
Related
I'm building a Rails app in which there are exercises and user_answers. When the user sees an exercise, there is a form that he can submit with the answer he thinks is right.
When submitting, I want the same view to be shown, without a refresh, but with a new div which has the solution of the exercise. The view shown is the exercises/show and a form rendered in the user_answers folder. Right now, my code looks like this:
exercises/show.html.erb
<div class="container exercise-page">
<h2 class="exercise-title">Exercício <%= #exercise.id %> de <%= #exercise.category %> - <%= #exercise.sub_category %></h2>
<br>
<%= render 'user_answers/user_answer_form' %>
<br>
<div id="solution_exercise">
</div>
user_answers/_user_answer_form.html.erb
<%= simple_form_for [#exercise, #user_answer], authenticity_token: true, remote:true, :method => 'POST', :html=> { class: 'col-xs-12 col-sm-12 col-lg-3 mx-auto text-center list-group', id:"form"} do |f| %>
<%= f.simple_fields_for :answers, include_id: false do |answer| %>
<%= f.input_field :answer,
label: 'Answer',
as: :radio_buttons,
class: 'form-check-input',
:item_label_class => 'list-group-item',
name: 'answer',
collection: #answers, label_method: :answer %>
<br>
<%= f.submit 'Submit Answer!', class: "btn btn-outline-success", id: "submit" %>
<% end %>
user_answers_controller.rb
def create
#user_answer = UserAnswer.new(user_answer_params)
#answers = Exercise.find(params[:exercise_id]).answers
#exercise = Exercise.find(params[:exercise_id])
#user_answer.exercise = #exercise
#user_answer.answer = #exercise.answers.find(params[:answer])
#user_answer.user_id = current_user.id
#user_answer.save
respond_to do |format|
format.js
end
end
user_answers/create.js.erb
solution = document.querySelector("solution_exercise")
solution.innerHTML = '<%= j render partial: "solution" %>'
user_answers/_solution.html.erb
<p>Your answer: <%=current_user.user_answers.last.answer.answer%></p>
The error:
Does someone know what I'm doing wrong?
I have a search form on my page with two buttons - "news" and "events".
I can get the Rails search function to work correctly but I don't want a search form, I want the user to press one of the two buttons and the search to be executed.
I figure this probably has to be done with JQuery so when a button is clicked, either "news" or "events" are added to the rails input form and it submits the form. Please appreciate where I'm going wrong here as the form won't submit and the search won't execute.
Application.js
$(document).on('ready page:load', function () {
$(".news-button").click(function(){
$("#search").val("news");
$("#news-search").submit();
});
$(".events-button").click(function(){
$("#search").val("event");
$("#news-search").submit();
});
});
Index.html.erb
<%= form_tag news_index_path, :method => 'get', :id => "news_search" do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<div class="col-sm-3 sort-buttons">
<%= link_to "News", "", class: "btn btn-standard news-button" %>
<%= link_to "Events", "", class: "btn btn-standard events-button" %>
</div>
<div class="row">
<% #news.each do |n| %>
<div class="col-sm-4">
<div class="news-wrap">
<%= image_tag("#{n.image}", class: "img-responsive", alt: "Event/News Postings") %>
</div>
</div>
<% end %>
</div>
News.rb
class News < ApplicationRecord
if Rails.env.development?
def self.search(search)
if search
where('category LIKE ?', "%#{search}%")
else
scoped
end
end
end
end
News Controller:
def index
if params[:search]
#news = News.search(params[:search]).page(params[:page]).per_page(10)
else
#news = News.all.page(params[:page]).per_page(10)
end
end
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.
I'm using a bootstrap modal and the Simple_form gem with Ruby on Rails. All runs fine once submitted. The problem is when when there are validation errors, the modal closes as the submit button has been pressed. The errors are only shown if the modal button is clicked once more.
I need the end-user to see the error messages once the form is submitted - I've been thinking I could use javascript to relaunch the modal if there are errors but I can't quite figure out how. Any help would be great thanks.
My modal is as follows:
<%= link_to "Click here »".html_safe,
"#myModal", data: { toggle: 'modal'}, class: "button-main", id: "button-contact" %>
<!-- Modal -->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Email Contact Form</h4>
</div>
<div class="modal-body">
<%= simple_form_for #contact, url: contact_path, html: { id: "contact-form" } do |f| %>
<%= f.error_notification %>
<%= f.input :name, error: 'Name is mandatory, please specify one' %>
<%= f.input :email, label: 'Your Email' %>
<%= f.input :email_confirmation, label: 'Confirm your Email address' %>
<%= f.input :preferred_time, collection: ["Any time", "9am - 12pm Tuesday", "2pm - 6pm Tuesday", "9am - 12pm Wednesday"], selected: "Any time" %>
<%= f.input :subject, collection: ["New Appointment", "Request to change existing appointment", "Information", "Other"], selected: "New Appointment" %>
<%= f.input :message, as: :text %>
<div class="modal-footer">
<%= f.button :submit, "Send", 'data-disable-with' => "Sending...", class: "submit-button", id: "modal-send" %>
<p class="warning">
Certain email providers have been known to mark our replies as spam, please be sure to check your junk mail inbox if awaiting a response
</p>
<% end %>
</div>
</div>
</div>
</div>
</div>
and my model:
class Contact
include ActiveModel::Model
include ActiveModel::Conversion
include ActiveModel::Validations
attr_accessor :name, :email, :email_confirmation, :message, :preferred_time, :subject
validates :name,
presence: true
validates :email,
presence: true, length: { minimum: 6, maxium: 30 }, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }, confirmation: true
validates :email_confirmation,
presence: true, length: { minimum: 6, maxium: 30 }, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
validates :message,
presence: true, length: { minimum: 6, maxium: 1000 }
end
Thanks
I'm sure it's not the cleanest or best way to do this, but finally found a solution to load the modal again only if there are errors. Just added the following to my page with the modal.
<% if #contact.errors.present?%>
<script>
$(window).load(function () {
$('#myModal').modal('show');
});
</script>
<% end %>
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.