15

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?

Chetan Datta
  • 447
  • 9
  • 19
Anton S.
  • 969
  • 1
  • 11
  • 29
  • if you want your page not being redirected. Just return your ajax response without header: location – plonknimbuzz Dec 30 '17 at 19:38
  • @plonknimbuzz I want to redirect on `ajax:success` and stay on the same page when `ajax:error`... I guess the problem with my ajax and controller settings. Thank you very much for the respond :) – Anton S. Dec 30 '17 at 19:44
  • Hello.. I checked your github profile. this is not an opensource project? It would be probably easy to find the solution by debugging this...if you let me fork your project otherwise I can get a deeper look – Fabrizio Bertoglio Jan 02 '18 at 10:43
  • @FabrizioBertoglio Hi, I will create a new project in the next hour or two and will let you know here in the comments! Thank you very much for you help and time ! – Anton S. Jan 02 '18 at 11:36
  • @FabrizioBertoglio I added you to the private repository. Thank you very much one more time :) – Anton S. Jan 02 '18 at 13:42
  • @AnthonyBrooks : sry yesterday i was busy. can you try this https://pastebin.com/werj49vP – plonknimbuzz Jan 02 '18 at 18:09
  • @plonknimbuzz unfortunately this code won't work. The problem with my controller. When the modal opens, only `new.js.erb` works. I had the same problem with my `jQuery autocomplete` before [here](https://stackoverflow.com/questions/47957120/jquery-ui-autocomplete-in-remote-modal-ror) – Anton S. Jan 03 '18 at 15:24
  • may i look that with teamviewer? i still think this looks like client prob not server prob for me. i dont know ruby. but your code is still readable. and i really confuse why my fiddle didnt work. – plonknimbuzz Jan 03 '18 at 17:31
  • @fabrizio : did you found any mistake with the controller? – plonknimbuzz Jan 03 '18 at 17:35
  • 1
    I am still investigating your SO post. The reason you never get answer to you question is that you are not writing them correctly. You question has too many information and on a certain point of view it is very broad. My advice is trying to break down you problem in different pieces. For example, you have step 1, 2, 3, 4. Step 1,2,4 works because you debugged the process so step 3 is the problem. Then create a simple project were you recreate the issue (step 3) in a simplified way. The problem persist? then post it on SO. It does not? this is a clue you can use to solve the problem – Fabrizio Bertoglio Jan 04 '18 at 10:03
  • for debugging I advice this gem https://github.com/deivid-rodriguez/pry-byebug – Fabrizio Bertoglio Jan 04 '18 at 10:04
  • also usually you end up writing code like this (confuse and not DRY), when you do not write test specs (with Test Unit or RSpec). If you unit test you write good MVC code, you use inheritance, modules..etc.. so that you can test all those methods in the appropriate Unit/Class. If you are building a big project, I advice you based on my personal experience to write specs, do refactoring and write clean code so it is maintainable. You can read some books about this, it will help you – Fabrizio Bertoglio Jan 04 '18 at 10:13
  • @FabrizioBertoglio I added you as a collaborator to my github project yesterday :) – Anton S. Jan 04 '18 at 13:02
  • sorry, your question would take more then my 30 minutes that I invest daily on SO, while many other questions are open in the ROR section that I can solve.. On average I get more then 50 points in 30 minutes on SO. So you understand, sometimes the best is posting a good question to get an answer. – Fabrizio Bertoglio Jan 04 '18 at 13:42

1 Answers1

2

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

goose3228
  • 361
  • 1
  • 8
  • Thank you very much for your answer! Really appreciate your time. Will try to implement your solution. – Anton S. Jan 30 '18 at 14:46