Skip to content

Commit

Permalink
feat(Form): created form array & item
Browse files Browse the repository at this point in the history
  • Loading branch information
alisahinozcelik committed Sep 13, 2020
1 parent 6a442d2 commit 176bb46
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 10 deletions.
97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,103 @@ export class FooComponent {

Additional form components, directives, validators etc.

### Form Array

Array of models for template driven forms

----------------------------------------------

#### ArrayDirective

* selector: `[thaArrayModel]`
* exportAs: `thaArrayModel`
* extends: `NgModel`

#### ArrayItemDirective

* selector: `[thaArrayItem]`
* exportAs: `thaArrayItem`
* extends: `NgModel`
* do not forget to use `formControl` for builtin form controls

----------------------------------------------

Basic Usage:
```html
<!-- model: ['foo', 'bar']-->

<form #form="ngForm">
<div thaArrayModel name="foo">
<input formControl thaArrayItem *ngFor="let item of model">
</div>
</form>

<pre>{{form.value | json}}</pre>
<!--{ foo: [
'input 1 text',
'input 2 text',
]} -->
```

Set values from `thaArrayModel`:
```html
<!-- model: ['foo', 'bar']-->

<form>
<div [thaArrayModel]="model" name="list">
<input formControl thaArrayItem *ngFor="let item of model">
</div>
</form>
```

Set values from `thaArrayItem`:
```html
<!-- model: ['foo', 'bar']-->

<form>
<div thaArrayModel name="list">
<input formControl [thaArrayItem]="item" *ngFor="let item of model">
</div>
</form>
```

Disable all items:
```html
<!-- model: ['foo', 'bar']-->

<form>
<div thaArrayModel name="list" disabled>
<input formControl [thaArrayItem]="item" *ngFor="let item of model">
</div>
</form>
```

Use `formControl` to get form control classes from angular
```html
<!-- model: ['foo', 'bar']-->

<form>
<div formControl thaArrayModel name="list"> <!-- class="ng-valid ng-pristine ng-untouched" -->
<input formControl [thaArrayItem]="item" *ngFor="let item of model">
</div>
</form>
```

Template variables can be used
```html
<!-- model: ['foo', 'bar']-->

<form>
<div formControl thaArrayModel name="list" #list="thaArrayModel">
<ng-template ngFor let-item [ngForOf]="model">
<input formControl [thaArrayItem]="item" #itemModel="thaArrayItem" required>
<pre>{{itemModel.valid}}</pre>
</ng-template>
</div>
<pre>{{ list.errors | json }}</pre>
</form>
```

### Form Disabled

Shorthand to disable/enable all controls in a form.
Expand Down
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
"util",
"utils",
"library",
"form",
"image",
"input",
"disable",
"array",
"route",
"router",
"data",
Expand Down Expand Up @@ -38,6 +41,10 @@
"e2e": "ng e2e"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/thalesrc/ng-utils.git"
},
"private": false,
"publishConfig": {
"access": "public"
Expand Down
32 changes: 31 additions & 1 deletion projects/ng-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,42 @@
"name": "@thalesrc/ng-utils",
"version": "0.0.0",
"description": "A package to store commonly used angular application contents",
"keywords": ["angular", "ng", "util", "utils", "library", "image-input"],
"keywords": [
"angular",
"ng",
"util",
"utils",
"library",
"form",
"image",
"input",
"disable",
"array",
"route",
"router",
"data",
"portal",
"substitute",
"overlay",
"modal",
"dialog",
"stream",
"host",
"listener",
"size",
"resize",
"change",
"animate"
],
"author": {
"name": "Ali Şahin Özçelik",
"email": "alisahinozcelik@gmail.com"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/thalesrc/ng-utils.git"
},
"private": false,
"publishConfig": {
"access": "public"
Expand Down
27 changes: 24 additions & 3 deletions projects/ng-utils/src/form/array/array-item.directive.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import { Directive, forwardRef, Inject, OnChanges, OnInit, Optional, Self } from '@angular/core';
import { Directive, forwardRef, Inject, Input, OnDestroy, 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 { Observable, Subject } from 'rxjs';
import { distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { InputStream } from '../../utils/input-stream';

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

@Directive({
selector: '[thaArrayItem]',
providers: [
{provide: NgControl, useExisting: forwardRef(() => FormArrayItemDirective)}
]
],
exportAs: 'thaArrayItem'
})
export class FormArrayItemDirective extends NgModel implements OnInit, OnChanges {
export class FormArrayItemDirective extends NgModel implements OnInit, OnDestroy {
private onDestroy$ = new Subject();

@Input('thaArrayItem')
@InputStream()
public itemValue$: Observable<any>;

constructor(
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
Expand All @@ -29,5 +39,16 @@ export class FormArrayItemDirective extends NgModel implements OnInit, OnChanges

public ngOnInit() {
this['_setUpControl' + '']();

this.itemValue$.pipe(distinctUntilChanged(), takeUntil(this.onDestroy$)).subscribe(value => {
this.control.setValue(value, {emitEvent: true, onlySelf: false});
});
}

public ngOnDestroy() {
super.ngOnDestroy();

this.onDestroy$.next();
this.onDestroy$.complete();
}
}
28 changes: 25 additions & 3 deletions projects/ng-utils/src/form/array/array.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import {
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 { Observable, Subject } from 'rxjs';
import { distinctUntilChanged, map, startWith, takeUntil } from 'rxjs/operators';

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

@Directive({
selector: '[thaArrayModel]',
providers: [
{provide: NgControl, useExisting: forwardRef(() => FormArrayDirective)}
]
],
exportAs: 'thaArrayModel'
})
export class FormArrayDirective extends NgModel implements AfterContentInit, OnDestroy {
private onDestroy$ = new Subject();
Expand All @@ -27,6 +29,10 @@ export class FormArrayDirective extends NgModel implements AfterContentInit, OnD
@Input()
public name: string;

@Input('thaArrayModel')
@InputStream()
public modelValue$: Observable<any[]>;

constructor(
@Optional() @Host() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
Expand All @@ -47,6 +53,22 @@ export class FormArrayDirective extends NgModel implements AfterContentInit, OnD
this.valueAccessor.statusChanges$.pipe(takeUntil(this.onDestroy$)).subscribe(array => {
this.control.setErrors(array.errors);
});

this.modelValue$.pipe(
distinctUntilChanged(),
takeUntil(this.onDestroy$)
).subscribe(value => {
value = value || [];

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

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

public ngOnDestroy() {
Expand Down
15 changes: 13 additions & 2 deletions projects/playground/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,20 @@
</textarea>

<form #arrayForm="ngForm">
<div thaArrayModel name="test">
<input *ngFor="let item of arrayInputs" formControl thaArrayItem>
<div thaArrayModel name="foo">
<input *ngFor="let item of arrayInputs" formControl [thaArrayItem]="item">
</div>
<button (click)="arrayInputs.push(null)"> Add Input to array</button>
</form>
<pre>{{arrayForm.value | json}}</pre>

<form>
<div formControl thaArrayModel name="list" #list="thaArrayModel">
<ng-template ngFor let-item [ngForOf]="arrayInputs">
<input formControl [thaArrayItem]="item" #itemModel="thaArrayItem" required>
<pre>{{itemModel.value}}</pre>
</ng-template>
</div>
</form>

<pre>{{list.errors | json}}</pre>
2 changes: 1 addition & 1 deletion projects/playground/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class AppComponent {
// tslint:disable-next-line:max-line-length
public image = new BehaviorSubject('');

public arrayInputs = [];
public arrayInputs = ['test'];

constructor() {
// setInterval(this.changeImage.bind(this), 1000);
Expand Down

0 comments on commit 176bb46

Please sign in to comment.