Skip to content

About

💪 Plugin for ActiveAdmin. Provides batch Update and Delete for scoped_collection (Filters + Scope) across all pages.

Topics

Resources

Code of conduct

Stars

56 stars

Watchers

4 watching

Forks

Repository files navigation

Build Status Coverage

ActiveAdmin Scoped Collection Actions

Plugin for ActiveAdmin. Provides batch Update and Delete for scoped_collection (Filters + Scope) across all pages.

Collection actions in the ActiveAdmin sidebar

The confirmation dialog with the update form

Contents

Description

This gem gives you the ability to perform various batch actions on any filtered (or scoped) resource. An action applies to all records across all pages. It is similar to an ActiveAdmin batch action, but affects all filtered records. This is useful when you want to delete or update a lot of records in one click.

Installation

Add this line to your application's Gemfile:

# last version
gem 'active_admin_scoped_collection_actions'
# master branch
gem 'active_admin_scoped_collection_actions', github: 'activeadmin-plugins/active_admin_scoped_collection_actions'

And then execute:

bundle

Add the following line at the end of app/assets/javascript/active_admin.js:

//= require active_admin_scoped_collection_actions

Also include the CSS in app/assets/stylesheets/active_admin.css.scss:

@import "active_admin_scoped_collection_actions";

Usage

Usually you need two standard actions: Delete and Update.

For example, if you have resource Posts and you want to have a delete action, add:

scoped_collection_action :scoped_collection_destroy

Example:

ActiveAdmin.register Post do
  config.batch_actions = true

  scoped_collection_action :scoped_collection_destroy

  index do
    # ...
  end
end

Important Visit the Posts page with your browser and you will see no changes. Now, perform any filter with the Filters sidebar. Only after you filter will you see a delete button. It will be in the sidebar under Filters.

Update action

Update is the second standard action and is more complex. It has a form hash wrapped in a Proc:

scoped_collection_action :scoped_collection_update, form: -> do
  { name: 'text',
    diagonal: 'text',
    manufactured_at: 'datepicker',
    vendor_id: Vendor.all.map { |region| [region.name, region.id] },
    has_3g: [['Yes', 't'], ['No', 'f']] }
end

In this example the Phone model has fields:

  • name — varchar string
  • diagonal — integer (or float)
  • manufactured_at — datetime
  • vendor_id — association belongs_to :vendor, class_name: 'Vendor', foreign_key: :vendor_id
  • has_3g — boolean

The form parameter is a Proc which returns a Hash. It defines which fields you want to be able to update. Hash keys are column names in the database, hash values are types of HTML inputs. Supported values are text, checkbox, datepicker, and an Array of options for a selectbox. If you want something more complex, you can build your own forms.

A field value can also be a Hash, {type: 'text', class: 'my-widget'}. Such a field is rendered as a plain input with your own class on it, so you can turn it into any widget (datetime picker, autocomplete, etc.) with your own JavaScript. The dialog triggers the mass_update_modal_dialog:after_open event on body with the form as an argument — this is the place to initialize your widgets:

$(document).on('mass_update_modal_dialog:after_open', function (event, form) {
  $(form).find('input.my-widget').myWidget();
});

Custom actions

Example: we have a Phone resource with a manufactured_at column. We need an action which will erase this date.

In the ActiveAdmin resource:

ActiveAdmin.register Phone do
  config.batch_actions = true

  scoped_collection_action :erase_date do
    scoped_collection_records.update_all(manufactured_at: nil)
  end

  index do
    # ...
  end
end

This simple code will create a new "Erase date" button in the sidebar. After clicking it, the user will see the confirmation message "Are you sure?". After confirming, all filtered records will be updated.

Details and settings

Why don't I see the sidebar with collection actions?

Sidebar visibility by default depends on several things.

First you must set:

config.batch_actions = true

Internally this gem uses batch_actions, so without them collection actions will not work.

scoped_collection_action :something_here

Your resource should have some collection actions. If it does not have any, the sidebar will not appear.

By default we do not allow performing actions on all the records — this protects you from accidental deletion. The sidebar with buttons appears only after you filter or scope the resource records.

And lastly, you can manage sidebar visibility through the resource config:

# Always
config.scoped_collection_actions_if = -> { true }
# Only for scopes
config.scoped_collection_actions_if = -> { params[:scope] }
# etc.

You can also manage the visibility of each action individually:

scoped_collection_action :scoped_collection_destroy, if: proc { can? :destroy, Blog }

Can I use my own handler for the update and delete actions?

You can pass a block to the default update and delete actions, and do a custom redirect after it. Use head with a location: instead of redirect_to.

This example renders a form which allows changing the name field, and after that redirects to the dashboard page:

scoped_collection_action :scoped_collection_update,
                         form: -> {
                           { name: 'text' }
                         } do
  scoped_collection_records.update_all(name: params[:changes][:name])
  flash[:notice] = 'Name successfully changed.'
  head :no_content, location: admin_dashboard_path
end

How can I rename a button?

Every scoped_collection_action has the :title option.

Example:

scoped_collection_action :erase_date, title: 'Nullify' do
  scoped_collection_records.update_all(manufactured_at: nil)
end

How can I change the modal dialog title?

Similar to the button title — use the :confirm option:

scoped_collection_action :scoped_collection_destroy, confirm: 'Delete all phones?'

How can I tell the user how many records are affected?

The :confirm_summary option adds a sentence with the number of affected records to the dialog:

scoped_collection_action :scoped_collection_destroy, confirm_summary: true
Delete all?

You are going to delete 100500 record(s).

                                   [ OK ] [ Cancel ]

true uses the default text — the confirm_destroy_summary locale key for :scoped_collection_destroy and confirm_action_summary for any other action. Pass a String or a Proc to use your own text. {count} in the text is replaced with the number.

The amount is requested from the server with the same filters, scope and checked records the action itself will use, so it costs one extra COUNT query. Until the answer arrives the message is not displayed at all — there is a spinner in its place and OK is disabled, so nothing can be confirmed before the user sees what they confirm.

When that request happens depends on which option is set:

  • neither :confirm_summary nor :confirm_submit — nothing is counted and no request is made;
  • :confirm_summary — once, when the dialog is opened;
  • :confirm_submit — once, when OK is pressed and the second step appears, and not at all when the user checked no fields, because then there is nothing to confirm and the action runs straight away.

Setting both does not make two requests. :confirm_submit takes over, and :confirm_summary only supplies the wording for the sentence on its second step.

Both options in one flow — an update action with confirm_submit: and the summary it shows, from the sidebar button to the result:

confirm_summary and confirm_submit

How can I ask for an extra confirmation before the form is submitted?

An action without a form is confirmed by the modal dialog itself — it has no fields and the user just presses OK. An action with a form applies changes immediately after OK is pressed. Use the :confirm_submit option to turn such an action into two steps, where the second one replaces the fields with a summary of what is going to happen:

scoped_collection_action :scoped_collection_update,
                         confirm_submit: true,
                         form: -> { { body: 'text' } }
Are you sure?

You are going to update 100500 record(s) with:
  Body: Text here...
  Author_id: Jane

                                     [ OK ] [ Back ]

"Back" returns to the form with everything the user filled in still there. Only the fields the user checked are listed, and when nothing is checked there is nothing to confirm — the action is performed right away. The text of the sentence is the confirm_submit_summary locale key, or the :confirm_summary option when you set it.

true shows the default message ("Are you sure?"). Pass a String or a Proc to use your own:

scoped_collection_action :scoped_collection_update,
                         confirm_submit: 'Update all filtered phones?',
                         form: -> { { name: 'text' } }

Can I replace the pop-up form with my own?

Yes, but you must also take care of the mandatory parameters passed to the server.

scoped_collection_action :my_pop_action, class: 'my_popup'

Now in the HTML page you have a button:

<button class="my_popup" data="{&quot;auth_token&quot;:&quot;2a+KLu5u9McQENspCiep0DGZI6D09fCVXAN9inrwRG0=&quot;,&quot;batch_action&quot;:&quot;my_pop_action&quot;,&quot;confirm&quot;:&quot;Are you sure?&quot;}">My pop action</button>

Without a handler, clicking the button does nothing.

You can render the form in any way you want:

  • it can be a popup (Fancybox, Simplemodal, etc.), or an inline collapsible form;
  • it can even be a separate full page.

One thing is important — how you send the data to the server. It should be:

Method POST
URL /admin/collection_path/batch_action
Query string identical to the current page

The easiest way to build it is:

url = window.location.pathname + '/batch_action' + window.location.search

And the request body params should look like:

changes[manufactured_at] = "2015-07-21 18:11"
changes[diagonal] = "7"
changes[some_filed_name]='new value'
authenticity_token = "2a+KLu5u9McQENspCiep0DGZI6D09fCVXAN9inrwRG0="
batch_action = "my_pop_action"

authenticity_token and batch_action can be taken from the data attribute of the button.

Example in JavaScript:

url = window.location.pathname + '/batch_action' + window.location.search
form_data = {
  changes: { "manufactured_at": "2015-07-21 18:11", "diagonal": "7" },
  collection_selection: [],
  authenticity_token: "2a+KLu5u9McQENspCiep0DGZI6D09fCVXAN9inrwRG0=",
  batch_action: "my_pop_action"
}
$.post(url, form_data).always(function () {
  window.location.reload();
});

How do I notify the user about success and errors?

We recommend using Rails flash messages.

Example with updating the phone diagonal attribute. In this case the Phone model has a validation:

class Phone < ActiveRecord::Base
  validates :diagonal, numericality: { only_integer: true }
end
scoped_collection_action :change_diagonal, form: { diagonal: 'text' } do
  errors = []
  scoped_collection_records.find_each do |record|
    errors << record.errors.full_messages.join('. ') unless record.update(diagonal: params[:changes][:diagonal])
  end
  if errors.empty?
    flash[:notice] = 'Diagonal changed successfully'
  else
    flash[:error] = errors.join('. ')
  end
  head :no_content
end

When you try to update the diagonal with "5.6" you will see a flash error:

Diagonal must be an integer.

But if you use your own custom popup, you can show messages with JS.

Can I perform an action only on selected items?

The standard index page of a resource with batch_action enabled has a selectable column.

If you have checked some items and perform any collection action, the handler will take care of it. If you write custom actions, you should do it like this:

scoped_collection_action :do_something do
  scoped_collection_records.find_each do |record|
    record.update(name: 'x')
  end
end

License

Released under the MIT License.

About

💪 Plugin for ActiveAdmin. Provides batch Update and Delete for scoped_collection (Filters + Scope) across all pages.

Topics

Resources

Code of conduct

Stars

56 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages