Skip to content
Looking for every option?See the full API reference →

Searchable associations ​

When a target resource has too many records for a simple dropdown, searchable replaces the association field's <select> (and the attach modal's record picker) with a search-as-you-type input backed by your own query.

Requirements ​

A query source must be defined, in one of two places:

  • self.search = { query: ... } on the target resource, shared by every searchable picker that points at it (full docs here), or
  • a query: proc inside the field's searchable: { ... } hash, scoped to that single picker (details here).

If neither is present, the picker stays empty.

Precedence

A field-level searchable: { query: } or { item: } overrides the matching key on the target resource for that picker only. Everything else about writing those procs is the same as resource search.

Boolean form ​

Pass searchable: true to opt in with the target resource's search configuration.

ruby
# app/avo/resources/course.rb
class Avo::Resources::Course < Avo::BaseResource
  def fields
    field :links, as: :has_many, searchable: true
  end
end

On this example, the links field will use the Avo::Resources::Link resource's query: and item: configuration.

Hash form ​

When you need more granular control over a single picker, pass a hash instead of true. Use it to override the target resource's query: or item: for that field, or to gate the picker with enabled:.

ruby
# app/avo/resources/review.rb
class Avo::Resources::Review < Avo::BaseResource
  def fields
    field :user, as: :belongs_to,
      searchable: {
        query: -> { query.ransack(name_cont: q).result(distinct: false) },
        item: -> do
          {
            title: "Reviewer: #{record.user.first_name}",
            description: record.user.email
          }
        end,
        enabled: -> { current_user.admin? }
      }
  end
end

query: ​

Runs while the user is typing. The records it returns fill the dropdown. See resource search for how to write the proc; the same rules apply here.

enabled: ​

Boolean or proc. Determines whether the picker uses the searchable widget. If it evaluates to false, the field falls back to the standard <select>. Handy for gradual rollouts or role-based gating.

item: ​

Configures how each row renders in the dropdown. See the Searchable associations API for the available keys.

Default options ​

If you want to change what appears when the user clicks the field before typing anything, branch on q.blank? in the field's query: proc.

ruby
# app/avo/resources/review.rb
class Avo::Resources::Review < Avo::BaseResource
  def fields
    field :user,
      as: :belongs_to,
      searchable: {
        query: -> {
          if q.blank?
            query.where(active: true).order(created_at: :desc).limit(10)
          else
            query.ransack(name_cont: q).result(distinct: false)
          end
        }
      }
  end
end

Polymorphic belongs_to ​

Both the boolean and hash forms work on polymorphic belongs_to associations. The picker runs separately for each target type: selecting Post routes the search to Post's configuration, while selecting Project routes it to Project's.

With boolean form ​

ruby
class Avo::Resources::Comment < Avo::BaseResource
  def fields
    field :commentable,
      as: :belongs_to,
      polymorphic_as: :commentable,
      types: [::Post, ::Project],
      searchable: true
  end
end

Define self.search[:query] on each target resource:

ruby
# app/avo/resources/post.rb
class Avo::Resources::Post < Avo::BaseResource
  self.search = {
    query: -> { query.ransack(name_cont: q).result(distinct: false) }
  }
end

# app/avo/resources/project.rb
class Avo::Resources::Project < Avo::BaseResource
  self.search = {
    query: -> { query.ransack(name_cont: q).result(distinct: false) }
  }
end

With hash form ​

On a polymorphic field, a field-level searchable: { ... } defines one proc shared across all types. When behavior needs to differ per type, branch on query.klass (or query.klass.name):

ruby
field :reviewable,
  as: :belongs_to,
  polymorphic_as: :reviewable,
  types: [::Post, ::Project],
  searchable: {
    query: -> {
      case query.klass.name
      when "Post"    then query.ransack(body_cont: q).result(distinct: false)
      when "Project" then query.ransack(name_cont: q).result(distinct: false)
      end
    }
  }