Rails - unsuccessful edit in modal on index site - ruby-on-rails

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

Related

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 bootstrap modal client side validation

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') %>");

Rails - Editing item from list by bootstrap modal

I'm new in Rails. I try to make a list of groups (/groups) where I want to have edit and delete option in each row. Editing should be implemented by modal because Group has only one attribute - name. So I don't want to open /groups/number/edit.
My problem is: I don't know how to bind chosen group with form_for. Maybe it isn't good approach indeed.
Modal is displayed, but the name field is blank. When I used debug for #group is blank too. I don't know why.
Here is my code(single group row):
<li id="group-<%= group.id %>" class="list-group-item">
<span class="group-name"><%= group.name %></span>
<%= link_to edit_group_path(group.id), "data-toggle" => "modal", "data-target" => "#myModal", :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>
<%= form_for(#group) do |f| %>
<div class="modal-body">
<%= debug #group %>
<p>
<%= f.label :name, "Nazwa" %>
<%= f.text_field :name, class: 'form-control'%>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Anuluj</button>
<%= f.submit "Zapisz zmiany", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
</div>
Controller
def edit
#group = current_user.groups.find_by(id: params[:id])
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
redirect_to groups_path
end
end
I will assume that the answer to my question above is a yes, and so, I'll just continue to answer.
First thing: You don't want to be repeating different Modals for each group, you want only a single Modal to be used by all the groups.
Second thing: You are very correct with the idea of wanting to send the group.id to the edit method in your controller, for it to return that group back into your Modal
However, what is wrong is how you are trying to achieve this.
<%= link_to edit_group_path(group.id), "data-toggle" => "modal", "data-target" => "#myModal", :class => "edit-option btn" do %>
You are telling your link to do two things when clicked: (1) go to edit path (2) open modal. Which one will take more importance? Right now, it is the modal that is opening, and not the edit path working, which means that you are not getting to the controller at all, so accounting for why your #group is blank.
To achieve this, you will have to tell your link to do only one thing, and when that is successful, do the other.
What I'll advice is to use Ajax (by setting remote: true) on your link to go to the controller, and then open the modal on response.
Here is a breakdown of the steps:
1.Set remote: true to make the call with Ajax
<%= link_to edit_group_path(group.id), remote: true %>
2.Tell controller action (in this case, the edit action) to respond to js
def edit
#group = current_user.groups.find_by(id: params[:id])
format.js
end
Prepare an edit.js.erb file to handle the response (this will be in the same directory which your other view files are)
Populate the form with the #group from inside the response
Put the populated form in the modal
Use Javascript to show the modal
$('.modal-title').html("Edit Group")
$('.modal-body').html("<%= escape_javascript( render partial: 'form', locals: {group: #group} ) %>")
$('#myModal').modal()
As simple and as direct as that. Hope this helps a lot.

Issue getting variable to pass from Controller Action to Mailer template in Ruby on Rails

I'm having trouble getting a variable filled with text to pass from my controller action "Submit", to my email template.
The code listed is to allow you to follow the variable named #message through my app.
To start, what I have is a basic "show" view for my model "ECN". It contains some nested information, but that is not relevant. I'm using twitter bootstrap to create a modal form. My show view file contains the following:
<button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#submitModal">
Submit for Approval
</button>
<!-- Submit Modal -->
<div class="modal fade" id="submitModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">X</button>
<h4 class="modal-title" id="myModalLabel">Edit Email</h4>
</div>
<div class="modal-body">
<%= render 'submit_email_fields' %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<%= button_to "Submit ECN", {action: "submit", :id => #ecn.id}, {class: "btn btn-primary"} %>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
Basically the modal pops up and renders the _submit_email_fields file which is shown next:
<%= form_tag :action => 'submit', :id => #ecn.id %>
<p><label for="email_message"></label><br/>
<%= text_area 'email', 'message', value: render('submitted.text.erb'), class: "span6" %></p>
<%= form_tag %>
At this point, I have created a form with a text_area that is populated with a text file "submitted.text.erb". I have a basic email set up, and when the user presses the button "Submit for Approval" in the show view, the modal pops up with a pre-filled form that they can edit before hitting the Submit ECN button. The information in the text area is then saved into the parameter email[:message].
Next lets look at the submit action in my controller:
def submit
#ecn = Ecn.find(params[:id])
#email = params[:email]
#message = #email[:message]
#email_list = EmailList.all
respond_to do |format|
EcnNotifier.submit_engineering(#ecn, #message).deliver
format.html { redirect_to home_url, alert: "Ecn has been submitted for approval." }
format.json { render json: #ecns }
end
end
This action takes the parameters sets them to instance variables before running my mailer EcnNotifier on them. I've verified that the variable #message contains the full text from the form at this point in time. However, the email sends through with no body. Let's look at the mailer method submit_engineering(#ecn, #message):
def submit_engineering(ecn, message)
#ecn = ecn
#message = message
#email = EmailList.where(department: "Engineering").all
to = []
#email.each do |e|
to.push e.email
end
mail to: to, subject: 'ECN approval', template_name: 'submitted'
end
This should take my message and continue to use it under the name #message. This mailer send out the template named "submitted". This template contains the following:
<%= #message %>
<h1>TEST</h1>
The template should simply print out the contents of #message, however the email sent is empty except for the TEST test I've added to verify the template was being used correctly.
My question is, why is my #message variable not passing correctly from the controller to the final email? Thanks for reading this far and I appreciate your help.

Flash messages appearing twice in rails

My flash messages are appearing twice and my web research tells me this is due to render and redirect displaying the messages. I think I need to use flash.now[] or flash[] somewhere to sort this but I can't work out where it needs to go
guidelines_controller.rb
def update
#guideline = Guideline.find(params[:id])
respond_to do |format|
if #guideline.update_attributes(params[:guideline])
#guideline.update_attribute(:updated_by, current_user.id)
format.html { redirect_to #guideline, notice: 'Guideline was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "show" }
format.json { render json: #guideline.errors, status: :unprocessable_entity }
end
end
end
layouts/application.html.erb
<div class="container">
<% flash.each do |type, message| %>
<div class="alert <%= flash_class type %>">
<button class="close" data-dismiss="alert">x</button>
<%= message %>
</div>
<% end %>
</div>
application_helper.rb
def flash_class(type)
case type
when :alert
"alert-error"
when :notice
"alert-success"
else
""
end
end
guideline_controller.rb
def show
#guideline = Guideline.find(params[:id])
if #guideline.updated_by
#updated = User.find(#guideline.updated_by).profile_name
end
if User.find(#guideline.user_id)
#created = User.find(#guideline.user_id).profile_name
end
respond_to do |format|
format.html # show.html.erb
format.json { render json: #guideline }
end
end
You can do something like this in order to save some lines of code, and display the messages just once:
<%- if flash.any? %>
<%- flash.keys.each do |flash_key| %>
<%- next if flash_key.to_s == 'timedout' %>
<div class="alert-message <%= flash_key %>">
<a class="close" data-dismiss="alert" href="#"> x</a>
<%= flash.discard(flash_key) %>
</div>
<%- end %>
<%- end %>
By using flash.discard, you show the flash message an avoid rendering twice
Just putting this here for anyone else having trouble.
I had flash messages appearing twice because I had something in application.html.erb telling my app to display flash messages, but I had previously generated views with rails generate scaffold posts etc, so that had automatically added the flash messages to all the views.
So the solution was to remove them from the views.
Here's a great tutorial that demonstrates removal for just one model/view
So basically, if you have something like this in your application.html.erb:
<% if notice %>
<p class="alert alert-success"><%= notice %></p>
<% end %>
<% if alert %>
<p class="alert alert-danger"><%= alert %></p>
<% end %>
Then simply remove the equivalent from each of the views. I.e. remove this line from the top of each view
<p id="notice"><%= notice %></p>
I was also having the same issue and also due to another call of <%= render 'shared/alerts' %> later after my check for flash. I liked #rorra's idea of doing a flash_key. But here in the year 2020, it wasn't working as #tessad stated. It would show the message, but not format in bootstrap correctly.
I was able to change their code to work with BootStrap 4. It even dismisses as it is supposed to. The three things that needed to change were all dealing with the class of the div used to display the flash notice.
<div class="alert-message <%= flash_key %>">
alert-message becomes just alert, and the flash_key has to have alert- before it.
<div class="alert alert-<%= flash_key %>">
The last thing is I was sending it to the view from the controller as flash[:notice], which is not a recognized bootstrap alert. When I changed it to flash[:warning] it showed up correctly.
Here is the final code that worked for me. Putting it here in case anyone needs it now 7 years after the initial answer was given.
<div id="container">
<%- if flash.any? %>
<%- flash.keys.each do |flash_key| %>
<%- next if flash_key.to_s == 'timedout' %>
<div class="alert alert-<%= flash_key %>">
<a class="close" data-dismiss="alert" href="#"> x</a>
<%= flash.discard(flash_key) %>
</div>
<%- end %>
<%- end %>
</div>

Resources