Searchable select boxes (via Select2) for ActiveAdmin forms and filters. Extends the ActiveAdmin resource DSL to allow defining JSON endpoints to fetch options from asynchronously.
Add activeadmin-searchable_select to your Gemfile:
gem'activeadmin-searchable_select'Import stylesheets and require javascripts:
// active_admin.css.scss@import"active_admin/searchable_select";//active_admin.js//=require active_admin/searchable_selectExecute:
$ npm i @codevise/activeadmin-searchable_select
Or
$ yarn add @codevise/activeadmin-searchable_select
Or add manually to package.json:
"dependencies": {
"@codevise/activeadmin-searchable_select": "1.6.0"
}and execute:
$ yarn
Add the following line into app/javascript/active_admin.js:
import'@codevise/activeadmin-searchable_select';Add the following line into app/javascript/stylesheets/active_admin.scss:
@import'@codevise/activeadmin-searchable_select';To add search functionality to a select box, use the
:searchable_select input type:
ActiveAdmin.registerProductdoformdo |f|
f.input(:category,as: :searchable_select)endendThis also works for filters:
ActiveAdmin.registerProductdofilter(:category,as: :searchable_select)endBy default, you can only select one at a time for a filter. You can specify a multi-select with:
ActiveAdmin.registerProductdofilter(:category,as: :searchable_select,multiple: true)endFor large collections, rendering the whole set of options can be to
expensive. Use the ajax option to fetch a set of matching options
once the user begins to type:
ActiveAdmin.registerProductdofilter(:category,as: :searchable_select,ajax: true)endIf the input attribute corresponds to an ActiveAdmin resource, it is
expected to provide the JSON endpoint that provides the options. Use
the searchable_select_options method to define the required
collection action:
ActiveAdmin.registerCategorydosearchable_select_options(scope: Category.all,text_attribute: :name)endBy default, scope needs to be a Ransack enabled ActiveRecord
collection proxy determining which options are available. The
attribute given by text_attribute will be used to get a display name
for each record. Via Ransack, it is also used to filter by search
term. Limiting result set size is handled automatically.
You can customize the display text:
ActiveAdmin.registerCategorydosearchable_select_options(scope: Category.all,text_attribute: :name,display_text: ->(record){"Category: #{record.name}"})endNote that text_attribute is still required to perform filtering via
Ransack. You can pass the filter option, to specify your own
filtering strategy:
ActiveAdmin.registerCategorydosearchable_select_options(scope: Category.all,text_attribute: :name,filter: lambda |term,scope|
scope.ransack(name_cont_all: term.split(' ')).resultend)endscope can also be a lambda which is evaluated in the context of the
collection action defined by the helper:
ActiveAdmin.registerCategorydosearchable_select_options(scope: ->{Category.allowed_for(current_user)},text_attribute: :name)endIf the input attribute is set on the form's object, ajax based searchable selects will automatically render a single option to ensure the selected item is displayed correctly even before options have been loaded asynchronously.
If the resource that provides the options endpoint cannot be guessed
based on the input attribute name, you can pass an object with a
resource key as ajax option:
ActiveAdmin.registerProductdoformdo |f|
f.input(:additional_category,as: :searchable_select,ajax: {resource: Category})endendA single ActiveAdmin resource can define multiple options endpoints:
ActiveAdmin.registerCategorydosearchable_select_options(name: :favorites,scope: Category.favorites,text_attribute: :name)searchable_select_options(name: :recent,scope: Category.recent,text_attribute: :name)endTo specify which collection to use, pass an object with a
collection_name key as ajax option:
ActiveAdmin.registerProductdoformdo |f|
f.input(:category,as: :searchable_select,ajax: {collection_name: :favorites})endendActiveAdmin Searchable Select querying is performed by Ransack. As such, you can build your query in a way that it can query multiple attributes at once.
ActiveAdmin.registerUserdosearchable_select_options(scope: User.all,text_attribute: :username,filter: lambdado |term,scope|
scope.ransack(email_or_username_cont: term).resultend)endIn this example, the all scope will query email OR username.
You can add the additional payload as dsl option:
ActiveAdmin.registerCategorydosearchable_select_options(scope: Category.all,text_attribute: :name,additional_payload: ->(record){{foo: record.bar}})endresponse example which uses additional_payload:
{
"results": [{ "id": "1", "text": "Bicycles", "foo": "Bar" }],
"pagination": { "more": "false" }
}You can pass additional parameters to the options endpoint:
ActiveAdmin.registerProductdoformdo |f|
f.input(:category,as: :searchable_select,ajax: {params: {some: 'value'}})endendThe lambda passed as scope can receive those parameters as first
argument:
ActiveAdmin.registerCategorydosearchable_select_options(scope: lambdado |params|
Category.find_all_by_some(params[:some])end,text_attribute: :name)endExample for the following setup:
# ModelsclassOptionType < ActiveRecord::Base;endclassOptionValue < ActiveRecord::Basebelongs_to:option_typeendclassProduct < ActiveRecord::Basebelongs_to:option_typehas_many:variantsendclassVariant < ActiveRecord::Basebelongs_to:productbelongs_to:option_valueend# ActiveAdminActiveAdmin.register(OptionType)ActiveAdmin.register(Product)ActiveAdmin.register(OptionValue)dobelongs_to:option_typesearchable_select_options(scope: lambdado |params|
OptionValue.where(option_type_id: params[:option_type_id])end,text_attribute: :value)endIt is possible to pass path parameters for correctly generating URLs for nested resources fetching via path_params
ActiveAdmin.register(Variant)dobelongs_to:productformdo |f|
...
f.input(:option_value,as: :searchable_select,ajax: {resource: OptionValue,path_params: {option_type_id: f.object.product.option_type_id}})
...
endendThis will generate the path for fetching as all_options_admin_option_type_option_values(option_type_id: f.object.product.option_type_id) (e.g. /admin/option_types/2/option_values/all_options)
When writing UI driven feature specs (i.e. with Capybara),
asynchronous loading of select options can increase test
complexity. activeadmin-searchable_select provides an option to
render all available options just like a normal select input while
still exercsing the same code paths including scope and
text_attribute handling.
For example with RSpec/Capybara, simply set inline_ajax_options true
for feature specs:
RSpec.configuredo |config|
config.before(:each)do |example|
ActiveAdmin::SearchableSelect.inline_ajax_options=(example.metadata[:type] == :feature)endendIt is possible to pass and define configuration options to Select2
via data-attributes using nested (subkey) options.
Attributes need to be added to the input_html option in the form input.
For example you can tell Select2 how long to wait after a user
has stopped typing before sending the request:
...
f.input(:category,as: :searchable_select,ajax: true,input_html: {data: {'ajax--delay'=>500}})
...To run the tests install bundled gems and invoke RSpec:
$ bundle
$ bundle exec rspec
The test suite can be run against different versions of Rails and
Active Admin (see Appraisals file):
$ appraisal install
$ appraisal rspec
Please make sure changes conform with the styleguide:
$ bundle exec rubocop
Based on mfairburn/activeadmin-select2.