Last Updated: September 29, 2021
·
2.516K
· 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
Did you know?

JavaScript on a page runs with the privileges of that origin. One altered library, tag manager, or snippet can read inputs, change what people see, and call your APIs as the user.

Security insights from Cside