我有一个Angular 2模型驱动表单,我试图在文本字段上进行自定义验证,该字段调用我的API来验证输入的名称是否唯一。但是,我在网上尝试了大约20个不同的例子,但根本没有任何例子。
HTML:
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
import { Http } from '@angular/http';
import { FormGroup, FormArray, FormControl, Validators, FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import 'rxjs/add/operator/switchMap';
var jQuery: any = require("jquery");
import { ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { Tray } from '../../models/tray';
import { PartsService } from '../../services/parts.service';
import { PartsValidationService } from '../../validators/partsValidation.service';
@Component({
selector: 'parts',
styleUrls: ['../../../styles/main.css'],
templateUrl: './partsForm.component.html',
providers: [PartsService, SeriesService, PartsValidationService]
})
export class PartsFormComponent implements OnInit{
trayForm: FormGroup;
trayModalVisible: boolean;
@ViewChild('trayModal') trayModal: ModalComponent;
constructor(
private partsService: PartsService,
private partsValidationService: PartsValidationService,
private route: ActivatedRoute,
private location: Location,
private router: Router,
private http: Http,
fb: FormBuilder
) {
this.trayForm = fb.group({
trayName: new FormControl('', Validators.required, partsValidationService.validate)
});
}
ngOnInit(): void {
}
openModal(modalName: any) {
modalName.open();
}
closeModal(modalName: any) {
modalName.close();
}
addTray() {
debugger;
this.partsService.saveTray(this.trayForm.value).subscribe(data => {
this.closeModal(this.trayModal);
});
}
}
组件:
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
import { Http } from '@angular/http';
import { FormGroup, FormArray, FormControl, Validators, FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import 'rxjs/add/operator/switchMap';
var jQuery: any = require("jquery");
import { ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { Tray } from '../../models/tray';
import { PartsService } from '../../services/parts.service';
import { PartsValidationService } from '../../validators/partsValidation.service';
@Component({
selector: 'parts',
styleUrls: ['../../../styles/main.css'],
templateUrl: './partsForm.component.html',
providers: [PartsService, SeriesService, PartsValidationService]
})
export class PartsFormComponent implements OnInit{
trayForm: FormGroup;
trayModalVisible: boolean;
@ViewChild('trayModal') trayModal: ModalComponent;
constructor(
private partsService: PartsService,
private partsValidationService: PartsValidationService,
private route: ActivatedRoute,
private location: Location,
private router: Router,
private http: Http,
fb: FormBuilder
) {
this.trayForm = fb.group({
trayName: new FormControl('', Validators.required, partsValidationService.validate)
});
}
ngOnInit(): void {
}
openModal(modalName: any) {
modalName.open();
}
closeModal(modalName: any) {
modalName.close();
}
addTray() {
debugger;
this.partsService.saveTray(this.trayForm.value).subscribe(data => {
this.closeModal(this.trayModal);
});
}
}
验证服务:
import { Injectable } from '@angular/core';
import { Http, Headers } from '@angular/http';
import { NG_ASYNC_VALIDATORS, Validator, AbstractControl } from "@angular/forms";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/Rx';
import { Tray } from '../models/tray';
@Injectable()
export class ValidationService {
constructor(private http: Http) { }
validate(c: AbstractControl) {
return this.getTrayByName(c.value);
}
getTrayByName(trayName:any) {
return (trayName) => {
this.http
.get(`api/Parts/TrayByName/` + trayName)
.map((response: any) => { return response.json() }).subscribe();
}
}
}
当我运行这个时,我得到一个"无法读取属性' getTrayByName'未定义"错误。我已经尝试了许多其他配置,并没有任何调用API,但如果我在浏览器或Fiddler中运行调用,它会调用API。我花了一天半的时间在这里我错过了什么?
提前致谢!
答案 0 :(得分:0)
您的验证器函数必须只是 - 接受FormControl并返回布尔值(同步或异步)的纯函数。
如果您需要提供其他对象/参数以允许验证器完成其工作,那么您基本上必须将传递给FormControl的函数作为返回函数的函数。
类似的东西:
validateTrayName(http: Http) {
return (c: AbstractControl) => {
http
.get(`api/Parts/TrayByName/` + c.value)
.map((response: any) => { return response.json() });
}
}
将Http服务传入validateTrayName函数并返回可以使用该Http服务进行验证的函数。
然后在你的组件中,
this.trayForm = fb.group({
trayName: new FormControl('', Validators.required, this.validateTrayName(this.http))
});
或更简单地说,
this.trayForm = fb.group({
trayName: ['', Validators.required, this.validateTrayName(this.http)]
});
其中http和validateTrayName是组件的属性 (一旦掌握了它,你就可以移动东西以更好地满足你的需求)
修改强>
我很抱歉 - 以前的版本不正确 - 对于有效值,返回的异步值应该为null,如果无效,则返回错误类型的映射及其相应的错误消息。
因此,您可以将另一个地图链接到上面的示例中,但要注意输出的结构:
validateTrayName(http: Http) {
return (c: AbstractControl) => {
http
.get(`api/Parts/TrayByName/` + c.value)
.map((response: any) => { return response.json() })
.map(result => result.success ? null : { trayName: result.errorMsg });
}
上面的示例假设如果请求是针对有效项目,则响应为{ success: true }
,如果无效,则为{ success: false, errorMsg: 'this is not valid' }
答案 1 :(得分:0)
validateTrayName() {
return (control) =>
{
this.http.get(`api/Parts/TrayByName/` + control.value)
.map((response: any) => { return response.json() }).subscribe(data => {
if (data === null || control.value.length < 1) {
debugger;
return Observable.of({ nomatch: 'is invalid' });
} else {
debugger;
return Observable.of(null);
}
});
}
Plunk中的示例有效,直到您实际添加了http请求。这就是我现在所拥有的:
答案 2 :(得分:0)
让它工作,这是最终使它工作的原因:
validateTrayName(c: FormControl) {
return new Observable(observer => {
this.partsService.getTrayByName(c.value).subscribe(data => {
if (data === null) {
observer.next(null);
} else {
observer.next({ asyncInvalid: true });
}
});
}).first().debounceTime(300);
}
使用以下FormControl:
trayName: ['', [Validators.required], this.validateTrayName.bind(this)],