projects/cobbler-frontend/src/app/common/key-value-editor/key-value-editor.component.ts
ControlValueAccessor
Validator
providers |
{
provide: NG_VALUE_ACCESSOR, multi: true, useExisting: KeyValueEditorComponent,
}
{
provide: NG_VALIDATORS, multi: true, useExisting: KeyValueEditorComponent,
}
|
selector | cobbler-key-value-editor |
standalone | true |
imports |
MatCardModule
CdkDropList
CdkDrag
MatFormFieldModule
MatInputModule
MatIconModule
ReactiveFormsModule
MatButtonModule
|
templateUrl | ./key-value-editor.component.html |
styleUrl | ./key-value-editor.component.scss |
Properties |
|
Methods |
Inputs |
constructor(dialog: MatDialog)
|
||||||
Parameters :
|
label | |
Type : string
|
|
Default value : ''
|
|
addOption |
addOption()
|
Returns :
void
|
buildFormGroup |
buildFormGroup()
|
Returns :
void
|
deleteKey | ||||||
deleteKey(key: string)
|
||||||
Parameters :
Returns :
void
|
drop | ||||||
drop(event: CdkDragDrop
|
||||||
Parameters :
Returns :
void
|
registerOnChange | ||||||
registerOnChange(fn: any)
|
||||||
Parameters :
Returns :
void
|
registerOnTouched | ||||||
registerOnTouched(fn: any)
|
||||||
Parameters :
Returns :
void
|
registerOnValidatorChange | ||||||
registerOnValidatorChange(fn: () => void)
|
||||||
Parameters :
Returns :
void
|
setDisabledState | ||||||
setDisabledState(isDisabled: boolean)
|
||||||
Parameters :
Returns :
void
|
setFormGroupDisabledState | ||||||
setFormGroupDisabledState(isDisabled: boolean)
|
||||||
Parameters :
Returns :
void
|
validate | ||||||
validate(control: AbstractControl)
|
||||||
Parameters :
Returns :
ValidationErrors | null
|
writeValue | ||||||
writeValue(obj: Map
|
||||||
Parameters :
Returns :
void
|
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>
=
<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>