Skip to content

Instantly share code, notes, and snippets.

@tinynumbers
Last active August 15, 2024 16:11
Show Gist options
  • Save tinynumbers/5896537 to your computer and use it in GitHub Desktop.
Save tinynumbers/5896537 to your computer and use it in GitHub Desktop.
ActiveAdmin controller extension to persist resource index filters between requests.
# this stuff goes in config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
# ...
# put these lines in the "Controller Filters" section of the ActiveAdmin.setup block
# These two are defined in ActiveAdmin::FilterSaver::Controller, which is loaded below.
config.before_filter :restore_search_filters
config.after_filter :save_search_filters
# ...
end
# put the following lines below the main ActiveAdmin.setup block (also in config/initializers/active_admin.rb)
require 'active_admin/filter_saver/controller'
ActiveAdmin.before_load do |app|
# Add our Extensions
ActiveAdmin::BaseController.send :include, ActiveAdmin::FilterSaver::Controller
end
# -*- encoding : utf-8 -*-
# put this in lib/active_admin/filter_saver/controller.rb
module ActiveAdmin
module FilterSaver
# Extends the ActiveAdmin controller to persist resource index filters between requests.
#
# @author David Daniell / тιηуηυмвєяѕ <[email protected]>
module Controller
private
SAVED_FILTER_KEY = :last_search_filter
def restore_search_filters
filter_storage = session[SAVED_FILTER_KEY]
if params[:clear_filters].present?
params.delete :clear_filters
if filter_storage
logger.info "clearing filter storage for #{controller_key}"
filter_storage.delete controller_key
end
if request.post?
# we were requested via an ajax post from our custom JS
# this render will abort the request, which is ok, since a GET request will immediately follow
render json: { filters_cleared: true }
end
elsif filter_storage && params[:action].to_sym == :index && params[:q].blank?
saved_filters = filter_storage[controller_key]
unless saved_filters.blank?
params[:q] = saved_filters
end
end
end
def save_search_filters
if params[:action].to_sym == :index
session[SAVED_FILTER_KEY] ||= Hash.new
session[SAVED_FILTER_KEY][controller_key] = params[:q]
end
end
# Get a symbol for keying the current controller in the saved-filter session storage.
def controller_key
#params[:controller].gsub(/\//, '_').to_sym
current_path = request.env['PATH_INFO']
current_route = Rails.application.routes.recognize_path(current_path)
current_route.sort.flatten.join('-').gsub(/\//, '_').to_sym
end
end
end
end
# put this in e.g. app/assets/javascripts/admin/index-filters.coffee
# and include this in app/assets/javascripts/active_admin.js
$ ->
# Extend the clear-filters button to clear saved filters
$('.clear_filters_btn').click (evt) ->
# This will send a synchronous post with clear_filters set to true -
# our AA FilterSaver controller extension looks for this parameter to
# know when to clear session-stored filters for a resource - and then
# the default AA clear-filters button behavior will issue a get request
# to actually re-render the page.
$.ajax this.href, {
async: false,
data: { clear_filters: true },
type: 'POST'
}
@Hassan9229
Copy link

How can we get session result in resource model?

@JuanLUJoanne
Copy link

Hi,
I follow the above instruction.
I have add the

active_admin.rb, controller.rb, index-filters.coffee

to my code base.
But I found restore_search_filters is not called.
the params[:q] is nil.
who can help to tell me what's missing ?
Thanks.

user.rb

ActiveAdmin.register User do
  menu parent: 'User Management'
  config.filters = false


controller do
    before_filter :filter_params, only: [:index]
    before_filter :restore_search_filters, only: [:batch_update]
    after_filter :save_search_filters, only: [:index]


    def batch_update(ids, state)
      updated_user = []
      users = User.find(ids)
      users.each do |user|
        if user.update state: User::STATES.index(state.to_sym)
          updated_user << "##{user.id} "
        else
          flash[:alert] << "##{user.id} update failed."
        end
      end
      flash[:alert] = "You have successfully changed the state to #{state} for #{updated_user.size} #{'user'.pluralize(updated_user.size)}"
      params[:q] = restore_search_filters
      redirect_to admin_users_path(params[:q])
    end

  def filter_params
      @filter_params ||= params[:admin_users] || {}
    end
end

the filter in the view:

filters.html.erb

<%= semantic_form_for :admin_users, url: admin_users_path, method: :get, html: { class: 'filter_form' } do |f| %>
  <div class="filter_form_field filter_select">
    <%= f.label 'first_name' %>
    <%= f.text_field 'first_name', value: @filter_params[:first_name] %>
  </div>
  <div class="filter_form_field filter_select">
    <%= f.label 'last_name' %>
    <%= f.text_field 'last_name', value: @filter_params[:last_name] %>
  </div>

@fabien-michel
Copy link

Javascript doesn't work for me. (Hacky method not called before original click event handler). I've to change JS code by this :

# Extend the clear-filters button to clear saved filters
$(document).on 'ready page:load', ->
  original_click = $._data($('.clear_filters_btn')[0], "events").click[0].handler
  $('.clear_filters_btn').off('click')
  $('.clear_filters_btn').on 'click', (evt) ->
    # This will send a synchronous post with clear_filters set to true -
    # our AA FilterSaver controller extension looks for this parameter to
    # know when to clear session-stored filters for a resource - and then
    # the default AA clear-filters button behavior will issue a get request
    # to actually re-render the page.
    $.ajax this.href, {
      async: false,
      data: { clear_filters: true },
      type: 'POST'
    }
    original_click.apply(this, [evt])

@tlueker
Copy link

tlueker commented Mar 30, 2017

I had issues where I could not clear the filters once set. The clear filter ajax seemed to work, but when the page reloaded the filters were still there. Clearing them manually and then hitting Filter button also failed, as the result was aparams[:q] = nil.

I ended up modifying restore_search_filters to look at the value of params["commit"] to decided if I wanted to load params[:q].

  def restore_search_filters
    filter_storage = session[SAVED_FILTER_KEY]
    if params[:clear_filters].present? || (params["commit"].present? && params["commit"] == "Filter")
      params.delete :clear_filters
      if filter_storage
        filter_storage.delete controller_key
        params[:q] = nil if params["commit"].present? && params["commit"] != "Filter"
      end
      if request.post?
        # we were requested via an ajax post from our custom JS
        # this render will abort the request, which is ok, since a GET request will immediately follow
        render json: { filters_cleared: true }
      end
    elsif filter_storage && params[:action].to_sym == :index && params[:q].blank? && (params["commit"].blank? || params["commit"] != "Filter")
      saved_filters = filter_storage[controller_key]
      unless saved_filters.blank?
        params[:q] = saved_filters
      end
    end
  end

@sonniimukh
Copy link

I ended up with the original solution and the following JS approach which is more clear and follows the logic of native Clear Filters action:

$ ->
  # Clear Filters button
  $('.clear_filters_btn').off('click')
  $('.clear_filters_btn').click (e) ->
    params = window.location.search.slice(1).split('&')
    regex = /^(q\[|q%5B|q%5b|page|commit)/
    if typeof Turbolinks != 'undefined'
      Turbolinks.visit(window.location.href.split('?')[0] + '?clear_filters=1&' + (param for param in params when not param.match(regex)).join('&'))
      e.preventDefault()
    else
      window.location.search = 'clear_filters=1&' + (param for param in params when not param.match(regex)).join('&')

Had also to apply the Devise workaround proposed by @elsurudo.

Other than that works perfectly fine on top of Rails 5.1.4 and ActiveAdmin 1.1.0.

Thanks all!

@mirelon
Copy link

mirelon commented Dec 1, 2017

Rails 5.0.6:
controller key needs to return string instead of symbol:

      # Get a symbol for keying the current controller in the saved-filter session storage.
      def controller_key
        #params[:controller].gsub(/\//, '_').to_sym
        current_path = request.env['PATH_INFO']
        current_route = Rails.application.routes.recognize_path(current_path)
        current_route.sort.flatten.join('-').gsub(/\//, '_')
      end

@Sprachprofi
Copy link

Thank you all! The following is a complete working implementation for Rails 7 and vanilla JS:

add this to config/initializers/active_admin.rb

ActiveAdmin.setup do |config|
  ...
  config.before_action :restore_search_filters
  config.after_action :save_search_filters
  ...
end

require 'active_admin/filter_saver/controller'

ActiveAdmin.before_load do |app|
  # Add our Extensions
  ActiveAdmin::BaseController.send :include, ActiveAdmin::FilterSaver::Controller
end

new file: /lib/active_admin/filter_saver/controller.rb

# -*- encoding : utf-8 -*-
module ActiveAdmin
  module FilterSaver

    # Extends the ActiveAdmin controller to persist resource index filters between requests.
    #
    # @author David Daniell / тιηуηυмвєяѕ <[email protected]>
    # improvements by tlueker and mirelon
    module Controller

      private

      SAVED_FILTER_KEY = :last_search_filter

      def restore_search_filters
        filter_storage = session[SAVED_FILTER_KEY]
        if params[:clear_filters].present? || (params["commit"].present? && params["commit"] == "Filter")
          params.delete :clear_filters
          if filter_storage
            logger.info "clearing filter storage for #{controller_key}"
            filter_storage.delete controller_key
          end
          if request.post?
            # we were requested via an ajax post from our custom JS
            # this render will abort the request, which is ok, since a GET request will immediately follow
            render json: { filters_cleared: true }
          end
        elsif filter_storage && params[:action].to_sym == :index && params[:q].blank? && (params["commit"].blank? || params["commit"] != "Filter")
          saved_filters = filter_storage[controller_key]
          unless saved_filters.blank?
            params[:q] = saved_filters
          end
        end
      end

      def save_search_filters
        if params[:action].to_sym == :index
          session[SAVED_FILTER_KEY] ||= Hash.new
          session[SAVED_FILTER_KEY][controller_key] = params[:q]
        end
      end

      # Get a symbol for keying the current controller in the saved-filter session storage.
      def controller_key
        #params[:controller].gsub(/\//, '_').to_sym
        current_path = request.env['PATH_INFO']
        current_route = Rails.application.routes.recognize_path(current_path)
        current_route.sort.flatten.join('-').gsub(/\//, '_')
      end

    end

  end
end

add this to the bottom of active_admin.js or into an imported javascript file

$(document).ready(function() {
  $('.clear_filters_btn').on('click', function() {
      $.ajax(this.href, {
        async: false,
        data: { clear_filters: true },
        type: 'POST'
      });
    });
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment