DashboardCode/BsMultiSelect

The change event is not firing

MikeAlhayek opened this issue · 18 comments

First of all, thanks a lot for this plugin!

I am trying to listen for the change event. Here is what I have done

// drilldown.js
const modelsMenu = document.querySelector('.drilldown-models-menu');

if(modelsMenu != null) {
    modelsMenu.addEventListener('change', e => {
        console.log('change was fired');
    });
}

// this works!
window.addEventListener("load", function () {
    var event = new Event('change');
    modelsMenu .dispatchEvent(event);
});
// activator.js
$(function () {
    // is there no way to this plugin without jQuery?
    $('.content-picker').each((_i, item) => {
        var menu = $(item);
        menu.bsMultiSelect({
            placeholder: menu.attr('placeholder') || ''
        });
    });

});

But for some reason the change event isn't firing. When I manually dispatch the change event on load, it works. But when I select an item from the content-picker menu the change event does not fire. Am I doing something wrong here?

I see you have initiated the plugin as $(element).bsMultiSelect() And if you initiate it as jQuery plugin, use jquery events $.on. They are not the same as DOM events, compleatly different beast. This is standard jquery plugins convention: jquery events for jquery plugin, dom events if you initiate without jquery. If you think I understand it wrong please show me popular plugins that do it different way (may be this is obsolette convention?).

@rpokrovskij
I guess I understood it wrong. Since this is a bootstrap 5 plug-in, is there a way to not depend on jQuery? Is there a way to initialize it using vanilla js without jQuery?

@rpokrovskij Thanks for your help. I am a bit struggling to initialize the plugin.
With out using Module/import, how can one create a new instance?

I am using gulp to concatenate js code into a single file. so in the gulp task I included BsMultiSelect.esm.js along with the below code. But, that did not work.

let menus = document.getElementsByClassName('content-picker');

for (let i = 0; i < menus.length; i++) {
    new BsMultiSelect(menus[i], {
        placeholder: menus[i].getAttribute('placeholder') || ''
    });
}

I also tried the following also, but that gives me dashboardcode undefined.

let menus = document.getElementsByClassName('content-picker');

for (let i = 0; i < menus.length; i++) {
    dashboardcode.BsMultiSelect(menus[i], {
        placeholder: menus[i].getAttribute('placeholder') || ''
    });
}

Thanks for your help

Use second method.

  1. https://dashboardcode.github.io/BsMultiSelect/snippetUmd.html

If you load UMD bundle (not ESM!) you have window.DashboardCode.BsMultiSelect as well as $.fn.BsMultiSelect (if there is $) - here I forward bootstrap 5 convention.

@rpokrovskij
I included node_modules/@dashboardcode/bsmultiselect/dist/js/BsMultiSelect.bs4.js << is this the UMD? I then added the following code

let menus = document.getElementsByClassName('content-picker');

for (let i = 0; i < menus.length; i++) {
    dashboardcode.BsMultiSelect(menus[i], {
        placeholder: menus[i].getAttribute('placeholder') || ''
    });
}

but that gives me an error

Uncaught ReferenceError: dashboardCode is not defined

yes this is umd (you can open it and see the UMD template at the start)

dashboardCode ?

I mean in error . Should be dashboardcode . https://dashboardcode.github.io/BsMultiSelect/snippetUmd.html (open as source also, it is also usefull)

@rpokrovskij Thanks for your continuous help!

Its running with no error now, but the change event isn't firing. here is the code that I am using

let menus = document.getElementsByClassName('content-picker');

for (let i = 0; i < menus.length; i++) {
    menus[i].addEventListener('change', (e) => {
        console.log('change event was fired for BsMultiSelect ....');
    });

    dashboardcode.BsMultiSelect(menus[i], {
        placeholder: menus[i].getAttribute('placeholder') || ''
    });

    console.log('registering BsMultiSelect...');
}

I am including node_modules/@dashboardcode/bsmultiselect/dist/js/BsMultiSelect.bs4.js

I am expecting the console.log('change event was fired for BsMultiSelect ....'); to get called every time I pick/remove an item from the menu. The BsMultiSelect is active on the element

Thank you for the report. It should work, going to check it.

It just works. Please fill snippet with yours specific https://codepen.io/rpokrovskij/pen/qBjJvwN

I guess it works with bs5 not 4. Here is a snippet where it does not work https://codepen.io/crestapps/pen/eYRPodN

No, it is just because of jquery is found (there I follow bootstrap - it have the same behaivour). Also bootstrap is seaching for 'data-bs-no-jquery' attribute to disable "attaching to jquery", decision which I do not like at all -> it forces plugins be loaded after body is loaded . But it is supported.

Option that I would prefer (if I need): load bsMultiSelect before jquery and after popper. Switch <script> order!

Also do you really need change as an event? It is "not versatile" snce would not work if you initiate DIV (and use js data) . I would in all casses inject my handler with options

e.g. in sample

dashboardcode.bsMultiSelect(e, {
              setSelected: function(option /*element*/, value /*true|false*/){
                 // to update other controls consider to call your handler through window.setTimer 0; removing it from current event loop 
                  if (value) 
                      // ..
                  else  
                      // ..
                 
              }
          }); 

But it doesn't work well "update all", "disable all" functionality. But again if you have this funcitonality - inject your code where you call "update all", "disable all".

What works best for you? What specific you consider.

@rpokrovskij
The reason why I want to use the change event, is because I have another script that should work with all selectors and listens for the change event. I tried to fire a custom event and add that custom ever to my other script but that did not work. by did not work i mean my other script did not fire when the custom ever was triggers

$(function () {

    let menus = document.getElementsByClassName('content-picker');

    for (let i = 0; i < menus.length; i++) {
        let element = menus[i];
        var menu = $(element);
        menu.bsMultiSelect({
            placeholder: menu.attr('placeholder') || ''
        });

        menu.on('change', (e) => {
            console.log('js change');
            var event = new Event('manualChange');
            element.dispatchEvent(event);
        });
    }

});

in the other script

const modelsMenu = document.querySelector('.drilldown-models-menu');

if (modelsMenu != null) {

    modelsMenu.addEventListener('change manualChange', e => {

     console.log('a change or manualChange...');
    });
}

but that either did not work.

Not sure what is the right approach here. my admin panel used bootstrap 4 but eventually will be converted to bootstrap 5. I need the js script to handle both scenarios so when I cover I Can just swap out dependencies.

bsmultiselect umd bundle for bootsrap 5 have the same logic - nothing would changed for you with migration to BS5 staying with UMD

let isJQyery = window.JQuery && !window.document.body.hasAttribute('data-bs-no-jquery'); // BS5 way
    if (isJQyery) {
        trigger = (e, eventName) => $(e).trigger(eventName);
    } else {
        trigger = composeEventTrigger(window);
    }

But I like your way "transform it to DOM event" : just stay with 'change' event not 'manualChange'. Why var event = new Event('manualChange');?

@rpokrovskij for some reason when I fire the new Event('change'); it seems to be going into infinite loop where the jquery calls the dom event and the dom event calls js change event. Is there a way to fix that?

Ok then this way doesn't work, remove it.
But as I said this works: just switch scripts places (put BsMultiSelect.js before jquery.js).

@rpokrovskij loading BsMultiSelect.js before jquery.js worked!

Thank a lot!

Thank you for trying BsMultiSelect.

you can go to my SO account https://stackoverflow.com/users/506147/roman-pokrovskij?tab=questions and "vote" for my questions - if I could get answers on them then BsMultiSelect could become better