diff --git a/packages/base/src/UI5Element.ts b/packages/base/src/UI5Element.ts index 09c2c192842e..5d0c9249beec 100644 --- a/packages/base/src/UI5Element.ts +++ b/packages/base/src/UI5Element.ts @@ -79,6 +79,14 @@ const defaultConverter = { if (type === Number) { return value === null ? undefined : parseFloat(value); } + + if (type === Object || type === Array) { + try { + return JSON.parse(value as string) as object | Array; + } catch { + return value; + } + } return value; }, toAttribute(value: unknown, type: unknown) { @@ -88,7 +96,7 @@ const defaultConverter = { // don't set attributes for arrays and objects if (type === Object || type === Array) { - return null; + return JSON.stringify(value); } // object, array, other diff --git a/packages/base/src/UI5ElementMetadata.ts b/packages/base/src/UI5ElementMetadata.ts index 86fd0562b5b1..b3538c5b4a9a 100644 --- a/packages/base/src/UI5ElementMetadata.ts +++ b/packages/base/src/UI5ElementMetadata.ts @@ -133,7 +133,7 @@ class UI5ElementMetadata { */ hasAttribute(propName: string): boolean { const propData = this.getProperties()[propName]; - return propData.type !== Object && propData.type !== Array && !propData.noAttribute; + return propData.type !== Object && !propData.noAttribute; } /** diff --git a/packages/main/src/ComboBox.ts b/packages/main/src/ComboBox.ts index efc2eb1ec86f..51a7ddd08a7d 100644 --- a/packages/main/src/ComboBox.ts +++ b/packages/main/src/ComboBox.ts @@ -100,6 +100,7 @@ const SKIP_ITEMS_SIZE = 10; */ interface IComboBoxItem extends UI5Element { text?: string, + value?: string, headerText?: string, focused: boolean, isGroupItem?: boolean, @@ -236,6 +237,23 @@ class ComboBox extends UI5Element implements IFormInputElement { @property() value = ""; + /** + * Defines the value and the selected item of the component. + * Example: ["fieldValue", "value"]. + * Where "fieldValue" is the value of the input and "value" is the value of the selected item. + * + * @detault undefined + * @public + */ + @property({ type: Array }) + set complexValue(value: Array) { + this._useComplexValue = true; + this._complexValue = value; + } + get complexValue() : Array { + return this._complexValue; + } + /** * Determines the name by which the component will be identified upon submission in an HTML form. * @@ -367,7 +385,7 @@ class ComboBox extends UI5Element implements IFormInputElement { @property({ type: Boolean, noAttribute: true }) _iconPressed = false; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _filteredItems: Array = []; @property({ type: Number, noAttribute: true }) @@ -458,6 +476,8 @@ class ComboBox extends UI5Element implements IFormInputElement { _lastValue: string; _selectedItemText = ""; _userTypedValue = ""; + _useComplexValue = false; + _complexValue: Array = []; _valueStateLinks: Array = []; _composition?: InputComposition; @i18n("@ui5/webcomponents") @@ -469,7 +489,7 @@ class ComboBox extends UI5Element implements IFormInputElement { } get formValidity(): ValidityStateFlags { - return { valueMissing: this.required && !this.value }; + return { valueMissing: this.required && !this.getValue() }; } async formElementAnchor() { @@ -477,7 +497,7 @@ class ComboBox extends UI5Element implements IFormInputElement { } get formFormattedValue() { - return this.value; + return this.getValue(); } constructor() { @@ -488,7 +508,7 @@ class ComboBox extends UI5Element implements IFormInputElement { } onBeforeRendering() { - this._effectiveShowClearIcon = (this.showClearIcon && !!this.value && !this.readonly && !this.disabled); + this._effectiveShowClearIcon = (this.showClearIcon && !!this.getValue() && !this.readonly && !this.disabled); if (this._initialRendering || this.filter === "None") { this._filteredItems = this.items; @@ -502,7 +522,7 @@ class ComboBox extends UI5Element implements IFormInputElement { const hasNoVisibleItems = !this._filteredItems.length || !this._filteredItems.some(i => i._isVisible); // If there is no filtered items matching the value, show all items when the arrow is pressed - if (((hasNoVisibleItems && !isPhone()) && this.value)) { + if (((hasNoVisibleItems && !isPhone()) && this.getValue())) { this.items.forEach(this._makeAllVisible.bind(this)); this._filteredItems = this.items; } @@ -527,9 +547,21 @@ class ComboBox extends UI5Element implements IFormInputElement { return slottedIconsCount + clearIconCount + arrowDownIconsCount; } + setValue(value: string, key = "") { + if (this._useComplexValue) { + this.complexValue = [value, key]; + } else { + this.value = value; + } + } + + getValue() { + return this._useComplexValue ? this.complexValue[0] : this.value; + } + onAfterRendering() { - if (this.inner && this.value !== this.inner.value) { - this.value = this.inner.value; + if (this.inner && this.getValue() !== this.inner.value) { + this.setValue(this.inner.value); } this.storeResponsivePopoverWidth(); @@ -555,10 +587,10 @@ class ComboBox extends UI5Element implements IFormInputElement { this._autocomplete = false; if (!e.relatedTarget || (e.relatedTarget !== this.shadowRoot!.querySelector(".ui5-input-clear-icon"))) { - this._lastValue = this.value; + this._lastValue = this.getValue(); } - !isPhone() && (e.target as HTMLInputElement).setSelectionRange(0, this.value.length); + !isPhone() && (e.target as HTMLInputElement).setSelectionRange(0, this.getValue().length); } _focusout(e: FocusEvent) { @@ -587,7 +619,7 @@ class ComboBox extends UI5Element implements IFormInputElement { _beforeOpenPopover() { if (isPhone()) { - this._getPickerInput().value = this.value; + this._getPickerInput().value = this.getValue(); } } @@ -618,7 +650,7 @@ class ComboBox extends UI5Element implements IFormInputElement { } if (this._selectionPerformed) { - this._lastValue = this.value; + this._lastValue = this.getValue(); this._selectionPerformed = false; } @@ -660,7 +692,7 @@ class ComboBox extends UI5Element implements IFormInputElement { _resetFilter() { this._userTypedValue = ""; - this.inner.setSelectionRange(0, this.value.length); + this.inner.setSelectionRange(0, this.getValue().length); this._filteredItems = this._filterItems(""); this._selectMatchingItem(); } @@ -682,8 +714,8 @@ class ComboBox extends UI5Element implements IFormInputElement { this.inner.focus(); this._resetFilter(); - if (isPhone() && this.value && !this._lastValue) { - this._lastValue = this.value; + if (isPhone() && this.getValue() && !this._lastValue) { + this._lastValue = this.getValue(); } this._toggleRespPopover(); @@ -691,7 +723,7 @@ class ComboBox extends UI5Element implements IFormInputElement { _handleMobileKeydown(e: KeyboardEvent) { if (isEscape(e)) { - this.value = this._lastValue || ""; + this.setValue(this._lastValue || ""); this.filterValue = this._lastValue || ""; this._closeRespPopover(); } @@ -700,7 +732,7 @@ class ComboBox extends UI5Element implements IFormInputElement { _handleMobileInput(e: CustomEvent) { const { target } = e; this.filterValue = (target as Input).value; - this.value = (target as Input).value; + this.setValue((target as Input).value); this.fireDecoratorEvent("input"); } @@ -716,7 +748,7 @@ class ComboBox extends UI5Element implements IFormInputElement { this._filteredItems = this._filterItems(value); - this.value = value; + this.setValue(value); this.filterValue = value; this._clearFocus(); @@ -761,7 +793,8 @@ class ComboBox extends UI5Element implements IFormInputElement { } _startsWithMatchingItems(str: string): Array { - const allItems:Array = this._getItems().filter(item => !isInstanceOfComboBoxItemGroup(item)); + const allItems:Array = this._getItems().slice(this._getItems().findIndex(item => item.focused === true), this._getItems().length); + return Filters.StartsWith(str, allItems, "text"); } @@ -796,7 +829,7 @@ class ComboBox extends UI5Element implements IFormInputElement { handleNavKeyPress(e: KeyboardEvent) { const allItems = this._getItems(); - if (this.focused && (isHome(e) || isEnd(e)) && this.value) { + if (this.focused && (isHome(e) || isEnd(e)) && this.getValue()) { return; } @@ -845,13 +878,13 @@ class ComboBox extends UI5Element implements IFormInputElement { if (this.open) { this._itemFocused = true; - this.value = isGroupItem ? "" : currentItem.text!; + this.setValue(isGroupItem ? "" : currentItem.text!); this.focused = false; currentItem.focused = true; } else { this.focused = true; - this.value = isGroupItem ? nextItem.text! : currentItem.text!; + this.setValue(isGroupItem ? nextItem.text! : currentItem.text!); currentItem.focused = false; } @@ -862,7 +895,7 @@ class ComboBox extends UI5Element implements IFormInputElement { return; } // autocomplete - this._handleTypeAhead(this.value, this.open ? this._userTypedValue : ""); + this._handleTypeAhead(this.getValue(), this.open ? this._userTypedValue : ""); this.fireDecoratorEvent("input"); } @@ -897,7 +930,7 @@ class ComboBox extends UI5Element implements IFormInputElement { if (this.hasValueStateText && isOpen) { this._filteredItems[0].selected = false; - this.value = this._userTypedValue; + this.setValue(this._userTypedValue); } return; @@ -937,7 +970,7 @@ class ComboBox extends UI5Element implements IFormInputElement { } _keyup() { - this._userTypedValue = this.value.substring(0, this.inner.selectionStart || 0); + this._userTypedValue = this.getValue().substring(0, this.inner.selectionStart || 0); } _keydown(e: KeyboardEvent) { @@ -969,7 +1002,7 @@ class ComboBox extends UI5Element implements IFormInputElement { if (this.open && !focusedItem?.isGroupItem) { this._closeRespPopover(); this.focused = true; - this.inner.setSelectionRange(this.value.length, this.value.length); + this.inner.setSelectionRange(this.getValue().length, this.getValue().length); } else if (this._internals.form) { submitForm(this); } @@ -977,9 +1010,9 @@ class ComboBox extends UI5Element implements IFormInputElement { if (isEscape(e)) { this.focused = true; - const shouldResetValueAndStopPropagation = !this.open && this.value !== this._lastValue; + const shouldResetValueAndStopPropagation = !this.open && this.getValue() !== this._lastValue; if (shouldResetValueAndStopPropagation) { - this.value = this._lastValue; + this.setValue(this._lastValue); // stop propagation to prevent closing the popup when using the combobox inside it e.stopPropagation(); } @@ -1003,7 +1036,7 @@ class ComboBox extends UI5Element implements IFormInputElement { this._itemFocused = true; selectedItem.focused = true; this.focused = false; - } else if (this.open && this._filteredItems.length && !this.value.length) { + } else if (this.open && this._filteredItems.length && !this.getValue().length) { // If no item is selected, select the first non-group item on "Show" (F4, Alt+Up/Down) const firstNonGroupItem = this._getItems().findIndex(item => item._isVisible && !item.isGroupItem); this._handleItemNavigation(e, firstNonGroupItem, true /* isForward */); @@ -1091,10 +1124,10 @@ class ComboBox extends UI5Element implements IFormInputElement { _closeRespPopover(e?: Event | null) { if ((e && (e.target as HTMLElement).classList.contains("ui5-responsive-popover-close-btn"))) { if (this._selectedItemText) { - this.value = this._selectedItemText; + this.setValue(this._selectedItemText); this.filterValue = this._selectedItemText; } else { - this.value = this._lastValue || ""; + this.setValue(this._lastValue || ""); this.filterValue = this._lastValue || ""; } } @@ -1149,7 +1182,7 @@ class ComboBox extends UI5Element implements IFormInputElement { const currentlyFocusedItem = allItems.find(item => item.focused === true); if (currentlyFocusedItem?.isGroupItem) { - this.value = this.filterValue; + this.setValue(this.filterValue); return; } @@ -1166,7 +1199,11 @@ class ComboBox extends UI5Element implements IFormInputElement { this.inner.value = value; this.inner.setSelectionRange(filterValue.length, value.length); - this.value = value; + this.setValue(value); + + if (this._useComplexValue) { + this.complexValue = [value, item.value || ""]; + } } _selectMatchingItem() { @@ -1191,7 +1228,12 @@ class ComboBox extends UI5Element implements IFormInputElement { this._filteredItems.forEach(item => { if (!shouldSelectionBeCleared && !itemToBeSelected) { - itemToBeSelected = ((!item.isGroupItem && (item.text === this.value)) ? item : item?.items?.find(i => i.text === this.value)); + if (isInstanceOfComboBoxItemGroup(item)) { + itemToBeSelected = item.items?.find(i => i.text === this.value); + } else { + // eslint-disable-next-line no-nested-ternary + itemToBeSelected = this._useComplexValue ? (item.value === this.complexValue[1] ? item : undefined) : (item.text === this.getValue() ? item : undefined); + } } }); @@ -1231,9 +1273,9 @@ class ComboBox extends UI5Element implements IFormInputElement { } _fireChangeEvent() { - if (this.value !== this._lastValue) { + if (this.getValue() !== this._lastValue) { this.fireDecoratorEvent("change"); - this._lastValue = this.value; + this._lastValue = this.getValue(); } } @@ -1251,15 +1293,18 @@ class ComboBox extends UI5Element implements IFormInputElement { this._selectedItemText = item.text || ""; this._selectionPerformed = true; - const sameItemSelected = this.value === this._selectedItemText; - const sameSelectionPerformed = this.value.toLowerCase() === this.filterValue.toLowerCase(); + const sameItemSelected = this.getValue() === this._selectedItemText; + const sameSelectionPerformed = this.getValue().toLowerCase() === this.filterValue.toLowerCase(); if (sameItemSelected && sameSelectionPerformed) { this._fireChangeEvent(); // Click on an already typed, but not memoized value shouold also trigger the change event return this._closeRespPopover(); } - this.value = this._selectedItemText; + this.setValue(this._selectedItemText); + if (this._useComplexValue) { + this.complexValue = [this._selectedItemText, item.value || ""]; + } if (!item.selected) { this.fireDecoratorEvent("selection-change", { @@ -1271,7 +1316,7 @@ class ComboBox extends UI5Element implements IFormInputElement { this._closeRespPopover(); // reset selection - this.inner.setSelectionRange(this.value.length, this.value.length); + this.inner.setSelectionRange(this.getValue().length, this.getValue().length); } _onItemFocus() { @@ -1297,11 +1342,11 @@ class ComboBox extends UI5Element implements IFormInputElement { _clear() { const selectedItem = this.items.find(item => item.selected); - if (selectedItem?.text === this.value) { + if (selectedItem?.text === this.getValue()) { this.fireDecoratorEvent("change"); } - this.value = ""; + this.setValue(""); this.fireDecoratorEvent("input"); if (this._isPhone) { diff --git a/packages/main/src/ComboBoxItem.ts b/packages/main/src/ComboBoxItem.ts index a59d7530ec98..acd19f7e03df 100644 --- a/packages/main/src/ComboBoxItem.ts +++ b/packages/main/src/ComboBoxItem.ts @@ -29,6 +29,15 @@ class ComboBoxItem extends ListItemBase implements IComboBoxItem { @property() text?: string; + /** + * Defines the value of the `ui5-combobox-item`. + * Used for selection. Check ComboBox' complexValue property for more information. + * @default undefined + * @public + */ + @property() + value?: string; + /** * Defines the additional text of the component. * @default undefined diff --git a/packages/main/src/ComboBoxTemplate.tsx b/packages/main/src/ComboBoxTemplate.tsx index b76f1dbbb8fa..c841a2ff0089 100644 --- a/packages/main/src/ComboBoxTemplate.tsx +++ b/packages/main/src/ComboBoxTemplate.tsx @@ -16,7 +16,7 @@ export default function ComboBoxTemplate(this: ComboBox) { } ) { + this._useComplexValue = true; + this._complexValue = value; + } + get complexValue() : Array { + return this._complexValue; + } + /** * Determines the name by which the component will be identified upon submission in an HTML form. * @@ -421,12 +439,12 @@ class MultiComboBox extends UI5Element implements IFormInputElement { open = false; @property() - _valueBeforeOpen = this.value; + _valueBeforeOpen = this.getValue(); - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _filteredItems!: Array; - @property({ type: Array }) + @property({ type: Array, noAttribute: true }) _previouslySelectedItems!: Array; @property({ type: Boolean }) @@ -540,6 +558,8 @@ class MultiComboBox extends UI5Element implements IFormInputElement { _preventTokenizerToggle?: boolean; _isOpenedByKeyboard?: boolean; _itemToFocus?: IMultiComboBoxItem; + _useComplexValue = false; + _complexValue: Array = []; _itemsBeforeOpen: Array; selectedItems: Array; _valueStateLinks: Array; @@ -556,7 +576,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { get formValidity(): ValidityStateFlags { const selectedItems = (this.items || []).filter(item => item.selected); - return { valueMissing: this.required && !this.value && !selectedItems.length }; + return { valueMissing: this.required && !this.getValue() && !selectedItems.length }; } async formElementAnchor() { @@ -569,7 +589,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (selectedItems.length && this.name) { const formData = new FormData(); - formData.append(this.name, this.value); + formData.append(this.name, this.getValue()); for (let i = 0; i < selectedItems.length; i++) { formData.append(this.name, selectedItems[i].text!); @@ -578,7 +598,19 @@ class MultiComboBox extends UI5Element implements IFormInputElement { return formData; } - return this.value; + return this.getValue(); + } + + setValue(value: string) { + if (this._useComplexValue) { + this.complexValue = [value, ...this.complexValue.slice(1, this.complexValue.length)]; + } else { + this.value = value; + } + } + + getValue() { + return this._useComplexValue ? this.complexValue[0] : this.value; } constructor() { @@ -629,7 +661,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._dialogInputValueState = this.valueState; } - this.value = value; + this.setValue(value); this._shouldFilterItems = true; this.valueBeforeAutoComplete = value; @@ -637,8 +669,8 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } _inputChange() { - if (!this._clearingValue && this._lastValue !== this.value) { - this._lastValue = this.value; + if (!this._clearingValue && this._lastValue !== this.getValue()) { + this._lastValue = this.getValue(); this.fireDecoratorEvent("change"); } } @@ -661,7 +693,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { matchingItem.selected = !initiallySelected; this._getResponsivePopover().preventFocusRestore = false; this.open = false; - this.value = ""; + this.setValue(""); } } @@ -733,7 +765,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const newValue = this.valueBeforeAutoComplete || this._inputLastValue; input.value = newValue; - this.value = newValue; + this.setValue(newValue); this.valueState = ValueState.Negative; this._shouldAutocomplete = false; @@ -743,7 +775,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } this._inputLastValue = input.value; - this.value = input.value; + this.setValue(input.value); this._filteredItems = filteredItems; if (!isPhone()) { @@ -757,14 +789,23 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this.fireDecoratorEvent("input"); } + _getSelectedValues(): Array { + return this.complexValue.slice(1, this.complexValue.length); + } + _tokenDelete(e: CustomEvent) { this._previouslySelectedItems = this._getSelectedItems(); const token: Token[] = e.detail.tokens; const deletingItems = this._getItems().filter(item => token.some(t => t.getAttribute("data-ui5-id") === item._id)); - deletingItems.forEach(item => { - item.selected = false; - }); + if (this._useComplexValue) { + const valuesToDelete = deletingItems.map(item => item.value); + this.complexValue = [this.complexValue[0], ...this._getSelectedValues().filter(val => !valuesToDelete.includes(val))]; + } else { + deletingItems.forEach(item => { + item.selected = false; + }); + } this._deleting = true; this._preventTokenizerToggle = true; @@ -921,7 +962,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { matchingItems.forEach(item => { item.selected = true; - this.value = ""; + this.setValue(""); const changePrevented = this.fireSelectionChange(); @@ -992,7 +1033,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const items = this._getItems(); const selectedItem = this._getSelectedItems()[0]; const focusedToken = this._tokenizer.tokens.find(token => token.focused); - const value = this.value; + const value = this.getValue(); const matchingItem = this._getItems().find(item => item.text?.localeCompare(value, undefined, { sensitivity: "base" }) === 0); e.preventDefault(); @@ -1041,11 +1082,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const isAutoCompleted = ((innerInput.selectionEnd || 0) - (innerInput.selectionStart || 0)) > 0; if (isAutoCompleted) { - this.value = this.valueBeforeAutoComplete; + this.setValue(this.valueBeforeAutoComplete); } if (!this.noValidation || (!this.open && this.noValidation)) { - this.value = this._lastValue; + this.setValue(this._lastValue); } } @@ -1260,7 +1301,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (this.open) { firstListItem && focusRef && this.list?._itemNavigation.setCurrentItem(focusRef); - this.value = this.valueBeforeAutoComplete || this.value; + this.setValue(this.valueBeforeAutoComplete || this.getValue()); // wait item navigation to apply correct tabindex await renderFinished(); @@ -1309,7 +1350,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const itemsCount = items.length; const previousItemIdx = this.currentItemIdx; - if (previousItemIdx > -1 && items[previousItemIdx].text !== this.value) { + if (previousItemIdx > -1 && items[previousItemIdx].text !== this.getValue()) { this.currentItemIdx = -1; } @@ -1328,7 +1369,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { return; } - this.value = currentItem.text!; + this.setValue(currentItem.text!); this._innerInput.value = currentItem.text!; this._innerInput.setSelectionRange(0, currentItem.text!.length); } @@ -1337,7 +1378,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const items = this._getItems(); let previousItemIdx = this.currentItemIdx; - if ((!this.value && previousItemIdx !== -1) || (previousItemIdx !== -1 && this.value && this.value !== items[previousItemIdx].text)) { + if ((!this.getValue() && previousItemIdx !== -1) || (previousItemIdx !== -1 && this.getValue() && this.getValue() !== items[previousItemIdx].text)) { previousItemIdx = -1; } @@ -1365,13 +1406,13 @@ class MultiComboBox extends UI5Element implements IFormInputElement { return; } - this.value = currentItem.text!; + this.setValue(currentItem.text!); this._innerInput.value = currentItem.text!; this._innerInput.setSelectionRange(0, currentItem.text!.length); } _handleEnter() { - const lowerCaseValue = this.value.toLowerCase(); + const lowerCaseValue = this.getValue().toLowerCase(); const matchingItem = this._getItems().find(item => (!item.isGroupItem && item.text!.toLowerCase() === lowerCaseValue)); const oldValueState = this.valueState; const innerInput = this._innerInput; @@ -1389,8 +1430,16 @@ class MultiComboBox extends UI5Element implements IFormInputElement { }); } else { this._previouslySelectedItems = this._getSelectedItems(); - matchingItem.selected = true; - this.value = ""; + + if (this._useComplexValue) { + if (matchingItem.value) { + this.complexValue = [this.complexValue[0], ...[matchingItem.value, ...new Set(this._getSelectedValues())]]; + } + } else { + matchingItem.selected = true; + } + + this.setValue(""); // during composition prevent _inputLiveChange for proper input clearing if (this._isComposing) { this._suppressNextLiveChange = true; @@ -1546,6 +1595,9 @@ class MultiComboBox extends UI5Element implements IFormInputElement { // don't call selection change right after selection as user can cancel it on phone if (!isPhone()) { changePrevented = this.fireSelectionChange(); + if (this._useComplexValue) { + this.complexValue = [this.complexValue[0], ...e.detail.selectedItems.map(item => (item as IMultiComboBoxItem).value || "")]; + } if (changePrevented) { e.preventDefault(); @@ -1562,7 +1614,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if (!e.detail.selectionComponentPressed && !isSpace(castedEvent) && !isSpaceCtrl(castedEvent)) { this.open = false; - this.value = ""; + this.setValue(""); // if the item (not checkbox) is clicked, call the selection change if (isPhone()) { @@ -1577,7 +1629,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { return; } - this.value = this.valueBeforeAutoComplete || ""; + this.setValue(this.valueBeforeAutoComplete || ""); } fireSelectionChange() { @@ -1641,11 +1693,11 @@ class MultiComboBox extends UI5Element implements IFormInputElement { }; }); - this._valueBeforeOpen = this.value; + this._valueBeforeOpen = this.getValue(); this._dialogInputValueState = this.valueState; // in order to use the autocomplete feature of the input we should not set value in state - this._innerInput.value = this.value; + this._innerInput.value = this.getValue(); if (this.filterSelected) { const selectedItems = this._filteredItems.filter(item => item.selected); @@ -1706,7 +1758,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const innerInput = this._innerInput; filterValue = filterValue || ""; - this.value = value!; + this.setValue(value!); innerInput.value = value!; innerInput.setSelectionRange(filterValue.length, value!.length); @@ -1741,13 +1793,17 @@ class MultiComboBox extends UI5Element implements IFormInputElement { const autoCompletedChars = input && (input.selectionEnd || 0) - (input.selectionStart || 0); const value = input && input.value; + if (this._useComplexValue) { + this._syncSelection(); + } + if (this.open) { const list = this._getList(); const selectedListItemsCount = this.items.filter(item => item.selected).length; this._allSelected = selectedListItemsCount > 0 && ((selectedListItemsCount === this.items.length) || (list?.getSlottedNodes("items").length === selectedListItemsCount)); } - this._effectiveShowClearIcon = (this.showClearIcon && !!this.value && !this.readonly && !this.disabled); + this._effectiveShowClearIcon = (this.showClearIcon && !!this.getValue() && !this.readonly && !this.disabled); this._inputLastValue = value; @@ -1788,6 +1844,17 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } } + _syncSelection() { + const selectedValues = this.complexValue.slice(1, this.complexValue.length); + + // set selected property of the items based on the complex value + this._getItems().forEach(item => { + if (isInstanceOfMultiComboBoxItem(item) && item.value) { + item.selected = selectedValues.includes(item.value); + } + }); + } + onAfterRendering() { this._getList(); this.valueStateOpen = this.shouldDisplayOnlyValueStateMessage || (this._handleLinkNavigation && !this.open); @@ -1819,7 +1886,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } _clear() { - this.value = ""; + this.setValue(""); this._inputDom.value = ""; this.fireDecoratorEvent("input"); @@ -1847,7 +1914,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._toggleTokenizerPopover(); - this.value = this._valueBeforeOpen; + this.setValue(this._valueBeforeOpen); } handleOK() { @@ -1860,7 +1927,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { } if (!this.noValidation) { - this.value = ""; + this.setValue(""); } this._toggleTokenizerPopover(); @@ -1909,7 +1976,7 @@ class MultiComboBox extends UI5Element implements IFormInputElement { this._clearingValue = false; if (!isPhone() && e.target === this._innerInput) { - this._innerInput.setSelectionRange(0, this.value.length); + this._innerInput.setSelectionRange(0, this.getValue().length); } this._tokenizer.tokens.forEach(token => { token.selected = false; @@ -1933,14 +2000,14 @@ class MultiComboBox extends UI5Element implements IFormInputElement { if ((!this.shadowRoot!.contains(e.relatedTarget as Node) || focusIsGoingInPopover) && !this._deleting && !this._clearingValue) { this.focused = false; - if (this._lastValue !== this.value) { + if (this._lastValue !== this.getValue()) { this._inputChange(); } this._tokenizer.expanded = this.open; // remove the value if user focus out the input and focus is not going in the popover if (!isPhone() && !this.noValidation && !focusIsGoingInPopover) { - this.value = ""; + this.setValue(""); } } } diff --git a/packages/main/src/MultiComboBoxTemplate.tsx b/packages/main/src/MultiComboBoxTemplate.tsx index 553e87ed3baa..67244051ba39 100644 --- a/packages/main/src/MultiComboBoxTemplate.tsx +++ b/packages/main/src/MultiComboBoxTemplate.tsx @@ -63,7 +63,7 @@ export default function MultiComboBoxTemplate(this: MultiComboBox) { + + + + + + ComboBox test page + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + + + + +
+
+ + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + + + + + \ No newline at end of file