2016-11-28 135 views
1

我試圖環繞下面的問題我的頭:角2反應形式+指令驗證

我有一個「谷歌的地方,自動完成」的指令,增加了自動完成功能,輸入字段。

現在我也希望它能夠強制谷歌的地方選擇,只有當用戶選擇了一個地方'有效'。

E.g:

@Directive({ 
    selector: '[googlePlace][formControlName], [googlePlace][ngModel]', 
    providers: [{provide: NG_VALIDATORS, useExisting: GooglePlaceDirective, multi: true}] 
}) 
export class GooglePlaceDirective implements Validator, OnChanges { 

    valid = false; 
    @Output() googlePlaceAddressChange: any = new EventEmitter(); 
    @Input() googlePlaceAddress: any; 

    @Output() ngModelChange: any = new EventEmitter(); 

    private autocomplete: any; 
    constructor(private googleMapService: GoogleMapsService, 
       private element: ElementRef, 
       private zone: NgZone) { 
    } 

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

    private placeChanged(place) { 
     this.zone.run(() => { 
      this.googlePlaceAddress = { 
       address: this.element.nativeElement.value, 
       formattedAddress: place.formatted_address, 
       latitude: place.geometry.location.lat(), 
       longitude: place.geometry.location.lng() 
      }; 
      this.valid = true; 
      this.googlePlaceAddressChange.emit(this.googlePlaceAddress); 
      this.ngModelChange.emit(this.element.nativeElement.value); 
     }); 
    } 

    ngOnChanges(changes): void { 
     let googlePlaceDefined = typeof (changes.googlePlaceAddress) !== 'undefined'; 
     let modelDefined = typeof (changes.ngModel) !== 'undefined'; 

     if(modelDefined && !googlePlaceDefined) { 
      this.valid = false; 
     } else if(googlePlaceDefined && !modelDefined) { 
      this.valid = true; 
     } 
    } 

    validate(control: AbstractControl) { 
     return this.valid === false ? {'googlePlaceAddress': true} : null; 
    } 
} 

如果我使用這個指令在模板驅動形式:

... 
<input name="addr" type="text" [(ngModel)]="textValue" [(googlePlaceAddress)]="googleAddress" required> 
<p *ngIf="addr.errors.googlePlaceAddress">Please select a proposed address</p> 
... 

它工作正常。

現在我需要在反應形式使用FormGroup

let groups = [ 
    new FormControl('', [Validators.required]) 
]; 

/** HTML **/ 
... 
<input [id]="addr" 
    [formControlName]="address" 
    class="form-control" 
    type="text" 
    googlePlace 
    [placeholder]="question.label" 
    [(googlePlaceAddress)]="googleAddress"> 
... 

然而,在這種情況下,從指令驗證永遠不會觸發使用。

我想angular2希望它使用無表單時給予通過,:

new FormControl('', [Validators.required, ???]) 

我一定是什麼地方採取了錯誤的道路。

回答

1

以供將來參考:

我解決我的問題與值訪問共創出它的一個組成部分:

@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使用它,它只會在用戶選擇谷歌地點時纔有效。

編輯

的HTML:

<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); 
    } 
} 

的 '用法':

隨着模板ngModel

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

非常有趣,我想做的和你一樣。請你可以顯示'GoogleMapsService'和你如何將它綁定到輸入? –

+1

我在編輯中添加了這些服務 – RVandersteen