Angular2模型驱动的表单来自Observable API调用的自定义字段验证

时间:2017-03-24 18:37:18

标签: validation angular observable

我有一个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。我花了一天半的时间在这里我错过了什么?

提前致谢!

3 个答案:

答案 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)],