src/app/core/language/edit-available-locales/edit-available-locales.component.ts
Edit component for the enabled languages of a system (site settings availableLocales).
Only available if the system is set up as multi-language (multiLingualConfigEnabled),
otherwise it points to that feature being disabled.
The default language (defaultLanguage in the same form) always stays in the list,
because it is always enabled (also if nothing else is selected). It is marked among the options.
CustomFormControlDirective<ConfigurableEnumValue[]>
| changeDetection | ChangeDetectionStrategy.OnPush |
| providers |
EditAvailableLocalesComponent
|
| selector | app-edit-available-locales |
| standalone | true |
| imports |
MatTooltipModule
|
| styleUrls | ./edit-available-locales.component.scss |
| templateUrl | ./edit-available-locales.component.html |
| styleUrl | ./edit-available-locales.component.scss |
No results matching.
Properties |
|
Methods |
Inputs |
Outputs |
| formFieldConfig | |
Type : FormFieldConfig
|
|
| 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
|
|
| placeholder | |
Type : string
|
|
|
Inherited from
CustomFormControlDirective
|
|
| required | |
Type : boolean
|
|
|
Inherited from
CustomFormControlDirective
|
|
| value | |
Type : T
|
|
|
Inherited from
CustomFormControlDirective
|
|
| valueChange | |
Type : EventEmitter
|
|
|
Inherited from
CustomFormControlDirective
|
|
| onContainerClick | ||||||
onContainerClick(event: MouseEvent)
|
||||||
|
Inherited from
CustomFormControlDirective
|
||||||
|
Open the options when clicking anywhere in the field.
Bound to the click of the autocomplete in the template: the surrounding
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
|
| writeValue | |||||||||||||||
writeValue(value: T, notifyFormControl: unknown)
|
|||||||||||||||
|
Inherited from
CustomFormControlDirective
|
|||||||||||||||
|
Implementation for Angular ControlValueAccessor interface that links the form control value to the component value
Parameters :
Returns :
void
|
| Readonly defaultLanguage |
Type : unknown
|
Default value : signal<ConfigurableEnumValue | undefined>(
undefined,
)
|
|
the default language, which cannot be removed from the enabled languages |
| Readonly localeToString |
Type : unknown
|
Default value : () => {...}
|
| Readonly multiLingualConfigEnabled |
Type : unknown
|
Default value : environment.multiLingualConfigEnabled
|
| Readonly options |
Type : ConfigurableEnumValue[]
|
Default value : availableLocales.values
|
|
all selectable locales (not user-editable, so no "create option") |
| 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,
DestroyRef,
inject,
input,
OnInit,
signal,
viewChild,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ReactiveFormsModule } from "@angular/forms";
import { MatFormFieldControl } from "@angular/material/form-field";
import { MatTooltipModule } from "@angular/material/tooltip";
import { environment } from "#src/environments/environment";
import { BasicAutocompleteComponent } from "../../common-components/basic-autocomplete/basic-autocomplete.component";
import { CustomFormControlDirective } from "../../common-components/basic-autocomplete/custom-form-control.directive";
import { FormFieldConfig } from "../../common-components/entity-form/FormConfig";
import { FaDynamicIconComponent } from "../../common-components/fa-dynamic-icon/fa-dynamic-icon.component";
import { FeatureDisabledInfoComponent } from "../../common-components/feature-disabled-info/feature-disabled-info.component";
import { ConfigurableEnumValue } from "../../basic-datatypes/configurable-enum/configurable-enum.types";
import { DynamicComponent } from "../../config/dynamic-components/dynamic-component.decorator";
import { availableLocales } from "../languages";
import { EditComponent } from "../../entity/entity-field-edit/dynamic-edit/edit-component.interface";
/**
* Edit component for the enabled languages of a system (site settings `availableLocales`).
*
* Only available if the system is set up as multi-language (`multiLingualConfigEnabled`),
* otherwise it points to that feature being disabled.
* The default language (`defaultLanguage` in the same form) always stays in the list,
* because it is always enabled (also if nothing else is selected). It is marked among the options.
*/
@DynamicComponent("EditAvailableLocales")
@Component({
selector: "app-edit-available-locales",
templateUrl: "./edit-available-locales.component.html",
styleUrl: "./edit-available-locales.component.scss",
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ReactiveFormsModule,
BasicAutocompleteComponent,
MatTooltipModule,
FeatureDisabledInfoComponent,
FaDynamicIconComponent,
],
providers: [
{
provide: MatFormFieldControl,
useExisting: EditAvailableLocalesComponent,
},
],
})
export class EditAvailableLocalesComponent
extends CustomFormControlDirective<ConfigurableEnumValue[]>
implements EditComponent, OnInit
{
formFieldConfig = input<FormFieldConfig>();
private readonly destroyRef = inject(DestroyRef);
private readonly autocomplete = viewChild(BasicAutocompleteComponent);
/** all selectable locales (not user-editable, so no "create option") */
readonly options: ConfigurableEnumValue[] = availableLocales.values;
readonly localeToString = (locale: ConfigurableEnumValue) => locale?.label;
readonly multiLingualConfigEnabled = environment.multiLingualConfigEnabled;
/** the default language, which cannot be removed from the enabled languages */
readonly defaultLanguage = signal<ConfigurableEnumValue | undefined>(
undefined,
);
ngOnInit() {
const defaultLanguageControl =
this.formControl?.parent?.get("defaultLanguage");
if (!this.multiLingualConfigEnabled || !defaultLanguageControl) return;
this.defaultLanguage.set(defaultLanguageControl.value);
this.keepDefaultLanguage();
defaultLanguageControl.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
this.defaultLanguage.set(value);
this.keepDefaultLanguage();
});
this.formControl.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.keepDefaultLanguage());
}
/**
* Make sure the default language is part of the list, also an empty one
* (the enabled languages are the default language plus the selection).
* It is appended, matching the order of `SiteSettingsService.enabledLocales`.
*/
private keepDefaultLanguage() {
const defaultLanguage = this.defaultLanguage();
const enabled: ConfigurableEnumValue[] = this.formControl.value ?? [];
if (
!defaultLanguage?.id ||
enabled.some(({ id }) => id === defaultLanguage.id)
) {
return;
}
this.formControl.setValue([...enabled, defaultLanguage]);
}
/**
* Open the options when clicking anywhere in the field.
* Bound to the click of the autocomplete in the template: the surrounding `mat-form-field`
* only calls `onContainerClick` of its direct control (the generic dynamic-edit wrapper,
* where it is a no-op), so it never reaches this component.
*/
override onContainerClick(event: MouseEvent) {
this.autocomplete()?.onContainerClick(event);
}
}
@if (multiLingualConfigEnabled) {
<div class="flex-row gap-small">
<app-basic-autocomplete
#autocomplete
(click)="onContainerClick($event)"
class="locales-select flex-grow"
[formControl]="formControl"
[multi]="true"
[options]="options"
[optionToString]="localeToString"
display="chips"
>
<ng-template let-option>
{{ option.label }}
@if (option.id === defaultLanguage()?.id) {
<span
class="default-language-marker"
matTooltip="The default language is always enabled."
i18n-matTooltip
i18n
>(default language)</span
>
}
</ng-template>
</app-basic-autocomplete>
@if (enabled()) {
<app-fa-dynamic-icon
icon="caret-down"
class="form-field-icon-suffix"
(click)="autocomplete.showAutocomplete()"
></app-fa-dynamic-icon>
}
</div>
} @else {
<app-feature-disabled-info
featureName="multiple languages"
i18n-featureName
[featureEnabled]="false"
[compactView]="true"
></app-feature-disabled-info>
}
./edit-available-locales.component.scss
.locales-select {
min-width: 0;
overflow: hidden;
}
.default-language-marker {
margin-inline-start: 4px;
font-size: 0.85em;
opacity: 0.7;
}