How to render a modal after failed update in Rails 5.2? - ruby-on-rails

I use a modal window to edit a schedule from the schedules index. Each schedule provides a link to the edit method:
<%= link_to edit_scheduler_production_schedule_path(schedule),
title: t('.Edit'),
data: { toggle: "modal", target: "#childModal" },
class: "mat-icon-button mat-button-base mat-secondary" do %>
<span class="fa fa-edit"></span>
<% end %>
The edit method from ProductionSchedulesController is quite simple:
# GET /production_schedules/1/edit
def edit
render layout: false
end
So is the edit.html.erb file:
<% provide :childModalTitle do %>
<%= t('.Edit') %>
<% end %>
<%= render 'form' %>
And the edit form is quite light too:
<%= form_with model: [namespace, #production_job, #production_schedule], html: {id: "edit_form"} do |f| %>
<div class="modal-header">
<h5 class="modal-title" id="editScheduleModalLabel">
<%= t('scheduler.production_schedules.edit.Edit') %>
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<%= render partial: "shared/error_list", locals: { errors: #production_schedule.errors } %>
<section class="tabbable" id="schedule_information">
---
</section>
</div>
<div class="modal-footer">
<button type="button" class="mat-stroked-button mat-button-base" data-dismiss="modal">
<%= t('Cancel') %>
</button>
<button class="mat-flat-button mat-button-base mat-primary">
<%= t('Submit') %>
</button>
</div>
<% end %>
The update method is the place where I get frustrated:
# PATCH/PUT /production_schedules/1 or /production_schedules/1.json
def update
#production_job = ProductionJob.find(#production_schedule.production_job_id)
Rails.logger.interactions.info "Schedule update - id: #{#production_schedule.id},
code: #{#production_schedule.code},
mode: #{#production_schedule.mode.code}"
if #production_schedule.mode.code == 'TEST' or #production_schedule.mode.code == 'MESSAGE'
#production_schedule.next_run = nil
end
respond_to do |format|
if #production_schedule.update(production_schedule_params)
json_parameters_serialization(#production_schedule)
#production_schedule.update_attribute(:status_id, statuses.find { |x| x["code"] == "READY" }.id || 0)
format.html { redirect_to scheduler_production_job_path(#production_job), notice: "Production schedule was successfully updated." }
format.json { render :show, status: :ok, location: #production_schedule }
else
#production_schedule.update_attribute(:status_id, statuses.find { |x| x["code"] == "INACTIVE" }.id || 0)
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #production_schedule.errors, status: :unprocessable_entity }
end
end
end
If the update fails, edit view is rendered, but not in a modal.
How can I re-render this form in a modal when the update method rejects the update?
Thanks a lot!

In Rails 6.0 and 5.2, all forms using form_with implement remote: true by default. These forms will submit data using an XHR (Ajax) request. So, you need to respond to the request in the same format.
Update modal body of edit_form (Wrapping error messages in div)
<div class="modal-body">
<div id="errorList">
<%= render partial: "shared/error_list", locals: { errors: #production_schedule.errors } %>
</div>
</div>
Add update.js.erb
<% if (!#production_schedule.errors.present?) %>
window.location.reload(); # To refresh the page.
<% else %> # To render error messages in modal when update failed.
$("#errorList").html('<%= escape_javascript( render(partial: "shared/error_list", locals: { errors: #production_schedule.errors) ) %>');
<% end %>

Related

Rails 7 turbo-stream broadcast to specific channel not working

I have a Post model and Comment, and Post has_many Comment and I wanna real-timely update the comment when you are in a specific post, meaning, the comments live-updating should only visible when multiple users are in the same post, for example: in posts/6
For View I have:
<div>
<p>
<strong>Title:</strong>
<%= post.title %>
</p>
<p>
<strong>Content:</strong>
<%= post.content %>
</p>
<p>
<strong>User:</strong>
<%= post.user.username %>
</p>
</div>
<hr/>
<%= form_for [post, Comment.new] do |f| %>
Body: <%= f.text_area :body, rows: 5, cols: 20 %>
<%= f.submit "Submit" %>
<% end %>
<hr/>
<h1>Comments:</h1>
<%= turbo_stream_from post %>
<%= turbo_frame_tag post do %>
<div id="<%= dom_id(post) %>">
<% post.comments.includes(:user).each do |comment| %>
<%= render comment %>
<% end %>
</div>
<% end %>
In Comment#create action I have:
post = Post.find(params[:post_id])
#comment = post.comments.new(comment_params)
#comment.user_id = current_user.id
respond_to do |format|
if #comment.save
format.turbo_stream { render turbo_stream: turbo_stream.prepend("post_#{post.id}", partial: "comments/comment", locals: { comment: #comment }) }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
In Comment model I have:
after_create_commit {
broadcast_prepend_to(target: "post_#{self.post.id}")
}
And in terminal I can see
[ActionCable] Broadcasting to : "<turbo-stream action=\"prepend\" target=\"post_6\"><template> <div>\n <p>\n <strong>Title:</strong>\n adsd\n </p>\n\n <p>\n <strong>Content:</strong>\n Asda\n </p>\n\n <p>\n <strong>User:</strong>\n Super Saiyan Vegeta\n </p>\n </div>\n\n <hr/>\...
Which is correct, but the comments list in a specific post still not updating, and I do see I have id="post_6" in the view...can someone tell me why? Thanks.
Try to change
<div id="<%= dom_id(post) %>">
to
<div id="post_<%= post.id %>">
And use such method in controller
if #comment.save
#comment.broadcast_prepend_to to post, target: "post_#{post.id}", partial: "comments/comment", locals: { comment: #comment }
end
instead of broadcast from model

AJAX not working in Rails same-page update

I am trying to build a Q&A app with Ajax on the Index.html.erb. I manage to get the form remotely loading, but when saving the records, the AJAX does not work and the user is taken to the normal show.html.erb. Apart from the Ajax not kicking off, everything works well.
My code is as below:
index.html.erb (Contain a partial for input, and a partial for results)
<div>
<h3 class="section_title"> Q&A </h3>
<hr>
<div id="qanda-form" style="display:none;"> </div>
</div>
<div id="qandas">
<%= render 'qandas/qanda' %>
</div>
_qanda.html.erb (is the partial for results)
<% #qandas.each do |my_qanda| %>
<div class="col-md-9">
<div>
Created <%= local_time(my_qanda.created_at) %>, by <%= User.find_by(id: my_qanda.user_id).full_name %>
</div>
</div>
<% end %>
_form.html.erb (is the input form - has nested form via Cocoon)
<%= simple_form_for #qanda, remote: true do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div class="col-md-12 form-inputs">
<div class="col-md-8">
<%= f.input :title, label: 'Q&A Title:' %>
</div>
</div>
<div class="qandasquestions">
<%= f.simple_fields_for :qandasquestions do |builder| %>
<% render 'qandas/qandasquestion_fields', f: builder %>
<% end %>
<div class="links btn-group" style="min-height: 34px !important">
<%= f.button :submit, "Publish Q&A", class: "btn btn-default" %>
<%= link_to_add_association 'Add Question', f, :qandasquestions, class: 'btn btn-default', data: {association_insertion_node: '.qandasquestions', association_insertion_method: :append} %>
<%= link_to 'Back', qandas_path, class: "btn btn-default" %>
<%= f.input :company, :as => :hidden, :input_html => {:value => current_user.company} %>
</div>
</div>
<% end %>
Controller:
def index
#qandas = Qanda.all
respond_to do |format|
#qandas = Qanda.all
format.html
format.json
end
end
def create
#qanda = current_user.qandas.build(qanda_params)
respond_to do |format|
if #qanda.save!
#qandas = Qanda.all
format.html { redirect_to #qanda, notice: 'Qanda was successfully created.' }
format.json {render :layout => false}
else
format.html { render :new }
format.json { render json: #qanda.errors, status: :unprocessable_entity }
end
end
end
create.js.erb
$('#qandas').html("<%= j render partial: 'qandas/qanda' %>");
$('#qanda-form').slideUp(350);
new.js.erb
$('#qanda-form').html("<%= j render 'qandas/form' %>");
$('#qanda-form').slideDown(350);
Anybody can see why the Ajax does not kick off please? why am I redirected to the traditional SHOW page please?
Try updating your code to this and let me know if it's working?
def create
#qanda = current_user.qandas.build(qanda_params)
if #qanda.save!
#qandas = Qanda.all
else
#errors = #qanda.errors
end
end

undefined method `hirahana_value' for nil:NilClass

I try to understand what my buddy has coded to call in a show#view some instance variable. I have a problem to understand his code and call the method hirahana_value.
Here is my controller
class SymbolesController < ApplicationController
before_action :set_symbole, only: [:show, :edit, :update, :destroy, :load_form, :load_attributes_form]
def index
#symboles = Symbole.all
end
def show
end
def new
#symbole = Symbole.new
end
def edit
end
def create
#symbole = Symbole.new()
if symbole_params[:symbole_type].present?
#symbole.build_kanji_attribute if symbole_params[:symbole_type] == "kanji"
#symbole.build_hiragana_and_katagana_attribute if symbole_params[:symbole_type] == "hiragana_and_katagana"
end
#symbole.assign_attributes(symbole_params)
respond_to do |format|
if #symbole.save
format.html { redirect_to #symbole, notice: 'Symbole was successfully created.' }
format.json { render :show, status: :created, location: #symbole }
else
format.html { render :new }
format.json { render json: #symbole.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #symbole.update(symbole_params)
format.html { redirect_to #symbole, notice: 'Symbole was successfully updated.' }
format.json { render :show, status: :ok, location: #symbole }
else
format.html { render :edit }
format.json { render json: #symbole.errors, status: :unprocessable_entity }
end
end
end
def destroy
#symbole.destroy
respond_to do |format|
format.html { redirect_to symboles_url, notice: 'Symbole was successfully destroyed.' }
format.json { head :no_content }
end
end
def load_form
#symbole = Symbole.new if #symbole.nil?
if params[:form_name].present?
respond_to do |format|
format.js { render "ajax_form_"+params[:form_name] }
end
end
end
def load_attributes_form
#symbole = Symbole.new if #symbole.nil?
if params[:form_name].present?
respond_to do |format|
format.js { render "ajax_form_"+params[:form_name] }
end
end
end
private
def set_symbole
if params[:id].present?
#symbole = Symbole.find(params[:id])
else
#symbole = Symbole.new
end
end
def symbole_params
params.require(:symbole).permit(:lang, :example_fr, :symbole_type, :css_class, kanji_attribute_attributes: [:value,:concept, :fr],
hiragana_and_katagana_attribute_attributes: [:hirahana_value,:katagana_value, :fr]
)
end
end
And here is the new#view to create my object
<%= simple_form_for #symbole, :defaults => {:required => true} do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.hidden_field :lang, value: "Japonais"%>
<%= f.select :symbole_type, options_for_select([["Kanji", "kanji"], ["Hiragana/Katagana", "hiragana_and_katagana"]]) , {:prompt => "Type de symbole", selected: #symbole.symbole_type}, {required: true} %>
<div id="specific_attributes">
</div>
<%= f.input :example_fr, placeholder: "Exemple", label: "Exemple" %>
<%= f.input :css_class, placeholder: "Nom de la classe css" , label: "Classe"%>
</div>
<div class="form-actions">
<%= f.button :submit, "Valider" %>
</div>
<% end %>
here is the show#view, I just want to call
<ul class="list-inline text-center card-frame">
<li>
<div class="card">
<div class="front">
<!-- PARAMETRER LE RESPONSIVE BOOTSTRAP -->
<!-- <div class="col-sm- col-xs-4 col-md-3"> -->
<div class="card-hiragana hiragana-<%=#hiragana_and_katagana_attributes.hirahana_value.downcase.last%>">
<h1><b><%= #hiragana_and_katagana_attributes.hirahana_value %></b></h1>
</div>
<div class="card-katakana">
<p><%= #hiragana_and_katagana_attributes.hirahana_value %></p>
</div>
<!-- </div> -->
</div>
<div class="back">
<div class="col-sm-3 col-xs-4 col-md-3 containerbackcards-<%=#hiragana_and_katagana_attributes.hirahana_value.downcase.last%>">
<div class="backcard-hiragana">
<h1><b><%= #hiragana_and_katagana_attributes.hirahana_value %></b></h1>
</div>
<div class="card-bigletter">
<p><%= #hiragana_and_katagana_attributes.hirahana_value %></p>
</div>
</div>
</div>
</div>
</li>
</ul>
<div class="text-center buttons-view-post">
<% if current_user.try(:admin?) %>
<%= link_to "Modifier l'hiragana", edit_hiragana_path(#hiragana), class: "btn btn-default btn-md" %>
<% end %>
<%= link_to "Voir tous les hiraganas", hiraganas_path, class: "btn btn-default btn-md" %>
</div>
</div>
</div>
</div>
</div>
The controller instance variable #hiragana_and_katagana_attributes has not been given a value within the context of the #show action, at least not in a way that's immediately visible. Wherever you are expecting this object to get instantiated, that's not happening.

Rails don't render layout when create record with Ajax

My problem is after I create a new record with ajax I get a NOMETHODERROR and I must reload the page to see the new record.
The Error I get in terminal.
Rendered time_entries/_time_entry.html.erb (3.4ms) Rendered
time_entries/create.js.erb (4.9ms) Completed 500 Internal Server Error
in 41ms (ActiveRecord: 4.4ms)
NoMethodError - undefined method `each' for nil:NilClass:
app/views/time_entries/_time_entry.html.erb:1:in '_app_views_time_entries__time_entry_html_erb___4086905375499854267_70174828432680'
What I do wrong?
time_entries_controller.erb
class TimeEntriesController < ApplicationController
...
respond_to :html, :js
def index
#time_entries = current_user.time_entries.all.order("date DESC")
#time_entries_days = #time_entries.group_by{ |t| t.date.beginning_of_day }
respond_with(#time_entry)
end
def create
#time_entry = TimeEntry.new(timeentry_params)
#time_entry.user_id = current_user.id
respond_to do |format|
if #time_entry.save
format.html { redirect_to #time_entry, notice: 'Arbeitszeit wurde eingetragen' }
format.json { render :show, status: :created, location: #time_entry }
format.js
else
format.html { render :new }
format.json { render json: #time_entry.errors, status: :unprocessable_entity }
format.js
end
end
end
...
_time_entry.html.erb
the partial
<% #time_entries_days.each do |day, time_entries| %>
<% if day.today? %>
<div class="column-12 list-group">Today, <%= l day, format: :dm %></div>
<% else %>
<div class="column-12 list-group"><%= l day, format: :dm %></div>
<% end %>
<ul class="lists">
<% time_entries.each do |time_entry| %>
<li class="list-item" id="time_entry_<%= time_entry.id %>">
<div class="list-item__content">
<h3 class="list-item__title">
<%= link_to time_entry.category.name, time_entry %>
<span><%= l time_entry.start_time, format: :hm %></span> - <span><%= l time_entry.end_time, format: :hm %></span>
</h3>
<p><%= time_entry.note %></p>
</div>
<span class="list-item__label"><%= time_entry.hours %>h</span>
</li>
<% end %>
</ul>
<% end %>
index.html.erb
<% if can? :create, TimeEntry %>
<%= link_to 'Add new Time Entry', new_time_entry_path, remote: true, class: 'btn btn-primary', :data => { :'popup-open' => 'popup-1' } %>
<% end %>
...
<div class="row" id="container_time_entries">
<%= render "time_entry" %>
</div>
create.js.erb for testing
I don't get an alert after I create a new time entry.
alert("HALLO");
create.js.erb normal
$('.modal-bg').hide();
$('.popup').fadeOut(350);
$('#container_time_entries').html("<%= j(render 'time_entry') %>");
You do not set the variable #time_entries_days in your create method, but you need it in your _time_entry.html.erb .
Set it and you should be good to go.

Value update automatically though javascript is off

In my application I am showing all the products like this
store.html.erb
<% #products.each do |product| %>
<div class="entry" >
<%= image_tag(product.image_url) %>
<h3><%= product.title %></h3>
<%= sanitize(product.description) %>
<div class="price_line" >
<span class="price" ><%= number_to_currency(product.price) %></span>
<%= button_to "add to cart", line_items_path(:product_id => product),
:remote => true %>
</div>
</div>
<% end %>
I am sending ajax request with :remote => true and in my line items controller I don't have any .js format
line_items_controller.rb
def create
#cart = current_cart
product = Product.find(params[:product_id])
#line_item = #cart.add_product(product.id)
respond_to do |format|
if #line_item.save
format.html { redirect_to(store_url) }
format.xml { render :xml => #line_item,
:status => :created, :location => #line_item }
else
format.html { render :action => "new" }
format.xml { render :xml => #line_item.errors,
:status => :unprocessable_entity }
end
end
end
What I understand so far is that, since there is no format.js add to cart should not add any product to the cart and nothing should happen. But when I add .js format in the controller
format.js { #current_item = #line_item }
and update the page I see that add to cart worked behind but did not show the result. after updating the page I see 15 or so items in the cart. How is this happening?
Make changes here
<% #products.each do |product| %>
<div class="entry" >
<%= image_tag(product.image_url) %>
<h3><%= product.title %></h3>
<%= sanitize(product.description) %>
<div class="price_line" >
<span class="price" ><%= number_to_currency(product.price) %></span>
<%= button_to "add to cart", line_items_path(:product_id => product.id),
:remote => true %> #changes line
</div>
</div>
<% end %>
and for update you have to make .js file for that and update elements and onject of html page through jquery or javascript.
And use only one format so controller method go to one responsive area.

Resources