Last Updated: September 29, 2021
·
2.24K
· volontarian

Multi step form / wizard responses for Ajax (modals) and HTML requests.

Therefor you only have to call this new method at the end of your controller action:

render_or_redirect_by_request_type

Then you can either set @path to redirect per Ajax for Ajax requests (@method is :get and @data is {} by default) or per redirect_to for other requests or set @template to render a template to a DOM element with selector @target for Ajax requests or render a template to the response body for other requests.

If you set @path for an Ajax request the targeted action should call "renderOrRedirectByRequestType" or respond with a JavaScript view to be evaluated.

Some conventions for Ajax requests if @path is present:

  • The response should return a JavaScript to be evaluated: in this action you should not use renderorredirectorsettemplate but just use the default render behaviour and render actionname.js(.erb).
  • @target is "#bootstrap_modal" by default.
  • "@target _ is _ modal" is set to true by default and will wrap the template by a Twitter Bootstrap Modal layout with title set to @modaltitle which is I18n.t("#{controllername}.#{action_name}.title") by default. You can disable it.
  • @template_format is "html" by default and you can also set it to "js" and the response will be evaluated.

Further conventions for Ajax requests:

  • @path will be set to #action_name unless @path is present.
  • Flash messages with key :notice or :alert will be alerted.

application_controller.rb

private

def render_or_redirect_by_request_type
    if request.xhr? || request.env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
      render_javascript_response
    elsif @template.present?
      render @template
    elsif @path.present?
      redirect_to @path
    end
  end

def render_javascript_response
  @method ||= :get
  @data ||= {}
  @template ||= action_name unless @path.present?
  @template_format ||= 'html'
  @target ||= "#bootstrap_modal"
  @target_is_modal = @target_is_modal.nil? ? true : @target_is_modal
  @modal_title ||= I18n.t("#{controller_name}.#{action_name}.title")

  render partial: 'shared/javascript_response.js', layout: false
end

shared/ _ javascript _ response.js.erb

<% message = flash[:notice] || flash[:alert] %>
<% flash.delete(:notice); flash.delete(:alert) %>
<% alert = message.present? ? "alert('#{message}');" : '' %>

<% if @path.present? %>
  $.ajax({ url: "<%= @path %>", data: <%= raw @data.to_json %>, type: "<%= @method.to_s.upcase %>", dataType: "script"}).done(function(data) {
    eval(data);
    <%= raw alert %>
  })
  .fail(function(data) {
    <%= raw alert %>
    alert("Failed to load <%= @path %>!");
  });
<% elsif @template.present? %>
  <% if @template_format == 'html' %>
    <% if @target_is_modal %>
      $(@target).html("<%= escape_javascript(
        render(
          partial: 'shared/layouts/twitter_bootstrap/modal', 
          locals: { title: @modal_title, body: render(template: "#{controller_name}/#{@template}.html") }
        ) 
      ) %>");
    <% else %>
      $("<%= @target %>").html("<%= escape_javascript render(template: "#{controller_name}/#{@template}.html") %>");
    <% end %>
  <% elsif @template_format == 'js' %>
    <%= render template: "#{controller_name}/#{@template}.js" %>
  <% end %>
  <%= raw alert %>
<% elsif message.present? %>
  <%= raw alert %>
<% end %>

shared/layouts/ twitter_ bootstrap /_modal.html.erb

<% body ||= nil %>
<% footer ||= nil %>
<div class="modal-header">
  <button type="button" id="close_bootstrap_modal_button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
  <h3><%= title %></h3>
</div>
<div class="modal-body" style="overflow-y:none;">
  <%= body || yield(:modal_body) %>
</div>
<div class="modal-footer">
  <%= footer || yield(:modal_footer) %>
</div>

Have a fresh tip? Share with Coderwall community!

Post
Post a tip