src/app/core/admin/admin-widget-dialog/admin-widget-dialog.component.ts

Description

Generic dashboard widget editing dialog that also displays a custom component for the specific widget type.

Example

Metadata

Relationships

Used by

No results matching.

Index

Properties
Methods

Constructor

constructor()

Methods

onCancel
onCancel()
Returns : void
onSave
onSave()
Returns : void
prettifyTitle
prettifyTitle(componentName: string)
Parameters :
Name Type Optional
componentName string No
Returns : string

Properties

commonConfig
Type : { subtitle?: TranslatableText; explanation?: TranslatableText }
Default value : {}

raw values, so that translations into other languages are kept on save

data
Type : unknown
Default value : inject(MAT_DIALOG_DATA) as AdminWidgetDialogData
dialogRef
Type : unknown
Default value : inject(MatDialogRef<AdminWidgetDialogComponent>)
settingsComponentConfig
Type : DynamicComponentConfig
widgetConfigForm
Type : FormControl
widgetConfigInvalid
Type : Signal<boolean>

tracked as a signal, because the settings component may change validity after this view was checked

import {
  Component,
  inject,
  ChangeDetectionStrategy,
  Signal,
} from "@angular/core";
import { toSignal } from "@angular/core/rxjs-interop";
import { map } from "rxjs/operators";
import {
  MAT_DIALOG_DATA,
  MatDialogRef,
  MatDialogModule,
} from "@angular/material/dialog";
import { MatButtonModule } from "@angular/material/button";
import { DynamicComponentConfig } from "../../config/dynamic-components/dynamic-component-config.interface";
import { DynamicComponentDirective } from "../../config/dynamic-components/dynamic-component.directive";
import { FormControl, FormsModule } from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import {
  MatExpansionPanel,
  MatExpansionPanelHeader,
  MatExpansionPanelTitle,
} from "@angular/material/expansion";
import { TranslatableText } from "../../config/multi-lingual-config";
import { TranslatableTextInputComponent } from "../../config/translatable-text-input/translatable-text-input.component";
import { resolveLocaleText } from "../../language/active-locale";

export interface AdminWidgetDialogData {
  widgetConfig: DynamicComponentConfig;
  settingsComponent: string;
  title: string;
}

/**
 * Generic dashboard widget editing dialog
 * that also displays a custom component for the specific widget type.
 */
@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: "app-admin-widget-dialog",
  standalone: true,
  imports: [
    MatDialogModule,
    MatButtonModule,
    MatFormFieldModule,
    FormsModule,
    DynamicComponentDirective,
    MatExpansionPanel,
    MatExpansionPanelHeader,
    MatExpansionPanelTitle,
    TranslatableTextInputComponent,
  ],
  templateUrl: "./admin-widget-dialog.component.html",
  styleUrls: ["./admin-widget-dialog.component.scss"],
})
export class AdminWidgetDialogComponent {
  settingsComponentConfig: DynamicComponentConfig;
  widgetConfigForm: FormControl;
  /** tracked as a signal, because the settings component may change validity after this view was checked */
  widgetConfigInvalid: Signal<boolean>;

  /** raw values, so that translations into other languages are kept on save */
  commonConfig: {
    subtitle?: TranslatableText;
    explanation?: TranslatableText;
  } = {};

  dialogRef = inject(MatDialogRef<AdminWidgetDialogComponent>);
  data = inject(MAT_DIALOG_DATA) as AdminWidgetDialogData;

  constructor() {
    this.commonConfig.subtitle = this.data.widgetConfig.config?.subtitle;
    this.commonConfig.explanation = this.data.widgetConfig.config?.explanation;

    this.widgetConfigForm = new FormControl(this.data.widgetConfig.config);
    this.widgetConfigInvalid = toSignal(
      this.widgetConfigForm.statusChanges.pipe(
        map((status) => status === "INVALID"),
      ),
      { initialValue: this.widgetConfigForm.invalid },
    );
    this.settingsComponentConfig = {
      component: this.data.settingsComponent,
      config: {
        formControl: this.widgetConfigForm,
      },
    };
  }

  prettifyTitle(componentName: string): string {
    return componentName.replace(/([a-z])([A-Z])/g, "$1 $2");
  }

  onSave() {
    if (this.widgetConfigForm.invalid) {
      return;
    }
    const config: Record<string, unknown> = {
      ...(this.widgetConfigForm.value ?? {}),
    };

    for (const key of ["subtitle", "explanation"] as const) {
      if (hasText(this.commonConfig[key])) {
        config[key] = this.commonConfig[key];
      } else {
        delete config[key];
      }
    }

    const updatedConfig = {
      ...this.data.widgetConfig,
      config,
    };
    this.dialogRef.close(updatedConfig);
  }

  onCancel() {
    this.dialogRef.close();
  }
}

/** whether a plain or multi-lingual text has any non-blank text in some language */
function hasText(value: TranslatableText | undefined): boolean {
  const text = resolveLocaleText(value);
  return typeof text === "string" && text.trim().length > 0;
}
<h2 mat-dialog-title i18n>{{ prettifyTitle(data.title) }} Settings</h2>

<mat-dialog-content class="dialog-content">
  <!-- Widget-specific settings -->
  <ng-template [appDynamicComponent]="settingsComponentConfig"></ng-template>

  <!-- Advanced fields in expansion panel -->
  <mat-expansion-panel>
    <mat-expansion-panel-header>
      <mat-panel-title i18n> Subtitle & Explanation </mat-panel-title>
    </mat-expansion-panel-header>

    <mat-form-field appearance="fill" class="full-width">
      <mat-label i18n>Subtitle</mat-label>
      <app-translatable-text-input
        [(ngModel)]="commonConfig.subtitle"
      ></app-translatable-text-input>
    </mat-form-field>
    <mat-form-field appearance="fill" class="full-width">
      <mat-label i18n>Explanation</mat-label>
      <app-translatable-text-input
        [(ngModel)]="commonConfig.explanation"
        [multiline]="true"
        [rows]="2"
      ></app-translatable-text-input>
    </mat-form-field>
  </mat-expansion-panel>
</mat-dialog-content>

<mat-dialog-actions class="dialog-actions">
  <button
    mat-raised-button
    color="accent"
    (click)="onSave()"
    [disabled]="widgetConfigInvalid()"
    cdkFocusInitial
    i18n
  >
    Apply
  </button>
  <button mat-button (click)="onCancel()" i18n>Cancel</button>
</mat-dialog-actions>

./admin-widget-dialog.component.scss

Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""