Skip to content

Commit

Permalink
feat(Form): WIP form array
Browse files Browse the repository at this point in the history
  • Loading branch information
alisahinozcelik committed Sep 13, 2020
1 parent 2918118 commit 6a442d2
Show file tree
Hide file tree
Showing 13 changed files with 584 additions and 2 deletions.
8 changes: 8 additions & 0 deletions projects/ng-utils/src/form/array/array-item.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { FormArrayItemDirective } from './array-item.directive';

describe('FormArrayItemDirective', () => {
it('should create an instance', () => {
const directive = new FormArrayItemDirective(null, null, null);
expect(directive).toBeTruthy();
});
});
33 changes: 33 additions & 0 deletions projects/ng-utils/src/form/array/array-item.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Directive, forwardRef, Inject, OnChanges, OnInit, Optional, Self } from '@angular/core';
import {
AsyncValidator, AsyncValidatorFn, ControlValueAccessor, NG_ASYNC_VALIDATORS, NG_VALIDATORS,
NG_VALUE_ACCESSOR, NgControl, NgModel, Validator, ValidatorFn
} from '@angular/forms';

import { setUpControl } from './form-array-utils';

@Directive({
selector: '[thaArrayItem]',
providers: [
{provide: NgControl, useExisting: forwardRef(() => FormArrayItemDirective)}
]
})
export class FormArrayItemDirective extends NgModel implements OnInit, OnChanges {
constructor(
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[]
) {
super(null, validators, asyncValidators, valueAccessors);
}

private ['_setUpControl' + '']() {
this['_registered' + ''] = true;

setUpControl(this.control, this);
}

public ngOnInit() {
this['_setUpControl' + '']();
}
}
54 changes: 54 additions & 0 deletions projects/ng-utils/src/form/array/array-value-accessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ControlValueAccessor, FormArray } from '@angular/forms';
import { noop } from '@thalesrc/js-utils/legacy';
import { BehaviorSubject, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

export class ArrayValueAccessor implements ControlValueAccessor {
private onChange: (v: any) => void = noop;
private onTouched = noop;
public array = new FormArray([]);
public destroy$ = new Subject();

public statusChanges$ = new BehaviorSubject(this.array);

constructor() {
this.array.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
this.onChange(value);
});

this.array.statusChanges.pipe(takeUntil(this.destroy$)).subscribe(status => {
if (status === 'VALID') {
this.array.setErrors(null, {emitEvent: false});
} else {
this.array.setErrors(this.array.controls.map(({errors}) => errors), {emitEvent: false});
}

this.statusChanges$.next(this.array);
});
}

public writeValue(value: any) {
value = value || [];

if (!(value instanceof Array)) {
throw new Error('Value is not an array');
}

this.array.setValue((<[]> this.array.value || []).map((v, i) => value[i] || v || null) , {emitEvent: true});
}

public registerOnChange(fn: (v: any) => void) {
this.onChange = fn;
}
public registerOnTouched(fn: () => void) {
this.onTouched = fn;
}

public setDisabledState(disabled: boolean) {
const method = disabled ? 'disable' : 'enable';

for (const control of this.array.controls) {
control[method]();
}
}
}
8 changes: 8 additions & 0 deletions projects/ng-utils/src/form/array/array.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { FormArrayDirective } from './array.directive';

describe('FormArrayDirective', () => {
it('should create an instance', () => {
const directive = new FormArrayDirective(null, null, null);
expect(directive).toBeTruthy();
});
});
61 changes: 61 additions & 0 deletions projects/ng-utils/src/form/array/array.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
AfterContentInit, ContentChildren, Directive, forwardRef, Host, Inject, Input, OnDestroy, Optional, QueryList, Self
} from '@angular/core';
import {
AsyncValidator, AsyncValidatorFn, ControlContainer, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgModel, Validator, ValidatorFn
} from '@angular/forms';
import { Subject } from 'rxjs';
import { map, startWith, takeUntil } from 'rxjs/operators';

import { ArrayValueAccessor } from './array-value-accessor';
import { FormArrayItemDirective } from './array-item.directive';

@Directive({
selector: '[thaArrayModel]',
providers: [
{provide: NgControl, useExisting: forwardRef(() => FormArrayDirective)}
]
})
export class FormArrayDirective extends NgModel implements AfterContentInit, OnDestroy {
private onDestroy$ = new Subject();

public valueAccessor: ArrayValueAccessor;

@ContentChildren(FormArrayItemDirective)
private items: QueryList<FormArrayItemDirective>;

@Input()
public name: string;

constructor(
@Optional() @Host() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>
) {
super(parent, validators, asyncValidators, [new ArrayValueAccessor()]);
}

public ngAfterContentInit() {
this.items.changes.pipe(startWith(null), map(() => this.items.toArray()), takeUntil(this.onDestroy$)).subscribe(items => {
this.valueAccessor.array.clear();

for (const [index, item] of items.entries()) {
this.valueAccessor.array.insert(index, item.control);
}
});

this.valueAccessor.statusChanges$.pipe(takeUntil(this.onDestroy$)).subscribe(array => {
this.control.setErrors(array.errors);
});
}

public ngOnDestroy() {
super.ngOnDestroy();

this.onDestroy$.next();
this.onDestroy$.complete();

this.valueAccessor.destroy$.next();
this.valueAccessor.destroy$.complete();
}
}
19 changes: 19 additions & 0 deletions projects/ng-utils/src/form/array/array.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormArrayItemDirective } from './array-item.directive';
import { FormArrayDirective } from './array.directive';

@NgModule({
declarations: [
FormArrayDirective,
FormArrayItemDirective,
],
imports: [
FormsModule
],
exports: [
FormArrayDirective,
FormArrayItemDirective
]
})
export class ArrayModule { }
Loading

0 comments on commit 6a442d2

Please sign in to comment.