Choices-js / Choices

A vanilla JS customisable select box/text input plugin ⚡️
https://choices-js.github.io/Choices/
MIT License
6.25k stars 616 forks source link

How to get a handle for an initialized Choices element #376

Closed aleehedl closed 6 years ago

aleehedl commented 6 years ago

If I have initialized my Choices element in the following way:

new Choices(document.querySelector(#choices'));

Is there a way to access the initialized Choices element somewhere else in the code if I don't have a reference to the original value? As far as I know, using new Choices always returns a new, uninitialized value.

The use case could be, for example, that I want to programmatically set the value (.setValue())

jshjohnson commented 6 years ago

Hey,

All you need to do is assign it to a variable e.g.

const instance = new Choices(document.querySelector(#choices'));

instance.setValue();

👍

aleehedl commented 6 years ago

Hi,

The point was exactly that I don't have the option to assign it to a variable.

On Wed, 28 Mar 2018, 17:31 Josh Johnson, notifications@github.com wrote:

Hey,

All you need to do is assign it to a variable e.g.

const instance = new Choices(document.querySelector(#choices'));instance.setValue(...);

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/jshjohnson/Choices/issues/376#issuecomment-376907996, or mute the thread https://github.com/notifications/unsubscribe-auth/AANfNycNy85QKI_GV30YXvXuVy0-GlBEks5ti569gaJpZM4S-tQH .

jshjohnson commented 6 years ago

Ahh I see, sorry.

As far as I know you could chain the method you want to call like:

new Choices(document.querySelector(#choices')).setValue();

There is no way of accessing an instance without referencing a variable as far as I know though.

aleehedl commented 6 years ago

For example, in FullCalendar you can get an instantiated calendar and then run methods, like this:

$('#calendar').fullCalendar().next();

You can try it in the console in the FullCalendar frontpage. A similar feature would be really useful with Choices.js.

jshjohnson commented 6 years ago

See my previous comment. They are behaving the same way. One is just using jQuery's API.

aleehedl commented 6 years ago

It does not behave the same way AFAIK. I can demonstrate my issue this way: I go to the Choices frontpage, open devtools console and type:

(new Choices('#choices-multiple-labels')).setValue(['Three'])

What I'd expect to happen is the option Three on the existing dropdown (initialized: true) to be selected. This does not happen.

fernandoagarcia commented 6 years ago

@aleehedl If for whatever reason you don't have access to the initialization code then you're better off just destroying then reinitializing ChoicesJS. Possible even removing the '.choices' element altogether. But on another note if you have the ability to access the code where you initialized when you wrote 'new Choices(document.querySelector(#choices'));' then you can it to a variable then push that variable to an array attached to window. I do this when I have to add my initializations via a for loop. Otherwise I think we need more context to help you out. A JS fiddle would help us troubleshoot your issue better.

ghost commented 6 years ago

I just add new instance to data with $(element).data('choices', choices); And then I retrieve it by var choices = $(element).data('choices');But this is not good solution. There should be some other way to do it.

pliebig commented 6 years ago

@aleehedl i totally agree to your point. i got the same problem, i want to destroy an existing choices element which is not saved in any variable. Every other library like slick, swiper or fullcalender offers the opportunity to call an existing element and run existing commands / methods. How does it work for choices?

jshjohnson commented 6 years ago

@pliebig @aleehedl The develop branch has been updated to return an array of instances 👍

heylookalive commented 6 years ago

@jshjohnson hiya, what's the name of the method added to get the instances?

fs86 commented 5 years ago

@jshjohnson hiya, what's the name of the method added to get the instances?

I would like to know that too ...

jshjohnson commented 5 years ago

@heylookalive @fs86 When you create a new instance of Choices it will now return an array of instances if it has created more than 1.

So:

const [instance1, instance2] = new Choices(document.querySelector(#choices'));
fs86 commented 5 years ago

Dosn't work for me. If i call new Choices() then my controls will get reinitialized.

jshjohnson commented 5 years ago

@fs86 Not going to be able to debug your code without seeing it!

fs86 commented 5 years ago

I wanted to create a small test project but I can't get it to work.

jsFunctions.js:

window.jsFunctions = {
  initializeChoices: function () {
    const controls = document.querySelectorAll('select');
    new Choices(controls, {
      searchEnabled: false,
      shouldSort: false
    });
  }
}

Error message:

Uncaught TypeError: Cannot read property 'element' of undefined
    at e.value (choices.min.js:2)
    at new e (choices.min.js:2)
    at Object.initializeChoices (jsFunctions.js:5)
    at HTMLDocument.<anonymous> (index.html:32)
fs86 commented 5 years ago

@jshjohnson I have created a small repo for you where you can see the issue. Instead of getting an instance of an already initialized select element, the element will get reinitialized and added to the DOM every time you hit the button.

Here is the repo: https://github.com/fs86/ChoicesTest

jshjohnson commented 5 years ago

@fs86 You are assigning the instance to a variable and not doing anything with it. You need to create a closure.

Something like:

let choices;

document.addEventListener('DOMContentLoaded', function () {
  choices = new Choices('select');
});
fs86 commented 5 years ago

Thanks for your help, but I get still the same result. The select box is added multiple times to the DOM. See my updated repo.

Edit: Ok I think I understand. Do I have to hold the instances in a global variable or so? Actually I need to retrieve an instance of an already initialized element without holding all instances in an array. Is this possible?

cheh commented 5 years ago

Anybody got it? How to retrieve an instance of an already initialized element? Or with global variables only?

msimcha commented 4 years ago

I found that you can set the value of an existing Choices instance that you cannot access but triggering the mousedown event on the item with the value for which you're looking:

handleSelect (obj, value) { let selectFields = obj.closest('.choices').querySelectorAll('.choices__item--selectable') for (let i = 0; i < selectFields.length; i++) { if (selectFields[i].dataset.value === value) { console.warn(selectFields[i].dataset.value) selectFields[i].dispatchEvent(new Event('mousedown')) } } } (obj here is a select element)

tristan-bellosta commented 2 years ago

Really a shame not to be able to retrieve the instance from the HTML element... :'(

herbert-van-Vliet commented 2 years ago

If you don't want to store the TomSelects in a global, you can keep the reference with the HTML element itself, like so: document.getElementById( '#list' )._TomSelect = new TomSelect( ....

and then later use e.g. document.getElementById( '#list' )._TomSelect.setValue( 'some value' )

HermanSchoenfeld commented 2 years ago

Pretty stupid they don't give you the instance. It should be attached to the DOM object like every other lib.

PINHOf commented 1 year ago

Pretty stupid they don't give you the instance. It should be attached to the DOM object like every other lib.

Fucking hell, I had to dig the whole internet to find out that this plugin developed by 1000 developers...doesn't give the instance. Seriously, how ridiculous this is...

ilkerccom commented 1 year ago

I found that you can set the value of an existing Choices instance that you cannot access but triggering the mousedown event on the item with the value for which you're looking:

handleSelect (obj, value) { let selectFields = obj.closest('.choices').querySelectorAll('.choices__item--selectable') for (let i = 0; i < selectFields.length; i++) { if (selectFields[i].dataset.value === value) { console.warn(selectFields[i].dataset.value) selectFields[i].dispatchEvent(new Event('mousedown')) } } } (obj here is a select element)

Thanks. You saved time! I made the code readable.

const selectInput = document.querySelector("#selectInput");
if (selectInput.tagName == 'SELECT') {
    const choiceItems = selectInput.closest(".choices").querySelectorAll(".choices__item--selectable");
    for (let i = 0; i < choiceItems.length; i++) {
        if (choiceItems[i].dataset.value === "{VALUE_TO_BE_SET}") {
            choiceItems[i].dispatchEvent(new Event('mousedown'));
        }
    }
}
dphaas2004 commented 1 year ago

@HermanSchoenfeld I agree. The workaround I have found for this was pretty simple. may not work for @aleehedl but for anyone with source code access should work fine. Just simply append the returned choices object to the element at initialization. let element = document.querySelector("#ChoicesDiv"); element.choices = new Choices(element, {});

from there you can run methods in other functions pretty easily.

https://codepen.io/dphaas2004/pen/xxMJYaV

hope it helps someone as I too spent most of my day searching the API and code for a way to to do this.

acenkus commented 11 months ago

@HermanSchoenfeld I agree. The workaround I have found for this was pretty simple. may not work for @aleehedl but for anyone with source code access should work fine. Just simply append the returned choices object to the element at initialization. let element = document.querySelector("#ChoicesDiv"); element.choices = new Choices(element, {});

from there you can run methods in other functions pretty easily.

https://codepen.io/dphaas2004/pen/xxMJYaV

hope it helps someone as I too spent most of my day searching the API and code for a way to to do this.

That's actually not bad solution, I think! Thanks!

ngohuytrieu commented 7 months ago

handleSelect (obj, value) { let selectFields = obj.closest('.choices').querySelectorAll('.choices__item--selectable') for (let i = 0; i < selectFields.length; i++) { if (selectFields[i].dataset.value === value) { console.warn(selectFields[i].dataset.value) selectFields[i].dispatchEvent(new Event('mousedown')) } } }

Dude, you saved my life. It took me days for this solution. Thanks bro!