Open vetinary opened 1 month ago
There have been several other similar issues (both in v3 and v4), but the bug is still not fixed. Temporary solution is customizing the #value
slot:
<template #value="slotProps">
<template v-if="slotProps.value">
<span style="color: var(--p-select-color)">{{ label }}</span>
</template>
<span v-else>
{{ slotProps.placeholder }}
</span>
</template>
@dzhebrak thank you for the suggestion.
It was the first thing I tried to apply to my code before reporting the issue.
Unfortunately, template for value
is not applicable for editable
mode, since the editable
mode involves usage of the InputText component, and template for value
is ignored in that mode.
[!IMPORTANT] It's because the label and editableInputValue computed properties uses the findSelectedOptionIndex() method, which only searches visibleOptions.
Consider creating a new method findSelectedOptionIndexAll() and have label and editableInputValue use that instead. I haven't done substantial testing, but from the few tests I have done, it doesn't look like it will have any adverse effect.
findSelectedOptionIndex() {
return this.hasSelectedOption ? this.options.findIndex((option) => this.isValidSelectedOption(option)) : -1;
}
[!TIP] Now, if you want a workaround... https://stackblitz.com/edit/vitejs-vite-bwrqua?file=src%2FApp.vue
First we'll have to ref the component
const comSelect = ref();
<ComponentSelect ref="comSelect" editable filter v-model="selectedId"...>
Then a couple of support functions.
whoCalledMe() to be able to figure out which functions calls our function overrides.
function waitForComSelect() {
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (comSelect.value) {
clearInterval(interval);
clearTimeout(timeout);
resolve();
}
}, 100); // Check every 100 miliseconds
const timeout = setTimeout(() => {
clearInterval(interval);
reject(
new Error(
'Timeout: comSelect did not load within 1 second.'
)
);
}, 1000); // Cancel after 100 miliseconds
});
};
function whoCalledMe() { const stack = new Error().stack.split('\n'); return stack.map(line => { const match = line.match(/at (\S+)/); return match ? match[1] : null; }).filter(name => name); };
Because **editableInputValue** is readonly, being a computed property, we will have to get creative.
- First wait for **comSelect** to be ready, and then override the functions.
- When **editableInputValue** calls **findSelectedOptionIndex()** we will return -2 to trick **editableInputValue** into calling **getOptionLabel**.
- Then when **editableInputValue** calls **getOptionLabel** we will use our own **findSelectedOptionIndexAll** and return the correct option.
```js
waitForComSelect()
.then(() => {
comSelect.value.findSelectedOptionIndexAll = function() {
return this.hasSelectedOption ? this.options.findIndex((option) => this.isValidSelectedOption(option)) : -1;
};
comSelect.value.findSelectedOptionIndex = function() {
const stackFunc = whoCalledMe();
// If calling function was editableInputValue()
if (stackFunc.includes('Proxy.editableInputValue')) {
// If hasSelectedOption return -2 to trick editableInputValue() to call getOptionLabel
// We will find the actual option when getOptionLabel is called
return this.hasSelectedOption ? -2 : -1;
} else {
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
}
};
comSelect.value.getOptionLabel = function(option) {
const stackFunc = whoCalledMe();
// If calling function was editableInputValue()
if (stackFunc.includes('Proxy.editableInputValue')) {
// Find the actual option and return it
const selectedOptionIndex = this.findSelectedOptionIndexAll();
return selectedOptionIndex !== -1
? (this.optionLabel ? resolveFieldData(this.options[selectedOptionIndex], this.optionLabel) : this.options[selectedOptionIndex])
: this.modelValue || '';
} else {
return this.optionLabel ? resolveFieldData(option, this.optionLabel) : option;
}
};
})
.catch((error) => {
console.log(error.message);
});
Describe the bug
I use Select component with both
editable
andfilter
properties enabled. When I just choose an existing option from the dropdown area, everything works great until I try to filter options: when I do that, text data in selected area is changed with the value of the selected option.Reproducer
https://stackblitz.com/edit/vitejs-vite-swaew4?file=src%2FApp.vue
PrimeVue version
4.0.0
Vue version
3.x
Language
ALL
Build / Runtime
Vite
Browser(s)
No response
Steps to reproduce the behavior
Expected behavior
Filter actions must not affect the area of the selected value