Displaying errors when a Twitter Bootstrap modal window contains a Rails form helper

Go To StackoverFlow.com

2

I'm asking about using a Twitter Bootstrap modal window with a Rails form helper and displaying form validation error messages. I'm using Twitter Bootstrap 2.0 with Rails 3.2.

I've got a form inside a Twitter Bootstrap modal window. There's a button on the page to open the modal window and display the form. All good, except when the form is submitted with errors, the page is displayed and error messages can't be seen because the form is inside the (initially undisplayed) modal window.

I need to use jQuery to test if the form contains a Rails error message and override the "display: none;" style (or toggle the modal window to display) when the page is rendered.

I've tried adding the following code to my assets/javascripts/application.js file but it doesn't work:

$('document').ready(function() {
  if ($('#error_explanation').length > 0) {
    $("#request_invite").css("display", "block");
  }
})

and also:

$('document').ready(function() {
  if ($('#error_explanation').length > 0) {
    $("#request_invite").modal('toggle');
  }
})

What do I need to do to display the modal window or toggle it open when there's an error message present?

Here's the code that displays the form inside the Twitter Bootstrap modal window (Haml):

#request-invite.modal{:style => "display: none;"}
  = form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
    .modal-header
      %a.close{"data-dismiss" => "modal"} ×
      %h3 Request Invitation
    .modal-body
      - if @user.errors.any?
        #error_explanation
          %ul
            - @user.errors.full_messages.each do |msg|
              %li= msg
      %p
        = f.label :email
        %br/
        = f.email_field :email
    .modal-footer
      = f.submit "Request Invitation", :class => "btn.btn-success"
      %a.btn{"data-dismiss" => "modal", :href => "#"} Close
#romance-copy{:style => "text-align: center; margin-top: 100px"}
  %h2 Want in?
#call-to-action{:style => "text-align: center; margin-top: 100px"}
  %a.btn.btn-primary.btn-large{"data-toggle" => "modal", :href => "#request-invite"} Request invite
2012-04-04 17:21
by Daniel Kehoe
It is possible that the modal is not loading on document.ready. May be its loading on document.ready() . Try performing the javascript inside the modal popup for debugging purpose - Sairam 2012-04-04 17:53
Kenrick Chien pointed out the typo: request_invite != request-invite. Both code snippets work. The snippet with "modal('toggle')" is optimal because the modal has a dark background that covers the page which appears when the modal is triggered. Just using "display: block" shows the modal but doesn't apply the dark background - Daniel Kehoe 2012-04-04 21:44


3

Looks like a typo? Your div in Haml has the ID request-invite (with a dash). However, your jQuery is looking for an ID with an underscore.

2012-04-04 19:52
by Kenrick Chien
Yep, a typo. facepalmDaniel Kehoe 2012-04-04 21:38


2

I want to offer another option. The way you use should work, but why not just use AJAX to show the error msg when there is any error. What the response JS does is to add the extra styles to show the error msg.

I need to use jQuery to test if the form contains a Rails error message and override the "display: none;" style (or toggle the modal window to display) when the page is rendered.

Take a look at http://www.twitmark.me/. Try sign in with twitter. Input a faked invitation code and see what the error looks like. Is this a better solution?

2012-04-05 00:15
by wanghq
Twitmark.me's validation looks like exactly what I want to do. Are you reloading the popup's content in order to tag the outer <div> with the error class and add the inline-help object? How do you trigger the reload--from JQuery? What does the controller do on failure? I feel like I've got most of the pieces but am having trouble stitching them together - Mark Berry 2012-11-14 03:23
Your controller action just needs to respond .js request - wanghq 2012-11-14 05:05
Your controller action just needs to respond ajax request with js. The view code is like https://gist.github.com/4070421 - wanghq 2012-11-14 05:32
Thanks very much. I will give this a try. It is confusing because the default controller code contains something like this for a failure: format.json { render json: @invitation.error, status: :unprocessable_entity }. But I also saw an example that had the JSON response and added something like this: format.js {render 'create' }. But it looks like your code would not need format_json at all, right - Mark Berry 2012-11-15 01:55
They are similar. I prefer returning js than json because I don't want to write a lot of js code to add the error styles to the form. As you can see from my link, rendering form with the @invitation adds the error styles, the only js needs to do is to add the html to the page - wanghq 2012-11-15 06:19
Thanks again. I'm still stuck so I posted a new question: Display inline errors with simple_form in a Bootstrap Ajax modalMark Berry 2012-11-16 00:20


2

As stated by @wanghq (and in part by @miked), I think the best way is to use AJAX to render the html in the modal and submit the form data. This way, all the logic is delegated to server side, using JS only for the effects and keeping views clean (no helpers nor logic involved inside views).

with this solution is that you can gracefully degrade when JS doesn't work on the client by using same controller action to render the form. A simple example:

def new
  @user = User.new
  # some stuff...
  render :handler => 'haml', :layout => !request.xhr?
end

# similar logic should be used for #create action

for the (d)html part, you might use ruby to detect if it should be displayed because of errors. I mean something like this (you might write an helper if needed):

#request-invite.modal{:style => "display: #{ @user.errors.any? ? 'block' : 'none' };"}

as a side note, using simple_form 2.0.x, you'll get twitter-bootstrap markup support (I mean, it will generate bootstrap-friendly markup for your forms). In this way, you'll DRY some logic inside templates (eg: checking @user.errors to diplay error messages)

in this way, you should not need JS to do checks ;)

my 2cents ;-)

2012-04-05 08:33
by Andrea Pavoni


1

I might be misunderstanding the problem, but from what I can see, your error_explanation div isn't hidden (display:none) when the modal loads, it doesn't exist because it's conditioned in the if @user.errors.any? block. Therefore it won't matter what javascript you throw at it, it's not there.

For starters, try:

.modal-body
  #error_explanation
    - if @user.errors.any?
      %ul
        - @user.errors.full_messages.each do |msg|
          %li= msg

this way, the div will exist and the ul will only show items if their are errors to display.

2012-04-04 19:52
by miked


1

Why do you need jQuery? Why not just use the conditional you already have?

This code sucks, you could probably clean it up (move it into helper or something), but you'll get the idea.

- def display?; @user.errors.any? ? "block;" : "none;"; end;
#request-invite.modal{:style => "display: " + display? }
2012-04-04 20:53
by mwoods79
Excellent suggestion. But there's an advantage to using jQuery to toggle the modal. The modal has a dark background that covers the page which appears when the modal is triggered. Just using "display: block" shows the modal but doesn't apply the dark background - Daniel Kehoe 2012-04-04 21:31
BTW, the correct code has parans: #request-invite.modal{:style => "display: " + (@user.errors.any? ? "block;" : "none;") - Daniel Kehoe 2012-04-04 21:36
I moved it into a method to make it a little cleaner. I don't ever use twitter modals, I think they suck (javascript wise that is). In my current app I use a template and a custom javascript object to hide, show, and transtion depending on what was clicked. This turns out to be far few lines of javascript than the bootstrap modal plugin. And this is a much more flexible solution in the long run. All the styles for the backdrop and the modal are in the css. $('<div class="modal-backdrop"/>').appendTo(document.body)mwoods79 2012-04-04 23:02
Ads