File

projects/cobbler-frontend/src/app/common/key-value-editor/key-value-editor.component.ts

Implements

ControlValueAccessor Validator

Metadata

Index

Properties
Methods
Inputs

Constructor

constructor(dialog: MatDialog)
Parameters :
Name Type Optional
dialog MatDialog No

Inputs

label
Type : string
Default value : ''

Methods

addOption
addOption()
Returns : void
buildFormGroup
buildFormGroup()
Returns : void
deleteKey
deleteKey(key: string)
Parameters :
Name Type Optional
key string No
Returns : void
drop
drop(event: CdkDragDrop)
Parameters :
Name Type Optional
event CdkDragDrop<string[]> No
Returns : void
registerOnChange
registerOnChange(fn: any)
Parameters :
Name Type Optional
fn any No
Returns : void
registerOnTouched
registerOnTouched(fn: any)
Parameters :
Name Type Optional
fn any No
Returns : void
registerOnValidatorChange
registerOnValidatorChange(fn: () => void)
Parameters :
Name Type Optional
fn function No
Returns : void
setDisabledState
setDisabledState(isDisabled: boolean)
Parameters :
Name Type Optional
isDisabled boolean No
Returns : void
setFormGroupDisabledState
setFormGroupDisabledState(isDisabled: boolean)
Parameters :
Name Type Optional
isDisabled boolean No
Returns : void
validate
validate(control: AbstractControl)
Parameters :
Name Type Optional
control AbstractControl No
Returns : ValidationErrors | null
writeValue
writeValue(obj: Map)
Parameters :
Name Type Optional
obj Map<string | any> No
Returns : void

Properties

isDisabled
Default value : true
keyOrder
Type : string[]
Default value : Array.from(this.keyValueOptions.keys())
keyOrderFormGroup
Default value : new FormGroup({})
keyValueOptions
Type : Map<string | any>
Default value : new Map<string, any>()
Protected Readonly Object
Default value : Object
onChange
Type : any
onTouched
Type : any
import {
  CdkDrag,
  CdkDragDrop,
  CdkDropList,
  moveItemInArray,
} from '@angular/cdk/drag-drop';
import { Component, Inject, Input } from '@angular/core';
import {
  AbstractControl,
  ControlValueAccessor,
  FormControl,
  FormGroup,
  NG_VALIDATORS,
  NG_VALUE_ACCESSOR,
  ReactiveFormsModule,
  ValidationErrors,
  Validator,
} from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatDialog } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import {
  DialogKeyValueInputComponent,
  DialogKeyValueInputReturnData,
} from '../dialog-key-value-input/dialog-key-value-input.component';

@Component({
  selector: 'cobbler-key-value-editor',
  standalone: true,
  imports: [
    MatCardModule,
    CdkDropList,
    CdkDrag,
    MatFormFieldModule,
    MatInputModule,
    MatIconModule,
    ReactiveFormsModule,
    MatButtonModule,
  ],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      multi: true,
      useExisting: KeyValueEditorComponent,
    },
    {
      provide: NG_VALIDATORS,
      multi: true,
      useExisting: KeyValueEditorComponent,
    },
  ],
  templateUrl: './key-value-editor.component.html',
  styleUrl: './key-value-editor.component.scss',
})
export class KeyValueEditorComponent
  implements ControlValueAccessor, Validator
{
  @Input() label = '';
  keyValueOptions: Map<string, any> = new Map<string, any>();
  onChange: any;
  onTouched: any;
  keyOrder: string[] = Array.from(this.keyValueOptions.keys());
  keyOrderFormGroup = new FormGroup({});
  isDisabled = true;

  constructor(@Inject(MatDialog) readonly dialog: MatDialog) {}

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  registerOnValidatorChange(fn: () => void): void {}

  setDisabledState(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
    this.setFormGroupDisabledState(isDisabled);
  }

  setFormGroupDisabledState(isDisabled: boolean): void {
    if (isDisabled) {
      this.keyOrderFormGroup.disable();
    } else {
      this.keyOrderFormGroup.enable();
    }
  }

  validate(control: AbstractControl): ValidationErrors | null {
    return undefined;
  }

  writeValue(obj: Map<string, any>): void {
    if (!(obj instanceof Map)) {
      throw new Error("obj wasn't of type Map!");
    }
    this.keyValueOptions = obj;
    this.keyOrder = Array.from(this.keyValueOptions.keys());
    this.buildFormGroup();
  }

  buildFormGroup(): void {
    for (let key of this.keyOrder) {
      const formGroupControls = {
        key: new FormControl({ value: key, disabled: true }),
        value: new FormControl({
          value: this.keyValueOptions.get(key),
          disabled: true,
        }),
      };
      this.keyOrderFormGroup.addControl(
        key + 'FormGroup',
        new FormGroup(formGroupControls),
      );
    }
    this.setFormGroupDisabledState(this.isDisabled);
  }

  deleteKey(key: string): void {
    let newOptions = new Map<string, any>(this.keyValueOptions);
    newOptions.delete(key);
    this.onChange(newOptions);
    this.onTouched();
    this.writeValue(newOptions);
  }

  addOption(): void {
    const dialogRef = this.dialog.open(DialogKeyValueInputComponent);

    dialogRef
      .afterClosed()
      .subscribe((dialogResult: DialogKeyValueInputReturnData) => {
        if (dialogResult === undefined) {
          // undefined means abort adding the key
          return;
        }
        let newOptions = new Map<string, any>(this.keyValueOptions);
        newOptions.set(dialogResult.key, dialogResult.value);
        this.onChange(newOptions);
        this.onTouched();
        this.writeValue(newOptions);
      });
  }

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.keyOrder, event.previousIndex, event.currentIndex);
  }

  protected readonly Object = Object;
}
<mat-card appearance="outlined">
  <mat-card-header>
    <mat-card-title>{{ label }}</mat-card-title>
  </mat-card-header>
  @if (this.keyValueOptions.size === 0) {
    <p style="text-align: center">Empty list of options</p>
  } @else {
    <div
      cdkDropList
      [cdkDropListDisabled]="isDisabled"
      class="example-list"
      [formGroup]="keyOrderFormGroup"
      (cdkDropListDropped)="drop($event)"
    >
      @for (key of keyOrder; track key) {
        <form class="example-box" formGroupName="{{ key }}FormGroup">
          <mat-form-field>
            <input
              matInput
              formControlName="key"
              placeholder="Key"
              value="{{ key }}"
            />
          </mat-form-field>
          &nbsp;=&nbsp;
          <mat-form-field>
            <input
              matInput
              formControlName="value"
              placeholder="Value"
              value="{{ keyValueOptions.get(key) }}"
            />
          </mat-form-field>
          <button
            mat-icon-button
            [disabled]="isDisabled"
            (click)="deleteKey(key)"
          >
            <mat-icon>delete</mat-icon>
          </button>
          <button mat-icon-button [disabled]="isDisabled" cdkDrag>
            <mat-icon>menu</mat-icon>
          </button>
        </form>
      }
    </div>
  }
  <button mat-button [disabled]="isDisabled" (click)="addOption()">
    Add option
  </button>
</mat-card>
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""