src/app/core/entity-details/form/form.component.ts

Description

A simple wrapper function of the EntityFormComponent which can be used as a dynamic component e.g. as a panel for the EntityDetailsComponent.

Example

Metadata

Relationships

Used by

No results matching.

Index

Properties
Methods
Inputs

Constructor

constructor()

Inputs

creatingNew
Type : boolean
Default value : false
entity
Type : E
fieldGroups
Type : FieldGroup[]

Methods

cancelClicked
cancelClicked()
Returns : void
Async saveClicked
saveClicked()
Returns : any

Properties

Readonly form
Type : unknown
Default value : this.formResource.value
import {
  ChangeDetectionStrategy,
  Component,
  DestroyRef,
  effect,
  inject,
  input,
  resource,
} from "@angular/core";
import { Entity } from "../../entity/model/entity";
import { getParentUrl } from "../../../utils/utils";
import { Router } from "@angular/router";
import { Location } from "@angular/common";
import { DynamicComponent } from "../../config/dynamic-components/dynamic-component.decorator";
import { InvalidFormFieldError } from "../../common-components/entity-form/invalid-form-field.error";
import { EntityFormService } from "../../common-components/entity-form/entity-form.service";
import { EntityForm } from "#src/app/core/common-components/entity-form/entity-form";
import { AlertService } from "../../alerts/alert.service";
import { MatButtonModule } from "@angular/material/button";
import { EntityFormComponent } from "../../common-components/entity-form/entity-form/entity-form.component";
import { DisableEntityOperationDirective } from "../../permissions/permission-directive/disable-entity-operation.directive";
import { FieldGroup } from "./field-group";
import { ViewComponentContext } from "../../ui/abstract-view/view-component-context";
import { PublicFormConfig } from "../../../features/public-form/public-form-config";
import { PublicFormPermissionService } from "../../../features/public-form/public-form-permission.service";

/**
 * A simple wrapper function of the EntityFormComponent which can be used as a dynamic component
 * e.g. as a panel for the EntityDetailsComponent.
 */
@DynamicComponent("Form")
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-form",
  templateUrl: "./form.component.html",
  styleUrls: ["./form.component.scss"],
  imports: [
    MatButtonModule,
    EntityFormComponent,
    DisableEntityOperationDirective,
  ],
})
export class FormComponent<E extends Entity> {
  private router = inject(Router);
  private location = inject(Location);
  private entityFormService = inject(EntityFormService);
  private alertService = inject(AlertService);
  private viewContext = inject(ViewComponentContext, { optional: true });
  private readonly permissionService = inject(PublicFormPermissionService);
  private readonly destroyRef = inject(DestroyRef);
  entity = input<E>();
  creatingNew = input<boolean>(false);
  fieldGroups = input<FieldGroup[]>();

  private readonly formResource = resource({
    params: () => ({ entity: this.entity(), fieldGroups: this.fieldGroups() }),
    loader: async ({ params: { entity, fieldGroups } }) => {
      if (!entity || !fieldGroups) return undefined;
      return this.entityFormService.createEntityForm(
        [].concat(...fieldGroups.map((group) => group.fields)),
        entity,
        this.destroyRef,
      );
    },
  });
  readonly form = this.formResource.value;

  constructor() {
    effect(() => {
      const form = this.form();
      if (form && !this.creatingNew()) {
        form.formGroup.disable();
      }
    });
  }

  async saveClicked() {
    const entity = this.entity();
    const form = this.form();
    if (!entity || !form) {
      return;
    }
    try {
      // Check if this is a PublicFormConfig and needs permission checking
      if (entity.getType() === PublicFormConfig.ENTITY_TYPE) {
        const entityType = form.formGroup.getRawValue()["entity"] as string;
        if (!(await this.permissionService.checkOnSave(entityType))) return;
      }

      await this.entityFormService.saveChanges(form, entity);

      if (this.creatingNew() && !this.viewContext?.isDialog) {
        await this.router.navigate([
          getParentUrl(this.router),
          entity.getId(true),
        ]);
      }
    } catch (err) {
      if (!(err instanceof InvalidFormFieldError)) {
        this.alertService.addDanger(err.message);
      }
    }
  }

  cancelClicked() {
    const entity = this.entity();
    const form = this.form();
    if (!entity || !form) {
      return;
    }

    if (this.creatingNew()) {
      this.location.back();
      return;
    }
    this.entityFormService.resetForm(form, entity);
    form.formGroup.disable();
  }
}

/**
 * Config format that the FormComponent handles.
 */
export interface FormConfig {
  fieldGroups: FieldGroup[];
}
<div
  class="form-buttons-wrapper padding-bottom-small flex-row gap-small align-self-end"
>
  @if (form()?.formGroup.enabled) {
    <button
      mat-raised-button
      color="accent"
      (click)="saveClicked()"
      i18n="Save button for forms"
    >
      Save
    </button>

    <button
      mat-stroked-button
      (click)="cancelClicked()"
      i18n="Cancel button for forms"
    >
      Cancel
    </button>
  }

  @if (form()?.formGroup.disabled && !entity()?.anonymized) {
    <button
      mat-raised-button
      (click)="form()?.formGroup.enable()"
      *appDisabledEntityOperation="{
        entity: entity(),
        operation: 'update',
      }"
      i18n="Edit button for forms"
    >
      Edit
    </button>
  }
</div>

@if (form()) {
  <app-entity-form
    [entity]="entity()"
    [fieldGroups]="fieldGroups()"
    [form]="form()"
  ></app-entity-form>
}

./form.component.scss

@use "variables/sizes";
@use "variables/colors";

:host {
  display: flex;
  flex-direction: column;
}

.form-buttons-wrapper {
  position: sticky;
  top: sizes.$regular;
  z-index: 100;
  margin-right: 3px;

  background-color: colors.$background;
  padding: sizes.$x-small;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""