Autocomplete/typeahead input ready to be used with Bootstrap 4 in Rails 5.
For Bootstrap 3 and Rails 4 - see bootstrap3_autocomplete_input gem.
Features:
- Adds an input with autocomplete/typeahead compatible with Bootstrap 4.
- Works with SimpleForm.
- Uses the autocomplete library from Bootstrap-3-Typeahead that works with Bootstrap 4.
gem 'simple_form'
gem 'bootstrap_autocomplete_input'
It assumes that you have Bootstrap 4 in your application. For example, you can use the bootstrap-rubygem gem.
gem 'jquery-rails', '~>4.2.2'
gem 'jquery-ui-rails'
gem 'sass-rails'
gem 'bootstrap', '~> 4.0.0.alpha6'
# Tooltips and popovers depend on tether for positioning. If you use them, add tether to the Gemfile:
source 'https://rails-assets.org' do
gem 'rails-assets-tether', '>= 1.3.3'
end
# if problems with SSL - use source 'http://insecure.rails-assets.org'
...
end
Include two js files (bootstrap3-typeahead.min, bootstrap-autocomplete-input.min) in your assets file 'app/assets/javascripts/application.js' after 'bootstrap.js'.
//= require jquery2
//= require jquery_ujs
//= require bootstrap
//= require bootstrap3-typeahead.min
//= require bootstrap-autocomplete-input
//= require bootstrap-autocomplete-input-init
for Turbolinks:
//= require jquery2
//= require jquery_ujs
//= require bootstrap
//= require bootstrap3-typeahead.min
//= require bootstrap-autocomplete-input
//= require bootstrap-autocomplete-input-init-turbolinks
All CSS for autocomplete input is already contained in Bootstrap 3 CSS.
For example, if you use bootstrap-sass, your css files 'app/assets/stylesheets/application.css.scss' might be like
@import "bootstrap";
use with Webpacker
yarn add https://github.com/maxivak/bootstrap-autocomplete-input-assets
app/javascript/packs/application.js
...
// autocomplete
import 'bootstrap-autocomplete-input-assets/src/js/bootstrap3-typeahead.min.js';
import 'bootstrap-autocomplete-input-assets/src/js/bootstrap-autocomplete-input.min.js';
Assuming that we have models Order and Client.
class Order < ActiveRecord::Base
belongs_to :client
end
class Client < ActiveRecord::Base
has_many :orders
end
We will add autocomplete input to order's form for editing its client.
class ClientsController < ApplicationController
autocomplete :client, :name
...
end
This will generate the controller method 'autocomplete_client_name' that returns JSON data with clients.
Add routes for the generated controller action:
resources :clients do
get :autocomplete_client_name, :on => :collection
end
This will generate route with name autocomplete_client_name_clients
which can be accesedby path autocomplete_client_name_clients_path.
specify what to display as a value in a text field:
class Client < ActiveRecord::Base
belongs_to :order
def to_s
name
end
end
use :autocomplete input type for the client's field in 'app/views/orders/_form.html.haml'
=simple_form_for @order do |f|
= f.error_notification
= f.input :field1
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url
= f.button :submit, 'Save', :class=> 'btn-primary'
The gem has method "autocomplete" to generate an action in your controller:
class ClientsController < ApplicationController
autocomplete :client, :name
This will add new action 'autocomplete_client_name' where :client is the model class name and :name is the field name in the model. The action uses params[:q] for the searching term and queries the database.
The action returns data in JSON format:
[["id1", "name1"], ["id2", "name2"], ..]
which is an array of ids and titles.
class ClientsController < ApplicationController
autocomplete :client, :name, { options_hash_here }
class ClientsController < ApplicationController
autocomplete :client, :name, { :column_name => 'title_long' }
It will search in this column in database.
class ClientsController < ApplicationController
autocomplete :client, :name, { extra_columns: [:lastname, :birthdate], column_name: 'fullname' }
- Method used for getting ID of the record.
- Make sure that columns this method depends on are included in :extra_columns option.
class ClientsController < ApplicationController
autocomplete :client, :name, { :display_value => 'fullname', :full_model=>true }
...
end
Additional WHERE conditions
- Add additional filter using :scopes option
autocomplete :client, :name, { :scopes => [:w_usa, :w_active]}
# scopes in model
class Client < ActiveRecord::Base
...
# scopes
scope :w_usa, -> { where(region_id: 1) }
scope :w_active, -> { where(active: true) }
end
- Add additional filter using :where option with search condition
autocomplete :client, :name, { :where => 'region_id=1' }
- For dynamic search conditions use :where_method option
class ClientsController < ApplicationController
autocomplete :client, :name, { :where_method => :w_region }
...
def w_region
# get value from params
v = params[:region_id]
if v
return "region_id=#{v}"
end
nil
end
end
class Client < ActiveRecord::Base
def name_with_birthdate firstname+', '+birthdate end end
It will display search results using this method. You must set option :full_model=>true if you search by one column but you want to display value from other columns.
```ruby
# JSON data returned by the controller
[[1, 'Mark Twen, 1980'], [2, 'John Deer, 1985'], ..]
specify options:
= simple_form_for @order do |f|
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url, :option_name => opt_value, :option_name2=>value2
You have several options to get data for search:
- :source => url - get static data downloaded from URL
- :source_query => url - query data with a keyword from URL
- :source_array => array - data is stored in local array of strings
examples:
= f.input :client, :as => :autocomplete, :source => autocomplete_client_name_clients_url
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url
= f.input :client, :as => :autocomplete, :source_array => ['item1', 'item2', ..]
Autocomplete input adds a hidden field to use the id of the selected item. Unless you use local array as data source (:source_array), the hidden field for the id is added automatically.
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url
This will generate HTML like
<input id="order_client_id" type="hidden" value="0" name="order[client_id]">
<input id="order_client" name="order[client]"
class="autocomplete optional" type="text" value="*some value*"
data-source-query="http://localhost:3000/clients/autocomplete_client_name" data-provide="typeahead" data-field-id="order_client_id"
autocomplete="off">
This will update the field with id "order_client_id" with the id of the selected object.
Data posted from the form to the server will contain the id of the selected client (params[:order][:client_id])
params[:order]
{
client_id: 5,
client: 'client name',
...
}
If you don't want to have a hidden field for object id, set :field_id option to false:
= f.input :client, :as => :autocomplete, :source => autocomplete_client_name_clients_url, :field_id=>false
This will generate HTML without a hidden field:
<input id="order_client" name="order[client]"
class="autocomplete optional" type="text" value="*value*"
data-source="http://localhost:3000/clients/autocomplete_client_name"
data-provide="typeahead"
autocomplete="off">
The max number of items to display in the dropdown. Default is 8.
= simple_form_for @order do |f|
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url, :items => 2
The minimum character length needed before triggering autocomplete suggestions. You can set it to 0 so suggestion are shown even when there is no text when lookup function is called. Default is 1.
= simple_form_for @order do |f|
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url, :minLength=>3
View the full list of options at https://github.com/bassjobsen/Bootstrap-3-Typeahead. Note that not all options from that list are supported by the current gem.
The callback function to be execute after an item is selected.
= simple_form_for @order do |f|
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url, :minLength=>3, :afterSelect=>'after_select_client'
:javascript
function after_select_client(item){
console.log('item is selected');
console.log(item);
}
= simple_form_for @order, html: { autocomplete: 'off' } do |f|
-#.. other fields..
= f.input :client, :as => :autocomplete, :source => autocomplete_client_name_clients_url
Data will be loaded from the server once after page loads, and all queries to search a string will be against the stored data. Do not use this if you have a big number of items for client.
= f.input :client, :as => :autocomplete, :source_query => autocomplete_client_name_clients_url
Data will loaded from the server every time you type a new string in the field.
class ClientsController < ApplicationController
autocomplete :client, :name, { :display_value => 'name_with_birthdate', :full_model=>true }
...
end
# model
class Client < ActiveRecord::Base
def name_with_birthdate
firstname+', '+birthdate
end
end
You can write your own method to retrieve data instead of
autocomplete :client, :name
which actually generates method 'autocomplete_client_name'.
See example - https://github.com/maxivak/bootstrap3_autocomplete_input/wiki/search-custom-method
This gem uses the ORM library for ActiveRecord models included in rails3-jquery-autocomplete
Write tests