Skip to content

Commit

Permalink
refactor(ɵcomponents): migrate to strictly typed Angular forms
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcarrian authored and danielwiehl committed Jul 19, 2023
1 parent b591d30 commit 0f858ac
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import {ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input, OnDestroy} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, ReactiveFormsModule} from '@angular/forms';
import {ControlValueAccessor, NG_VALUE_ACCESSOR, NonNullableFormBuilder, ReactiveFormsModule} from '@angular/forms';
import {noop, Subject} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
Expand All @@ -36,7 +36,7 @@ export class SciCheckboxComponent implements ControlValueAccessor, OnDestroy {
private _cvaChangeFn: (value: any) => void = noop;
private _cvaTouchedFn: () => void = noop;

public formControl = new FormControl(false, {updateOn: 'change', nonNullable: true});
public formControl = this._formBuilder.control(false, {updateOn: 'change'});

/**
* Sets focus order in sequential keyboard navigation.
Expand All @@ -50,7 +50,7 @@ export class SciCheckboxComponent implements ControlValueAccessor, OnDestroy {
coerceBooleanProperty(disabled) ? this.formControl.disable() : this.formControl.enable();
}

constructor(private _cd: ChangeDetectorRef) {
constructor(private _cd: ChangeDetectorRef, private _formBuilder: NonNullableFormBuilder) {
this.formControl.valueChanges
.pipe(takeUntil(this._destroy$))
.subscribe(checked => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, HostBinding, HostListener, Input, OnDestroy, Output, ViewChild} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, ReactiveFormsModule} from '@angular/forms';
import {ControlValueAccessor, NG_VALUE_ACCESSOR, NonNullableFormBuilder, ReactiveFormsModule} from '@angular/forms';
import {takeUntil} from 'rxjs/operators';
import {noop, Subject} from 'rxjs';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
Expand Down Expand Up @@ -78,12 +78,12 @@ export class SciFilterFieldComponent implements ControlValueAccessor, OnDestroy
}

/* @docs-private */
public formControl: FormControl;
public formControl = this._formBuilder.control('', {updateOn: 'change'});

constructor(private _host: ElementRef,
private _focusManager: FocusMonitor,
private _cd: ChangeDetectorRef) {
this.formControl = new FormControl('', {updateOn: 'change', nonNullable: true});
private _cd: ChangeDetectorRef,
private _formBuilder: NonNullableFormBuilder) {
this.formControl.valueChanges
.pipe(takeUntil(this._destroy$))
.subscribe(value => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ <h2 *ngIf="title">{{title}}</h2>

<ng-container *ngFor="let keyValueGroup of keyValueFormArray.controls; index as i;">
<!-- key -->
<ng-container *ngIf="keyValueGroup.get(PARAM_NAME)!.disabled; then key_readonly; else key_editable"></ng-container>
<ng-container *ngIf="keyValueGroup.controls.key.disabled; then key_readonly; else key_editable"></ng-container>
<!-- value -->
<input [formControl]="$any(keyValueGroup.get(PARAM_VALUE))" [attr.id]="id + '_' + i" class="e2e-value">
<input [formControl]="keyValueGroup.controls.value" [attr.id]="id + '_' + i" class="e2e-value">
<!-- 'remove' button -->
<button *ngIf="removable" class="material-icons e2e-remove" type="button" (click)="onRemove(i)" title="remove entry">remove</button>

<ng-template #key_readonly>
<label [for]="id + '_' + i">{{keyValueGroup.get(PARAM_NAME)!.value}}</label>
<label [for]="id + '_' + i">{{keyValueGroup.controls.key.value}}</label>
</ng-template>
<ng-template #key_editable>
<input [formControl]="$any(keyValueGroup.get(PARAM_NAME))" class="e2e-key">
<input [formControl]="keyValueGroup.controls.key" class="e2e-key">
</ng-template>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@
*/

import {Component, ElementRef, HostBinding, Input} from '@angular/core';
import {FormArray, FormBuilder, ReactiveFormsModule} from '@angular/forms';
import {FormArray, FormControl, FormGroup, NonNullableFormBuilder, ReactiveFormsModule} from '@angular/forms';
import {Dictionary, Maps} from '@scion/toolkit/util';
import {UUID} from '@scion/toolkit/uuid';
import {NgFor, NgIf} from '@angular/common';

export const PARAM_NAME = 'paramName';
export const PARAM_VALUE = 'paramValue';

/**
* Allows entering key-value pairs.
*/
Expand All @@ -33,15 +30,13 @@ export const PARAM_VALUE = 'paramValue';
})
export class SciKeyValueFieldComponent {

public readonly PARAM_NAME = PARAM_NAME;
public readonly PARAM_VALUE = PARAM_VALUE;
public readonly id = UUID.randomUUID();

@Input()
public title?: string | undefined;

@Input({required: true})
public keyValueFormArray!: FormArray;
public keyValueFormArray!: FormArray<FormGroup<KeyValueEntry>>;

@Input()
@HostBinding('class.removable')
Expand All @@ -54,7 +49,7 @@ export class SciKeyValueFieldComponent {
@HostBinding('attr.tabindex')
public tabindex = -1;

constructor(private _formBuilder: FormBuilder, private _host: ElementRef<HTMLElement>) {
constructor(private _formBuilder: NonNullableFormBuilder, private _host: ElementRef<HTMLElement>) {
}

public onRemove(index: number): void {
Expand All @@ -67,8 +62,8 @@ export class SciKeyValueFieldComponent {

public onAdd(): void {
this.keyValueFormArray.push(this._formBuilder.group({
[PARAM_NAME]: this._formBuilder.control(''),
[PARAM_VALUE]: this._formBuilder.control(''),
key: this._formBuilder.control(''),
value: this._formBuilder.control(''),
}));
}

Expand All @@ -81,14 +76,14 @@ export class SciKeyValueFieldComponent {
*
* By default, if empty, `null` is returned.
*/
public static toDictionary(formArray: FormArray, returnNullIfEmpty?: true): Dictionary | null;
public static toDictionary(formArray: FormArray, returnNullIfEmpty: false): Dictionary;
public static toDictionary(formArray: FormArray, returnNullIfEmpty: boolean): Dictionary | null;
public static toDictionary(formArray: FormArray, returnNullIfEmpty: boolean = true): Dictionary | null {
public static toDictionary(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty?: true): Dictionary | null;
public static toDictionary(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty: false): Dictionary;
public static toDictionary(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty: boolean): Dictionary | null;
public static toDictionary(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty: boolean = true): Dictionary | null {
const dictionary: Dictionary = {};
formArray.controls.forEach(formGroup => {
const paramName = formGroup.get(PARAM_NAME)!.value;
dictionary[paramName] = formGroup.get(PARAM_VALUE)!.value;
const key = formGroup.controls.key.value;
dictionary[key] = formGroup.controls.value.value;
});

if (!Object.keys(dictionary).length && returnNullIfEmpty) {
Expand All @@ -103,10 +98,18 @@ export class SciKeyValueFieldComponent {
*
* By default, if empty, `null` is returned.
*/
public static toMap(formArray: FormArray, returnNullIfEmpty?: true): Map<string, any> | null;
public static toMap(formArray: FormArray, returnNullIfEmpty: false): Map<string, any>;
public static toMap(formArray: FormArray, returnNullIfEmpty: boolean): Map<string, any> | null;
public static toMap(formArray: FormArray, returnNullIfEmpty: boolean = true): Map<string, any> | null {
public static toMap(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty?: true): Map<string, any> | null;
public static toMap(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty: false): Map<string, any>;
public static toMap(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty: boolean): Map<string, any> | null;
public static toMap(formArray: FormArray<FormGroup<KeyValueEntry>>, returnNullIfEmpty: boolean = true): Map<string, any> | null {
return Maps.coerce(SciKeyValueFieldComponent.toDictionary(formArray, returnNullIfEmpty), {coerceNullOrUndefined: false}) ?? null;
}
}

/**
* Represents the entry of the form array.
*/
export interface KeyValueEntry {
key: FormControl<string>;
value: FormControl<string>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
*
* @see https://github.com/ng-packagr/ng-packagr/blob/master/docs/secondary-entrypoints.md
*/
export {SciKeyValueFieldComponent} from './key-value-field.component';
export {SciKeyValueFieldComponent, KeyValueEntry} from './key-value-field.component';

0 comments on commit 0f858ac

Please sign in to comment.