来自食谱的方法.
我的主要目标是学习Angular(也就是说,我是一个菜鸟,因为
总是).
我正在尝试一个只允许整数的文本输入字段
一定范围(1-26).我以为我会变聪明并写一个验证器
采用任意数字和范围列表(例如,
“1,3,5,7,11-19,100-200”),并检查给定值是否为
其中一个允许的值.
我的问题是自定义验证器(匿名函数
从不被调用的allowednumericValuesValidator返回?)
(但请注意,构建的必需验证器运行正常.)对于
那个问题,也没有任何定义的方法
调用AllowednumericValuesDirective.验证器来源
代码本身会被加载,但这就是事情的进展.
使用Angular 2.2.3,angular-cli 1.0.0-beta.22-1.
浏览器是Chrome 55.0.2883.95(64位)
来源是https://github.com/JohnL4/Diaspora,但我会尽力
相关部分在这里.
这就是我所做的:
我的验证器看起来像这样:
import { Directive,Input,OnChanges,SimpleChanges } from '@angular/core'; import { AbstractControl,NG_VALIDATORS,Validator,ValidatorFn,Validators } from '@angular/forms'; const SELECTOR: string = 'allowednumericValues'; // <---------------- breakpoint here class Range { constructor ( public Low: number,public High: number) {} } export function allowednumericValuesValidator( anAllowedValuesspec: string): ValidatorFn { let errors: string[] = []; // (<---- breakpoint here) Errors from parsing allowed values specification let ranges : Range[] = []; // Allowed ranges,used in validation. let rangeSpecs = anAllowedValuesspec.split( /\s*,\s*/); for (let r of rangeSpecs) { let ends : string[] = r.split( /\s*-\s*/); if (ends.length == 1) { let end : number = Number(ends[0]); if (isNaN( end)) errors.push( r + " is NaN"); else ranges.push( new Range( end,end)); } else if (ends.length == 2) { let low:number = Number(ends[0]); let high:number = Number(ends[1]); if (isNaN( low) || isNaN( high)) errors.push( r + " has NaN"); else ranges.push( new Range( low,high)); } else errors.push( r + " has bad Syntax"); } if (errors.length > 0) throw new Error( errors.join( "; ")); return (control: AbstractControl): {[key: string]: any} => { const numberToBeValidated = control.value; // <---------------- breakpoint here const num = Number( numberToBeValidated); if (isNaN( num)) return {SELECTOR: {numberToBeValidated}}; let isGood: boolean = false; for (let r of ranges) { if (r.Low <= num && num <= r.High) { isGood = true; break; } } return isGood ? null : {SELECTOR: {numberToBeValidated}}; }; } @Directive({ selector: '[' + SELECTOR + ']',// Note: not using extra '[ngForm]' or '[ngModel]' here because the cookbook example doesn't. providers: [{provide: NG_VALIDATORS,useExisting: AllowednumericValuesDirective,multi: true}] }) export class AllowednumericValuesDirective implements Validator,OnChanges { @input() allowednumericValues: string; private valFn = Validators.nullValidator; // <---------------- breakpoint here ngOnChanges( changes: SimpleChanges): void { const change = changes[ SELECTOR]; if (change) { const val: string = change.currentValue; this.valFn = allowednumericValuesValidator( val); } else this.valFn = Validators.nullValidator; } validate( control: AbstractControl): {[key: string]: any} { return this.valFn( control); } }
如果我在const SELECTOR赋值上放置一个断点,它就会被击中
(callstack大约有6个__webpack_require__来电),但是
之后没有任何东西被调用(没有其他断点被击中,
我也没有调用任何console.log()语句.
我的shared.module.ts,在同一个共享目录中
验证器,看起来像这样:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedComponent } from './shared.component'; // angular-cli stuck this in here; I'm not sure I need it. import { AllowednumericValuesDirective } from './allowed-numeric-values.directive'; @NgModule({ imports: [ CommonModule ],declarations: [SharedComponent,AllowednumericValuesDirective] }) export class SharedModule { }
我的app.module.ts看起来像这样(我有4个组件,但我只是
关注“params”一个,其他三个工作正常):
import { browserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { RouterModule } from '@angular/router'; import { SharedModule } from './shared/shared.module'; import { AppComponent } from './app.component'; import { ClusterDetailsComponent } from './cluster-details/cluster-details.component'; import { DotComponent } from './dot/dot.component'; import { GeneratorParamsComponent } from './generator-params/generator-params.component'; import { TabsComponent } from './tabs/tabs.component'; import { XmlComponent } from './xml/xml.component'; @NgModule({ declarations: [ AppComponent,ClusterDetailsComponent,DotComponent,GeneratorParamsComponent,TabsComponent,XmlComponent ],imports: [ browserModule,FormsModule,HttpModule,SharedModule,// I don't need to put the validator in the `declarations` property above,do I? RouterModule.forRoot([ { path: '',// Initial load. redirectTo: '/params',pathMatch: 'full' },{ path: 'params',component: GeneratorParamsComponent },{ path: 'details',component: ClusterDetailsComponent },{ path: 'xml',component: XmlComponent },{ path: 'dot',component: DotComponent } ]) ],providers: [],bootstrap: [AppComponent] }) export class AppModule { }
generator-params.component.html看起来像这样:
<p></p> <form #parmsForm="ngForm" class="form-horizontal"> <!-- "form-horizontal" is Bootstrap class --> <div class="form-group"> <!-- "form-group" is Bootstrap class --> <label for="numSystems" class="col-sm-3 control-label"> <!-- "control-label" is the class for labels in HORIZONTAL forms. --> Number of systems in cluster </label> <div class="col-sm-2"> <input id="numSystems" name="numSystems" type="text" class="form-control" required maxlength="2" allowednumericValues="1-26" [(ngModel)]="numSystems"> </div> <div *ngIf="formErrors.numSystems" class="col-sm-6 alert alert-danger"> {{ formErrors.numSystems }} </div> </div> <div class="form-group"> <div class="col-sm-offset-3 col-sm-9"> <div class="checkBox"> <!-- "checkBox" is Bootstrap class --> <label for="slipstreamsHighLow"> <input id="slipstreamsHighLow" name="slipstreamsHighLow" type="checkBox" /> Slipstreams Differentiated Between High & Low Slipknots </label> </div> </div></div> <div class="form-group"> <div class="col-sm-offset-3 col-sm-9"> <button id="goBtn" (click)="generateCluster()" class="btn btn-default btn-warning" title="ObvIoUsly,this will hammer your existing cluster. Be sure you have it backed up or otherwise saved,or that you don't care." > Go! </button> <button id="revertBtn" class="btn btn-default" (click)="revertParams()">Revert</button> </div> </div> </form>
最后,generator-params.component.ts看起来像这样:
import { Component,OnInit,ViewChild } from '@angular/core'; import { FormsModule,NgForm } from '@angular/forms'; import { Cluster } from '../cluster'; @Component({ selector: 'app-generator-params',templateUrl: './generator-params.component.html',styleUrls: ['./generator-params.component.css'] }) export class GeneratorParamsComponent implements OnInit { private numSystems: string; // = "6"; // get numSystems() : string { return this._numSystems; } // set numSystems( value: string) { this._numSystems = value; } parmsForm: NgForm; @ViewChild( 'parmsForm') currentForm: NgForm; formErrors = { 'numSystems': '' }; validationMessages = { 'numSystems': { 'required': "A number of systems is required",'allowednumericValues': "Value must be one of the allowed numeric values" } }; private _cluster: Cluster; constructor(aCluster: Cluster) { this._cluster = aCluster; if (aCluster && aCluster.numSystems) this.numSystems = aCluster.numSystems.toString(); // this.strSystems = this.numSystems.toString(); // this.numSystems = "6"; // aCluster.numSystems.toString(); } ngOnInit() { } /** See form validation cookbook "recipe" */ ngAfterViewChecked() { this.formChanged(); } public generateCluster() { // this._cluster = new Cluster( this.numSystems); // Don't new up,just update in place? this._cluster.numSystems = Number( this.numSystems); // this.cluster.generateSystems(); } public revertParams() { this.numSystems = this._cluster.numSystems.toString(); } formChanged() { if (this.currentForm === this.parmsForm) return; this.parmsForm = this.currentForm; if (this.parmsForm) this.parmsForm.valueChanges.subscribe( data => this.onValueChanged( data)); } onValueChanged( data?: any) { if (!this.parmsForm) return; const form = this.parmsForm.form; for (const field in this.formErrors) { this.formErrors[field] = ''; const control = form.get( field); if (control && control.dirty && !control.valid) { const messages = this.validationMessages[field]; for (const key in control.errors) { this.formErrors[field] += messages[key] + ' '; } } } } }
我想我已经根据食谱正确地搞定了一切,但是
显然我错过了一些东西,因为自定义验证器不是
射击.
谁能告诉我我错过了什么?
谢谢.