src/app/core/entity/entity-field-select/entity-field-select.component.ts
Dropdown field offering all fields of a given entity type (for admin and action screens).
BasicAutocompleteComponent<
FormFieldConfig,
string
>
| changeDetection | ChangeDetectionStrategy.OnPush |
| providers |
EntityFieldSelectComponent
|
| selector | app-entity-field-select |
| standalone | true |
| templateUrl | ../../common-components/basic-autocomplete/basic-autocomplete.component.html |
AdminEntityGeneralSettingsComponent
AutomatedFieldMappingComponent
BirthdayDashboardSettingsComponent
ConditionsEditorComponent
EditImportColumnMappingComponent
EditNewMatchActionComponent
EditPrefilledValuesComponent
EntityBulkEditComponent
EntityCountDashboardSettingsComponent
EntityFieldsMenuComponent
ImportMatchExistingComponent
ReviewDuplicatesComponent
Methods |
|
Inputs |
Outputs |
| placeholder | |
Type : string
|
|
Default value : $localize`:EntityFieldSelect placeholder:Select Record Field`
|
|
|
Inherited from
CustomFormControlDirective
|
|
| entityType | |
Type : string | EntityConstructor
|
|
|
The entity type whose fields to display. |
|
| optionToString | |
Type : (option: FormFieldConfig) => string
|
|
Default value : // fall back to the id so the internal `_id` field (which has no label) is not blank (option) => option.label ?? option.id
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
| showInternalIdField | |
Type : boolean
|
|
Default value : false
|
|
|
Whether to show the internal _id field in the dropdown. Useful for import contexts where matching by UUID is needed. |
|
| valueMapper | |
Type : (option: FormFieldConfig) => string
|
|
Default value : (option) => option.id
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
| createOption | |
Type : (input: string) => Promise<O>
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
| createOptions | |
Type : CreateOptionConfig<O>[]
|
|
Default value : []
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
| display | |
Type : "text" | "chips" | "none"
|
|
Default value : "text"
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
|
Display the selected items as simple text, as chips or not at all (if used in combination with another component) |
|
| displayFullLengthOptionLabel | |
Type : boolean
|
|
Default value : false
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
|
Whether dropdown option labels should be shown in full length. Set to false to truncate labels with ellipsis. |
|
| hideOption | |
Type : (option: O) => boolean
|
|
Default value : () => false
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
| maxOptionsToDisplay | |
Type : number
|
|
Default value : 100
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
|
Maximum number of options to display in the dropdown. If more options match the current filter, a hint is shown to type to narrow results. Set to 0 for no limit. Defaults to 100. |
|
| multi | |
Type : boolean
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
|
Whether the user should be able to select multiple values. |
|
| options | |
Type : O[]
|
|
Default value : []
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
|
The options to display in the autocomplete dropdown.
If you pass complex objects here, you can customize what value is displayed and what value is output/stored
by overriding the |
|
| reorder | |
Type : boolean
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
|
Whether the user can manually drag & drop to reorder the selected items |
|
| aria-describedby | |
Type : string
|
|
|
Inherited from
CustomFormControlDirective
|
|
| disabled | |
Type : boolean
|
|
|
Inherited from
CustomFormControlDirective
|
|
| ngControl | |
Type : any
|
|
Default value : inject(NgControl, { optional: true, self: true })
|
|
|
Inherited from
CustomFormControlDirective
|
|
| required | |
Type : boolean
|
|
|
Inherited from
CustomFormControlDirective
|
|
| value | |
Type : T
|
|
|
Inherited from
CustomFormControlDirective
|
|
| autocompleteFilterChange | |
Type : (o: O) => boolean
|
|
|
Inherited from
BasicAutocompleteComponent
|
|
| valueChange | |
Type : EventEmitter
|
|
|
Inherited from
CustomFormControlDirective
|
|
| compareEnumValues |
compareEnumValues(a: any, b: any)
|
|
Inherited from
BasicAutocompleteComponent
|
|
Compare two enum values by id if present, otherwise by reference.
Returns :
boolean
|
| Async createFromConfig | ||||||
createFromConfig(marker: CreateOptionMarker<O>)
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Parameters :
Returns :
any
|
| Protected createOptionAriaLabel | |||||||||
createOptionAriaLabel(option: CreateOptionConfig<O>, input: string)
|
|||||||||
|
Inherited from
BasicAutocompleteComponent
|
|||||||||
|
Parameters :
Returns :
string
|
| Protected createOptionDisplay | ||||||
createOptionDisplay(input: string)
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Used in template to display the "Add new" option label. Delegates to optionToString so callers with a custom optionToString (e.g. showing an example date) get a preview in the "Add new" option too. Falls back to the raw input when optionToString throws or returns null/undefined (e.g. when O is an object type whose properties don't exist on a plain string).
Parameters :
Returns :
string
|
| Protected createOptionLabel | |||||||||
createOptionLabel(option: CreateOptionConfig<O>, input: string)
|
|||||||||
|
Inherited from
BasicAutocompleteComponent
|
|||||||||
|
Parameters :
Returns :
string
|
| dropChips | ||||||
dropChips(event: CdkDragDrop
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Parameters :
Returns :
void
|
| Protected isSelected | ||||||
isSelected(option: SelectableOption<O | V>)
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Whether the given option is part of the current selection. Reads the _selectedOptions signal, so that options rendered in the open dropdown are updated as soon as the selection changes.
Parameters :
Returns :
boolean
|
| onContainerClick | ||||||
onContainerClick(event: MouseEvent)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| Protected onDropdownClosed |
onDropdownClosed()
|
|
Inherited from
BasicAutocompleteComponent
|
|
The dropdown closed, which ends the selection unless the field still has the focus (e.g. after closing the dropdown with the Escape key).
Returns :
void
|
| onFocusOut | ||||||
onFocusOut(event: FocusEvent)
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Parameters :
Returns :
void
|
| Protected onOptionSelectionChange | ||||||
onOptionSelectionChange(event: MatOptionSelectionChange)
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Apply a selection the user made while the dropdown is kept open for a multi-select field. Material only emits its own
Parameters :
Returns :
void
|
| select | ||||||
select(selected: string | SelectableOption
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Parameters :
Returns :
void
|
| showAutocomplete | ||||||
showAutocomplete(valueToRevertTo?: string)
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Parameters :
Returns :
void
|
| Protected toCreateOptionValue | |||||||||
toCreateOptionValue(option: CreateOptionConfig<O>, input: string)
|
|||||||||
|
Inherited from
BasicAutocompleteComponent
|
|||||||||
|
Parameters :
Returns :
CreateOptionMarker<O>
|
| unselect | ||||||
unselect(option: SelectableOption<O | V>)
|
||||||
|
Inherited from
BasicAutocompleteComponent
|
||||||
|
Parameters :
Returns :
void
|
| Public updatePanelWidth |
updatePanelWidth()
|
|
Inherited from
BasicAutocompleteComponent
|
|
Set the width of the dropdown panel programmatically to match the parent form field. (this is not possible with pure CSS) Note: If the field is close to the viewport edge, Angular Material's overlay system may shift the dropdown horizontally to keep it visible, causing minor misalignment. This is expected and ensures accessibility.
Returns :
void
|
| writeValue | ||||||||||||
writeValue(val: V[] | V, notifyFormControl: unknown)
|
||||||||||||
|
Inherited from
CustomFormControlDirective
|
||||||||||||
|
Parameters :
Returns :
void
|
| blur |
blur()
|
|
Inherited from
CustomFormControlDirective
|
|
Returns :
void
|
| focus |
focus()
|
|
Inherited from
CustomFormControlDirective
|
|
Returns :
void
|
| registerOnChange | ||||||
registerOnChange(fn: any)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| registerOnTouched | ||||||
registerOnTouched(fn: any)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| setDescribedByIds | ||||||
setDescribedByIds(ids: string[])
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| setDisabledState | ||||||
setDisabledState(isDisabled: boolean)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Parameters :
Returns :
void
|
| Protected optionsSource |
Type : unknown
|
Default value : computed(() => {
const optionsById = new Map<string, FormFieldConfig>();
for (const option of this.options() ?? []) {
optionsById.set(option.id, option);
}
for (const inferredOption of this.fieldOptions()) {
if (!optionsById.has(inferredOption.id)) {
optionsById.set(inferredOption.id, inferredOption);
}
}
return [...optionsById.values()];
})
|
|
Inherited from
BasicAutocompleteComponent
|
|
Explicitly given options are merged with the fields inferred from the entity's schema, so that callers can offer additional, calculated columns (e.g. "distance") that do not exist as a field in the schema. An explicit config takes precedence over the inferred field of the same id. |
| _selectedOptions |
Type : unknown
|
Default value : signal<SelectableOption<O, V>[]>([])
|
|
Inherited from
BasicAutocompleteComponent
|
| autocomplete |
Type : unknown
|
Default value : viewChild(MatAutocompleteTrigger)
|
|
Inherited from
BasicAutocompleteComponent
|
| autocompleteFilterFunction |
Type : function
|
|
Inherited from
BasicAutocompleteComponent
|
| autocompleteForm |
Type : unknown
|
Default value : new FormControl("")
|
|
Inherited from
BasicAutocompleteComponent
|
| autocompleteOptions |
Type : Signal<SelectableOption[]>
|
Default value : toSignal(
this.autocompleteSuggestedOptions,
{ initialValue: [] as SelectableOption<O, V>[] },
)
|
|
Inherited from
BasicAutocompleteComponent
|
| autocompleteSuggestedOptions |
Type : unknown
|
Default value : this.autocompleteForm.valueChanges.pipe(
filter((val) => typeof val === "string"),
map((val) => this.updateAutocomplete(val)),
startWith([] as SelectableOption<O, V>[]),
)
|
|
Inherited from
BasicAutocompleteComponent
|
| Protected availableCreateOptions |
Type : unknown
|
Default value : computed<CreateOptionConfig<O>[]>(() => {
if (this.createOptions().length > 0) {
return this.createOptions();
}
if (!this.createOption()) {
return [];
}
return [{ label: "", create: this.createOption() }];
})
|
|
Inherited from
BasicAutocompleteComponent
|
| effectiveDisplay |
Type : unknown
|
Default value : computed<"text" | "chips" | "none">(() =>
this.multi() && this.reorder() && this.display() === "text"
? "chips"
: this.display(),
)
|
|
Inherited from
BasicAutocompleteComponent
|
|
Derived display mode. Reorderable multi-select uses chips without mutating the public input. |
| hasMoreOptions |
Type : unknown
|
Default value : signal(false)
|
|
Inherited from
BasicAutocompleteComponent
|
| inputElement |
Type : unknown
|
Default value : viewChild("inputElement", { read: MatInput }) as Signal<
(MatInput & { _elementRef: ElementRef<HTMLElement> }) | undefined
>
|
|
Inherited from
BasicAutocompleteComponent
|
| isInSearchMode |
Type : WritableSignal<boolean>
|
Default value : signal(false)
|
|
Inherited from
BasicAutocompleteComponent
|
|
display the search input rather than the selected elements only (when the form field gets focused). |
| panelWidth |
Type : unknown
|
Default value : signal("200px")
|
|
Inherited from
BasicAutocompleteComponent
|
|
Dynamic width of the autocomplete dropdown panel. Set to match the full width of the Material form field container (including icons/padding). |
| retainSearchValue |
Type : string
|
|
Inherited from
BasicAutocompleteComponent
|
|
Keep the search value to help users quickly multi-select multiple related options without having to type filter text again |
| showAddOption |
Type : unknown
|
Default value : signal(false)
|
|
Inherited from
BasicAutocompleteComponent
|
|
whether the "add new" option is logically allowed in the current context (e.g. not creating a duplicate) |
| templateRef |
Type : unknown
|
Default value : contentChild(TemplateRef)
|
|
Inherited from
BasicAutocompleteComponent
|
| trackByOptionValueFn |
Type : TrackByFunction<SelectableOption<O, V>> | undefined
|
Default value : () => {...}
|
|
Inherited from
BasicAutocompleteComponent
|
| viewportHeight |
Type : unknown
|
Default value : computed(() => {
const contentHeight = (this.autocompleteOptions()?.length ?? 0) * 48;
const availableHeight =
BasicAutocompleteComponent.PANEL_MAX_HEIGHT -
BasicAutocompleteComponent.FOOTER_RESERVE;
return Math.min(contentHeight, availableHeight);
})
|
|
Inherited from
BasicAutocompleteComponent
|
| virtualScrollViewport |
Type : unknown
|
Default value : viewChild(CdkVirtualScrollViewport)
|
|
Inherited from
BasicAutocompleteComponent
|
| controlType |
Type : string
|
Default value : "custom-control"
|
|
Inherited from
CustomFormControlDirective
|
| elementRef |
Type : unknown
|
Default value : inject<ElementRef<HTMLElement>>(ElementRef)
|
|
Inherited from
CustomFormControlDirective
|
| Readonly enabled |
Type : Signal<boolean>
|
Default value : computed(() => !this._disabled())
|
|
Inherited from
CustomFormControlDirective
|
|
Whether the control is currently enabled, as a signal (tracks disabled). |
| errorStateMatcher |
Type : unknown
|
Default value : inject(ErrorStateMatcher)
|
|
Inherited from
CustomFormControlDirective
|
| id |
Type : unknown
|
Default value : `custom-form-control-${CustomFormControlDirective.nextId++}`
|
|
Inherited from
CustomFormControlDirective
|
| Static nextId |
Type : number
|
Default value : 0
|
|
Inherited from
CustomFormControlDirective
|
| onChange |
Type : unknown
|
Default value : () => {...}
|
|
Inherited from
CustomFormControlDirective
|
| onTouched |
Type : unknown
|
Default value : () => {...}
|
|
Inherited from
CustomFormControlDirective
|
| parentForm |
Type : unknown
|
Default value : inject(NgForm, { optional: true })
|
|
Inherited from
CustomFormControlDirective
|
| parentFormGroup |
Type : unknown
|
Default value : inject(FormGroupDirective, { optional: true })
|
|
Inherited from
CustomFormControlDirective
|
| stateChanges |
Type : unknown
|
Default value : new Subject<void>()
|
|
Inherited from
CustomFormControlDirective
|
| Readonly valueSignal |
Type : Signal<T>
|
Default value : computed(() => this._value())
|
|
Inherited from
CustomFormControlDirective
|
|
The current value of the control as a signal.
Authoritative in both modes: it reflects the bound |
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
Input,
} from "@angular/core";
import { MatFormFieldControl } from "@angular/material/form-field";
import {
BASIC_AUTOCOMPLETE_COMPONENT_IMPORTS,
BasicAutocompleteComponent,
} from "app/core/common-components/basic-autocomplete/basic-autocomplete.component";
import { FormFieldConfig } from "../../common-components/entity-form/FormConfig";
import { EntityRegistry } from "../database-entity.decorator";
import { EntityConstructor } from "../model/entity";
import { EntitySchema } from "../schema/entity-schema";
/**
* Dropdown field offering all fields of a given entity type
* (for admin and action screens).
*/
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "app-entity-field-select",
imports: BASIC_AUTOCOMPLETE_COMPONENT_IMPORTS,
templateUrl:
"../../common-components/basic-autocomplete/basic-autocomplete.component.html",
providers: [
{ provide: MatFormFieldControl, useExisting: EntityFieldSelectComponent },
],
})
export class EntityFieldSelectComponent extends BasicAutocompleteComponent<
FormFieldConfig,
string
> {
// `placeholder` is part of the `MatFormFieldControl` contract (Angular Material
// reads it as a plain property), so it stays a decorator `@Input()` rather than a
// signal input; here we only override the default value.
@Input() override placeholder: string =
$localize`:EntityFieldSelect placeholder:Select Record Field`;
override optionToString = input<(option: FormFieldConfig) => string>(
// fall back to the id so the internal `_id` field (which has no label) is not blank
(option) => option.label ?? option.id,
);
override valueMapper = input<(option: FormFieldConfig) => string>(
(option) => option.id,
);
/**
* Whether to show the internal _id field in the dropdown.
* Useful for import contexts where matching by UUID is needed.
*/
showInternalIdField = input(false);
/** The entity type whose fields to display. */
entityType = input<string | EntityConstructor>();
private entityRegistry = inject(EntityRegistry);
private readonly resolvedEntityType = computed(() => {
const entity = this.entityType();
if (!entity) {
return undefined;
}
return typeof entity === "string"
? this.entityRegistry.get(entity)
: entity;
});
private readonly fieldOptions = computed(() => {
const entityType = this.resolvedEntityType();
if (!entityType) {
return [];
}
return this.getAllFieldProps(entityType.schema);
});
/**
* Explicitly given options are merged with the fields inferred from the entity's schema,
* so that callers can offer additional, calculated columns (e.g. "distance")
* that do not exist as a field in the schema.
* An explicit config takes precedence over the inferred field of the same id.
*/
protected override optionsSource = computed(() => {
const optionsById = new Map<string, FormFieldConfig>();
for (const option of this.options() ?? []) {
optionsById.set(option.id, option);
}
for (const inferredOption of this.fieldOptions()) {
if (!optionsById.has(inferredOption.id)) {
optionsById.set(inferredOption.id, inferredOption);
}
}
return [...optionsById.values()];
});
private getAllFieldProps(schema: EntitySchema): FormFieldConfig[] {
return [...schema.entries()]
.filter(
([prop, fieldSchema]) =>
(!fieldSchema.isInternalField && !!fieldSchema.label) ||
(this.showInternalIdField() && prop === "_id"),
)
.map(([name, fieldSchema]) => ({ ...fieldSchema, id: name }));
}
}
<div matAutocompleteOrigin #autocompleteOrigin="matAutocompleteOrigin">
<!--Display-->
@if (effectiveDisplay() === "text" || effectiveDisplay() === "none") {
<input
[id]="id"
[hidden]="isInSearchMode() || effectiveDisplay() === 'none'"
[disabled]="!enabled()"
matInput
style="text-overflow: ellipsis; width: calc(100% - 50px)"
(focusin)="showAutocomplete()"
(focusout)="showAutocomplete()"
[value]="displayText"
[placeholder]="placeholder"
/>
} @else {
<input
[hidden]="true"
[disabled]="!enabled()"
matInput
(focusin)="showAutocomplete()"
(focusout)="showAutocomplete()"
[matChipInputFor]="chipList"
/>
<mat-chip-grid
#chipList
cdkDropList
cdkDropListOrientation="mixed"
[cdkDropListDisabled]="!reorder()"
(cdkDropListDropped)="dropChips($event)"
class="chip-drop-list"
>
@for (item of _selectedOptions(); track item.asValue) {
<mat-chip-row
cdkDrag
[cdkDragDisabled]="!reorder()"
[editable]="enabled()"
class="chip"
[style.background-color]="item.asValue?.['color']"
>
<ng-template
[ngTemplateOutlet]="chipContent"
[ngTemplateOutletContext]="{ $implicit: item }"
></ng-template>
@if (enabled()) {
<button matChipRemove (click)="unselect(item)">
<app-fa-dynamic-icon
i18n-matTooltip="
tooltip for remove icon on chips of dropdown item
"
matTooltip="Remove"
icon="xmark"
></app-fa-dynamic-icon>
</button>
}
<ng-template cdkDragPreview>
<mat-chip-row
[editable]="false"
class="chip chip-preview"
[style.background-color]="item.asValue?.['color']"
>
<ng-template
[ngTemplateOutlet]="chipContent"
[ngTemplateOutletContext]="{ $implicit: item }"
></ng-template>
</mat-chip-row>
</ng-template>
</mat-chip-row>
}
</mat-chip-grid>
}
<!--Search-->
<input
[hidden]="!isInSearchMode()"
#inputElement
[formControl]="autocompleteForm"
matInput
style="text-overflow: ellipsis"
[matAutocomplete]="autoSuggestions"
[appKeepPanelOpenOnSelect]="!!multi()"
[matAutocompleteConnectedTo]="autocompleteOrigin"
(focusout)="onFocusOut($event)"
[placeholder]="placeholder"
/>
</div>
<!--
Autocomplete
-->
<mat-autocomplete
[disableRipple]="true"
#autoSuggestions="matAutocomplete"
(optionSelected)="select($event.option.value)"
(closed)="onDropdownClosed()"
autoActiveFirstOption
[hideSingleSelectionIndicator]="true"
[panelWidth]="panelWidth()"
>
<!-- Select All and Clear Buttons on top of options via content projection-->
<div>
<ng-content select="[autocompleteHeader]"></ng-content>
</div>
<div>
@if (!displayFullLengthOptionLabel()) {
<cdk-virtual-scroll-viewport
[style.height.px]="viewportHeight()"
[itemSize]="48"
minBufferPx="200"
maxBufferPx="500"
>
<mat-option
[value]="item"
(onSelectionChange)="onOptionSelectionChange($event)"
*cdkVirtualFor="
let item of autocompleteOptions();
trackBy: trackByOptionValueFn
"
>
<ng-container
[ngTemplateOutlet]="optionContent"
[ngTemplateOutletContext]="{ $implicit: item, truncate: true }"
></ng-container>
</mat-option>
</cdk-virtual-scroll-viewport>
} @else {
@for (item of autocompleteOptions(); track item.asValue) {
<mat-option
[value]="item"
class="option-with-wrap"
(onSelectionChange)="onOptionSelectionChange($event)"
>
<ng-container
[ngTemplateOutlet]="optionContent"
[ngTemplateOutletContext]="{ $implicit: item, truncate: false }"
></ng-container>
</mat-option>
}
}
</div>
<ng-template #chipContent let-item>
@if (reorder()) {
<span class="drag-handle color-accent" cdkDragHandle>
<app-fa-dynamic-icon icon="grip-vertical"></app-fa-dynamic-icon>
</span>
}
@if (!templateRef()) {
{{ item.asString }}
} @else {
<ng-template
[ngTemplateOutlet]="templateRef()"
[ngTemplateOutletContext]="{ $implicit: item.initial }"
></ng-template>
}
</ng-template>
<ng-template #optionContent let-item let-truncate="truncate">
<div
class="option-content flex-row disable-autocomplete-active-color align-center"
>
@if (multi()) {
<mat-checkbox [checked]="isSelected(item)"></mat-checkbox>
}
@if (!templateRef()) {
<span
class="option-label"
[class.text-truncate]="truncate"
[class.text-wrap]="!truncate"
[matTooltip]="item.asString"
matTooltipPosition="above"
[class.not-defined-label]="item.isEmpty"
[class.invalid-label]="item.isInvalid"
>
{{ item.asString }}
</span>
} @else {
<div class="option-label item-option">
<ng-template
[ngTemplateOutlet]="templateRef()"
[ngTemplateOutletContext]="{ $implicit: item.initial }"
></ng-template>
</div>
}
</div>
</ng-template>
<!-- Create new option -->
@if (showAddOption() && inputElement.value) {
@for (option of availableCreateOptions(); track $index) {
<mat-option
[value]="toCreateOptionValue(option, inputElement.value)"
[attr.aria-label]="createOptionAriaLabel(option, inputElement.value)"
(onSelectionChange)="onOptionSelectionChange($event)"
>
<em
i18n="
Label for adding an option in a dropdown|e.g. Add new My new Option
"
>Add new</em
>
{{ createOptionLabel(option, inputElement.value) }}
</mat-option>
}
}
<mat-option style="display: none">
<!-- This mat-option is never displayed ("display: none") but has to be there,
because the footer below will only be displayed with at least one mat-option -->
</mat-option>
<div class="autocomplete-footer">
@if (hasMoreOptions()) {
<em
class="more-options-hint"
i18n="Hint shown when dropdown has more options than displayed"
>Type to filter more results ...</em
>
}
<ng-content select="[autocompleteFooter]"></ng-content>
</div>
</mat-autocomplete>