File

src/app/core/entity/entity-field-edit/entity-field-edit.component.ts

Description

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.

Metadata

Index

Properties
Inputs

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.

Properties

Readonly _field
Type : unknown
Default value : 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; })

full field config extended from schema

Readonly formControl
Type : unknown
Default value : 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
Type : unknown
Default value : computed<boolean>(() => { const entity = this.entity(); const field = this._field(); return !!( entity?.anonymized && entity?.getSchema()?.get(field?.id)?.anonymize === "retain-anonymized" ); })
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;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""