2016-11-28 93 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