src/app/core/entity/entity-field-edit/entity-field-edit.component.ts
Generic component to display one entity property field's editComponent.
Dynamically extends field details from entity schema and loads the relevant, specific EditComponent implementation.
For viewComponent of a field, see EntityFieldViewComponent.
| changeDetection | ChangeDetectionStrategy.OnPush |
| selector | app-entity-field-edit |
| imports |
HelpButtonComponent
EntityFieldViewComponent
InheritedValueButtonComponent
FontAwesomeModule
MatButtonModule
MatTooltipModule
DynamicEditComponent
ReactiveFormsModule
MatFormFieldModule
ErrorHintComponent
|
| styleUrls | ./entity-field-edit.component.scss |
| templateUrl | ./entity-field-edit.component.html |
Properties |
|
Inputs |
| compactMode | |
Type : boolean
|
|
|
Whether to display the field in a limited space, hiding details like the help description button. |
|
| entity | |
Type : T
|
|
| field | |
Type : ColumnConfig
|
|
|
field id or full config |
|
| form | |
Type : EntityForm<T>
|
|
| hideInheritButton | |
Type : boolean
|
|
Default value : false
|
|
|
Whether to hide the inherit value button for inherited-field default values. |
|
| hideLabel | |
Type : boolean
|
|
|
Whether to display the field label or not. |
|
import { EntityForm } from "#src/app/core/common-components/entity-form/entity-form";
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
} from "@angular/core";
import { FormControl, ReactiveFormsModule } from "@angular/forms";
import { MatButtonModule } from "@angular/material/button";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatTooltipModule } from "@angular/material/tooltip";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { EntitySchemaService } from "app/core/entity/schema/entity-schema.service";
import { InheritedValueButtonComponent } from "../../../features/inherited-field/inherited-value-button/inherited-value-button.component";
import { EntityFormService } from "../../common-components/entity-form/entity-form.service";
import {
ColumnConfig,
FormFieldConfig,
toFormFieldConfig,
} from "../../common-components/entity-form/FormConfig";
import { ErrorHintComponent } from "../../common-components/error-hint/error-hint.component";
import { HelpButtonComponent } from "../../common-components/help-button/help-button.component";
import { EntityFieldViewComponent } from "../entity-field-view/entity-field-view.component";
import { Entity } from "../model/entity";
import { DynamicEditComponent } from "./dynamic-edit/dynamic-edit.component";
/**
* Generic component to display one entity property field's editComponent.
*
* Dynamically extends field details from entity schema and
* loads the relevant, specific EditComponent implementation.
*
* For viewComponent of a field, see EntityFieldViewComponent.
*/
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "app-entity-field-edit",
templateUrl: "./entity-field-edit.component.html",
styleUrls: ["./entity-field-edit.component.scss"],
imports: [
HelpButtonComponent,
EntityFieldViewComponent,
InheritedValueButtonComponent,
FontAwesomeModule,
MatButtonModule,
MatTooltipModule,
DynamicEditComponent,
ReactiveFormsModule,
MatFormFieldModule,
ErrorHintComponent,
],
})
export class EntityFieldEditComponent<T extends Entity = Entity> {
private entityFormService = inject(EntityFormService);
private entitySchemaService = inject(EntitySchemaService);
/** field id or full config */
field = input<ColumnConfig>();
entity = input<T>();
form = input<EntityForm<T>>();
/** Whether to display the field in a limited space, hiding details like the help description button. */
compactMode = input<boolean>();
/** Whether to display the field label or not. */
hideLabel = input<boolean>();
/** Whether to hide the inherit value button for inherited-field default values. */
hideInheritButton = input<boolean>(false);
/** full field config extended from schema */
readonly _field = computed<FormFieldConfig | undefined>(() => {
const field = this.field();
if (!field) return undefined;
const entity = this.entity();
if (entity?.getConstructor()) {
return this.entityFormService.extendFormFieldConfig(
field,
entity.getConstructor(),
);
}
const result = toFormFieldConfig(field);
// add editComponent (because we cannot rely on the entity's schema yet for a new field)
result.editComponent =
result.editComponent ??
this.entitySchemaService.getComponent(result, "edit");
return result;
});
readonly formControl = computed<FormControl | null>(() => {
const form = this.form();
const field = this._field();
if (!form || !field) return null;
return form.formGroup.get(field.id) as FormControl;
});
readonly isPartiallyAnonymized = computed<boolean>(() => {
const entity = this.entity();
const field = this._field();
return !!(
entity?.anonymized &&
entity?.getSchema()?.get(field?.id)?.anonymize === "retain-anonymized"
);
});
}
@if (_field() && form() && _field().editComponent) {
<div
class="flex-row"
[class.hidden]="_field()?.hideFromForm"
[class.readonlyAfterSet]="
_field()?.validators?.readonlyAfterSet &&
form()?.formGroup?.enabled &&
formControl()?.disabled
"
>
<div class="flex-grow">
<mat-form-field
[floatLabel]="hideLabel() ? 'auto' : 'always'"
class="allow-full-width"
id="entity-field__{{ _field().id }}"
>
@if (!hideLabel() && _field().displayFullLengthLabel) {
<div
class="full-label"
[class.full-label-disabled]="formControl()?.disabled"
>
{{ _field().label }}
@if (_field()?.validators?.required) {
<span [class.color-error]="formControl()?.enabled">*</span>
}
</div>
}
@if (!hideLabel() && !_field().displayFullLengthLabel) {
<mat-label>
<span [matTooltip]="_field().label" matTooltipPosition="before">
{{ _field().label }}
@if (_field()?.validators?.required) {
<span [class.color-error]="formControl()?.enabled">*</span>
}
</span>
</mat-label>
}
@if (isPartiallyAnonymized()) {
<fa-icon
matIconPrefix
class="input-prefix-icon"
icon="warning"
matTooltip="This data has been partially anonymized."
i18n-matTooltip
></fa-icon>
}
<app-dynamic-edit
[formControl]="formControl()"
[formFieldConfig]="_field()"
[entity]="entity()"
></app-dynamic-edit>
<mat-error>
<app-error-hint [form]="formControl()"></app-error-hint>
</mat-error>
</mat-form-field>
</div>
@if (_field()?.validators?.readonlyAfterSet && form()?.formGroup?.enabled) {
<button
mat-icon-button
i18n-matTooltip
matTooltip="This field value can only be set when creating a record and cannot be changed afterwards"
type="button"
>
<fa-icon icon="warning"></fa-icon>
</button>
}
@if (!compactMode()) {
<app-help-button [text]="_field().description"></app-help-button>
}
@if (!hideInheritButton()) {
<app-inherited-value-button
[form]="form()"
[field]="_field()"
[entity]="entity()"
></app-inherited-value-button>
}
</div>
} @else {
<app-entity-field-view
[field]="_field()"
[entity]="entity()"
></app-entity-field-view>
}
./entity-field-edit.component.scss
.full-label {
font-family: var(--mat-form-field-label-text-font, Roboto, Arial, sans-serif);
font-size: 0.75rem;
font-weight: 400;
color: var(--mat-form-field-label-text-color, rgba(0, 0, 0, 0.6));
line-height: 1.25rem;
display: block;
white-space: pre-line;
}
.full-label-disabled {
color: var(--mat-form-field-disabled-text-color, rgba(0, 0, 0, 0.38));
}
.hidden {
display: none;
}
.readonlyAfterSet {
opacity: 0.5;
}
.input-prefix-icon {
margin-top: 12px;
padding-right: 0.5em;
}
.allow-full-width {
/* allow full-width mat-form-fields if that is needed in child fields like edit-public-form-columns */
max-width: unset;
}