I am trying to create a reusable remote modal. The idea is to create a model container and render content with yield
into it and render content when needed.
Note: all HTML
code will be written in HAML
. I am using bootstrap-modal-rails gem to handle my modals.
My modal container layout (not a partial):
%div(class="modal" id="mainModal ajax-modal" tabindex="-1" role="dialog"){:'aria-labelledby'=>"mainModalLabel", :'aria-hidden'=>"true"}
%div(class="c-modal__container")
%div(class="c-modal__layout modal__content")
= yield
In my application.html.haml
, I have a div that defines the modal location as follows:
-# ...
%div(id="modal-holder")
- unless params[:nojs]
= javascript_include_tag 'desktop'
-# ...
Here is where my problem comes from. I have a book controller
where the user can add books. I have a multi-step registration and my new
action have to be my modal. When the user clicks New book
link, I am loading a new modal.
My New book
link:
= link_to "New book", new_book_path, class: "c-link--navbar", data: { modal: true }
CoffeeScript(re-written in jQuery
) for the modal (this code from this article):
$(function() {
var modal_holder_selector, modal_selector;
modal_holder_selector = '#modal-holder';
modal_selector = '.modal';
$(document).on('click', 'a[data-modal]', function() {
var location;
location = $(this).attr('href');
$.get(location, function(data) {
return $(modal_holder_selector).html(data).find(modal_selector).modal();
});
return false;
});
return $(document).on('ajax:success', 'form[data-modal]',
function(event, data, status, xhr) {
var url;
url = xhr.getResponseHeader('Location');
if (url) {
window.location = url;
} else {
$('.modal-backdrop').remove();
$(modal_holder_selector).html(data).find(modal_selector).modal();
}
return false;
});
});
And now my new action
for the yield
that will be loaded inside my remote modal:
= form_for(@book, remote: remote, html: {role: :form}, class: "c-form") do |f|
-# Forms here...
= f.submit "Add a new book", class: "c-btn"
If the user's information correct, my modal will redirect the user to general action
else I want to stay on the same page and flash an error message but the problem is that I am not using a partial
and it automatically redirects me to a new page with new action
.
My books_controller.rb
:
class BooksController < ApplicationController
respond_to :html, :json, :js
...
def index
@books = current_user.books
end
def new
@book = current_user.books.build
respond_modal_with @book
end
def create
@book = current_user.books.build(book_params)
if @book.save
redirect_to general_book_path(@book), notice: "Saved."
else
flash.now[:notice] = "Something went wrong."
render :new
end
end
...
end
Modal Responder:
class ModalResponder < ActionController::Responder
cattr_accessor :modal_layout
self.modal_layout = 'modal'
def render(*args)
options = args.extract_options!
if request.xhr?
options.merge! layout: modal_layout
end
controller.render *args, options
end
def default_render(*args)
render(*args)
end
def redirect_to(options)
if request.xhr?
head :ok, location: controller.url_for(options)
else
controller.redirect_to(options)
end
end
end
I tried to add something like
respond_to do |format|
format.html
format.json { render json: @book }
end
to my create
method but it doesn't work and still redirects me to the new page. Also, I've already asked a similar question and I've noticed that my JavaScript
code works on a separate page. As long as I understand the problem is with my remote modal action page. Can someone please help me to figure out how to stay on the same page when the user entered the wrong input and turn on my javascript?
TBH that's an abstract solution which is not directly related to your question, it works though (bootstrap+jq, based on production code)
Coffe script
$.showModal = (title, body) ->
$("#modal").find(".modal-title").html(title)
$("#modal").find(".modal-body").html(body)
$("#modal").modal("show")
$("#modal").find(".modal-submit").click () ->
$("#modal").find("form").submit()
Html - typical bootstrap modal
<div id="modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="<%= t :close %>" title="<%= t :close %>">
<span aria-hidden="true"></span>
</button>
</div>
<div class="modal-body">
<p></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal"><%= t :close %></button>
<button type="button" class="btn btn-success modal-submit"><%= t :save %></button>
</div>
</div>
</div>
</div>
new.js.erb or another rails view
$.showModal(
"<%= escape_javascript t :edit_comment %>",
"<%= escape_javascript(render 'form', :comment => @comment) %>"
);
link to open modal
<%= link_to t(:new), [:new, comment], :remote => true, class: "btn" %>
You probably would like to use your own ajax request - this one would do nothing in case of server error/timeout
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With