Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to trigger reactive form valueChanges subscription only on input blur and enter key in Angular 2

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

like image 575
Adrian Moisa Avatar asked Oct 10 '17 10:10

Adrian Moisa


1 Answers

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

like image 107
bryan60 Avatar answered Sep 27 '22 22:09

bryan60