Rails bootstrap modal client side validation - ruby-on-rails

I have a bootstrap modal with ajax:
<%= link_to 'Question', new_question_path, remote: true, class: 'btn btn-info' %>
_form_modal.html.erb
<div class="modal fade" id="form_modal" tabindex='-1'>
<%= simple_form_for #question, remote: true, validate: true do |f| %>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Question</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<div class="form-inputs">
<%= f.input :content, label: false %>
</div>
</div>
<div class="modal-footer">
<%= f.button :submit, "Ask", class:" btn btn-primary" %>
</div>
</div>
</div>
<% end %>
</div>
new.js.erb
$('#remotecontainer').html('<%= j render "questions/form_modal", locals: { product: #product } %>');
$('#form_modal').on('shown.bs.modal', function() {
$('form[data-validate]').enableClientSideValidations();
}).modal();
I'm accessing the modal through this: <div id='remotecontainer'></div>
The controller method:
def new
#question = Question.new
end
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to root_path, notice: "Question was successfully sent!" }
else
format.js { render 'new.js.erb'}
end
end
end
I have a validation inside the model: validates_length_of :content, maximum: 70
And I'm using the following two gems for the validations errors:
gem 'client_side_validations'
gem 'client_side_validations-simple_form'
The modal appears and if I submit the form with less then 70 characters the questions is created. But on the other hand if I type more then 70 characters, the validation error appears and something weird happens... If I type and re-submit the form with less then 70 characters it continues to give me the validation error and doesn't allow me to submit the form. I might be missing something here... So any ideas on how to fix this?
Update
Ok I tried this.... I added the exclamation mark to the controller create method: if #question.save! And I get the validation error at the console. The validation error doesnt appear on the modal at all. And then when I try to submit the form with lesser characters then 70, it goes through. So from my understanding something wrong with the two gems setup. Can anybody please give some help with this one?
(0.2ms) ROLLBACK
↳ app/controllers/questions_controller.rb:14
Completed 422 Unprocessable Entity in 10ms (ActiveRecord: 1.6ms)
ActiveRecord::RecordInvalid (Validation failed: Content is too long (maximum is 70 characters)):

I needed up putting this inside my modal:
<div id="formModal">
<%= render partial: 'layouts/messages' %>
</div>
This in as my controller method:
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
flash.now[:success] = "Question was successfully sent!"
format.js { render 'create.js.erb' }
else
flash.now[:alert] = "Something went wrong!"
format.js { render 'create.js.erb' }
end
end
end
and this inside create.js.erb
$("#formModal").html("<%= escape_javascript(render partial: 'layouts/messages') %>");

Related

Ruby on Rails 7 + Turbo - Not showing notice message

I am learning Ruby on Rails. Rails 7 + Turbo stream... I am following a tutorial and I am having a problem with displaying the Notice message.
Can you help me understand why I am not getting notice message after I create a new quote?
Here is a create action in QoutesController.rb. Only when I put this line of code:
flash[:notice] = "Gets displayed with this line and a refresh... "
before the format.html line, then I will get the notice message after I refresh the page upon creating a quote. Only then the message gets displayed.
Can you please help me understand this?
(Yes, I am using a Devise gem here, Turbo stream, Turbo Rails, Rails 7)
Thank you
def create
#quote = current_company.quotes.build(quote_params)
if #quote.save
respond_to do |format|
flash[:notice] = "Gets displayed with this line and a refresh... " #weird
format.html { redirect_to quotes_path, notice: "Quote was successfully created." }
format.turbo_stream
#debugger
end
else
render :new, status: :unprocessable_entity
end
end
In Rails 7, unrefresh page when you create successfully. So, flash[:notice] not display.
Create views/shared
In share folder, we have _flash.html.erb, _notices.html.erb
_flash.html.erb
<div class="alert <%= bootstrap_class_for(msg_type) %> alert-dismissible fade show" role="alert">
<div class="container text-center">
<%= message %>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div>
_notices.html.erb
<div id="flash">
<% flash.each do |msg_type, message| %>
<%= render partial: "shared/flash", locals: { msg_type: msg_type, message: message } %>
<% end %>
</div>
When you create success -> create views/qoutes/create.turbo_stream.erb
<%= turbo_stream.update "flash", partial: "shared/flash", locals: { msg_type: :notice, message: "your message" } %>
// msg_type: :notice, :alert, :error, :success
In views/layouts/application.html.erb
<%= render 'shared/notices' %>
<%= yield %>
In application_helper
module ApplicationHelper
def bootstrap_class_for(flash_type)
{
success: "alert-success",
error: "alert-danger",
alert: "alert-warning",
notice: "alert-info"
}.stringify_keys[flash_type.to_s] || flash_type.to_s
end
end
and if create failed
ex:
def create
if #object.save
// code
else
render_flash(:alert, full_messages(#post.errors.full_messages))
end
end
In application_controller
class ApplicationController < ActionController::Base
protected
def render_flash type, message
render turbo_stream: turbo_stream.update("flash", partial: "shared/flash", locals: { msg_type: type, message: message })
end
def full_messages messages
messages.join("\n")
end
end
=> This is my way. Hope to help you.

rails: redirect_to rendering but not reloading new page

I would like my application to show a list of all associated objects when one is updated, so I would like to load the index action after an update is completed.
I have tried formatting the block, removing the format, render, and redirect_to. All of them just remain on the edit page
Update action:
def update
respond_to do |format|
if #business_category.update_attributes(business_category_params)
format.html {redirect_to admin_business_categories_path}
return
end
end
end
edit view:
<div class="container">
<div class="row">
<div class="col-xs-12">
<%= link_to 'Back to categories', admin_business_categories_path %>
</div><!-- .col -->
<%= simple_form_for(#business_category, url: admin_business_category_path(#business_category), remote: true, html: { class: '' }) do |f| %>
<%= render 'form', f: f %>
<% end %>
</div><!-- .row -->
</div><!-- .container -->
_form partial:
<div class="col-xs-12 col-sm-10 col-md-10">
<%= f.input :name, label: 'Category Name' %>
</div>
<div class="col-xs-12 col-sm-2 col-md-2">
<div class="btn-group-vertical" role="group" aria-label="...">
<button id="businessCategoryCancelButton" class="btn btn-warning">CANCEL</button>
<%= f.submit 'SAVE', class: 'btn btn-success' %>
<br>
</div>
</div>
at the redirect_to a message appears in the console:
No template found for Admin::BusinessCategoriesController#update, rendering head :no_content
Completed 204 No Content in 1505ms (ActiveRecord: 1.0ms)
I don't know why it is looking for the update template or why it is not redirecting to the index action
I am trying to understand how format works and if/why it would be conflicting with the redirect. Any advice would be helpful
Because you are issuing an AJAX call (remote: true on your form).
You have the following options:
Add format.js to the controller
Remove remote: true from the form definition and the respond_to from the controller:
def update
if #business_category.update_attributes(business_category_params)
redirect_to admin_business_categories_path
else
render :edit
end
end
simple_form_for(#business_category, url: admin_business_category_path(#business_category), remote: true, html: { class: '' })
In your form you've mentioned remote: true. It process your request as a JS request. In your controller you've mentioned format.html {redirect_to admin_business_categories_path} but it will process it as format.js and look for update.js.erb file to handle the response because your format of request is 'JS' instead of 'HTML', hence it's showing an error.
You'll have to send the request as an HTML request.
According to your implementation. I think you just want to redirect in case of success and render edit page again in case of error.
You'll have to make 2 changes.
Remove remote: true from form as it doesn't match your requirement
Add the line format.html { render :edit } in else case of update_attributes()

Rails 4 - How to properly set up a validation action on the controller?

I have LDAP authentication through Devise, and before I create an user on the database (with the additional information due to business rules involved on the User), I'd like to collect some other information present on the company's servers.
I have a user_query action so that I can display the following modal:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Find User by Email</h3>
</div>
<div class="modal-body">
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
<%= form_tag('/users/new', method: :get, remote: true) do -%>
<%= text_field_tag 'user_email', nil, placeholder: 'Insert user email', class: 'form-control' %>
<%= submit_tag 'Get user information', remote: true, class: 'btn btn-default' %>
<% end -%>
</div>
</div>
</div>
It has a text box where the user inputs the email to query the server and check whether that user exists, if it does, redirect to the new User action, sending the found user information, else, I'd like it to display an error stating that the informed user email could not be found.
The action I created to do this validation after the user has informed the email, is the authenticate_user_email, as follows:
def authenticate_user_email
respond_to do |format|
if authenticated_user = Devise::LDAP::Adapter.get_ldap_entry(params[:user_email])
format.html { redirect_to action: :new, authenticated_user: authenticated_user }
else
format.json { render json: 'User email not found', status: :not_found }
end
end
end
However, I could not get this to work setting the authenticate_user_email as a after_action of user_query, nor do I know if this is the correct approach to do so, which is why I ask, what is the correct way to set up this validation (keeping in mind that every action is rendered within a modal)?
Thanks.
Change the if condition in authenticate_user_email method from
authenticated_user = Devise::LDAP::Adapter.get_ldap_entry(params[:user_email])
to
authenticated_user == Devise::LDAP::Adapter.get_ldap_entry(params[:user_email])
Also , have a look at Active Record Validation as #lurker suggested

Rails - unsuccessful edit in modal on index site

I have a list of group and I want to edit a single group name via modal. I use edit.js.erb file and bootstrap modal.
There are two cases, both wrong.
First:
Using "remote: true" in Partial and link_to(in single row file)
Then modal is displaying, but after SAVE button clicked always is rendering again and never close. (I don't know why)
Second:
Delete remote:true from partial.
Then modal is displaying, I can even save the changes. But when I type wrong name (blank or too long), line "render 'edit'" from Controller crash. (error: "Missing template groups/edit, application/edit...". I think rails doesn't know about edit.js.erb then, but how to repair it?
Have you got any ideas about this situation ?
Controller
def edit
#group = current_user.groups.find_by(id: params[:id])
respond_to do |format|
format.js # actually means: if the client ask for js -> return file.js
end
end
def update
#group = current_user.groups.find_by(id: params[:id])
if #group.update_attributes(group_params)
flash[:success] = "Nazwa grupy została zmieniona"
redirect_to groups_path
else
render 'edit'
end
end
Partial for displaying form_for in modal
<%= form_for(#group, remote: true) do |f| %>
<div>
<%= render 'shared/error2_messages', object: f.object%>
<p>
<%= f.label :name, "Nazwa" %>
<%= f.text_field :name, class: 'form-control'%>
</p>
</div>
<%= f.submit yield(:button_name), class: "btn btn-primary" %>
<% end %>
edit.js.erb file
<% provide(:button_name, 'Zapisz zmiany') %>
$('.modal-title').html("Edytuj nazwę grupy");
$('.modal-body').html("<%= escape_javascript( render partial: 'layouts/group_data_form', locals: {group: #group} ) %>");
$('#myModal').modal();
Single row in a list
<li id="group-<%= group.id %>" class="list-group-item">
<span class="group-name"><%= group.name %></span>
<%= link_to edit_group_path(group.id), remote: true, :class => "edit-option btn" do %>
<i class="fa fa-pencil-square-o fa-2x" ></i>
<% end %>
<%= link_to group, method: :delete, data: { confirm: "Na pewno chcesz usunąć tę grupę?" }, :class => "delete-option btn btn-danger" do %>
<i class="fa fa-trash-o" > usuń</i>
<% end %>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Zmień nazwę grupy</h4>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Anuluj</button>
</div>
</div>
</div>
</div>
This looks like a follow-up to your question of yesterday.
I won't say both your approaches are wrong, I'll say you are conbining two right approaches in such a way...
(1) since you used js to send save (with the use of remote: true on your form), all you need to do is to close the modal on successful save as will be defined from update.js.erb (just like you defined an edit.js.erb for the format.js in edit) as follow:
#update.js.erb
<% if #group.errors.empty? %>
$('.modal-body').html("");
$('#myModal').modal('hide');
$("#notice").html("Group was successfully updated.");
<% else %>
$("#notice").html("Error! Group not updated.");
<% end %>
#controller update action
def update
#group = current_user.groups.find_by(id: params[:id])
#group.update_attributes(group_params)
respond_to do |format|
format.html
format.js
end
end
What you essentially did here is to check if there is no error in the #group object(this will be true if #group got saved), then emptied your Modal and then hid it from view.
(2) When you removed remote: true you are no longer using js, so when you have a wrong name, the update does not work, and the render :edit portion of your update method kicks in. However, your edit action only specifies format.js, So what you may have to do in this case is to add format.html if you want to call with html.
def edit
#group = current_user.groups.find_by(id: params[:id])
respond_to do |format|
format.js # actually means: if the client ask for js -> return file.js
format.html
end
end
Note however that this may not open your modal, it will go to the edit_group_path of your app, which is the /groups/:id/edit

Rendering edit form via ajax

I am attempting to render a form via ajax. It works fine for creating a new venue, but when attempting to edit an existing venue the venue data should load into the form. The form (_modal) is rendered when the edit venue link is called, however the form is processed as a new form, with all fields blank. Calling #venue at the binding.pry, we see the correct venue object.
How would I get the form to process as edit rather than new? Ideas:
respond_to do |format|
format.js { *do stuff* }
Add more parameters to form?
Do it manually - change form attributes and/or fill in data with jquery
Appreciate any suggestions!
_modal.html.erb:
<section id="venue-modal" class='modal' title="Venue Form" class="">
<%= simple_form_for(#venue, remote: true) do |v| %>
<div id='closeButton'><a href='#'>x</a></div>
<% binding.pry %>
<%= v.input :name, label: "Venue Name" %>
<%= v.input :city %>
<%= v.input :state, collection: States.us_states %>
<div class="actions">
<%= v.submit 'Submit', class: 'button' %>
</div>
<% end %>
</section>
Renders following html:
<form accept-charset="UTF-8" action="/venues" class="simple_form new_venue" data-remote="true" id="new_venue" method="post">
Link calling the modal:
<%= link_to edit_venue_path(venue), :remote => true, :class => "edit_venue_link" do %>
Controller:
def edit
id = params[:id]
if id == 'new'
#venue = Venue.new
else
#venue = Venue.find(id)
end
end
Venue.js:
$('.edit_venue_link').on("ajax:success", function(e, data, status, xhr) {
// stuff happens
Replace the modal content with this line of code in modal.js.erb:
$('#venue-modal').html("<%= escape_javascript(render partial: 'venues/modal') %>");
Controller:
def edit
id = params[:id]
if id == 'new'
#venue = Venue.new
else
#venue = Venue.find(id)
end
respond_to do |format|
format.js { render 'venues/modal' }
end
end
Section tags moved out of the modal partial. The form then recognizes #venue and the fields load appropriately.

Resources