Forms 角2反应形式+;指令验证

Forms 角2反应形式+;指令验证,forms,validation,angular,Forms,Validation,Angular,我正试图解决以下问题: 我有一个“google place autocomplete”指令,它将自动完成功能添加到输入字段中 现在,我还希望它能够强制谷歌地点选择,并且只有当用户选择了一个地点时才“有效” 例如: 如果我在模板驱动的表单中使用此指令: ... <input name="addr" type="text" [(ngModel)]="textValue" [(googlePlaceAddress)]="googleAddress" required> <p *ngI

我正试图解决以下问题:

我有一个“google place autocomplete”指令,它将自动完成功能添加到输入字段中

现在,我还希望它能够强制谷歌地点选择,并且只有当用户选择了一个地点时才“有效”

例如:

如果我在模板驱动的表单中使用此指令:

...
<input name="addr" type="text" [(ngModel)]="textValue" [(googlePlaceAddress)]="googleAddress" required>
<p *ngIf="addr.errors.googlePlaceAddress">Please select a proposed address</p>
...
我一定是走错了路。

供将来参考:

我解决了用它和值访问器创建组件的问题:

@Component({
    selector: 'app-google-place',
    templateUrl: './google-place.component.html',
    styleUrls: ['./google-place.component.scss'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => GooglePlaceComponent),
            multi: true
        }
    ]
})
export class GooglePlaceComponent implements OnInit, ControlValueAccessor {
    @ViewChild('inputElement') inputElement: ElementRef;

    @Input() public placeholder: string = "Address";
    @Input() public textValue: string = "";

    private autocomplete: any;
    private _place = null;

    constructor(
        private googleMapService: GoogleMapsService,
        private zone: NgZone
    ) {
    }

    ngOnInit() {
        this.googleMapService
            .load()
            .subscribe(
                () => {
                    this.autocomplete = new google.maps.places.Autocomplete(this.inputElement.nativeElement);
                    this.autocomplete.addListener('place_changed', () => this.placeChanged());
                }
            );
    }

    placeChanged() {
        this.zone.run(() => {
            let place = this.autocomplete.getPlace();
            this._place = {
                address: this.inputElement.nativeElement.value,
                formattedAddress: place.formatted_address,
                latitude: place.geometry.location.lat(),
                longitude: place.geometry.location.lng()
            };

            this.propagateChange(this._place);
        });
    }

    onNgModelChange($event) {

        if(this._place !== null) {
            if(this._place.address !== $event) {
                this._place = null;
                this.propagateChange(this._place);
            }
        }
    }

    onBlur() {
        this.propagateTouched();
    }

    writeValue(obj: any): void {
        if(obj !== undefined) {
            this._place = obj;
        }
    }

    propagateChange = (_: any) => {};
    registerOnChange(fn) {
        this.propagateChange = fn;
    }

    propagateTouched = () => {};
    registerOnTouched(fn: any): void {
        this.propagateTouched = fn;
    }
}
使用这个,我可以在带有Validators.required的FormGroup中使用它,并且它只有在用户选择了GooglePlace时才有效

编辑

html:

<input type="text"
   (blur)="onBlur()"
   #inputElement
   class="form-control"
   [(ngModel)]="textValue"
   (ngModelChange)="onNgModelChange($event)">
“用法”:

使用模板ngModel

<app-google-place ([ngModel)]="place"></app-google-place>

非常有趣,我想和你一样做。请出示
谷歌地图服务
以及如何将其绑定到输入?
@Component({
    selector: 'app-google-place',
    templateUrl: './google-place.component.html',
    styleUrls: ['./google-place.component.scss'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => GooglePlaceComponent),
            multi: true
        }
    ]
})
export class GooglePlaceComponent implements OnInit, ControlValueAccessor {
    @ViewChild('inputElement') inputElement: ElementRef;

    @Input() public placeholder: string = "Address";
    @Input() public textValue: string = "";

    private autocomplete: any;
    private _place = null;

    constructor(
        private googleMapService: GoogleMapsService,
        private zone: NgZone
    ) {
    }

    ngOnInit() {
        this.googleMapService
            .load()
            .subscribe(
                () => {
                    this.autocomplete = new google.maps.places.Autocomplete(this.inputElement.nativeElement);
                    this.autocomplete.addListener('place_changed', () => this.placeChanged());
                }
            );
    }

    placeChanged() {
        this.zone.run(() => {
            let place = this.autocomplete.getPlace();
            this._place = {
                address: this.inputElement.nativeElement.value,
                formattedAddress: place.formatted_address,
                latitude: place.geometry.location.lat(),
                longitude: place.geometry.location.lng()
            };

            this.propagateChange(this._place);
        });
    }

    onNgModelChange($event) {

        if(this._place !== null) {
            if(this._place.address !== $event) {
                this._place = null;
                this.propagateChange(this._place);
            }
        }
    }

    onBlur() {
        this.propagateTouched();
    }

    writeValue(obj: any): void {
        if(obj !== undefined) {
            this._place = obj;
        }
    }

    propagateChange = (_: any) => {};
    registerOnChange(fn) {
        this.propagateChange = fn;
    }

    propagateTouched = () => {};
    registerOnTouched(fn: any): void {
        this.propagateTouched = fn;
    }
}
<input type="text"
   (blur)="onBlur()"
   #inputElement
   class="form-control"
   [(ngModel)]="textValue"
   (ngModelChange)="onNgModelChange($event)">
import {Injectable} from '@angular/core';
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class GoogleMapsService {

    private key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';

    private loaded = false;
    private currentRequest = null;

    constructor() {
    }

    load() {
        if (this.loaded) {
            return Observable.create((observer) => {
                observer.next();
                observer.complete();
            });
        }

        if (this.currentRequest === null) {
            //http://reactivex.io/rxjs/manual/overview.html#multicasted-observables
            const source = Observable.create((observer) => {
                this.loadMaps(observer);
            });

            const subject = new Subject();
            this.currentRequest = source.multicast(subject);
            this.currentRequest.connect();
        }

        return this.currentRequest;
    }

    private loadMaps(observer: any) {
        const script: any = document.createElement('script');
        script.src = 'https://maps.googleapis.com/maps/api/js?key=' + this.key + '&libraries=places';

        if (script.readyState) { // IE, incl. IE9
            script.onreadystatechange = () => {
                if (script.readyState == 'loaded' || script.readyState == 'complete') {
                    script.onreadystatechange = null;
                    this.loaded = true;
                    observer.next();
                    observer.complete();
                    this.currentRequest = null;
                }
            };
        } else {
            script.onload = () => { // Other browsers
                this.loaded = true;
                observer.next();
                observer.complete();
                this.currentRequest = null;
            };
        }

        script.onerror = () => {
            observer.error('Unable to load');
            this.currentRequest = null;
        };

        document.getElementsByTagName('head')[0].appendChild(script);
    }
}
<app-google-place ([ngModel)]="place"></app-google-place>