diff --git a/libs/components/src/chip-select/chip-select.component.ts b/libs/components/src/chip-select/chip-select.component.ts index ad197aaa6ce..8667ee1ea38 100644 --- a/libs/components/src/chip-select/chip-select.component.ts +++ b/libs/components/src/chip-select/chip-select.component.ts @@ -118,12 +118,12 @@ export class ChipSelectComponent implements ControlValueAccessor, A /** The label to show in the chip button */ protected get label(): string { - return this.selectedOption?.label || this.placeholderText(); + return this.selectedOption?.label() || this.placeholderText(); } /** The icon to show in the chip button */ protected get icon(): string { - return this.selectedOption?.icon || this.placeholderIcon(); + return this.selectedOption?.icon() || this.placeholderIcon(); } /** @@ -172,7 +172,7 @@ export class ChipSelectComponent implements ControlValueAccessor, A */ private findOption(tree: ChipSelectOption, value: T): ChipSelectOption | null { let result = null; - if (tree.value !== null && compareValues(tree.value, value)) { + if (tree.value !== null && compareValues(tree.value(), value)) { return tree; } @@ -266,7 +266,7 @@ export class ChipSelectComponent implements ControlValueAccessor, A return; } - this.notifyOnChange(option?.value ?? null); + this.notifyOnChange(option?.value() ?? null); } /** Implemented as part of NG_VALUE_ACCESSOR */ diff --git a/libs/components/src/select/option.component.ts b/libs/components/src/select/option.component.ts index 421c6273e43..54d8cac76a2 100644 --- a/libs/components/src/select/option.component.ts +++ b/libs/components/src/select/option.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, Input, booleanAttribute } from "@angular/core"; +import { Component, booleanAttribute, input } from "@angular/core"; import { Option } from "./option"; @@ -9,27 +9,11 @@ import { Option } from "./option"; template: ``, }) export class OptionComponent implements Option { - // TODO: Skipped for migration because: - // This input overrides a field from a superclass, while the superclass field - // is not migrated. - @Input() - icon?: string; + icon = input(); - // TODO: Skipped for migration because: - // This input overrides a field from a superclass, while the superclass field - // is not migrated. - @Input({ required: true }) - value: T; + value = input.required(); - // TODO: Skipped for migration because: - // This input overrides a field from a superclass, while the superclass field - // is not migrated. - @Input({ required: true }) - label: string; + label = input.required(); - // TODO: Skipped for migration because: - // This input overrides a field from a superclass, while the superclass field - // is not migrated. - @Input({ transform: booleanAttribute }) - disabled: boolean; + disabled = input(undefined, { transform: booleanAttribute }); } diff --git a/libs/components/src/select/option.ts b/libs/components/src/select/option.ts index 4d7cc70bda4..57385a3e6e4 100644 --- a/libs/components/src/select/option.ts +++ b/libs/components/src/select/option.ts @@ -1,6 +1,8 @@ +import { Signal } from "@angular/core"; + export interface Option { - icon?: string; - value: T | null; - label?: string; - disabled?: boolean; + icon?: Signal; + value: Signal; + label?: Signal; + disabled?: Signal; } diff --git a/libs/components/src/select/select.component.html b/libs/components/src/select/select.component.html index 25e4f135aad..c9467831612 100644 --- a/libs/components/src/select/select.component.html +++ b/libs/components/src/select/select.component.html @@ -1,9 +1,9 @@ { describe("initial state", () => { it("selected option should update when items input changes", () => { - expect(select.selectedOption?.value).toBeUndefined(); + expect(select.selectedOption()?.value).toBeUndefined(); - select.items = [ - { label: "Apple", value: "apple" }, - { label: "Pear", value: "pear" }, - { label: "Banana", value: "banana" }, - ]; + select.items.set([ + { label: signal("Apple"), value: signal("apple") }, + { label: signal("Pear"), value: signal("pear") }, + { label: signal("Banana"), value: signal("banana") }, + ]); - expect(select.selectedOption?.value).toBe("apple"); + expect(select.selectedOption()?.value).toBe("apple"); }); }); }); diff --git a/libs/components/src/select/select.component.ts b/libs/components/src/select/select.component.ts index cf6b595631d..c091c31bcde 100644 --- a/libs/components/src/select/select.component.ts +++ b/libs/components/src/select/select.component.ts @@ -13,6 +13,10 @@ import { Output, EventEmitter, input, + Signal, + computed, + model, + signal, } from "@angular/core"; import { ControlValueAccessor, @@ -37,31 +41,23 @@ let nextId = 0; templateUrl: "select.component.html", providers: [{ provide: BitFormFieldControl, useExisting: SelectComponent }], imports: [NgSelectModule, ReactiveFormsModule, FormsModule], + host: { + "[id]": "this.id()", + }, }) export class SelectComponent implements BitFormFieldControl, ControlValueAccessor { @ViewChild(NgSelectComponent) select: NgSelectComponent; - private _items: Option[] = []; /** Optional: Options can be provided using an array input or using `bit-option` */ - // TODO: Skipped for migration because: - // Accessor inputs cannot be migrated as they are too complex. - @Input() - get items(): Option[] { - return this._items; - } - set items(next: Option[]) { - this._items = next; - this._selectedOption = this.findSelectedOption(next, this.selectedValue); - } + items = model[]>(); readonly placeholder = input(this.i18nService.t("selectPlaceholder")); @Output() closed = new EventEmitter(); - protected selectedValue: T; - protected _selectedOption: Option; - get selectedOption() { - return this._selectedOption; - } + protected selectedValue = signal(undefined); + selectedOption: Signal> = computed(() => + this.findSelectedOption(this.items(), this.selectedValue()), + ); protected searchInputId = `bit-select-search-input-${nextId++}`; private notifyOnChange?: (value: T) => void; @@ -81,7 +77,7 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce if (value == null || value.length == 0) { return; } - this.items = value.toArray(); + this.items.set(value.toArray()); } @HostBinding("class") protected classes = ["tw-block", "tw-w-full", "tw-h-full"]; @@ -91,7 +87,7 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce get disabledAttr() { return this.disabled || null; } - // TODO: Skipped for migration because: + // TODO: Skipped for signal migration because: // Accessor inputs cannot be migrated as they are too complex. @Input() get disabled() { @@ -104,8 +100,7 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce /**Implemented as part of NG_VALUE_ACCESSOR */ writeValue(obj: T): void { - this.selectedValue = obj; - this._selectedOption = this.findSelectedOption(this.items, this.selectedValue); + this.selectedValue.set(obj); } /**Implemented as part of NG_VALUE_ACCESSOR */ @@ -125,11 +120,13 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce /**Implemented as part of NG_VALUE_ACCESSOR */ protected onChange(option: Option | null) { + this.selectedValue.set(option?.value()); + if (!this.notifyOnChange) { return; } - this.notifyOnChange(option?.value); + this.notifyOnChange(option?.value()); } /**Implemented as part of NG_VALUE_ACCESSOR */ @@ -158,10 +155,7 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce } /**Implemented as part of BitFormFieldControl */ - // TODO: Skipped for migration because: - // This input is used in combination with `@HostBinding` and migrating would - // break. - @HostBinding() @Input() id = `bit-multi-select-${nextId++}`; + id = input(`bit-multi-select-${nextId++}`); /**Implemented as part of BitFormFieldControl */ // TODO: Skipped for migration because: @@ -188,7 +182,7 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce } private findSelectedOption(items: Option[], value: T): Option | undefined { - return items.find((item) => item.value === value); + return items.find((item) => item.value() === value); } /**Emits the closed event. */