errohitsinghal/yii2-tom-select
Composer 安装命令:
composer require errohitsinghal/yii2-tom-select
包简介
Tom Select widget for Yii2 Framework with Bootstrap 5 support
README 文档
README
A Yii2 widget for Tom Select - a dynamic, framework agnostic, and lightweight (~16kb gzipped) <select> UI control with autocomplete and native-feeling keyboard navigation.
Features
- ✅ Bootstrap 5 integration
- ✅ Local vendor files (no CDN dependencies)
- ✅ Full Tom Select API support
- ✅ All plugins included
- ✅ Model and non-model usage
- ✅ AJAX/Remote data loading
- ✅ Custom rendering templates
- ✅ Event handling
- ✅ Multi-select support
- ✅ Tagging/item creation
- ✅ Diacritics support
Requirements
- PHP >= 7.4
- Yii2 >= 2.0.45
- yii2-bootstrap5 >= 2.0.0
Installation
Install via Composer:
composer require errohitsinghal/yii2-tom-select
Quick Start
Basic Select
use errohitsinghal\tomselect\TomSelect; <?= TomSelect::widget([ 'model' => $model, 'attribute' => 'category_id', 'items' => [ '1' => 'Option 1', '2' => 'Option 2', '3' => 'Option 3', ], ]) ?>
Without Model
<?= TomSelect::widget([ 'name' => 'category', 'value' => 1, 'items' => [ '1' => 'Option 1', '2' => 'Option 2', '3' => 'Option 3', ], ]) ?>
Multi-Select
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'tags', 'items' => $tagList, 'multiple' => true, 'plugins' => ['remove_button', 'clear_button'], ]) ?>
Configuration Options
Widget Properties
| Property | Type | Default | Description |
|---|---|---|---|
type |
string | 'select' |
Input type: 'select' or 'text' |
items |
array | [] |
Option items for select element |
multiple |
bool | false |
Allow multiple selections |
placeholder |
string | null |
Placeholder text |
create |
bool/callable | false |
Allow creating new items |
maxItems |
int/null | 1 |
Maximum items (null for unlimited) |
maxOptions |
int/null | 50 |
Maximum options in dropdown |
plugins |
array | [] |
Tom Select plugins to enable |
clientOptions |
array | [] |
Tom Select configuration options |
clientEvents |
array | [] |
Event handlers |
useBaseAsset |
bool | false |
Use base asset (without plugins) |
hideSelected |
bool | null |
Hide selected items from dropdown |
closeAfterSelect |
bool | null |
Close dropdown after selection |
allowEmptyOption |
bool | false |
Allow empty option |
searchField |
string/array | null |
Fields to search in |
sortField |
string/array | null |
Fields to sort by |
valueField |
string | 'value' |
Property to use as value |
labelField |
string | 'text' |
Property to use as label |
diacritics |
bool | true |
Enable diacritics support |
loadThrottle |
int | 300 |
Load throttle in milliseconds |
preload |
bool/string | false |
Preload options (true, false, or 'focus') |
renderOption |
JsExpression | null |
Custom option render function |
renderItem |
JsExpression | null |
Custom item render function |
load |
JsExpression | null |
Remote data loading function |
variableName |
string | null |
JS variable name for instance |
Available Plugins
Tom Select comes with many built-in plugins:
| Plugin | Description |
|---|---|
caret_position |
Use ← and → arrow keys to move between items |
change_listener |
Update when underlying input changes |
checkbox_options |
Display checkboxes in dropdown |
clear_button |
Add button to clear all selections |
drag_drop |
Allow drag-n-drop sorting of items |
dropdown_header |
Add header to dropdown |
dropdown_input |
Move input to dropdown |
input_autogrow |
Auto-grow input width as user types |
no_active_items |
Disable selecting items |
no_backspace_delete |
Prevent backspace deletion |
optgroup_columns |
Display optgroups as columns |
remove_button |
Add remove button to each item |
restore_on_backspace |
Restore deleted items on backspace |
virtual_scroll |
Virtual scrolling for large lists |
Usage with ActiveForm
The widget integrates seamlessly with Yii2's ActiveForm. You have two options:
Option 1: Using the Widget Directly
use errohitsinghal\tomselect\TomSelect; use yii\bootstrap5\ActiveForm; <?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'category_id')->widget(TomSelect::class, [ 'items' => $categories, 'placeholder' => 'Select a category...', ]) ?> <?= $form->field($model, 'tags')->widget(TomSelect::class, [ 'items' => $tagList, 'multiple' => true, 'create' => true, 'plugins' => ['remove_button'], ]) ?> <?php ActiveForm::end(); ?>
Option 2: Using the ActiveField Extension
For a cleaner syntax, use the custom ActiveField class:
use errohitsinghal\tomselect\TomSelect; use errohitsinghal\tomselect\ActiveField; use yii\bootstrap5\ActiveForm; <?php $form = ActiveForm::begin([ 'fieldClass' => ActiveField::class, ]); ?> // Basic select <?= $form->field($model, 'category_id')->tomSelect($categories, [ 'placeholder' => 'Select a category...', ]) ?> // Multi-select with plugins <?= $form->field($model, 'tags')->tomSelect($tagList, [ 'multiple' => true, 'plugins' => ['remove_button', 'clear_button'], ]) ?> // Tagging field (pre-configured) <?= $form->field($model, 'keywords')->tomSelectTags() ?> // AJAX loading field <?= $form->field($model, 'user_id')->tomSelectAjax('/api/users/search', [ 'valueField' => 'id', 'labelField' => 'name', 'searchField' => ['name', 'email'], 'placeholder' => 'Search users...', ]) ?> <?php ActiveForm::end(); ?>
ActiveField Helper Methods
| Method | Description |
|---|---|
tomSelect($items, $options) |
Basic Tom Select field |
tomSelectTags($options) |
Pre-configured tagging field with remove buttons |
tomSelectAjax($url, $options) |
Pre-configured AJAX loading field |
Complete Form Example
<?php use errohitsinghal\tomselect\ActiveField; use yii\bootstrap5\ActiveForm; use yii\web\JsExpression; ?> <?php $form = ActiveForm::begin([ 'fieldClass' => ActiveField::class, ]); ?> <div class="row"> <div class="col-md-6"> <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?> </div> <div class="col-md-6"> <?= $form->field($model, 'status')->tomSelect([ 'draft' => 'Draft', 'published' => 'Published', 'archived' => 'Archived', ]) ?> </div> </div> <div class="row"> <div class="col-md-6"> <?= $form->field($model, 'category_id')->tomSelect($categories, [ 'placeholder' => 'Select category...', 'allowEmptyOption' => true, ]) ?> </div> <div class="col-md-6"> <?= $form->field($model, 'tags')->tomSelectTags([ 'placeholder' => 'Add tags...', ]) ?> </div> </div> <div class="row"> <div class="col-md-6"> <?= $form->field($model, 'author_id')->tomSelectAjax('/api/users/search', [ 'placeholder' => 'Search author...', ]) ?> </div> <div class="col-md-6"> <?= $form->field($model, 'related_posts')->tomSelect($posts, [ 'multiple' => true, 'maxItems' => 5, 'plugins' => ['remove_button'], 'placeholder' => 'Select up to 5 related posts...', ]) ?> </div> </div> <div class="form-group"> <?= \yii\bootstrap5\Html::submitButton('Save', ['class' => 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?>
Form Validation Styling
Tom Select automatically integrates with Bootstrap 5 validation states. When a field has validation errors, the Tom Select control will display with the appropriate error styling.
// The widget respects Yii2's validation <?= $form->field($model, 'category_id')->tomSelect($categories) ?> // Custom error display <?= $form->field($model, 'category_id', [ 'errorOptions' => ['class' => 'invalid-feedback d-block'], ])->tomSelect($categories) ?>
Usage Examples
With Option Groups
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'vehicle', 'items' => [ 'Cars' => [ 'toyota' => 'Toyota', 'honda' => 'Honda', 'ford' => 'Ford', ], 'Motorcycles' => [ 'yamaha' => 'Yamaha', 'kawasaki' => 'Kawasaki', ], ], ]) ?>
Tagging (Create New Items)
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'tags', 'type' => TomSelect::TYPE_TEXT, 'multiple' => true, 'create' => true, 'plugins' => ['remove_button'], 'clientOptions' => [ 'persist' => false, 'createOnBlur' => true, ], ]) ?>
With Plugins and Options
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'users', 'items' => $userList, 'multiple' => true, 'plugins' => [ 'remove_button' => [ 'title' => 'Remove this item', ], 'clear_button' => [ 'title' => 'Remove all items', ], 'dropdown_header' => [ 'title' => 'Select Users', ], ], ]) ?>
AJAX Remote Data Loading
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'user_id', 'placeholder' => 'Search for a user...', 'clientOptions' => [ 'valueField' => 'id', 'labelField' => 'name', 'searchField' => ['name', 'email'], 'load' => new \yii\web\JsExpression('function(query, callback) { if (!query.length) return callback(); fetch("/api/users/search?q=" + encodeURIComponent(query)) .then(response => response.json()) .then(json => callback(json.results)) .catch(() => callback()); }'), ], ]) ?>
With Custom Rendering
use yii\web\JsExpression; <?= TomSelect::widget([ 'model' => $model, 'attribute' => 'country_id', 'items' => $countries, 'renderOption' => new JsExpression('function(data, escape) { return "<div class=\"option\">" + "<img src=\"/img/flags/" + escape(data.value) + ".png\" class=\"me-2\" width=\"20\">" + "<span>" + escape(data.text) + "</span>" + "</div>"; }'), 'renderItem' => new JsExpression('function(data, escape) { return "<div class=\"item\">" + "<img src=\"/img/flags/" + escape(data.value) + ".png\" class=\"me-2\" width=\"16\">" + escape(data.text) + "</div>"; }'), ]) ?>
Event Handling
use yii\web\JsExpression; <?= TomSelect::widget([ 'model' => $model, 'attribute' => 'category', 'items' => $categories, 'clientEvents' => [ 'change' => new JsExpression('function(value) { console.log("Selected:", value); // Trigger dependent dropdown update updateSubcategories(value); }'), 'item_add' => new JsExpression('function(value, item) { console.log("Added item:", value); }'), 'dropdown_open' => new JsExpression('function(dropdown) { console.log("Dropdown opened"); }'), ], ]) ?>
Virtual Scroll for Large Datasets
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'product_id', 'placeholder' => 'Search products...', 'plugins' => ['virtual_scroll'], 'maxOptions' => null, 'clientOptions' => [ 'valueField' => 'id', 'labelField' => 'name', 'searchField' => 'name', 'firstUrl' => new JsExpression('function(query) { return "/api/products?q=" + encodeURIComponent(query) + "&page=1"; }'), 'load' => new JsExpression('function(query, callback) { const url = this.getUrl(query); fetch(url) .then(response => response.json()) .then(json => { this.setNextUrl(query, json.next_url); callback(json.results); }) .catch(() => callback()); }'), ], ]) ?>
Using HTML Options
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'status', 'items' => $statuses, 'options' => [ 'id' => 'custom-id', 'class' => 'custom-class', 'data-custom' => 'value', ], ]) ?>
Dependent/Chained Dropdowns
// Parent dropdown <?= TomSelect::widget([ 'model' => $model, 'attribute' => 'country_id', 'items' => $countries, 'variableName' => 'countrySelect', 'clientEvents' => [ 'change' => new JsExpression('function(value) { citySelect.clear(); citySelect.clearOptions(); if (value) { citySelect.load(value); } }'), ], ]) ?> // Child dropdown <?= TomSelect::widget([ 'model' => $model, 'attribute' => 'city_id', 'items' => [], 'variableName' => 'citySelect', 'placeholder' => 'Select country first...', 'clientOptions' => [ 'valueField' => 'id', 'labelField' => 'name', 'load' => new JsExpression('function(countryId, callback) { fetch("/api/cities?country_id=" + countryId) .then(response => response.json()) .then(json => callback(json)) .catch(() => callback()); }'), ], ]) ?>
Pre-populating with Initial Options (AJAX)
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'user_id', 'clientOptions' => [ 'valueField' => 'id', 'labelField' => 'name', 'searchField' => ['name', 'email'], // Pre-populate with selected option 'options' => [ [ 'id' => $model->user_id, 'name' => $model->user->name, 'email' => $model->user->email, ], ], 'items' => [$model->user_id], 'load' => new JsExpression('function(query, callback) { if (!query.length) return callback(); fetch("/api/users?q=" + encodeURIComponent(query)) .then(response => response.json()) .then(json => callback(json)) .catch(() => callback()); }'), ], ]) ?>
API Methods
After initialization, you can access the Tom Select instance through the variable name:
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'category', 'items' => $categories, 'variableName' => 'mySelect', ]) ?> <script> // Clear selection mySelect.clear(); // Add option dynamically mySelect.addOption({value: 'new', text: 'New Option'}); // Select an item mySelect.addItem('new'); // Remove option mySelect.removeOption('value'); // Get current value var value = mySelect.getValue(); // Set value mySelect.setValue('newValue'); // Lock/disable mySelect.lock(); mySelect.disable(); // Unlock/enable mySelect.unlock(); mySelect.enable(); // Destroy instance mySelect.destroy(); </script>
Using Base Asset (Smaller Footprint)
If you don't need all plugins, use the base asset:
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'category', 'items' => $categories, 'useBaseAsset' => true, ]) ?>
Client Options Reference
All Tom Select options are supported via the clientOptions property. Common options include:
| Option | Type | Default | Description |
|---|---|---|---|
options |
array | [] |
Initial options list |
items |
array | [] |
Initial selected values |
create |
bool/function | false |
Allow creating new items |
createOnBlur |
bool | false |
Create item when leaving field |
createFilter |
RegExp/string/function | null |
Filter for new items |
delimiter |
string | ',' |
Delimiter for multiple values |
highlight |
bool | true |
Highlight matches |
persist |
bool | true |
Persist created items |
openOnFocus |
bool | true |
Open dropdown on focus |
maxOptions |
int | 50 |
Max options to display |
maxItems |
int | null |
Max items to select |
hideSelected |
bool | null |
Hide selected from dropdown |
closeAfterSelect |
bool | undefined |
Close after selection |
allowEmptyOption |
bool | false |
Allow empty option |
loadThrottle |
int | 300 |
Throttle for load requests |
loadingClass |
string | 'loading' |
Loading state CSS class |
placeholder |
string | undefined |
Placeholder text |
preload |
bool/string | false |
Preload options |
dropdownParent |
string | null |
Dropdown container |
addPrecedence |
bool | false |
"Add..." is default selection |
selectOnTab |
bool | false |
Select with tab key |
diacritics |
bool | true |
International character support |
duplicates |
bool | false |
Allow duplicate selections |
Events Reference
| Event | Arguments | Description |
|---|---|---|
initialize |
- | Control initialized |
change |
value | Value changed |
focus |
- | Control focused |
blur |
- | Control blurred |
item_add |
value, item | Item added |
item_remove |
value, item | Item removed |
item_select |
item | Item selected |
clear |
- | Selection cleared |
option_add |
value, data | Option added |
option_remove |
value | Option removed |
option_clear |
- | All options cleared |
optgroup_add |
id, data | Option group added |
optgroup_remove |
id | Option group removed |
optgroup_clear |
- | All option groups cleared |
dropdown_open |
dropdown | Dropdown opened |
dropdown_close |
dropdown | Dropdown closed |
type |
str | User typing |
load |
data | Options loaded |
destroy |
- | Control destroyed |
Styling
The widget uses Bootstrap 5 themed CSS by default. You can customize the appearance:
Override CSS Variables
.ts-wrapper { --ts-pr-clear-button: 0; --ts-pr-caret: 0; --ts-pr-min: .75rem; } .ts-control { border-radius: 0.375rem; } .ts-dropdown { border-radius: 0.375rem; }
Custom CSS Classes
<?= TomSelect::widget([ 'model' => $model, 'attribute' => 'category', 'items' => $categories, 'options' => [ 'class' => 'my-custom-select', ], ]) ?>
Troubleshooting
Widget not initializing
Make sure jQuery is loaded if you're using it elsewhere. Tom Select doesn't require jQuery but may conflict if jQuery plugins modify the DOM.
Options not showing
Check that your items array is properly formatted:
'items' => [ 'value1' => 'Label 1', 'value2' => 'Label 2', ]
AJAX not working
Ensure your endpoint returns JSON in the expected format:
[
{"id": 1, "name": "Option 1"},
{"id": 2, "name": "Option 2"}
]
And configure valueField and labelField to match your data structure.
Multiple selection not working
Make sure multiple is set to true:
'multiple' => true,
Support
If you find this package helpful, consider buying me a coffee! ☕
License
This package is open-sourced software licensed under the MIT License.
Credits
- Tom Select - The underlying JavaScript library
- Yii2 Framework - The PHP framework
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Changelog
1.0.0
- Initial release
- Full Tom Select API support
- Bootstrap 5 integration
- All plugins included
- Comprehensive documentation
errohitsinghal/yii2-tom-select 适用场景与选型建议
errohitsinghal/yii2-tom-select 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 58 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 01 月 02 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「autocomplete」 「tagging」 「widget」 「select」 「yii2」 「selectize」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 errohitsinghal/yii2-tom-select 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 errohitsinghal/yii2-tom-select 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 errohitsinghal/yii2-tom-select 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
The Cassandra php driver library IDE stubs which enables autocompletion in modern IDEs.
Tags plugin for CakePHP
Yii2 LightBox image galary widget uses Lightbox v2.10.0 by Lokesh Dhakar
Indexed Search Autocomplete - Extends the TYPO3 Core Extension Indexed_Search searchform with an autocomplete feature.
Dadata API client (https://dadata.ru)
A patch for TYPO3 to easily add tags just like categories to any element
统计信息
- 总下载量: 58
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 30
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-01-02