mirror of
https://github.com/tenrok/vue-select.git
synced 2026-06-22 10:30:34 +03:00
feat: scope the no-options slot (#1083)
Resolves #1071, Resolves #1081 https://vue-select.org/guide/slots.html#improving-the-default-no-options-text
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
<v-select>
|
||||||
|
<template v-slot:no-options="{ search, searching }">
|
||||||
|
<template v-if="searching">
|
||||||
|
No results found for <em>{{ search }}</em>.
|
||||||
|
</template>
|
||||||
|
<em style="opacity: 0.5;" v-else>Start typing to search for a country.</em>
|
||||||
|
</template>
|
||||||
|
</v-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BetterNoOptions',
|
||||||
|
};
|
||||||
|
</script>
|
||||||
+69
-22
@@ -7,13 +7,16 @@ Slots can be used to change the look and feel of the UI, or to simply swap out t
|
|||||||
|
|
||||||
### `selected-option`
|
### `selected-option`
|
||||||
|
|
||||||
#### Scope:
|
#### Scope:
|
||||||
|
|
||||||
- `option {Object}` - A selected option
|
- `option {Object}` - A selected option
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<slot name="selected-option" v-bind="(typeof option === 'object')?option:{[label]: option}">
|
<slot
|
||||||
{{ getOptionLabel(option) }}
|
name="selected-option"
|
||||||
|
v-bind="(typeof option === 'object')?option:{[label]: option}"
|
||||||
|
>
|
||||||
|
{{ getOptionLabel(option) }}
|
||||||
</slot>
|
</slot>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -27,16 +30,32 @@ Slots can be used to change the look and feel of the UI, or to simply swap out t
|
|||||||
- `multiple {Boolean}` - If the component supports the selection of multiple values
|
- `multiple {Boolean}` - If the component supports the selection of multiple values
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<slot v-for="option in valueAsArray" name="selected-option-container"
|
<slot
|
||||||
:option="(typeof option === 'object')?option:{[label]: option}" :deselect="deselect" :multiple="multiple" :disabled="disabled">
|
v-for="option in valueAsArray"
|
||||||
<span class="selected-tag" v-bind:key="option.index">
|
name="selected-option-container"
|
||||||
<slot name="selected-option" v-bind="(typeof option === 'object')?option:{[label]: option}">
|
:option="(typeof option === 'object')?option:{[label]: option}"
|
||||||
{{ getOptionLabel(option) }}
|
:deselect="deselect"
|
||||||
</slot>
|
:multiple="multiple"
|
||||||
<button v-if="multiple" :disabled="disabled" @click="deselect(option)" type="button" class="close" aria-label="Remove option">
|
:disabled="disabled"
|
||||||
<span aria-hidden="true">×</span>
|
>
|
||||||
</button>
|
<span class="selected-tag" v-bind:key="option.index">
|
||||||
</span>
|
<slot
|
||||||
|
name="selected-option"
|
||||||
|
v-bind="(typeof option === 'object')?option:{[label]: option}"
|
||||||
|
>
|
||||||
|
{{ getOptionLabel(option) }}
|
||||||
|
</slot>
|
||||||
|
<button
|
||||||
|
v-if="multiple"
|
||||||
|
:disabled="disabled"
|
||||||
|
@click="deselect(option)"
|
||||||
|
type="button"
|
||||||
|
class="close"
|
||||||
|
aria-label="Remove option"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
</slot>
|
</slot>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -44,28 +63,56 @@ Slots can be used to change the look and feel of the UI, or to simply swap out t
|
|||||||
|
|
||||||
### `spinner`
|
### `spinner`
|
||||||
|
|
||||||
|
#### Scope:
|
||||||
|
|
||||||
|
- `loading {Boolean}` - if the component is in a loading state
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<slot name="spinner">
|
<slot name="spinner" v-bind="scope.spinner">
|
||||||
<div class="spinner" v-show="mutableLoading">Loading...</div>
|
<div class="vs__spinner" v-show="mutableLoading">Loading...</div>
|
||||||
</slot>
|
</slot>
|
||||||
```
|
```
|
||||||
|
|
||||||
### `no-options`
|
### `open-indicator`
|
||||||
|
|
||||||
```html
|
```js
|
||||||
<slot name="no-options">Sorry, no matching options.</slot>
|
attributes : {
|
||||||
|
'ref': 'openIndicator',
|
||||||
|
'role': 'presentation',
|
||||||
|
'class': 'vs__open-indicator',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<slot name="open-indicator" v-bind="scope.openIndicator">
|
||||||
|
<component :is="childComponents.OpenIndicator" v-if="!noDrop" v-bind="scope.openIndicator.attributes"/>
|
||||||
|
</slot>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dropdown
|
## Dropdown
|
||||||
|
|
||||||
### `option`
|
### `option`
|
||||||
|
|
||||||
#### Scope:
|
|
||||||
|
|
||||||
- `option {Object}` - The currently iterated option from `filteredOptions`
|
- `option {Object}` - The currently iterated option from `filteredOptions`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<slot name="option" v-bind="(typeof option === 'object')?option:{[label]: option}">
|
<slot
|
||||||
{{ getOptionLabel(option) }}
|
name="option"
|
||||||
|
v-bind="(typeof option === 'object')?option:{[label]: option}"
|
||||||
|
>
|
||||||
|
{{ getOptionLabel(option) }}
|
||||||
|
</slot>
|
||||||
|
```
|
||||||
|
|
||||||
|
### `no-options`
|
||||||
|
|
||||||
|
The no options slot is displayed in the dropdown when `filteredOptions.length === 0`.
|
||||||
|
|
||||||
|
- `search {String}` - the current search text
|
||||||
|
- `searching {Boolean}` - if the component has search text
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<slot name="no-options" v-bind="scope.noOptions">
|
||||||
|
Sorry, no matching options.
|
||||||
</slot>
|
</slot>
|
||||||
```
|
```
|
||||||
|
|||||||
+18
-8
@@ -1,22 +1,32 @@
|
|||||||
::: tip 🚧
|
::: tip 🚧
|
||||||
This section of the guide is a work in progress! Check back soon for an update.
|
This section of the guide is a work in progress! Check back soon for an update.
|
||||||
Vue Select currently offers quite a few scoped slots, and you can check out the
|
Vue Select currently offers quite a few scoped slots, and you can check out the
|
||||||
[API Docs for Slots](../api/slots.md) in the meantime while a good guide is put together.
|
[API Docs for Slots](../api/slots.md) in the meantime while a good guide is put together.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
#### Scoped Slot `option`
|
### Scoped Slot `option`
|
||||||
|
|
||||||
vue-select provides the scoped `option` slot in order to create custom dropdown templates.
|
vue-select provides the scoped `option` slot in order to create custom dropdown templates.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<v-select :options="options" label="title">
|
<v-select :options="options" label="title">
|
||||||
<template v-slot:option="option">
|
<template v-slot:option="option">
|
||||||
<span :class="option.icon"></span>
|
<span :class="option.icon"></span>
|
||||||
{{ option.title }}
|
{{ option.title }}
|
||||||
</template>
|
</template>
|
||||||
</v-select>
|
</v-select>
|
||||||
```
|
```
|
||||||
|
|
||||||
Using the `option` slot with props `"option"` provides the current option variable to the template.
|
Using the `option` slot with props `"option"` provides the current option variable to the template.
|
||||||
|
|
||||||
<CodePen url="NXBwYG" height="500"/>
|
<CodePen url="NXBwYG" height="500"/>
|
||||||
|
|
||||||
|
### Improving the default `no-options` text
|
||||||
|
|
||||||
|
The `no-options` slot is displayed in the dropdown when `filteredOptions === 0`. By default, it
|
||||||
|
displays _Sorry, no matching options_. You can add more contextual information by using the slot
|
||||||
|
in your own apps.
|
||||||
|
|
||||||
|
<BetterNoOptions />
|
||||||
|
|
||||||
|
<<< @/.vuepress/components/BetterNoOptions.vue
|
||||||
|
|||||||
@@ -70,8 +70,8 @@
|
|||||||
{{ getOptionLabel(option) }}
|
{{ getOptionLabel(option) }}
|
||||||
</slot>
|
</slot>
|
||||||
</li>
|
</li>
|
||||||
<li v-if="!filteredOptions.length" class="vs__no-options" @mousedown.stop="">
|
<li v-if="filteredOptions.length === 0" class="vs__no-options" @mousedown.stop="">
|
||||||
<slot name="no-options">Sorry, no matching options.</slot>
|
<slot name="no-options" v-bind="scope.noOptions">Sorry, no matching options.</slot>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1013,6 +1013,10 @@
|
|||||||
spinner: {
|
spinner: {
|
||||||
loading: this.mutableLoading
|
loading: this.mutableLoading
|
||||||
},
|
},
|
||||||
|
noOptions: {
|
||||||
|
search: this.search,
|
||||||
|
searching: this.searching,
|
||||||
|
},
|
||||||
openIndicator: {
|
openIndicator: {
|
||||||
attributes: {
|
attributes: {
|
||||||
'ref': 'openIndicator',
|
'ref': 'openIndicator',
|
||||||
|
|||||||
@@ -56,4 +56,20 @@ describe('Scoped Slots', () => {
|
|||||||
|
|
||||||
expect(Select.find({ref: 'dropdownMenu'}).text()).toEqual('onetwothree');
|
expect(Select.find({ref: 'dropdownMenu'}).text()).toEqual('onetwothree');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('noOptions slot receives the current search text', async () => {
|
||||||
|
const noOptions = jest.fn();
|
||||||
|
const Select = mountDefault({}, {
|
||||||
|
scopedSlots: {'no-options': noOptions},
|
||||||
|
});
|
||||||
|
|
||||||
|
Select.vm.search = 'something not there';
|
||||||
|
Select.vm.open = true;
|
||||||
|
await Select.vm.$nextTick();
|
||||||
|
|
||||||
|
expect(noOptions).toHaveBeenCalledWith({
|
||||||
|
search: 'something not there',
|
||||||
|
searching: true,
|
||||||
|
})
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user