I'm working on a form that is supposed to update some ui parts only on input blur or enter key pressed. I'm a big fan of reactive forms and I'm trying to figure out what is the proper way. So far I haven't found too many answers for reactive forms. Most of them are focused on template driven forms. I got something like this so far:
Form component Dynamic form values change at runtime (No predictable filed names)
import { Component, Input, Output, EventEmitter,
ChangeDetectionStrategy } from '@angular/core'
import { FormBuilder, FormGroup, FormControl } from '@angular/forms'
import { Observable } from "rxjs/Observable"
import { DEBUG } from '../../../../config/config'
import * as Debug from 'debug'
// Interfaces
import { Foo } from '../../interfaces/foo'
// Services
import { FooService } from '../../services/foo.service'
// Debug
const debugOff = (...any) => { }, debug = Debug('app:FooCmp')
@Component({
selector: 'foo-data-cmp',
templateUrl: './foo.data.cmp.html',
changeDetection: ChangeDetectionStrategy.OnPush,
styleUrls: ['./foo.data.cmp.css']
})
export class FooDataCmp {
private _foo: Foo[] = null
@Input('foo')
set foo(foo: Foo[]) {
this._foo = foo
DEBUG.input && debug('Input foo:', this._foo)
}
get foo(): Foo[] {
return this._foo
}
// Forms
public fooForm: FormGroup
public fooForm$: Observable<any>
private updatedFoo: any[] // Updated form values
// Subscriptions
private subscriptions: any[] = []
constructor(
private formBuilder: FormBuilder,
private fooService: FooService,
) {
DEBUG.constr && debug('Construct FooDataCmp')
}
ngOnInit() {
DEBUG.init && debug('Initialise FooDataCmp')
// Form
this.initFooForm()
this.subcribeToFormChanges()
}
ngOnDestroy() {
DEBUG.destroy && debug('Destroy FooDataCmp')
this.subscriptions.forEach(sub => sub.unsubscribe())
}
private initFooForm() {
DEBUG.cmp && debug('Initialise foo form')
// Build the form
this.fooForm = this.formBuilder.group(this._foo)
// Prevent undefined error at first keystroke
this.updatedFoo = this._foo
}
private subcribeToFormChanges() {
this.fooForm$ = this.fooForm.valueChanges
let sub = this.fooForm$.subscribe(fooForm => {
DEBUG.cmp && debug('Form changes fooForm', fooForm)
this.updatedFoo = fooForm
})
this.subscriptions.push(sub)
}
/**
* <!> Important step in the data update process
* Update state store.
* Other services / components are subscribed to the state store itself
*/
public refreshAllFooRelatedData () {
DEBUG.cmp && debug('Refresh all foo related data')
DEBUG.cmp && debugOff('Updated foo', this.updatedFoo)
this.fooService.refreshAllFooRelatedData(this.updatedFoo)
}
public refreshAllFooRelatedDataOnEnter (e: KeyboardEvent) {
if (e.keyCode !== 13) {return}
DEBUG.cmp && debug('Refresh all foo related data (on enter)')
DEBUG.cmp && debugOff('Updated foo', this.updatedFoo)
this.fooService.refreshAllFooRelatedData(this.updatedFoo)
}
public debugTemplate() {
DEBUG.render && debug('Render FooDataCmp')
}
}
Form template
<form [formGroup]="fooForm">
<table class="list expiries">
<td *ngFor="let value of foo; let i = index">
<input type="text" [formControlName]="foo[i]"
(blur)="refreshAllFooRelatedData()"
(keydown)="refreshAllFooRelatedDataOnEnter($event)">
</td>
</table>
</form>
{{ debugTemplate() }}
Is this a decent solution or am I missing some trick? My question is if this is the proper way to do it. I've been expecting to find some form API options for handling this task. My approach was to build on top of the forms, but I would rather use the full form api if there is anything available for this task
if anyone in the future find this, there is actually something built into reactive forms for this, the updateOn
option:
new FormControl('', {updateOn: 'blur'});
this will cause valueChanges and validation and whatnot to only trigger on blur
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With