2017-04-20 6 views
4

In meiner App Angular 4.0.2 habe ich eine FormGroup, die eine verschachtelte Komponente enthält, die ControlValueAccessor und Validator implementiert. Diese Komponente validiert sich selbst asynchron. Das Problem ist, selbst wenn die verschachtelte Komponente gültig wird, bleibt das übergeordnete Element FormGroup ungültig.Warum skalieren asynchron validierte verschachtelte Steuerelemente in Angular 4 ihre Gültigkeit nicht an die übergeordnete FormGroup?

Aber, wenn ich einfach die Validierung zu synchron ändern, propagiert die Gültigkeit korrekt an die übergeordnete Gruppe.

Ich habe es eingekocht so minimal wie möglich auf die folgenden Plunker:

http://plnkr.co/edit/H26pqEE3sRkzKmmhrBgm

Hier ist eine zweite Plunker ein ähnliches Setup zeigt, aber dieses Mal des Eingang FormControl ist direkt in den FormGroup statt Teil einer verschachtelten Komponente sein. Hier breitet sich asynchrone Validierung korrekt:

http://plnkr.co/edit/DSt4ltoO1Cw2Nx1oXxbD

Warum ist mein asynchronen Validator seine Gültigkeit nicht ausbreiten, wenn sie innerhalb einer Komponente definiert?


Hier ist der Code von der ersten (gebrochen) Plunker:

import {Component, NgModule, VERSION, forwardRef} from '@angular/core' 
import {BrowserModule} from '@angular/platform-browser' 
import {FormsModule, ReactiveFormsModule} from '@angular/forms' 
import {Observable} from 'rxjs/Rx' 
import { 
    FormGroup, FormControl, FormBuilder, Validators, AbstractControl, Validator, 
    ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS, ValidationErrors 
} from '@angular/forms'; 


@Component({ 
    selector: 'my-app', 
    template: ` 
<p>When validated asyncronously, why is the parent form group invalid even though its 
inner control is valid?</p> 
<p>Try clearing the value to see the asyncronous validation at work.</p> 
<p>Try swapping line 58 for 60 to change to syncronous validation, which propagates correctly.</p> 
<hr /> 
<div><code>FormGroup valid = {{form.valid}}</code></div><br /> 
<div [formGroup]="form"> 
    <nested-control formControlName="nested"></nested-control> 
</div> 
<hr /> 
` 
}) 
export class App { 
    form: FormGroup; 
    constructor(fb: FormBuilder) { 
    this.form = fb.group({ 
     nested: ['required'] 
    }); 
    } 
} 


@Component({ 
    selector: 'nested-control', 
    providers: [{ 
     provide: NG_VALUE_ACCESSOR, 
     useExisting: forwardRef(() => NestedControl), 
     multi: true 
    }, { 
     provide: NG_VALIDATORS, 
     useExisting: forwardRef(() => NestedControl), 
     multi: true, 
    }], 
    template: ` 
<div [formGroup]="form"> 
    <input type="text" formControlName="input" (keyup)="keyup($event)"> 
    <code>NestedControl valid = {{form.controls.input.valid}}, errors = {{form.controls.input.errors|json}}</code> 
</div> 
` 
}) 
export class NestedControl implements ControlValueAccessor, Validator { 
    form: FormGroup; 
    private propagateChange =() => {}; 

    constructor(fb: FormBuilder) { 
    this.form = fb.group({ 
     // if we change the validation to be syncronous then everything works as expected: 
     //input: [null, Validators.required] 
     input: [null, null, this.asyncRequiredValidator] 
    }); 
    } 

    asyncRequiredValidator(control: AbstractControl): ValidationErrors { 
    // run the built in required validator after 1 second 
    return Observable.interval(1000).take(1).map(_ => { 
     const result = Validators.required(control); 
     console.log('result', result); 
     return result; 
    }); 
    } 

    keyup() { 
    this.propagateChange(); 
    } 

    writeValue(v: any): void { 
    this.form.setValue({ input: v }); 
    } 

    registerOnChange(fn: any): void { 
    this.propagateChange = fn; 
    } 

    registerOnTouched(fn: any): void { 
    } 

    validate(c: AbstractControl): ValidationErrors { 
    return this.form.valid ? null : {invalid: true}; 
    } 
} 


@NgModule({ 
    imports: [ BrowserModule, FormsModule, ReactiveFormsModule ], 
    declarations: [ App, NestedControl ], 
    bootstrap: [ App ] 
}) 
export class AppModule {} 

Antwort

3

Es gibt ein paar Probleme gab, die endlich herausgefunden ich, aber ich bin nicht sicher, ob diese Lösung optimal ist.

Zuerst muss die Komponente NG_ASYNC_VALIDATORS statt NG_VALIDATORS liefern (obwohl wie üblich ich das nicht überall ausdrücklich finden dokumentiert):

@Component({ 
    selector: 'nested-control', 
    providers: [{ 
     provide: NG_ASYNC_VALIDATORS, // <----- this 
     useExisting: forwardRef(() => NestedComponent), 
     multi: true, 
    } ... 

Als nächstes sollte AsyncValidator implementieren, anstatt Validator:

export class NestedComponent implements ControlValueAccessor, AsyncValidator { 
    ... 
    validate(c: AbstractControl): Observable<ValidationErrors | null> { 
     // must return result wrapped in Observable 
     return Observable.of(this.form.valid ? null : {invalid: true}); 
    } 
} 

Schließlich müssen wir nach der Validierung die Änderung explizit propagieren. Ich musste einen setTimeout verwenden, um sicherzustellen, dass der Status nach läuft der Validator propagiert wird, anstelle der aktuellen. Das scheint Hacky für mich, also wenn jemand einen besseren Weg kennt, lass es mich wissen. Hier

asyncRequiredValidator(control: AbstractControl): ValidationErrors { 
    // run the built in required validator after 1 second 
    return Observable.interval(1000).take(1).map(_ => { 
     const result = Validators.required(control); 
     console.log('result', result); 
     setTimeout(() => this.propagateChange()); // <!---- propagate post-validation state 
     return result; 
    }); 
    } 

ist der Plunker mit all diesem behoben:

http://plnkr.co/edit/6uSWVhdFgyHTvYjHlgmN?p=preview

+0

Hallo Ich habe versucht, aber es versagt für Multi-Form-Steuer witk einen Controler sysnc und asynch Validierung und andere Controler mit Synch Validierung haveing , Parent hat den Kindstatus nicht erwartet. – udaykumar

Verwandte Themen