Rails Ajaxified form doesn't render errors - ruby-on-rails

I'm trying to implement a simple uniqueness validation on a Rails Ajaxified form. Unfortunately, my validation error is not printed when the validation is not accepted. Can you help me to figure why?
model.rb
class WaitingList < ApplicationRecord
validates :email, uniqueness: true
end
controller.rb
def create_concours
#email = params[:email]
#waiting_user = WaitingList.new(email: #email, source: params[:source], concours_city: params[:choosen_city])
#First Ajax of Weekendr History snif
respond_to do |format|
if #waiting_user.save
UserMailer.welcome(#email).deliver_now
format.html { redirect_to concours_path }
format.js
else
format.html
format.js
end
end
end
_form.html.erb
<%= form_tag concours_path, {id: 'concours-form', remote: true} do %>
<%= text_field_tag :email,
value = nil,
class: "landing-form-input",
autocomplete: "off",
required: true,
pattern: '[^#]+#[^#]+\.[a-zA-Z]{2,6}',
placeholder: "Renseignez ton email ici"
%>
<i class="fa fa-envelope"></i>
<%= hidden_field_tag :choosen_city, '', id: 'c-choosen-city'%>
<%= hidden_field_tag 'source', params[:source] %>
<%= button_tag :submit, class: "landing-form-button" do %>
Je tente ma chance !
<% end %>
<% end %>
create_concours.js.erb
function refreshForm(innerHTML) {
const form = document.getElementById("concours-form");
form.innerHTML = innerHTML;
}
<% if #waiting_user.errors.any? %>
refreshForm('<%= j render "form" %>');
<% end %>

Related

Why does simple form not show validation error messages in Rails?

The :new view when redirected if validations are not matched and where I'd like to see the error messages:
<%= simple_form_for ([ #recipe, #recipe.comments.build]), class:"comment-form" do |f| %>
<%= f.error_notification %>
<%= f.object.errors.full_messages.join(", ") if f.object.errors.any? %>
<%= f.input :name, label: false, placeholder: "Your name", input_html: { value: #comment.name } %>
<%= f.input :comment, label: false, placeholder: "Tell us about your experience", input_html: { value: #comment.comment } %>
<%= f.submit "Submit", class: "btn-comment-submit" %>
<% end %>
This is my controller:
def new
#recipe = Recipe.find(params[:recipe_id])
#comment = Comment.new
#comment = #recipe.comments.build
end
def create
#comment = Comment.new(comment_params)
#recipe = Recipe.find(params[:recipe_id])
#comment.recipe = #recipe
if #comment.save
redirect_to recipe_path(#recipe)
else
render :new
end
end
You're not binding the #comment instance you have created in your controller to the form. Instead #recipe.comments.build always creates a new instance of Comment.
You can set the model with a conditional:
<%= simple_form_for([#recipe, #comment || #recipe.comments.build]) do |form| %>
<%= f.error_notification %>
<%= f.object.errors.full_messages.join(", ") if f.object.errors.any? %>
<%= f.input :name, label: false, placeholder: "Your name" %>
<%= f.input :comment, label: false, placeholder: "Tell us about your experience" %>
<%= f.submit "Submit", class: "btn-comment-submit" %>
<% end %>
Note that you don't need to set the values for the inputs. The form builder will do that for you. Thats kind of the whole point of it.
Or you can preferably ensure that you're setting #comment in the controller to keep the view as simple as possible:
class RecipiesController < ApplicationController
before_action :set_recipe
# ...
def show
#comment = #recipe.comments.new
end
# ...
end
<%= simple_form_for([#recipe, #comment]) do |form| %>
# ...
<% end %>
And you can clean up your create action and just create the comment off the recipe:
def create
#recipe = Recipe.find(params[:recipe_id])
#comment = #recipe.comments.new(comment_params)
if #comment.save
redirect_to #recipe
else
render :new
end
end

create.js.erb is called only one time after form submit

I have a simple_form_for in my project. My goal is to alert people when their email is already in database thanks to an alert and ajax to reload the form if there is an error. When I send my form for the first time, I get the good respond from create.js.erb. But if I send the form a second time without reloading the page with the browser, my controller create method code is taken into account but not my create.js.erb code. For example, if I put an alert in the create.js.erb file, it doesn't pop the second time I send the form by clicking on the button again. Can you help me to figure why ?
pages_controller.rb
def create
#email = waiting_list_params[:email]
#waiting_user = WaitingList.new(waiting_list_params)
respond_to do |format|
if #waiting_user.save
UserMailer.welcome(#email).deliver_now
format.html { redirect_to concours_path }
format.js
else
format.html { render 'pages/concours' }
format.js { flash[:error] = #waiting_user.errors.full_messages[0] }
end
end
end
create.js.erb
function refreshForm(innerHTML) {
const newForm = document.getElementById("concours-form");
newForm.innerHTML = innerHTML;
}
<% if #waiting_user.errors.any? %>
refreshForm('<%= j render "pages/concours_form" %>');
confPopup.style.display = "none";
<% else %>
confPopup.style.display = "flex";
<% end %>
_form.html.erb
<%= simple_form_for #waiting_user, {id: 'concours-form', url: concours_path, remote: true} do |f| %>
<%= f.error_notification %>
<%= f.text_field :email,
class: "landing-form-input",
autocomplete: "off",
required: true,
pattern: '[^#]+#[^#]+\.[a-zA-Z]{2,6}',
placeholder: "Renseignez ton email ici"
%>
<i class="fa fa-envelope"></i>
<%= f.hidden_field :concours_city,
value:'',
id: 'c-choosen-city'
%>
<%= f.hidden_field :source,
value: params[:source]
%>
<%= f.button :submit,
class: "landing-form-button",
value: "Je tente ma chance !"
%>
<% end %>
routes.rb
get 'concours', to: 'pages#concours'
post 'concours', to: 'pages#create'

Inline error messages in a form_for with remote: true

I have a form in a modal that looks like this:
<%= form_for (#change_office_address_), remote: true, format: :json, html: { class: :contact_form } do |f| %>
<div id="error_explanation" style='display:none;' class="bg-danger text-danger alert fade in alert-danger alert-dismissable errors">
<ul>
<% if #change_office_address_.errors.any? %>
<% #change_office_address_.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
</ul>
</div>
<%= f.hidden_field :city_id, value: #office.city.id %>
<%= f.hidden_field :office_id, value: #office.id %>
<%= f.hidden_field :insurer_id, value: #office.insurer.id %>
<%= f.text_field :name, placeholder: 'Name', class: 'form-control' %>
<br>
<%= f.text_field :email, placeholder: 'e-mail', class: 'form-control' %> <br>
<%= f.label :city_name, 'City' %>
<%= f.text_field :city_name, class: 'form-control', value: #office.city.name.mb_chars.titleize, readonly: true %>
<br>
<%= f.label :insurer_name, 'Insurer' %>
<%= f.text_field :insurer_name, class: 'form-control', value: #office.insurer.short_name, readonly: true %>
<br>
<%= f.label :office_name, 'Insurer\'s office address' %>
<%= f.text_field :office_name, class: 'form-control', value: #office.address, readonly: true %>
<br>
<%= f.text_field :edit_office_address, placeholder: 'New address', class: 'form-control' %> <br>
<%= f.text_area :comment, placeholder: 'Comment', class: 'form-control', cols: '30', rows: '5' %> <br>
<div class="text-center">
<%= f.submit 'Inform about deleting', class: 'btn btn-danger' %>
<%= f.submit 'Inform about changing address', class: 'btn btn-default' %>
</div>
<% end %>
After the form was submitted, and validations didn't pass, I can see error messages at the top of my form. But I'd like to show errors inline. I tried to add <span class="help-inline"><%= #change_office_address_.errors[:email] %></span> into my form, but it doesn't work.
A controller:
class ChangeOfficeAddressesController < ApplicationController
def create
#change_office_address = ChangeOfficeAddress.new(change_office_addresses_params)
respond_to do |format|
if params[:commit] == 'Inform about changing address'
if #change_office_address.save
format.html { ChangeOfficeAddressMailer.change_office_address_new(#change_office_addres).deliver_now
redirect_to :back, notice: 'Thanks.' }
format.json { redirect_to :back, status: :created, location: #change_office_address,
notice: Thanks.' }
else
format.json { render json: #change_office_address.errors.full_messages, status: :unprocessable_entity }
end
elsif params[:commit] == 'Inform about changing address'
#change_office_address.delete_office_address = 'Some text'
#change_office_address.edit_office_address = nil
if #change_office_address.save
format.html { ChangeOfficeAddressMailer.change_office_address_new(#change_office_addres).deliver_now
redirect_to :back, notice: 'Thanks.' }
format.json { redirect_to :back, status: :created, location: #change_office_address,
notice: 'Thanks.' }
else
format.json { render json: #change_office_address.errors.full_messages, status: :unprocessable_entity }
end
else
if #change_office_address.save
format.html { ChangeOfficeAddressMailer.change_office_address_new(#change_office_addres).deliver_now
redirect_to :back, notice: 'Thanks.' }
format.json { redirect_to :back, status: :created, location: #change_office_address,
notice: 'Thanks.' }
else
format.json { render json: #change_office_address.errors.full_messages, status: :unprocessable_entity }
end
end
end
end
private
def change_office_addresses_params
params.require(:change_office_address).permit(:email, :name, :edit_office_address, :add_office_address,
:delete_office_address, :office_id, :insurer_id, :city_id, :comment)
end
end
And application.js:
$(document).ready(function() {
return $(document).bind("ajaxError", "form.contact_form", function(event, jqxhr) {
var $contact_form, $error_container, $error_container_ul;
$contact_form = $(event.data);
$error_container = $("#error_explanation", $contact_form);
$error_container_ul = $("ul", $error_container).empty();
if ($error_container.is(":hidden")) {
$error_container.show();
} else {
$("#error_explanation").remove("#error_explanation");
}
return $.each(jqxhr.responseJSON, function(index, message) {
return $("<li>").html(message).appendTo($error_container_ul);
});
});
});
Is there any way to add those error messages? Thanks.
To get inline validations, I would use Jquery Validate and validate on your change_office_address model.
When I had to do something similar I used this SO post as reference, which I paraphrase after:
How to use jquery validation plugin in rails
First include the jquery validation file in your layout, which can be found here:
https://jqueryvalidation.org/
Then in your form add a class to the required fields such as <%= f.text_field :firstname, :class => "required" %>. Then write a method with the form_id to trigger the validation.
function validateofficeFuction() {
$("#change_office_form").validate({
errorClass: "authError"
})
}
$(document).ready(validateofficeFuction);
$(document).on('page:load', validateofficeFuction);
If you're using turbolinks 5, which is included in rails 5 instead of using $(document).ready use $(document).on "turbolinks:load"
If this is set up properly, you should get your error messages right underneath each field that failed instead at the top of the page or form.

'No implicit conversion of string into integer' with double nested form

Here is my form:
<%= simple_form_for #item do |item_builder| %>
<div class="well">
<%= item_builder.input :name %>
<%= item_builder.input :description, as: :text %>
<%= item_builder.input :tag_list %>
<%= item_builder.simple_fields_for :user_items do |user_item_builder| %>
<%= user_item_builder.input :foo, as: :hidden, input_html: { value: "bar" } %>
<%= user_item_builder.simple_fields_for :user_item_images do |user_item_images_builder| %>
<%= user_item_images_builder.input :picture, as: :file,
input_html: { multiple: true,
name: "item[user_items_attributes][user_item_images_attributes][][picture]" } %>
<% end %>
<% end %>
</div>
<div class="clearfix">
<%= item_builder.submit 'Submit new item request'%>
</div>
<% end %>
and my items_controller
def new
#item = Item.new
#user_item = #item.user_items.build
#user_item.user_item_images.build
end
def create
#item = Item.new item_params
#item.user_items.first.user_id = current_user.id
if #item.save
redirect_to items_path, notice: "Thank you for your item request!"
else
render :new
end
end
def item_params
params.require(:item).permit(:name, :description, :tag_list,
user_items_attributes: [:item_id,
user_item_images_attributes: [:user_item_id, :picture]]).merge(created_by: current_user.id, status: Item::STATUS[:pending])
end
I am getting an error no implicit conversion of String into Integer that points to the first line in my create action. item_params is undefined in the webconsole. Any ideas where my error might be?

Pass multiple selected parameters to a submission link

I have a setup attempting to automate submission to a backend. There are 3 different dropdown boxes with user-selectable parameters. I set up my website like this (all 'scaffolds'):
class Xsearch < ActiveRecord::Base
has_many :xentry
has_many :spectrafile
has_many :parameter
end
Each entry/file/parameter has the belongs_to :xsearch in their model.
The index.html.erb of the xsearch has the following:
<h1>Spectra Submitter</h1>
<h2>Select a database to search</h2>
<%= collection_select("xpost", :id, Xentry.all(), :title, :name ) %>
<h2>Select a parameter file to use</h2>
<%= collection_select(:ppost, :id, Parameter.all(), :name, :name ) %>
<h2>Select a Spectra file (or folder) to search</h2>
<%= collection_select(:spost, :id, Spectrafile.all(), :name, :name ) %>
<%= link_to "Search", :controller => "xsearches", :action => "search" %>
I've tried using submit_tag as well. I have no idea how to trigger a method in my controller (if it should be in the controller? Since it's from the browser I assumed to put it here) that has the selected values from each collection_select.
So in short: How can I take the selected values for each collection_select and pass them to a function? Where should that function be placed (in the controller or in the .html.erb file)?
Thanks, this has been driving me crazy.
I ended up solving the issue by moving to a form_new method. Here's my code for anyone else who is searching. The purpose of this script was to allow other users to submit a search to a cluster through an authorized user.
<%= form_for(#xsearch) do |f| %>
<% if #xsearch.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#xsearch.errors.count, "error") %> prohibited this xsearch from being saved:</h2>
<ul>
<% #xsearch.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<h2>Pick A Database To Search</h2>
<%= collection_select(:db, :id, #all_entries, :id, :name) %>
<h2>Pick A Parameter File To Use</h2>
<%= collection_select(:para, :id, #all_parameters, :id, :name) %>
<h2>Pick a File or Folder To Search</h2>
<%= collection_select(:sfile, :id, #all_spectrafiles, :id, :name) %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And the relevant bit in the controller's create: method
def create
#database = Xentry.find(params[:db][:id])
#paramfile = Parameter.find(params[:para][:id])
#spectrafile = Spectrafile.find(params[:sfile][:id])
#recurse = params[:recurse]
#email = params[:emailtag]
if #email.empty?
#email = "None"
end
if (#recurse == 'Yes')
#recurse = 1
else
#recurse = 0
end
#recurse = 0#disabled until kill methods are implemented
#hostname = hostname
#username = username
Net::SSH.start(#hostname, #username) do |session|
session.exec('source .bashrc')
session.exec('sh rscript.sh %s %s %s %s %d' % [#database['name'], #spectrafile['name'], #paramfile['name'], #email, #recurse])
#xsearch = Xsearch.new(:database => #database['name'], :parameters => #paramfile['name'], :spectra => #spectrafile['name'])
respond_to do |format|
if #xsearch.save
format.html { redirect_to #xsearch, notice: 'Search was successfully created.' }
format.json { render json: #xsearch, status: :created, location: #xsearch }
else
format.html { render action: "new" }
format.json { render json: #xsearch.errors, status: :unprocessable_entity }
end
end
end
end

Resources