Giter VIP home page Giter VIP logo

activeadmin-searchable_select's Introduction

ActiveAdmin Searchable Select

Gem Version NPM Version npm Build Status

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.

Installation

Add activeadmin-searchable_select to your Gemfile:

   gem 'activeadmin-searchable_select'
Using assets via Sprockets

Import stylesheets and require javascripts:

// active_admin.css.scss
@import "active_admin/searchable_select";
// active_admin.js
//= require active_admin/searchable_select
Using assets via Webpacker (or any other assets bundler) as a NPM module (Yarn package)

Execute:

$ 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';

Usage

Making Select Boxes Searchable

To add search functionality to a select box, use the :searchable_select input type:

   ActiveAdmin.register Product do
     form do |f|
       f.input(:category, as: :searchable_select)
     end
   end

This also works for filters:

   ActiveAdmin.register Product do
     filter(:category, as: :searchable_select)
   end

By default, you can only select one at a time for a filter. You can specify a multi-select with:

   ActiveAdmin.register Product do
     filter(:category, as: :searchable_select, multiple: true)
   end

Fetching Options via Ajax

For 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.register Product do
     filter(:category,
            as: :searchable_select,
            ajax: true)
   end

If 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.register Category do
     searchable_select_options(scope: Category.all,
                               text_attribute: :name)
   end

By 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.register Category do
     searchable_select_options(scope: Category.all,
                               text_attribute: :name,
                               display_text: ->(record) { "Category: #{record.name}" } )
   end

Note that text_attribute is still required to perform filtering via Ransack. You can pass the filter option, to specify your own filtering strategy:

   ActiveAdmin.register Category do
     searchable_select_options(scope: Category.all,
                               text_attribute: :name,
                               filter: lambda |term, scope|
                                 scope.ransack(name_cont_all: term.split(' ')).result
                               end)
   end

scope can also be a lambda which is evaluated in the context of the collection action defined by the helper:

   ActiveAdmin.register Category do
     searchable_select_options(scope: -> { Category.allowed_for(current_user) },
                               text_attribute: :name)
   end

If 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.

Specifying the Options Endpoint Resource

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.register Product do
     form do |f|
       f.input(:additional_category,
               as: :searchable_select,
               ajax: { resource: Category })
     end
   end

Multiple Options Endpoints per Resource

A single ActiveAdmin resource can define multiple options endpoints:

   ActiveAdmin.register Category do
     searchable_select_options(name: :favorites,
                               scope: Category.favorites,
                               text_attribute: :name)

     searchable_select_options(name: :recent,
                               scope: Category.recent,
                               text_attribute: :name)
   end

To specify which collection to use, pass an object with a collection_name key as ajax option:

   ActiveAdmin.register Product do
     form do |f|
        f.input(:category,
                as: :searchable_select,
                ajax: { collection_name: :favorites })
     end
   end

Querying Multiple Attributes

ActiveAdmin 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.register User do
     searchable_select_options(scope: User.all,
                               text_attribute: :username,
                               filter: lambda do |term, scope|
                                 scope.ransack(email_or_username_cont: term).result
                               end)
   end

In this example, the all scope will query email OR username.

You can add the additional payload as dsl option:

   ActiveAdmin.register Category do
     searchable_select_options(scope: Category.all,
                               text_attribute: :name,
                               additional_payload: ->(record) { { foo: record.bar } } )
   end

response example which uses additional_payload:

{
  "results": [{ "id": "1", "text": "Bicycles", "foo": "Bar" }],
  "pagination": { "more": "false" }
}

Passing Parameters

You can pass additional parameters to the options endpoint:

   ActiveAdmin.register Product do
     form do |f|
        f.input(:category,
                as: :searchable_select,
                ajax: {
                  params: {
                    some: 'value'
                  }
                })
     end
   end

The lambda passed as scope can receive those parameters as first argument:

   ActiveAdmin.register Category do
     searchable_select_options(scope: lambda do |params|
                                 Category.find_all_by_some(params[:some])
                               end,
                               text_attribute: :name)
   end

Path options for nested resources

Example for the following setup:

# Models
class OptionType < ActiveRecord::Base; end

class OptionValue < ActiveRecord::Base
  belongs_to :option_type
end

class Product < ActiveRecord::Base
  belongs_to :option_type
  has_many :variants
end

class Variant < ActiveRecord::Base
  belongs_to :product
  belongs_to :option_value
end

# ActiveAdmin
ActiveAdmin.register(OptionType)

ActiveAdmin.register(Product)

ActiveAdmin.register(OptionValue) do
  belongs_to :option_type
  searchable_select_options(scope: lambda do |params|
                                     OptionValue.where(
                                       option_type_id: params[:option_type_id]
                                     )
                                   end,
                            text_attribute: :value)
end

It is possible to pass path parameters for correctly generating URLs for nested resources fetching via path_params

ActiveAdmin.register(Variant) do
  belongs_to :product

  form do |f|
    ...
    f.input(:option_value,
            as: :searchable_select,
            ajax: {
              resource: OptionValue,
              path_params: {
                option_type_id: f.object.product.option_type_id
              }
            })
    ...
  end
end

This 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)

Inlining Ajax Options in Feature Tests

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.configure do |config|
    config.before(:each) do |example|
      ActiveAdmin::SearchableSelect.inline_ajax_options = (example.metadata[:type] == :feature)
    end
  end

Passing options to Select2

It 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
             }
           })
   ...

Development

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

Acknowledgements

Based on mfairburn/activeadmin-select2.

activeadmin-searchable_select's People

Contributors

alexplatteeuw avatar davidwparker avatar gerardosandoval avatar kiddrew avatar linqueta avatar mfairburn avatar pierrickouw avatar skumring avatar svalkanov avatar tf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

activeadmin-searchable_select's Issues

not have ability to add additional payload of the ajax response

At the moment ajax response contains the folowwing payload: text and id
We have ability to config text attributes but we don't have ability to add new payload.
It would be nice to have something like this:

searchable_select_options scope: Post, text_attributes: :tite, additional_payload: ->(record) { { custom_field: record.custom_field, created_at: record.created_at } }

I suggest that you do this because someone can only use your gem to build the JSON endpoint to provides the options

Multiple Select not showing all initially selected options

I have a multiple select box that works as expected to select and save multiple options. However, when I reload the form, only the first selected option shows up in the select2 box, even though all the previously selected relationships are saved in the database.

This only happens when using AJAX - when I provide a collection instead the box works as expected.

ActiveAdmin.register Offer do
  form do |f|
    f.input :buyers, as: :searchable_select, ajax: true, multiple: true
  end
end
ActiveAdmin.register Buyer do
  searchable_select_options(scope: Buyer.all.order(last_name: :asc),
                             text_attribute: :name_email,
                             filter: lambda{|term, scope| scope.ransack(first_name_or_last_name_or_email_cont: term.downcase, phone_number_cont: (term.gsub(/\D/, '').presence || "XXX"), m: 'or' ).result})
end

Undefined method 'all_options_admin_users_path'

Having issues getting this to work.

Note: we have 3x different namespaces for different Active Admin interfaces. (Admin2, Super, Custom).

admin2/flag.rb

  form do |f|
    f.semantic_errors
    f.inputs do
      f.input :assigned_user, as: :searchable_select, ajax: { resource: User }
      f.input :admin_notes
    end
    f.actions
  end

admin2/user.rb

searchable_select_options(scope: User.all, text_attribute: :username)

When that's setup like that, I get the following error:

undefined method `all_options_admin_users_path' for #<#<Class:0x00007fc178072b50>:0x00007fc178062b88>
Did you mean?  all_options_admin2_users_path
               all_options_admin2_users_url

Suggestions? Thanks!

Pagination incorrect results

I noticed a strange thing about the results being returned in the collection, not all of them were returned.
I started investigating the reason and noticed the following thing: Pagination returns page values in the parameters in the order 0, 2, 3...
The first API call does not include the page attribute in parameters. The second API call includes params[:page] = 2.
Accordingly, the following code in the gem begins to return incorrect results.

page_index = 0;
per_page = 10;
We will select records from 0 to 10

page_index = 2;
per_page = 10;

We will select records from 20 to 30 and skip records from 10 to 20.

Not sure if this is a bug in the code or pagination gem versions or something else. Maybe you could help with that?

Multiple Select Order

The order in multiple selects is not kept. It always re-orders in alphabetical order after selection.

  • Is it the expected behavior or a bug?
  • Is there any way to configure the input to keep the order in which the user selects the elements?

If that is not the case, would there be a way to implement that?

Support for blank option

First off, thank you very much for creating activeadmin-searchable_select. We've been using ActiveAdmin in our Rails app for 5 years, and as the amount of data in our database has grown, we've struggled with all the problems that select controls with too many options in them brings. Integrating AJAX-based select2 controls into ActiveAdmin with your gem was easy, and vastly improves the UX within ActiveAdmin.

One thing we're missing from the old select controls in our ActiveRecord filters and forms is a blank option and the ability to clear the currently selected option. Does activeadmin-searchable_select support this? If not, would it be possible to add this feature?

Thanks,
Denis

How to use this gem in custom active admin form page?

Is it possible to use searchable in custom Active Admin page?
If yes, how to do it?

All the docs examples is using the normal page like this ActiveAdmin.register "Model Here".

If I try to register custom page, searchable_select won't work in this form 🤔

ActiveAdmin.register_page 'Product' do
  page_action :create_product, method: :post do
  end

  content do
    confirm_submit = 'return confirm("Do you really want to create this product?")'
    form_for :data, url: admin_product_create_product_path, html: { onsubmit: confirm_submit }, method: :post do |f|
    
    # 1. This doesnt work.
    f.input(:additional_category,
            as: :searchable_select,
            ajax: { resource: Product })
    
    # 2. This doesnt work either.
    # f.input(:product_id,
    #   as: :searchable_select,
    #   ajax: { resource: Brand, collection_name: :products })
  end
end

Support for Select2 tagging

It would be great if users could be allowed to enter a new custom that's not in the collection to search for.
Select2 seems to provide support for this via the "tagging" feature, but I'm unable to get it to work.

Thus far what I've tried:

filter(
  :foo,
  as: :searchable_select,
  collection: ['bar'],
  input_html: {
    tags: true
  }
)

I got this by looking at this SO answer https://stackoverflow.com/a/30021059

Original docs from Select2 on this feature:
https://github.com/select2/select2/blob/develop/docs/pages/09.tagging/docs.md

I don't need complex logic for new tags, nor really ajax lookups or anything like that. The problem is I have a long list of string values and in some cases users would like to search on a value that's not in my list. I could simply change it to a plain input, but it's nice have some suggested values to search on.

I'd be more than happy to contribute a solution, I'm just not sure where to start.

CVE PRISMA-2021-0183 due to select2-rails

We are consuming activeadmin_searchable_select -v 1.8.0 which is pulling select2-rails -v 4.0 as a dependency and which is introducing this cve PRISMA-2021-0183.
activeadmin_addons removed the select2-rails dependency in -v 1.10.0 which resolved the CVE in the gem. can this be looked into as a priority?

Add ruby 3.0.0 support

What language does this apply to?
ruby

Describe the problem you are trying to solve.
Ruby 3.0.0 was released, which is not supported:

activeadmin-searchable_select-1.2.1 requires ruby version ~> 2.1, which is incompatible with the current version, ruby 3.0.0p0

Describe the solution you'd like
The activeadmin-searchable_select gem is installable under ruby 3.0.0.

Support for authorizing AJAX requests

We are using ActiveAdmin's PunditAdapter to provide authorization of the ActiveAdmin controller actions. After we integrated activeadmin-searchable_select with AJAX, we ran into an issue with the *_options controller actions. They would execute without first authorizing the request, and as a result would fail due to a Pundit::AuthorizationNotPerformedError error.

We have worked around the problem by overriding the ActiveAdmin::SearchableSelect::ResourceDSLExtension:: searchable_select_options method in an initializer, by calling an authorize method (in our ApplicationController class) prior to calling render within the collection_action block:

module ActiveAdmin
  module SearchableSelect
    module ResourceDSLExtension
      def searchable_select_options(name: :all, **options)
        option_collection = OptionCollection.new(name, options)
        config.searchable_select_option_collections[name] = option_collection

        collection_action(option_collection.collection_action_name) do
          #--------------------------------------------------
          # Customization
          authorize :application
          #--------------------------------------------------
          render(json: option_collection.as_json(self, params))
        end
      end
    end
  end
end

While this works, we would prefer not to override/replace code in the gem. Would it be possible to enhance the gem to use ActiveAdmin's built-in authorization to authorize the *_options requests?

Thanks,
Denis

Support for conditional collection

Hi there!

Is there any way of populating the select using the value of another input and refresh it when the other input changes?
e.g:

f.input(:user)
f.input(:posts,
        as: :searchable_select,
        collection: <user_selected_in_input>.posts
})

I would appreciate any help on this!

Scope and filtering

Hello! Nice library!

I would like to know if using scope can also be a filter. Suppose In my scope, I will use Post.limi(5), for example. This would make the only 5 posts be loaded?

Debounce (quietMillis) support

First of all, thank you for working on this project. This really helped dig us out of a hole of having ActiveAdmin select inputs that were waaaaayyy too long.

I'm looking for an option to debounce my searchable selects inputs to keep from pounding the search endpoints on every keystroke.

select2 already has an option called "quietMillis" nested under the "ajax" family of options. It's a debounce timeout.

Is there a way that this could be accomplished with activeadmin-searchable_select?

Set default values while loading the page

Hi, while loading the page by default I need some values to be selected in the searchable select field, and is it possible to dynamically load data to drop down. Can you please guide me on how to achieve it?

:searchable_select Not working

Rails 7.0.4
ActiveAdmin 2.13.1

Trying to use :searchable_select for filter and form. Both examples does not work (searchable select 2 select is not present)

filter :first_name, as: :searchable_select
f.input :category, label: 'Категория', as: :searchable_select, collection: categories_collection(false)

Ajax fail when active admin namespace is set to false

When in active admin initializer the namespace is setting to false
then ajax fail with message like this:

undefined method `all_options_root_categories_path' for #<#<Class:0x00007fec34ac4130>:0x00007fec34abe460>
Did you mean?  all_options_categories_path

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.