Why is Angular not showing the values of a string[] -
i've created plunker here - https://plnkr.co/edit/1aztv5k2gqic4erngrng?p=preview
tl;dr to see problem should open developer tools window , @ console log output. go <input> box , repeatedly add text , clear it. see value of errors output console, errors never appear in component's view.
i've written custom component implements ng_value_accessor give control databinding context, , ng_validators can have access data source (abstractcontrol) being validated.
i cannot see why *ngfor in component's template not listing errors can see present looking in console.log output.
import {component, ngmodule, forwardref} '@angular/core' import {browsermodule} '@angular/platform-browser' import { reactiveformsmodule, formsmodule } '@angular/forms'; import { formgroup, formbuilder, validators } '@angular/forms'; import { abstractcontrol, controlvalueaccessor, ng_validators, ng_value_accessor, validationerrors, validator } '@angular/forms'; import { observable } 'rxjs/observable'; import { behaviorsubject } 'rxjs/behaviorsubject'; import 'rxjs/add/operator/distinctuntilchanged'; @component({ selector: 'validation-errors', template: ` <div>errors should appear below line</div> <div *ngfor='let item of errors'>{{ item }}!!!</div> `, providers: [ { provide: ng_validators, useclass: forwardref(() => validationerrorscomponent), multi: true }, { provide: ng_value_accessor, useclass: forwardref(() => validationerrorscomponent), multi: true } ] }) export class validationerrorscomponent implements validator, controlvalueaccessor { public errors: string[] = []; private control: abstractcontrol = null; private updateerrors(errorsobject: any) { this.errors = []; if (errorsobject) { (const errortype of object.keys(errorsobject)) { this.errors.push(errortype); } } console.log('errors: ' + json.stringify(this.errors)); } validate(c: abstractcontrol): validationerrors | { if (this.control === null && c !== null && c !== undefined) { this.control = c; this.control.statuschanges .distinctuntilchanged() .subscribe(x => this.updateerrors(this.control.errors)); } } writevalue(obj: any): void { } registeronchange(fn: any): void { } registerontouched(fn: any): void { } } it doesn't seem change detection problem, have tried implementing using observables , async pipe , still shows no errors.
the consumer code looks this
//our root app component import {component, ngmodule, forwardref} '@angular/core' import {browsermodule} '@angular/platform-browser' import { reactiveformsmodule, formsmodule } '@angular/forms'; import { formgroup, formbuilder, validators } '@angular/forms'; import { abstractcontrol, controlvalueaccessor, ng_validators, ng_value_accessor, validationerrors, validator } '@angular/forms'; import { validationerrorscomponent } './validation-errors.component'; @component({ selector: 'my-app', template: ` <div [formgroup]='form'> <input formcontrolname='name' style="width: 100%"/> <validation-errors formcontrolname='name'></validation-errors> </div> `, }) export class app { public form: formgroup; constructor(formbuilder: formbuilder) { this.form = formbuilder.group({ name: ['delete text trigger required vaidation error', validators.required] }) } } @ngmodule({ imports: [ browsermodule, formsmodule, reactiveformsmodule ], exports: [ formsmodule, reactiveformsmodule, validationerrorscomponent ], declarations: [ app, validationerrorscomponent ], bootstrap: [ app ] }) export class appmodule {}
update: problem 3 instances of component being created. 1 markup in template, 1 ng_validators provider, , 1 ng_value_accessor provider. because had specified useclass instead of useexisting in provider declarations. use original code, think directive nicer implement adding <input> can share formcontrolname.
i have reverted old code consists of directive creates instance of validator. wishes achieve same goal here source. note create instances of validationerror, simple class.
/** * used denote validation error of kind * * @class validationerror */ export class validationerror { /** * @constructor * @param (string) message key translation of text display * @param parameters additional parameters (max-length, etc) */ constructor(public message: string, public parameters: any) {} } here source directive:
import { componentfactoryresolver, directive, forwardref, ondestroy, oninit, viewcontainerref } '@angular/core'; import { abstractcontrol, ng_validators, validationerrors, validator } '@angular/forms'; import { observable } 'rxjs/observable'; import { behaviorsubject } 'rxjs/behaviorsubject'; import { validationerror } '../../validation-error'; import { validationerrorscomponent } '../../components/validation-errors/validation-errors.component'; @directive({ selector: '[formcontrol][showvalidationerrors], ' + '[formcontrolname][showvalidationerrors], ' + '[ngmodel][showvalidationerrors]', providers: [ { provide: ng_validators, useexisting: forwardref(() => showvalidationerrorsdirective), multi: true } ] }) export class showvalidationerrorsdirective implements validator, oninit, ondestroy { errors: observable<validationerror[]>; private issubscribedtocontrol = false; private isdestroyed = false; private validationerrorscomponent: validationerrorscomponent; private errorssubject: behaviorsubject<validationerror[]>; constructor( private viewcontainerref: viewcontainerref, private componentfactoryresolver: componentfactoryresolver ) { this.errorssubject = new behaviorsubject([]); this.errors = observable.from(this.errorssubject); } ngoninit() { const factory = this.componentfactoryresolver.resolvecomponentfactory(validationerrorscomponent); const componentreference = this.viewcontainerref.createcomponent(factory); this.validationerrorscomponent = componentreference.instance; this.validationerrorscomponent.errors = this.errors; } validate(control: abstractcontrol): validationerrors | { this.subscribetocontrolerrors(control); return null; // haven't added errors } private subscribetocontrolerrors(control: abstractcontrol) { if (!this.issubscribedtocontrol) { this.issubscribedtocontrol = true; control.statuschanges .takewhile(x => !this.isdestroyed) .distinctuntilchanged() .map(x => control.errors) .subscribe(x => this.populateerrors(x)); } } private populateerrors(errorsobject: any) { const errors = []; if (errorsobject) { (const errortype of object.keys(errorsobject)) { errors.push(new validationerror(errortype, errorsobject[errortype])); } } this.errorssubject.next(errors); } registeronvalidatorchange(fn: () => void): void { } ngondestroy(): void { this.isdestroyed = true; } } here template component:
<div *ngfor="let error of errors | async"> {{ error.message | translate }} </div> and source component:
import { component, input } '@angular/core'; import { validationerror } '../../validation-error'; import { observable } 'rxjs/observable'; @component({ selector: 'validation-errors', templateurl: './validation-errors.component.html', styleurls: ['./validation-errors.component.scss'], }) export class validationerrorscomponent { @input() errors: observable<validationerror[]>; } and finally, how used in consumer:
<input formcontrolname="mobilenumber" showvalidationerrors />
Comments
Post a Comment