自定义验证程序未被调用
无法通过烹饪手册获得使用 方法的Angular 2自定义验证程序。自定义验证程序未被调用
我的主要目标是学习Angular(也就是说,我是一个noob,总是为 )。
我想要一个文本输入字段,只允许在 特定范围(1-26)中的整数。我想我会变得聪明,并写一个验证器 ,它需要一个任意的数字和范围列表(例如, “1,3,5,7,11-19,100-200”),并检查以确定给定的值是 的允许值之一。
我的问题是,自定义验证器(由allowedNumericValuesValidator
?返回的匿名函数 ?)永远不会被调用。 (但是请注意,内置的required
验证程序运行得很好。)对于 而言,也不会调用 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
分配,它被击中 (调用堆栈是半打__webpack_require__
),但是 之后没有任何东西被调用(其他断点都没有命中, 也没有任何console.log()
我发送的语句被调用。
我shared.module.ts
,在同一shared
目录中 验证,看起来是这样的:
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] + ' ';
}
}
}
}
}
我认为我按照食谱正确迷上了一切,但显然 我错过了什么,因为自定义验证是不是 射击。
谁能告诉我我错过了什么?
感谢。
所有你想与其他模块共享件,管道,指示您在模块声明的,你还需要将它们添加到exports
@NgModule({
declarations: [ MyDirective ],
exports: [ MyDirective ]
})
export class SharedModule {}
在编辑:我故意把一个产品名称(“Angular”)以及该主题中的特定版本,因为当询问新问题并提示可能类似的问题列表时,这些主题行是询问者是否具有关于问题是否相关的唯一指示。当我问到的时候,我看到了Symfony(无论那是什么)和Angular 2 RC2的问题,但是直到我钻完为止我才知道。任何强烈的反对将编辑推回? – JohnL4