UNPKG

aurelia-bootstrap-select

Version:
505 lines (399 loc) 16.7 kB
# Aurelia-Bootstrap-Select ### Introduction An Aurelia Custom Element for the 3rd party addon [Bootstrap-Select](http://silviomoreto.github.io/bootstrap-select/) ### Demo page [https://ghiscoding.github.io/Aurelia-Bootstrap-Plugins/#/aurelia/bootstrap-plugins/bootstrap-select](https://ghiscoding.github.io/Aurelia-Bootstrap-Plugins/#/aurelia/bootstrap-plugins/bootstrap-select) ### Screenshots Screenshots from the demo app ![Aurelia-Bootstrap-Select](/aurelia-bootstrap-select/printscreen/abp-select.jpg) ### Usage A quick example of the code in action. **Note**: We use the `collection.bind` attribute to pass the collection of all select options. ```html <abp-select collection.bind="allCampingStuff" selected-value.bind="camping" selected-item.bind="campingValue"></abp-select> ``` <a name="attributes"></a> ### Selected-Value vs Selected-Item For conveniencies, we provide 2 bindable attributes (both are `two-way` binding). The first `selected-value` provides a way to pull the index(es) or (option value), while the second binding `selected-item` is giving us the item(s) or most commonly the object(s) selected. **Note:** in a collection of strings only, these 2 bindable attributes would have the exact same output and so there isn't much benefit to use both in that particular case. Example: Given this collection of objects ```javascript let myCollection = [ { id: 1, option: 'Ketchup', company: 'Heinz' }, { id: 2, option: 'Mustard', company: 'French\'s' } ]; ``` ```html <abp-select picker-options.bind="selectOptions" selected-item.bind="condimentItem" selected-value.bind="condimentValue" collection.bind="myCollection" object-key="id"></abp-select> ``` _if the 2nd option is selected, the ouput would be:_ ```javascript selected-item // output --> { id: 2, option: 'Mustard', company: 'French\'s' } selected-value // output --> 2 ``` #### Use it in the View ```html <div> Value: ${condimentValue} Item: ${condimentItem | stringify} </div> ``` #### Use it in the ViewModel with the `changed` bindable event ```javascript export class Example { @bindable condimentItem; @bindable condimentValue; condimentItemChanged(newItem) { // output --> { id: 2, option: 'Mustard', company: 'French\'s' } console.log('Item changed:', newItem); } condimentValueChanged(newValue) { // output --> 2 console.log('Value changed:', newValue) } } ``` #### How to use it with a Complex Object? As per @Mobe91 comment, here's an example on how to use it with a complex object. ##### View ```html <abp-select element.bind="mypicker" selected-item.bind="condimentSelection.selectedCondimentItem" selected-value.bind="condimentSelection.selectedCondimentValue" collection.bind="allCampingStuff"> </abp-select> ``` ##### ViewModel ```javascript @autoinject export class MyController { bindingEngine: BindingEngine; allCampingStuff = [...]; // some collection condimentSelection = { selectedCondimentItem: null, selectedCondimentValue: null }; selectedCondimentItemSubscription: Subscription; constructor(bindingEngine: BindingEngine) { this.bindingEngine = bindingEngine; } attached() { let observer = this.bindingEngine.expressionObserver(this.condimentSelection, 'selectedCondimentItem'); this.selectedCondimentItemSubscription= observer.subscribe(newValue => this.selectedCondimentItemChanged(newValue)); } detached() { this.businessesSubscription.dispose(); } selectedCondimentItemChanged(newValue) { // handle value change } } ``` ### Available Options Every options of `Bootstrap Select` can be called through `picker-options.bind=""`. For the complete list, please visit the official site [Bootstrap Select - Options](http://silviomoreto.github.io/bootstrap-select/options/). **NOTE:** The picker options can also be defined globally through `main.js` via a `config.options` configuration, see [Global Options](#globaloption). To know which Global Options are available take a look at the [Global Options file](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/blob/master/aurelia-bootstrap-select/src/picker-global-options.js) Examples _from the View_ ```html <abp-select picker-options.bind="{ actionsBox: true }" ...></abp-select> ``` _or from the View and ViewModel_ ```html <abp-select picker-options.bind="selectOptions" ...></abp-select> ``` ```javascript export class Example { pickerOptions = { actionsBox: true, dropupAuto: true }; } ``` <a name="mapping"></a> ### Mapping Data Structure A default mapping data structure is used by the tool to apply certain styling or do certain action simply by reading a property pulled from the collection (for example, a property `disabled` in the collection can be used to disabled an option from the select). These mapping were created mainly for all the options that are available only as `data-zzz` (for examle `data-subtext`) that are defined in [Bootstrap-Select Examples](http://silviomoreto.github.io/bootstrap-select/examples/). The list of the mapping is the following ```javascript mappingDataStructure: { class: 'class', content: 'content', disabled: 'disabled', divider: 'divider', groupLabel: 'group', // used by optgroup groupDisabled: 'disabled', // used by optgroup icon: 'icon', maxOptions: 'maxOptions', // used by optgroup option: 'option', subtext: 'subtext', style: 'style', title: 'title', tokens: 'tokens' } ``` Example Let say we want to use subtext that is referenced in the collection has `company`, in that case we will want to rename the mapping from `subtext` to `company` (as shown in the example below). _from the View, use the `data-mapping-structure` attribute_ ```html <abp-select data-mapping-structure.bind="newMapping" collection.bind="allCondiments" ...></abp-select> ``` _in the ViewModel_ ```javascript // redefined the subtext mapping as company let newMapping = { subtext: 'company' }; // Given this collection of objects let myCollection = [ { id: 1, option: 'Ketchup', company: 'Heinz' }, { id: 2, option: 'Mustard', company: 'French\'s' } ]; ``` **NOTE:** The mapping data structure can also be defined globally through `main.js` via a `config.extra` configuration, see [Global Options](#globaloption) <a name="methods"></a> ### Available Methods/Functions Again every single methods which comes with `Bootstrap Select` are available. For the complete list, please visit the official site [Bootstrap Select - Functions](http://silviomoreto.github.io/bootstrap-select/methods/). To have access to the methods/functions, you will need to expose the element itself through `element.bind` that will then expose the methods _(also note that doing so will also give you access to `events`, `options` and `methods`)_. Example _View (exposing the element)_ ```html <abp-select element.bind="picker" ...></abp-select> ``` _ViewModel (calling the method)_ ```javascript export class Example { @bindable picker; pickerChanged() { this.picker.methods.selectAll(); } } ``` <a name="extramethods"></a> ### Extra Methods/Functions To provide more functionality, we added extra methods that are also exposed the same way as mentioned in previous section [Available Methods](#methods). The list of extra methods is the following: * `disableOptgroupByIndex(index, isDisable = true)` * `disableOptgroupByLabel(label, isDisable = true)` Example _ViewModel (calling the method)_ ```javascript export class Example { @bindable picker; pickerChanged() { // to disable the optgroup 'Breads', we can call this.picker.methods.disableOptgroupByLabel('Breads', true); } } ``` <a name="events"></a> ### Available Events Every events of `Bootstrap Select` are, as no surprises, available as well. For the complete list, please visit the official site [Bootstrap Select - Events](http://silviomoreto.github.io/bootstrap-select/options/#events). To have access to the `events`, you will need to expose the element itself through `element.bind` to expose the methods _(also note that doing so will also give you access to `events`, `options` and `methods`)_. **Note** The events are called with the syntax of `onEvent` which differs from the original syntax. Example, for the `dp.change`, we would use the `onChange` event. Example _View (exposing the element)_ ```html <abp-select element.bind="picker" ...></abp-select> ``` _ViewModel (calling the onEvent trigger)_ ```javascript export class Example { pickerChanged() { this.picker.events.onChanged = (e) => console.log('onChanged'); this.picker.events.onHide = (e) => console.log('onHide'); this.picker.events.onHidden = (e) => console.log('onHidden'); this.picker.events.onLoaded = (e) => console.log('onLoaded'); this.picker.events.onRendered = (e) => console.log('onRendered'); this.picker.events.onRefreshed = (e) => console.log('onRefreshed'); this.picker.events.onShow = (e) => console.log('onShow'); this.picker.events.onShown = (e) => console.log('onShown'); } } ``` ### Disabled There is multiple `disabled` options available. You can disable any of the following: * option * _by a mapping property in your collection (refer to [mapping data structure](#mapping))_ * optgroup * _by a [mapping](#mapping) property or a [method](#extramethods)_ * element (the custom element itself) * _by adding `disabled` directly on the element_ ### Multiple (select) To make the `Bootstrap Select` be a multi-select, simply add the `multiple="true"` (or just `multiple`) attribute to the element. _on View_ ```html <abp-select collection.bind="myCollection" multiple="true" ...></abp-select> ``` **Note:** The attribute is used as a boolean attribute, so passing `false` like `multiple="false"` will make the select act as a regular single select. ### Object-Key (attribute) When using a collection a objects, the tool will use an attribute called `object-key` (by default is set to `id`) to know which property of the object to do comparison Example: Given this collection of objects ```javascript let myCollection = [ { id: 1, option: 'Ketchup', company: 'Heinz' }, { id: 2, option: 'Mustard', company: 'French\'s' } ]; ``` _on View_ ```html <abp-select collection.bind="myCollection" object-key="id" ...></abp-select> ``` ### Optgroup To have optgroup in your select list, just use the attribute `has-optgroup="true"`. The optgroup will use `group` as the default mapping (if you want to change it, refer to [Mapping Data Structure](#mapping) by changing the `groupLabel` property) Example: Given this collection of objects, with a `group` property. ```javascript let myCollection = [ { id: 1, option: 'Ketchup', group: 'Condiments' }, { id: 2, option: 'Mustard', group: 'Condiments' }, { id: 10, option: 'Steam', group: 'Breads' }, { id: 12. option: 'Toasted', group: 'Breads' }, ]; ``` _in View, it will automatically use the `group` property_ ```html <abp-select collection.bind="allCondiments" has-optgroup="true"></abp-select> ``` **Note** The attribute is used as a boolean attribute, so passing `false` as `has-optgroup="false"` will not show the optgroup. ## Installation You can run the examples or build your own by doing the following. ### Aurelia-CLI / Webpack ```bash npm install --save aurelia-bootstrap-select ``` <a name="cli"></a> #### Aurelia-CLI For `CLI` you will need to add (`bootstrap-select` and `aurelia-bootstrap-select`) to your `aurelia.json` file. The exported class is `abp-select`. ```javascript { "name": "bootstrap-select", "main": "dist/js/bootstrap-select.js", "path": "../node_modules/bootstrap-select", "resources": [ "dist/css/bootstrap-select.min.css" ] }, { "name": "aurelia-bootstrap-select", "main": "index", "path": "../node_modules/aurelia-bootstrap-select/dist/amd", "resources": [ "**/*.{css,html}" ] }, ``` _index.html_ ```html <link rel="stylesheet" type="text/css" href="../node_modules/bootstrap-select/dist/css/bootstrap-select.min.css"> ``` ##### Bootstrap 4 An extra custom CSS file was added to address the Bootstrap 4 changes (until `Bootstrap-Select` comes out with version that supports it). If you followed the instruction earlier to modify the `aurelia.json` file then it should work without any modification, if it does not then make sure to have this portion (under the `aurelia-bootstrap-select` package): ```json "resources": [ "**/*.{css,html}" ] ``` <a name="webpack"></a> #### Aurelia-Webpack `Bootstrap-Select` and possibly others require to have the same `jQuery` accross the bundle. You will need to modify your `webpack.config.babel.js` for this to work correctly. ```diff const ENV... + const ProvidePlugin = require('webpack/lib/ProvidePlugin')('webpack/lib/ContextReplacementPlugin') let config = generateConfig( { entry: { 'app': ['./src/main' /* this is filled by the aurelia-webpack-plugin */], 'aurelia-bootstrap': coreBundles.bootstrap, 'aurelia': coreBundles.aurelia.filter(pkg => coreBundles.bootstrap.indexOf(pkg) === -1) }, output: { path: outDir }, + plugins: [ + new ProvidePlugin({ + $: "jquery", + jQuery: "jquery", + 'window.jQuery': 'jquery', + 'window.Tether': 'tether', + Tether: 'tether' + }) + ], + resolve: { + alias: { + // Force all modules to use the same jquery version. + 'jquery': path.join(__dirname, 'node_modules/jquery/src/jquery') + } + } }, ``` <a name="mainjs"></a> ### Aurelia (main.js) Make the plugin available globally in your `main.js` file. Please note the exported class is `abp-select` #### For WebPack only (main.js) ```javascript import 'bootstrap-select/dist/css/bootstrap-select.min.css'; ``` ##### Bootstrap 4 support An extra custom CSS file was added to address the changes (until `Bootstrap-Select` comes out with version that supports it, open BS4 issue [#1135](https://github.com/silviomoreto/bootstrap-select/issues/1135)), you can import this custom CSS file via: ```javascript import 'aurelia-bootstrap-select/dist/amd/bootstrap-select-bs4.css'; ``` #### CLI/WebPack (main.js) ```javascript export function configure(aurelia) { aurelia.use .standardConfiguration() .developmentLogging() .plugin('aurelia-bootstrap-select'); aurelia.start().then(() => aurelia.setRoot()); } ``` **Note on `aurelia-webpack-plugin 2.0`** If you started using the new `aurelia-webpack-plugin` version `2.0`, which is currently in [RC Pre-Release](https://github.com/aurelia/webpack-plugin/releases) and is already packaged in some of the [Aurelia Skeletons](https://github.com/aurelia/skeleton-navigation) (not all). You will have to use the `PLATFORM.ModuleName` wrapper. The previous code becomes: ```javascript aurelia.use.plugin(PLATFORM.moduleName('aurelia-bootstrap-select')); ``` <a name="globaloption"></a> ### Global Options You can change any of the global options directly in the `main.js` through a `config` as shown below: ```javascript export function configure(aurelia) { aurelia.use .standardConfiguration() .developmentLogging() .plugin('aurelia-bootstrap-select', config => { // extra attributes, with config.extra config.extra.mappingDataStructure = { subtext: 'company' }; // or any picker options, with config.options config.options.width = 'fit'; }); aurelia.start().then(() => aurelia.setRoot()); } ``` or with `aurelia-webpack-plugin 2.0` : ```javascript export function configure(aurelia) { aurelia.use.standardConfiguration().developmentLogging(); aurelia.use.plugin(PLATFORM.moduleName('aurelia-bootstrap-select'), config => { // extra attributes, with config.extra config.extra.mappingDataStructure = { subtext: 'company' }; // or any picker options, with config.options config.options.width = 'fit'; }); aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app'))); } ``` To know which Global Options are available take a look at the [Global Options file](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/blob/master/aurelia-bootstrap-select/src/picker-global-options.js) ## License [MIT License](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/blob/master/LICENSE) ## Contributions/Comments Contributions are welcome. This plugin was created to help the community (and myself), if you wish to suggest something and/or want to make a PR (Pull Request), please feel free to do so. ## Use it, like it? You like and use an `Aurelia-Bootstrap-Plugins`, please click on the :star: and spread the word.