Neurosis Prevention Mechanism

    aurelia-bootstrap-select

    1.4.1 • Public • Published

    Aurelia-Bootstrap-Select

    Introduction

    An Aurelia Custom Element for the 3rd party addon Bootstrap-Select

    Demo page

    https://ghiscoding.github.io/Aurelia-Bootstrap-Plugins/#/aurelia/bootstrap-plugins/bootstrap-select

    Screenshots

    Screenshots from the demo app

    Aurelia-Bootstrap-Select

    Usage

    A quick example of the code in action.

    Note: We use the collection.bind attribute to pass the collection of all select options.

    <abp-select collection.bind="allCampingStuff" selected-value.bind="camping" selected-item.bind="campingValue"></abp-select>

    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

    let myCollection = [
        { id: 1, option: 'Ketchup', company: 'Heinz' },
        { id: 2, option: 'Mustard', company: 'French\'s' }
    ];
    <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:

    selected-item   // output --> { id: 2, option: 'Mustard', company: 'French\'s' }
    selected-value  // output --> 2

    Use it in the View

    <div>
      Value: ${condimentValue}
      Item: ${condimentItem | stringify}
    </div>

    Use it in the ViewModel with the changed bindable event

    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
    <abp-select element.bind="mypicker"
       selected-item.bind="condimentSelection.selectedCondimentItem"
       selected-value.bind="condimentSelection.selectedCondimentValue"
       collection.bind="allCampingStuff">
    </abp-select>
    ViewModel
    @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.

    NOTE: The picker options can also be defined globally through main.js via a config.options configuration, see Global Options. To know which Global Options are available take a look at the Global Options file

    Examples

    from the View

    <abp-select picker-options.bind="{ actionsBox: true }" ...></abp-select>

    or from the View and ViewModel

    <abp-select picker-options.bind="selectOptions" ...></abp-select>
    export class Example {
        pickerOptions = {
          actionsBox: true,
          dropupAuto: true
        };
    }

    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. The list of the mapping is the following

    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

    <abp-select data-mapping-structure.bind="newMapping" collection.bind="allCondiments" ...></abp-select>

    in the ViewModel

    // 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

    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.

    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)

    <abp-select element.bind="picker" ...></abp-select>

    ViewModel (calling the method)

    export class Example {
      @bindable picker;
     
      pickerChanged() {
        this.picker.methods.selectAll();
      }
    }

    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. The list of extra methods is the following:

    • disableOptgroupByIndex(index, isDisable = true)
    • disableOptgroupByLabel(label, isDisable = true)

    Example

    ViewModel (calling the method)

    export class Example {
      @bindable picker;
     
      pickerChanged() {
        // to disable the optgroup 'Breads', we can call
        this.picker.methods.disableOptgroupByLabel('Breads', true);
      }
    }

    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.

    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)

    <abp-select element.bind="picker" ...></abp-select>

    ViewModel (calling the onEvent trigger)

    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
    • optgroup
    • 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

    <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

    let myCollection = [
        { id: 1, option: 'Ketchup', company: 'Heinz' },
        { id: 2, option: 'Mustard', company: 'French\'s' }
    ];

    on View

    <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 by changing the groupLabel property)

    Example:

    Given this collection of objects, with a group property.

    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

    <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

    npm install --save aurelia-bootstrap-select

    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.

    {
      "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

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

      "resources"[
        "**/*.{css,html}"
      ]

    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.

    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')
    +     }
    +  }
    },

    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)

    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), you can import this custom CSS file via:

    import 'aurelia-bootstrap-select/dist/amd/bootstrap-select-bs4.css';

    CLI/WebPack (main.js)

    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 and is already packaged in some of the Aurelia Skeletons (not all). You will have to use the PLATFORM.ModuleName wrapper. The previous code becomes:

    aurelia.use.plugin(PLATFORM.moduleName('aurelia-bootstrap-select'));

    Global Options

    You can change any of the global options directly in the main.js through a config as shown below:

    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 :

    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

    License

    MIT 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 ⭐️ and spread the word.

    Install

    npm i aurelia-bootstrap-select

    DownloadsWeekly Downloads

    214

    Version

    1.4.1

    License

    MIT

    Unpacked Size

    171 kB

    Total Files

    42

    Last publish

    Collaborators

    • ghiscoding