mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-08 08:52:19 +02:00
[MAJOR] Remove .ajax
and allow async function for .setChoices
(#701)
* WIP: remove ajax * copy #700 * remove ajax, add fetchChoices * extend setChoices * update README
This commit is contained in:
parent
1f5192b4ad
commit
e3cc6eaf1b
63
README.md
63
README.md
|
@ -863,7 +863,9 @@ choices.disable();
|
|||
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Set choices of select input via an array of objects, a value name and a label name. This behaves the same as passing items via the `choices` option but can be called after initialising Choices. This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). Passing an empty array as the first parameter, and a true `replaceChoices` is the same as calling `clearChoices` (see below).
|
||||
**Usage:** Set choices of select input via an array of objects (or function that returns array of object or promise of it), a value field name and a label field name.
|
||||
|
||||
This behaves the similar as passing items via the `choices` option but can be called after initialising Choices. This can also be used to add groups of choices (see example 3); Optionally pass a true `replaceChoices` value to remove any existing choices. Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). Passing an empty array as the first parameter, and a true `replaceChoices` is the same as calling `clearChoices` (see below).
|
||||
|
||||
**Example 1:**
|
||||
|
||||
|
@ -887,6 +889,22 @@ example.setChoices(
|
|||
```js
|
||||
const example = new Choices(element);
|
||||
|
||||
// Passing a function that returns Promise of choices
|
||||
example.setChoices(async () => {
|
||||
try {
|
||||
const items = await fetch('/items');
|
||||
return items.json();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Example 3:**
|
||||
|
||||
```js
|
||||
const example = new Choices(element);
|
||||
|
||||
example.setChoices(
|
||||
[
|
||||
{
|
||||
|
@ -1009,49 +1027,6 @@ example.setChoiceByValue('Two'); // Choice with value of 'Two' has now been sele
|
|||
|
||||
**Usage:** Enables input to accept new values/select further choices.
|
||||
|
||||
### ajax(fn);
|
||||
|
||||
**Input types affected:** `select-one`, `select-multiple`
|
||||
|
||||
**Usage:** Populate choices/groups via a callback.
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
var example = new Choices(element);
|
||||
|
||||
example.ajax(function(callback) {
|
||||
fetch(url)
|
||||
.then(function(response) {
|
||||
response.json().then(function(data) {
|
||||
callback(data, 'value', 'label');
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Example 2:**
|
||||
If your structure differs from `data.value` and `data.key` structure you can write your own `key` and `value` into the `callback` function. This could be useful when you don't want to transform the given response.
|
||||
|
||||
```js
|
||||
const example = new Choices(element);
|
||||
|
||||
example.ajax(function(callback) {
|
||||
fetch(url)
|
||||
.then(function(response) {
|
||||
response.json().then(function(data) {
|
||||
callback(data, 'data.key', 'data.value');
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Browser compatibility
|
||||
|
||||
Choices is compiled using [Babel](https://babeljs.io/) to enable support for [ES5 browsers](http://caniuse.com/#feat=es5). If you need to support a browser that does not support one of the features listed below, I suggest including a polyfill from the very good [polyfill.io](https://cdn.polyfill.io/v2/docs/):
|
||||
|
|
|
@ -320,7 +320,7 @@
|
|||
</select>
|
||||
|
||||
<label for="choices-single-remove-xhr"
|
||||
>Options from remote source (XHR) & remove button</label
|
||||
>Options from remote source (Fetch API) & remove button</label
|
||||
>
|
||||
<select
|
||||
class="form-control"
|
||||
|
@ -627,17 +627,17 @@
|
|||
placeholder: true,
|
||||
placeholderValue: 'Pick an Strokes record',
|
||||
maxItemCount: 5,
|
||||
}).ajax(function(callback) {
|
||||
fetch(
|
||||
}).setChoices(function() {
|
||||
return fetch(
|
||||
'https://api.discogs.com/artists/55980/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW',
|
||||
)
|
||||
.then(function(response) {
|
||||
response.json().then(function(data) {
|
||||
callback(data.releases, 'title', 'title');
|
||||
});
|
||||
return response.json();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error(error);
|
||||
.then(function(data) {
|
||||
return data.releases.map(function(release) {
|
||||
return { value: release.title, label: release.title };
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -685,46 +685,39 @@
|
|||
|
||||
var singleFetch = new Choices('#choices-single-remote-fetch', {
|
||||
searchPlaceholderValue: 'Search for an Arctic Monkeys record',
|
||||
}).ajax(function(callback) {
|
||||
fetch(
|
||||
'https://api.discogs.com/artists/391170/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW',
|
||||
)
|
||||
.then(function(response) {
|
||||
response.json().then(function(data) {
|
||||
callback(data.releases, 'title', 'title');
|
||||
singleFetch.setChoiceByValue('Fake Tales Of San Francisco');
|
||||
})
|
||||
.setChoices(function() {
|
||||
return fetch(
|
||||
'https://api.discogs.com/artists/391170/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW',
|
||||
)
|
||||
.then(function(response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
return data.releases.map(function(release) {
|
||||
return { label: release.title, value: release.title };
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error(error);
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function(instance) {
|
||||
instance.setChoiceByValue('Fake Tales Of San Francisco');
|
||||
});
|
||||
|
||||
var singleXhrRemove = new Choices('#choices-single-remove-xhr', {
|
||||
removeItemButton: true,
|
||||
searchPlaceholderValue: "Search for a Smiths' record",
|
||||
}).ajax(function(callback) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open(
|
||||
'get',
|
||||
}).setChoices(function(callback) {
|
||||
return fetch(
|
||||
'https://api.discogs.com/artists/83080/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW',
|
||||
true,
|
||||
);
|
||||
request.onreadystatechange = function() {
|
||||
var status;
|
||||
var data;
|
||||
if (request.readyState === 4) {
|
||||
status = request.status;
|
||||
if (status === 200) {
|
||||
data = JSON.parse(request.responseText);
|
||||
callback(data.releases, 'title', 'title');
|
||||
singleXhrRemove.setChoiceByValue('How Soon Is Now?');
|
||||
} else {
|
||||
console.error(status);
|
||||
}
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
)
|
||||
.then(function(res) {
|
||||
return res.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
return data.releases.map(function(release) {
|
||||
return { label: release.title, value: release.title };
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var genericExamples = new Choices('[data-trigger]', {
|
||||
|
|
|
@ -1,28 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1,user-scalable=no"
|
||||
/>
|
||||
<title>Choices</title>
|
||||
<meta
|
||||
name="description"
|
||||
itemprop="description"
|
||||
content="A lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency."
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="../assets/images/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-32x32.png"
|
||||
sizes="32x32"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-16x16.png"
|
||||
sizes="16x16"
|
||||
/>
|
||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="../assets/images/safari-pinned-tab.svg"
|
||||
color="#00bcd4"
|
||||
/>
|
||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||
<meta
|
||||
name="msapplication-config"
|
||||
content="../assets/images/browserconfig.xml"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
|
||||
<title>Choices</title>
|
||||
<meta name=description itemprop=description content="A lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../assets/images/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="../assets/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="../assets/images/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="../assets/images/manifest.json">
|
||||
<link rel="mask-icon" href="../assets/images/safari-pinned-tab.svg" color="#00bcd4">
|
||||
<link rel="shortcut icon" href="../assets/images/favicon.ico">
|
||||
<meta name="msapplication-config" content="../assets/images/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<!-- Ignore these -->
|
||||
<link rel="stylesheet" href="../assets/styles/base.min.css?version=6.0.3" />
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Ignore these -->
|
||||
<link rel="stylesheet" href="../assets/styles/base.min.css?version=6.0.3">
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link rel="stylesheet" href="../assets/styles/choices.min.css?version=6.0.3">
|
||||
<script src="../assets/scripts/choices.min.js?version=6.0.3"></script>
|
||||
<!-- End Choices includes -->
|
||||
<!-- Choices includes -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../assets/styles/choices.min.css?version=6.0.3"
|
||||
/>
|
||||
<script src="../assets/scripts/choices.min.js?version=6.0.3"></script>
|
||||
<!-- End Choices includes -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -33,7 +63,12 @@
|
|||
<label for="choices-basic">Basic</label>
|
||||
<button class="disable push-bottom">Disable</button>
|
||||
<button class="enable push-bottom">Enable</button>
|
||||
<select class="form-control" name="choices-basic" id="choices-basic" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-basic"
|
||||
id="choices-basic"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Find me">Choice 3</option>
|
||||
|
@ -43,7 +78,12 @@
|
|||
|
||||
<div data-test-hook="remove-button">
|
||||
<label for="choices-remove-button">Remove button</label>
|
||||
<select class="form-control" name="choices-remove-button" id="choices-remove-button" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-remove-button"
|
||||
id="choices-remove-button"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -53,7 +93,12 @@
|
|||
|
||||
<div data-test-hook="disabled-choice">
|
||||
<label for="choices-disabled-choice">Disabled choice</label>
|
||||
<select class="form-control" name="choices-disabled-choice" id="choices-disabled-choice" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-disabled-choice"
|
||||
id="choices-disabled-choice"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -63,7 +108,12 @@
|
|||
|
||||
<div data-test-hook="add-items-disabled">
|
||||
<label for="choices-add-items-disabled">Add items disabled</label>
|
||||
<select class="form-control" name="choices-add-items-disabled" id="choices-add-items-disabled" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-add-items-disabled"
|
||||
id="choices-add-items-disabled"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1" selected>Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -72,7 +122,13 @@
|
|||
|
||||
<div data-test-hook="disabled-via-attr">
|
||||
<label for="choices-disabled-via-attr">Disabled via attribute</label>
|
||||
<select class="form-control" name="choices-disabled-via-attr" id="choices-disabled-via-attr" multiple disabled>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-disabled-via-attr"
|
||||
id="choices-disabled-via-attr"
|
||||
multiple
|
||||
disabled
|
||||
>
|
||||
<option value="Choice 1" selected>Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -81,7 +137,12 @@
|
|||
|
||||
<div data-test-hook="selection-limit">
|
||||
<label for="choices-selection-limit">Input limit</label>
|
||||
<select class="form-control" name="choices-selection-limit" id="choices-selection-limit" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-selection-limit"
|
||||
id="choices-selection-limit"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -93,7 +154,12 @@
|
|||
|
||||
<div data-test-hook="prepend-append">
|
||||
<label for="choices-prepend-append">Prepend/append</label>
|
||||
<select class="form-control" name="choices-prepend-append" id="choices-prepend-append" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-prepend-append"
|
||||
id="choices-prepend-append"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -102,7 +168,12 @@
|
|||
|
||||
<div data-test-hook="render-choice-limit">
|
||||
<label for="choices-render-choice-limit">Render choice limit</label>
|
||||
<select class="form-control" name="choices-render-choice-limit" id="choices-render-choice-limit" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-render-choice-limit"
|
||||
id="choices-render-choice-limit"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -111,7 +182,12 @@
|
|||
|
||||
<div data-test-hook="search-floor">
|
||||
<label for="choices-search-floor">Search floor</label>
|
||||
<select class="form-control" name="choices-search-floor" id="choices-search-floor" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-search-floor"
|
||||
id="choices-search-floor"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -120,7 +196,12 @@
|
|||
|
||||
<div data-test-hook="placeholder">
|
||||
<label for="choices-placeholder">Placeholder</label>
|
||||
<select class="form-control" name="choices-placeholder" id="choices-placeholder" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-placeholder"
|
||||
id="choices-placeholder"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -129,12 +210,22 @@
|
|||
|
||||
<div data-test-hook="remote-data">
|
||||
<label for="choices-remote-data">Remote data</label>
|
||||
<select class="form-control" name="choices-remote-data" id="choices-remote-data" multiple></select>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-remote-data"
|
||||
id="choices-remote-data"
|
||||
multiple
|
||||
></select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="scrolling-dropdown">
|
||||
<label for="choices-scrolling-dropdown">Scrolling dropdown</label>
|
||||
<select class="form-control" name="choices-scrolling-dropdown" id="choices-scrolling-dropdown" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-scrolling-dropdown"
|
||||
id="choices-scrolling-dropdown"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -155,7 +246,12 @@
|
|||
|
||||
<div data-test-hook="groups">
|
||||
<label for="choices-groups">Choice groups</label>
|
||||
<select class="form-control" name="choices-groups" id="choices-groups" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-groups"
|
||||
id="choices-groups"
|
||||
multiple
|
||||
>
|
||||
<optgroup label="UK">
|
||||
<option value="London">London</option>
|
||||
<option value="Manchester">Manchester</option>
|
||||
|
@ -171,26 +267,47 @@
|
|||
|
||||
<div data-test-hook="custom-properties">
|
||||
<label for="choices-custom-properties">Custom properties</label>
|
||||
<select class="form-control" name="choices-custom-properties" id="choices-custom-properties" multiple></select>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-custom-properties"
|
||||
id="choices-custom-properties"
|
||||
multiple
|
||||
></select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="non-string-values">
|
||||
<label for="choices-non-string-values">Non-string values</label>
|
||||
<select class="form-control" name="choices-non-string-values" id="choices-non-string-values"></select>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-non-string-values"
|
||||
id="choices-non-string-values"
|
||||
></select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="within-form">
|
||||
<form>
|
||||
<label for="choices-within-form">Within form</label>
|
||||
<select class="form-control" name="choices-within-form" id="choices-within-form" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-within-form"
|
||||
id="choices-within-form"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="set-choice-by-value">
|
||||
<label for="choices-set-choice-by-value">Dynamically set choice by value</label>
|
||||
<select class="form-control" name="choices-set-choice-by-value" id="choices-set-choice-by-value" multiple>
|
||||
<label for="choices-set-choice-by-value"
|
||||
>Dynamically set choice by value</label
|
||||
>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-set-choice-by-value"
|
||||
id="choices-set-choice-by-value"
|
||||
multiple
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -199,7 +316,12 @@
|
|||
|
||||
<div data-test-hook="search-by-label">
|
||||
<label for="choices-search-by-label">Search by label</label>
|
||||
<select class="form-control" name="choices-search-by-label" id="choices-search-by-label" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-search-by-label"
|
||||
id="choices-search-by-label"
|
||||
multiple
|
||||
>
|
||||
<option value="value1">label1</option>
|
||||
<option value="value2">label2</option>
|
||||
</select>
|
||||
|
@ -210,13 +332,17 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const choicesBasic = new Choices('#choices-basic');
|
||||
|
||||
document.querySelector('button.disable').addEventListener('click', () => {
|
||||
choicesBasic.disable();
|
||||
});
|
||||
document
|
||||
.querySelector('button.disable')
|
||||
.addEventListener('click', () => {
|
||||
choicesBasic.disable();
|
||||
});
|
||||
|
||||
document.querySelector('button.enable').addEventListener('click', () => {
|
||||
choicesBasic.enable();
|
||||
});
|
||||
document
|
||||
.querySelector('button.enable')
|
||||
.addEventListener('click', () => {
|
||||
choicesBasic.enable();
|
||||
});
|
||||
|
||||
new Choices('#choices-remove-button', {
|
||||
removeItemButton: true,
|
||||
|
@ -254,16 +380,9 @@
|
|||
|
||||
new Choices('#choices-remote-data', {
|
||||
shouldSort: false,
|
||||
}).ajax((callback) => {
|
||||
fetch('/data')
|
||||
.then((response) => {
|
||||
response.json().then((data) => {
|
||||
callback(data, 'value', 'label');
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}).setChoices(async () => {
|
||||
const data = await fetch('/data');
|
||||
return data.json();
|
||||
});
|
||||
|
||||
new Choices('#choices-scrolling-dropdown', {
|
||||
|
@ -331,10 +450,12 @@
|
|||
|
||||
new Choices('#choices-within-form');
|
||||
|
||||
new Choices('#choices-set-choice-by-value').setChoiceByValue('Choice 2');
|
||||
new Choices('#choices-set-choice-by-value').setChoiceByValue(
|
||||
'Choice 2',
|
||||
);
|
||||
|
||||
new Choices('#choices-search-by-label', { searchFields: ['label'] });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -1,28 +1,58 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1,user-scalable=no"
|
||||
/>
|
||||
<title>Choices</title>
|
||||
<meta
|
||||
name="description"
|
||||
itemprop="description"
|
||||
content="A lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency."
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="../assets/images/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-32x32.png"
|
||||
sizes="32x32"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="../assets/images/favicon-16x16.png"
|
||||
sizes="16x16"
|
||||
/>
|
||||
<link rel="manifest" href="../assets/images/manifest.json" />
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="../assets/images/safari-pinned-tab.svg"
|
||||
color="#00bcd4"
|
||||
/>
|
||||
<link rel="shortcut icon" href="../assets/images/favicon.ico" />
|
||||
<meta
|
||||
name="msapplication-config"
|
||||
content="../assets/images/browserconfig.xml"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
|
||||
<title>Choices</title>
|
||||
<meta name=description itemprop=description content="A lightweight, configurable select box/text input plugin. Similar to Select2 and Selectize but without the jQuery dependency.">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="../assets/images/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" href="../assets/images/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="../assets/images/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="../assets/images/manifest.json">
|
||||
<link rel="mask-icon" href="../assets/images/safari-pinned-tab.svg" color="#00bcd4">
|
||||
<link rel="shortcut icon" href="../assets/images/favicon.ico">
|
||||
<meta name="msapplication-config" content="../assets/images/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<!-- Ignore these -->
|
||||
<link rel="stylesheet" href="../assets/styles/base.min.css?version=6.0.3" />
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Ignore these -->
|
||||
<link rel="stylesheet" href="../assets/styles/base.min.css?version=6.0.3">
|
||||
<!-- End ignore these -->
|
||||
|
||||
<!-- Choices includes -->
|
||||
<link rel="stylesheet" href="../assets/styles/choices.min.css?version=6.0.3">
|
||||
<script src="../assets/scripts/choices.min.js?version=6.0.3"></script>
|
||||
<!-- End Choices includes -->
|
||||
<!-- Choices includes -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../assets/styles/choices.min.css?version=6.0.3"
|
||||
/>
|
||||
<script src="../assets/scripts/choices.min.js?version=6.0.3"></script>
|
||||
<!-- End Choices includes -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -43,7 +73,11 @@
|
|||
|
||||
<div data-test-hook="remove-button">
|
||||
<label for="choices-remove-button">Remove button</label>
|
||||
<select class="form-control" name="choices-remove-button" id="choices-remove-button">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-remove-button"
|
||||
id="choices-remove-button"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -53,7 +87,11 @@
|
|||
|
||||
<div data-test-hook="disabled-choice">
|
||||
<label for="choices-disabled-choice">Disabled choice</label>
|
||||
<select class="form-control" name="choices-disabled-choice" id="choices-disabled-choice">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-disabled-choice"
|
||||
id="choices-disabled-choice"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -63,7 +101,11 @@
|
|||
|
||||
<div data-test-hook="add-items-disabled">
|
||||
<label for="choices-add-items-disabled">Add items disabled</label>
|
||||
<select class="form-control" name="choices-add-items-disabled" id="choices-add-items-disabled">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-add-items-disabled"
|
||||
id="choices-add-items-disabled"
|
||||
>
|
||||
<option value="Choice 1" selected>Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -72,7 +114,12 @@
|
|||
|
||||
<div data-test-hook="disabled-via-attr">
|
||||
<label for="choices-disabled-via-attr">Disabled via attribute</label>
|
||||
<select class="form-control" name="choices-disabled-via-attr" id="choices-disabled-via-attr" disabled>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-disabled-via-attr"
|
||||
id="choices-disabled-via-attr"
|
||||
disabled
|
||||
>
|
||||
<option value="Choice 1" selected>Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -81,7 +128,11 @@
|
|||
|
||||
<div data-test-hook="prepend-append">
|
||||
<label for="choices-prepend-append">Prepend/append</label>
|
||||
<select class="form-control" name="choices-prepend-append" id="choices-prepend-append">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-prepend-append"
|
||||
id="choices-prepend-append"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -90,7 +141,11 @@
|
|||
|
||||
<div data-test-hook="render-choice-limit">
|
||||
<label for="choices-render-choice-limit">Render choice limit</label>
|
||||
<select class="form-control" name="choices-render-choice-limit" id="choices-render-choice-limit">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-render-choice-limit"
|
||||
id="choices-render-choice-limit"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -99,7 +154,11 @@
|
|||
|
||||
<div data-test-hook="search-disabled">
|
||||
<label for="choices-search-disabled">Search disabled</label>
|
||||
<select class="form-control" name="choices-search-disabled" id="choices-search-disabled">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-search-disabled"
|
||||
id="choices-search-disabled"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -108,7 +167,11 @@
|
|||
|
||||
<div data-test-hook="search-floor">
|
||||
<label for="choices-search-floor">Search floor</label>
|
||||
<select class="form-control" name="choices-search-floor" id="choices-search-floor">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-search-floor"
|
||||
id="choices-search-floor"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -117,12 +180,20 @@
|
|||
|
||||
<div data-test-hook="remote-data">
|
||||
<label for="choices-remote-data">Remote data</label>
|
||||
<select class="form-control" name="choices-remote-data" id="choices-remote-data"></select>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-remote-data"
|
||||
id="choices-remote-data"
|
||||
></select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="scrolling-dropdown">
|
||||
<label for="choices-scrolling-dropdown">Scrolling dropdown</label>
|
||||
<select class="form-control" name="choices-scrolling-dropdown" id="choices-scrolling-dropdown">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-scrolling-dropdown"
|
||||
id="choices-scrolling-dropdown"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -143,7 +214,12 @@
|
|||
|
||||
<div data-test-hook="groups">
|
||||
<label for="choices-groups">Choice groups</label>
|
||||
<select class="form-control" name="choices-groups" id="choices-groups" multiple>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-groups"
|
||||
id="choices-groups"
|
||||
multiple
|
||||
>
|
||||
<optgroup label="UK">
|
||||
<option value="London">London</option>
|
||||
<option value="Manchester">Manchester</option>
|
||||
|
@ -159,7 +235,11 @@
|
|||
|
||||
<div data-test-hook="parent-child">
|
||||
<label for="choices-parent">Parent</label>
|
||||
<select class="form-control" name="choices-parent" id="choices-parent">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-parent"
|
||||
id="choices-parent"
|
||||
>
|
||||
<option value="Parent choice 1">Parent choice 1</option>
|
||||
<option value="Parent choice 2">Parent choice 2</option>
|
||||
<option value="Parent choice 3">Parent choice 3</option>
|
||||
|
@ -175,26 +255,44 @@
|
|||
|
||||
<div data-test-hook="custom-properties">
|
||||
<label for="choices-custom-properties">Custom properties</label>
|
||||
<select class="form-control" name="choices-custom-properties" id="choices-custom-properties"></select>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-custom-properties"
|
||||
id="choices-custom-properties"
|
||||
></select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="non-string-values">
|
||||
<label for="choices-non-string-values">Non-string values</label>
|
||||
<select class="form-control" name="choices-non-string-values" id="choices-non-string-values"></select>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-non-string-values"
|
||||
id="choices-non-string-values"
|
||||
></select>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="within-form">
|
||||
<form>
|
||||
<label for="choices-within-form">Within form</label>
|
||||
<select class="form-control" name="choices-within-form" id="choices-within-form">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-within-form"
|
||||
id="choices-within-form"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div data-test-hook="set-choice-by-value">
|
||||
<label for="choices-set-choice-by-value">Dynamically set choice by value</label>
|
||||
<select class="form-control" name="choices-set-choice-by-value" id="choices-set-choice-by-value">
|
||||
<label for="choices-set-choice-by-value"
|
||||
>Dynamically set choice by value</label
|
||||
>
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-set-choice-by-value"
|
||||
id="choices-set-choice-by-value"
|
||||
>
|
||||
<option value="Choice 1">Choice 1</option>
|
||||
<option value="Choice 2">Choice 2</option>
|
||||
<option value="Choice 3">Choice 3</option>
|
||||
|
@ -203,7 +301,11 @@
|
|||
|
||||
<div data-test-hook="search-by-label">
|
||||
<label for="choices-search-by-label">Search by label</label>
|
||||
<select class="form-control" name="choices-search-by-label" id="choices-search-by-label">
|
||||
<select
|
||||
class="form-control"
|
||||
name="choices-search-by-label"
|
||||
id="choices-search-by-label"
|
||||
>
|
||||
<option value="value1">label1</option>
|
||||
<option value="value2">label2</option>
|
||||
</select>
|
||||
|
@ -214,13 +316,17 @@
|
|||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const choicesBasic = new Choices('#choices-basic');
|
||||
|
||||
document.querySelector('button.disable').addEventListener('click', () => {
|
||||
choicesBasic.disable();
|
||||
});
|
||||
document
|
||||
.querySelector('button.disable')
|
||||
.addEventListener('click', () => {
|
||||
choicesBasic.disable();
|
||||
});
|
||||
|
||||
document.querySelector('button.enable').addEventListener('click', () => {
|
||||
choicesBasic.enable();
|
||||
});
|
||||
document
|
||||
.querySelector('button.enable')
|
||||
.addEventListener('click', () => {
|
||||
choicesBasic.enable();
|
||||
});
|
||||
|
||||
new Choices('#choices-remove-button', {
|
||||
removeItemButton: true,
|
||||
|
@ -242,12 +348,12 @@
|
|||
});
|
||||
|
||||
new Choices('#choices-render-choice-limit', {
|
||||
renderChoiceLimit: 1
|
||||
renderChoiceLimit: 1,
|
||||
});
|
||||
|
||||
new Choices('#choices-search-disabled', {
|
||||
searchEnabled: false
|
||||
})
|
||||
searchEnabled: false,
|
||||
});
|
||||
|
||||
new Choices('#choices-search-floor', {
|
||||
searchFloor: 5,
|
||||
|
@ -255,16 +361,9 @@
|
|||
|
||||
new Choices('#choices-remote-data', {
|
||||
shouldSort: false,
|
||||
}).ajax((callback) => {
|
||||
fetch('/data')
|
||||
.then((response) => {
|
||||
response.json().then((data) => {
|
||||
callback(data, 'value', 'label');
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}).setChoices(async () => {
|
||||
const res = await fetch('/data');
|
||||
return res.json();
|
||||
});
|
||||
|
||||
new Choices('#choices-scrolling-dropdown', {
|
||||
|
@ -276,7 +375,7 @@
|
|||
const parent = new Choices('#choices-parent');
|
||||
const child = new Choices('#choices-child').disable();
|
||||
|
||||
parent.passedElement.element.addEventListener('change', (event) => {
|
||||
parent.passedElement.element.addEventListener('change', event => {
|
||||
if (event.detail.value === 'Parent choice 2') {
|
||||
child.enable();
|
||||
} else {
|
||||
|
@ -310,8 +409,8 @@
|
|||
customProperties: {
|
||||
country: 'Portugal',
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
new Choices('#choices-non-string-values', {
|
||||
|
@ -343,10 +442,12 @@
|
|||
|
||||
new Choices('#choices-within-form');
|
||||
|
||||
new Choices('#choices-set-choice-by-value').setChoiceByValue('Choice 2');
|
||||
new Choices('#choices-set-choice-by-value').setChoiceByValue(
|
||||
'Choice 2',
|
||||
);
|
||||
|
||||
new Choices('#choices-search-by-label', { searchFields: ['label'] });
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -31,7 +31,6 @@ import {
|
|||
sortByScore,
|
||||
generateId,
|
||||
findAncestorByAttrName,
|
||||
fetchFromObject,
|
||||
isIE11,
|
||||
existsInArray,
|
||||
cloneObject,
|
||||
|
@ -44,6 +43,10 @@ const USER_DEFAULTS = /** @type {Partial<import('../../types/index').Choices.Opt
|
|||
* Choices
|
||||
* @author Josh Johnson<josh@joshuajohnson.co.uk>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('../../types/index').Choices.Choice} Choice
|
||||
*/
|
||||
class Choices {
|
||||
/* ========================================
|
||||
= Static properties =
|
||||
|
@ -222,7 +225,7 @@ class Choices {
|
|||
}
|
||||
|
||||
/* ========================================
|
||||
= Public functions =
|
||||
= Public methods =
|
||||
======================================== */
|
||||
|
||||
init() {
|
||||
|
@ -460,9 +463,93 @@ class Choices {
|
|||
return this;
|
||||
}
|
||||
|
||||
setChoices(choices = [], value = '', label = '', replaceChoices = false) {
|
||||
if (!this._isSelectElement || !value) {
|
||||
return this;
|
||||
/**
|
||||
* Set choices of select input via an array of objects (or function that returns array of object or promise of it),
|
||||
* a value field name and a label field name.
|
||||
* This behaves the same as passing items via the choices option but can be called after initialising Choices.
|
||||
* This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices.
|
||||
* Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc).
|
||||
*
|
||||
* **Input types affected:** select-one, select-multiple
|
||||
*
|
||||
* @template {object[] | ((instance: Choices) => object[] | Promise<object[]>)} T
|
||||
* @param {T} [choicesArrayOrFetcher]
|
||||
* @param {string} [value = 'value'] - name of `value` field
|
||||
* @param {string} [label = 'label'] - name of 'label' field
|
||||
* @param {boolean} [replaceChoices = false] - whether to replace of add choices
|
||||
* @returns {this | Promise<this>}
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const example = new Choices(element);
|
||||
*
|
||||
* example.setChoices([
|
||||
* {value: 'One', label: 'Label One', disabled: true},
|
||||
* {value: 'Two', label: 'Label Two', selected: true},
|
||||
* {value: 'Three', label: 'Label Three'},
|
||||
* ], 'value', 'label', false);
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const example = new Choices(element);
|
||||
*
|
||||
* example.setChoices(async () => {
|
||||
* try {
|
||||
* const items = await fetch('/items');
|
||||
* return items.json()
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const example = new Choices(element);
|
||||
*
|
||||
* example.setChoices([{
|
||||
* label: 'Group one',
|
||||
* id: 1,
|
||||
* disabled: false,
|
||||
* choices: [
|
||||
* {value: 'Child One', label: 'Child One', selected: true},
|
||||
* {value: 'Child Two', label: 'Child Two', disabled: true},
|
||||
* {value: 'Child Three', label: 'Child Three'},
|
||||
* ]
|
||||
* },
|
||||
* {
|
||||
* label: 'Group two',
|
||||
* id: 2,
|
||||
* disabled: false,
|
||||
* choices: [
|
||||
* {value: 'Child Four', label: 'Child Four', disabled: true},
|
||||
* {value: 'Child Five', label: 'Child Five'},
|
||||
* {value: 'Child Six', label: 'Child Six', customProperties: {
|
||||
* description: 'Custom description about child six',
|
||||
* random: 'Another random custom property'
|
||||
* }},
|
||||
* ]
|
||||
* }], 'value', 'label', false);
|
||||
* ```
|
||||
*/
|
||||
setChoices(
|
||||
choicesArrayOrFetcher = [],
|
||||
value = 'value',
|
||||
label = 'label',
|
||||
replaceChoices = false,
|
||||
) {
|
||||
if (!this.initialised)
|
||||
throw new ReferenceError(
|
||||
`setChoices was called on a non-initialized instance of Choices`,
|
||||
);
|
||||
if (!this._isSelectElement)
|
||||
throw new TypeError(`setChoices can't be used with INPUT based Choices`);
|
||||
|
||||
if (typeof value !== 'string' || !value) {
|
||||
throw new TypeError(
|
||||
`value parameter must be a name of 'value' field in passed objects`,
|
||||
);
|
||||
}
|
||||
|
||||
// Clear choices if needed
|
||||
|
@ -470,6 +557,34 @@ class Choices {
|
|||
this.clearChoices();
|
||||
}
|
||||
|
||||
if (!Array.isArray(choicesArrayOrFetcher)) {
|
||||
if (typeof choicesArrayOrFetcher !== 'function')
|
||||
throw new TypeError(
|
||||
`.setChoices must be called either with array of choices with a function resulting into Promise of array of choices`,
|
||||
);
|
||||
|
||||
// it's a choices fetcher
|
||||
requestAnimationFrame(() => this._handleLoadingState(true));
|
||||
const fetcher = choicesArrayOrFetcher(this);
|
||||
if (typeof fetcher === 'object' && typeof fetcher.then === 'function') {
|
||||
// that's a promise
|
||||
return fetcher
|
||||
.then(data => this.setChoices(data, value, label, replaceChoices))
|
||||
.catch(err => {
|
||||
if (!this.config.silent) console.error(err);
|
||||
})
|
||||
.then(() => this._handleLoadingState(false))
|
||||
.then(() => this);
|
||||
}
|
||||
// function returned something else than promise, let's check if it's an array of choices
|
||||
if (!Array.isArray(fetcher))
|
||||
throw new TypeError(
|
||||
`.setChoices first argument function must return either array of choices or Promise, got: ${typeof fetcher}`,
|
||||
);
|
||||
// recursion with results, it's sync and choices were cleared already
|
||||
return this.setChoices(fetcher, value, label, false);
|
||||
}
|
||||
|
||||
this.containerOuter.removeLoadingState();
|
||||
const addGroupsAndChoices = groupOrChoice => {
|
||||
if (groupOrChoice.choices) {
|
||||
|
@ -492,7 +607,7 @@ class Choices {
|
|||
};
|
||||
|
||||
this._setLoading(true);
|
||||
choices.forEach(addGroupsAndChoices);
|
||||
choicesArrayOrFetcher.forEach(addGroupsAndChoices);
|
||||
this._setLoading(false);
|
||||
|
||||
return this;
|
||||
|
@ -519,18 +634,7 @@ class Choices {
|
|||
return this;
|
||||
}
|
||||
|
||||
ajax(fn) {
|
||||
if (!this.initialised || !this._isSelectElement || !fn) {
|
||||
return this;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => this._handleLoadingState(true));
|
||||
fn(this._ajaxCallback());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/* ===== End of Public functions ====== */
|
||||
/* ===== End of Public methods ====== */
|
||||
|
||||
/* =============================================
|
||||
= Private functions =
|
||||
|
@ -1054,55 +1158,6 @@ class Choices {
|
|||
};
|
||||
}
|
||||
|
||||
_ajaxCallback() {
|
||||
return (results, value, label) => {
|
||||
if (!results || !value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedResults = isType('Object', results) ? [results] : results;
|
||||
|
||||
if (
|
||||
parsedResults &&
|
||||
isType('Array', parsedResults) &&
|
||||
parsedResults.length
|
||||
) {
|
||||
// Remove loading states/text
|
||||
this._handleLoadingState(false);
|
||||
this._setLoading(true);
|
||||
// Add each result as a choice
|
||||
parsedResults.forEach(result => {
|
||||
if (result.choices) {
|
||||
this._addGroup({
|
||||
group: result,
|
||||
id: result.id || null,
|
||||
valueKey: value,
|
||||
labelKey: label,
|
||||
});
|
||||
} else {
|
||||
this._addChoice({
|
||||
value: fetchFromObject(result, value),
|
||||
label: fetchFromObject(result, label),
|
||||
isSelected: result.selected,
|
||||
isDisabled: result.disabled,
|
||||
customProperties: result.customProperties,
|
||||
placeholder: result.placeholder,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this._setLoading(false);
|
||||
|
||||
if (this._isSelectOneElement) {
|
||||
this._selectPlaceholderChoice();
|
||||
}
|
||||
} else {
|
||||
// No results, remove loading state
|
||||
this._handleLoadingState(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_searchChoices(value) {
|
||||
const newValue = isType('String', value) ? value.trim() : value;
|
||||
const currentValue = isType('String', this._currentValue)
|
||||
|
|
|
@ -881,84 +881,74 @@ describe('choices', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ajax', () => {
|
||||
const callbackoutput = 'worked';
|
||||
|
||||
let handleLoadingStateStub;
|
||||
let ajaxCallbackStub;
|
||||
|
||||
const returnsEarly = () => {
|
||||
it('returns early', () => {
|
||||
expect(handleLoadingStateStub.called).to.equal(false);
|
||||
expect(ajaxCallbackStub.called).to.equal(false);
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
handleLoadingStateStub = stub();
|
||||
ajaxCallbackStub = stub().returns(callbackoutput);
|
||||
|
||||
instance._ajaxCallback = ajaxCallbackStub;
|
||||
instance._handleLoadingState = handleLoadingStateStub;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
instance._ajaxCallback.reset();
|
||||
instance._handleLoadingState.reset();
|
||||
});
|
||||
|
||||
describe('setChoices with callback/Promise', () => {
|
||||
describe('not initialised', () => {
|
||||
beforeEach(() => {
|
||||
instance.initialised = false;
|
||||
output = instance.ajax(() => {});
|
||||
});
|
||||
|
||||
returnsInstance(output);
|
||||
returnsEarly();
|
||||
it('should throw', () => {
|
||||
expect(() => instance.setChoices(null)).Throw(ReferenceError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('text element', () => {
|
||||
beforeEach(() => {
|
||||
instance._isSelectElement = false;
|
||||
output = instance.ajax(() => {});
|
||||
});
|
||||
|
||||
returnsInstance(output);
|
||||
returnsEarly();
|
||||
it('should throw', () => {
|
||||
expect(() => instance.setChoices(null)).Throw(TypeError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing invalid function', () => {
|
||||
beforeEach(() => {
|
||||
output = instance.ajax(null);
|
||||
instance._isSelectElement = true;
|
||||
});
|
||||
|
||||
returnsInstance(output);
|
||||
returnsEarly();
|
||||
it('should throw on non function', () => {
|
||||
expect(() => instance.setChoices(null)).Throw(TypeError, /Promise/i);
|
||||
});
|
||||
|
||||
it(`should throw on function that doesn't return promise`, () => {
|
||||
expect(() => instance.setChoices(() => 'boo')).to.throw(
|
||||
TypeError,
|
||||
/promise/i,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('select element', () => {
|
||||
let callback;
|
||||
it('fetches and sets choices', async () => {
|
||||
document.body.innerHTML = '<select id="test" />';
|
||||
const choice = new Choices('#test');
|
||||
const handleLoadingStateSpy = spy(choice, '_handleLoadingState');
|
||||
|
||||
beforeEach(() => {
|
||||
instance.initialised = true;
|
||||
instance._isSelectElement = true;
|
||||
ajaxCallbackStub = stub();
|
||||
callback = stub();
|
||||
output = instance.ajax(callback);
|
||||
});
|
||||
|
||||
returnsInstance(output);
|
||||
|
||||
it('sets loading state', done => {
|
||||
requestAnimationFrame(() => {
|
||||
expect(handleLoadingStateStub.called).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('calls passed function with ajax callback', () => {
|
||||
expect(callback.called).to.equal(true);
|
||||
expect(callback.lastCall.args[0]).to.eql(callbackoutput);
|
||||
let fetcherCalled = false;
|
||||
const fetcher = async inst => {
|
||||
expect(inst).to.eq(choice);
|
||||
fetcherCalled = true;
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
return [
|
||||
{ label: 'l1', value: 'v1', customProperties: 'prop1' },
|
||||
{ label: 'l2', value: 'v2', customProperties: 'prop2' },
|
||||
];
|
||||
};
|
||||
expect(choice._store.choices.length).to.equal(0);
|
||||
const promise = choice.setChoices(fetcher);
|
||||
await new Promise(resolve =>
|
||||
requestAnimationFrame(() => {
|
||||
expect(handleLoadingStateSpy.callCount).to.equal(1);
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
expect(fetcherCalled).to.be.true;
|
||||
const res = await promise;
|
||||
expect(res).to.equal(choice);
|
||||
expect(choice._store.choices[1].value).to.equal('v2');
|
||||
expect(choice._store.choices[1].label).to.equal('l2');
|
||||
expect(choice._store.choices[1].customProperties).to.equal('prop2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1353,31 +1343,29 @@ describe('choices', () => {
|
|||
instance.containerOuter.removeLoadingState.reset();
|
||||
});
|
||||
|
||||
const returnsEarly = () => {
|
||||
it('returns early', () => {
|
||||
expect(addGroupStub.called).to.equal(false);
|
||||
expect(addChoiceStub.called).to.equal(false);
|
||||
expect(clearChoicesStub.called).to.equal(false);
|
||||
});
|
||||
};
|
||||
|
||||
describe('when element is not select element', () => {
|
||||
beforeEach(() => {
|
||||
instance._isSelectElement = false;
|
||||
instance.setChoices(choices, value, label, false);
|
||||
});
|
||||
|
||||
returnsEarly();
|
||||
it('throws', () => {
|
||||
expect(() =>
|
||||
instance.setChoices(choices, value, label, false),
|
||||
).to.throw(TypeError, /input/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe('passing invalid arguments', () => {
|
||||
describe('passing no value', () => {
|
||||
beforeEach(() => {
|
||||
instance._isSelectElement = true;
|
||||
instance.setChoices(choices, undefined, 'label', false);
|
||||
});
|
||||
|
||||
returnsEarly();
|
||||
it('throws', () => {
|
||||
expect(() =>
|
||||
instance.setChoices(choices, null, 'label', false),
|
||||
).to.throw(TypeError, /value/i);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -142,19 +142,6 @@ export const getWindowHeight = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const fetchFromObject = (object, path) => {
|
||||
const index = path.indexOf('.');
|
||||
|
||||
if (index > -1) {
|
||||
return fetchFromObject(
|
||||
object[path.substring(0, index)],
|
||||
path.substr(index + 1),
|
||||
);
|
||||
}
|
||||
|
||||
return object[path];
|
||||
};
|
||||
|
||||
export const isIE11 = () =>
|
||||
!!(
|
||||
navigator.userAgent.match(/Trident/) &&
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
sanitise,
|
||||
sortByAlpha,
|
||||
sortByScore,
|
||||
fetchFromObject,
|
||||
existsInArray,
|
||||
cloneObject,
|
||||
dispatchEvent,
|
||||
|
@ -198,19 +197,6 @@ describe('utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('fetchFromObject', () => {
|
||||
it('fetches value from object using given path', () => {
|
||||
const object = {
|
||||
band: {
|
||||
name: 'The Strokes',
|
||||
},
|
||||
};
|
||||
|
||||
const output = fetchFromObject(object, 'band.name');
|
||||
expect(output).to.equal(object.band.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('existsInArray', () => {
|
||||
it('determines whether a value exists within given array', () => {
|
||||
const values = [
|
||||
|
|
112
types/index.d.ts
vendored
112
types/index.d.ts
vendored
|
@ -872,16 +872,47 @@ export default class Choices {
|
|||
*/
|
||||
getValue(valueOnly?: boolean): string | string[];
|
||||
|
||||
/** Direct populate choices
|
||||
*
|
||||
* @param {string[] | Choices.Item[]} items
|
||||
*/
|
||||
setValue(items: string[] | Choices.Item[]): this;
|
||||
|
||||
/**
|
||||
* Set choices of select input via an array of objects, a value name and a label name.
|
||||
* Set value of input based on existing Choice. `value` can be either a single string or an array of strings
|
||||
*
|
||||
* **Input types affected:** select-one, select-multiple
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* const example = new Choices(element, {
|
||||
* choices: [
|
||||
* {value: 'One', label: 'Label One'},
|
||||
* {value: 'Two', label: 'Label Two', disabled: true},
|
||||
* {value: 'Three', label: 'Label Three'},
|
||||
* ],
|
||||
* });
|
||||
*
|
||||
* example.setChoiceByValue('Two'); // Choice with value of 'Two' has now been selected.
|
||||
* ```
|
||||
*/
|
||||
setChoiceByValue(value: string | string[]): this;
|
||||
|
||||
/**
|
||||
* Set choices of select input via an array of objects (or function that returns array of object or promise of it),
|
||||
* a value field name and a label field name.
|
||||
* This behaves the same as passing items via the choices option but can be called after initialising Choices.
|
||||
* This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices.
|
||||
* Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc).
|
||||
*
|
||||
* **Input types affected:** select-one, select-multiple
|
||||
*
|
||||
* @example Example 1:
|
||||
* ```
|
||||
* @param {string} [value = 'value'] - name of `value` field
|
||||
* @param {string} [label = 'label'] - name of 'label' field
|
||||
* @param {boolean} [replaceChoices = false] - whether to replace of add choices
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const example = new Choices(element);
|
||||
*
|
||||
* example.setChoices([
|
||||
|
@ -891,8 +922,22 @@ export default class Choices {
|
|||
* ], 'value', 'label', false);
|
||||
* ```
|
||||
*
|
||||
* @example Example 2:
|
||||
* @example
|
||||
* ```js
|
||||
* const example = new Choices(element);
|
||||
*
|
||||
* example.setChoices(async () => {
|
||||
* try {
|
||||
* const items = await fetch('/items');
|
||||
* return items.json()
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* const example = new Choices(element);
|
||||
*
|
||||
* example.setChoices([{
|
||||
|
@ -920,35 +965,14 @@ export default class Choices {
|
|||
* }], 'value', 'label', false);
|
||||
* ```
|
||||
*/
|
||||
setValue(args: string[]): this;
|
||||
|
||||
/**
|
||||
* Set value of input based on existing Choice. `value` can be either a single string or an array of strings
|
||||
*
|
||||
* **Input types affected:** select-one, select-multiple
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* const example = new Choices(element, {
|
||||
* choices: [
|
||||
* {value: 'One', label: 'Label One'},
|
||||
* {value: 'Two', label: 'Label Two', disabled: true},
|
||||
* {value: 'Three', label: 'Label Three'},
|
||||
* ],
|
||||
* });
|
||||
*
|
||||
* example.setChoiceByValue('Two'); // Choice with value of 'Two' has now been selected.
|
||||
* ```
|
||||
*/
|
||||
setChoiceByValue(value: string | string[]): this;
|
||||
|
||||
/** Direct populate choices */
|
||||
setChoices(
|
||||
choices: Choices.Choice[],
|
||||
value: string,
|
||||
label: string,
|
||||
setChoices<
|
||||
T extends object[] | ((instance: Choices) => object[] | Promise<object[]>)
|
||||
>(
|
||||
choices: T,
|
||||
value?: string,
|
||||
label?: string,
|
||||
replaceChoices?: boolean,
|
||||
): this;
|
||||
): T extends object[] ? this : Promise<this>;
|
||||
|
||||
/**
|
||||
* Clear all choices from select.
|
||||
|
@ -984,28 +1008,4 @@ export default class Choices {
|
|||
* **Input types affected:** text, select-one, select-multiple
|
||||
*/
|
||||
disable(): this;
|
||||
|
||||
/**
|
||||
* Populate choices/groups via a callback.
|
||||
*
|
||||
* **Input types affected:** select-one, select-multiple
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* var example = new Choices(element);
|
||||
*
|
||||
* example.ajax(function(callback) {
|
||||
* fetch(url)
|
||||
* .then(function(response) {
|
||||
* response.json().then(function(data) {
|
||||
* callback(data, 'value', 'label');
|
||||
* });
|
||||
* })
|
||||
* .catch(function(error) {
|
||||
* console.log(error);
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
ajax(fn: (values: any) => any): this;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue