2017-04-11 15 views
6

this blog post'dan türetilmiş bir Açısal bileşen oluşturdum. Reaktif bir formda var ve form denetimi hatalarını almak istiyorum, bileşenin kendisi üzerinde denetimin bir hata olduğunda oluşturulacak stilize bir hata iletisi olacaktır. Ancak, NgControl sınıfını bileşene enjekte etmeye çalıştığımda, döngüsel referans sorunları alıyorum, bu yüzden kontrolde hatalara nasıl erişebilirim?ControlValueAccessor'u uygulayan bir bileşenden FormControl'e nasıl başvuru yapabilirim?

import { Component, Output, EventEmitter, Input, forwardRef } from '@angular/core'; 
import { 
    NgControl, 
    NG_VALUE_ACCESSOR, 
    ControlValueAccessor, 
    Validator, 
    AbstractControl, 
    FormControl, 
    NG_VALIDATORS 
} from '@angular/forms'; 

@Component({ 
    selector: 'form-field-input', 
    templateUrl: './form-field-input.component.html', 
    styleUrls: ['./form-field-input.component.less'], 
    providers: [{ 
     provide: NG_VALUE_ACCESSOR, 
     useExisting: forwardRef(() => FormFieldInputComponent), 
     multi: true 
    }] 
}) 
export class FormFieldInputComponent implements ControlValueAccessor { 

    private propagateChange = (_: any) => { }; 
    private propagateTouch = (_: any) => { }; 

    @Input('label') label: string; 
    @Input('type') type: string; 
    @Input('id') id: string; 
    @Input('formControlName') formControlName: string; 
    @Input('error') error: string; 
    @Input('classes') classes: any; 

    private value: string; 
    private data: any; 

    constructor() { 
     debugger; 
    } 

    private onChange(event) { 
     this.data = event.target.value; 
     this.propagateChange(this.data); 
     this.propagateTouch(this.data); 
    } 

    writeValue(obj: any): void { 
     this.data = obj; 
    } 
    registerOnChange(fn: any): void { 
     this.propagateChange = fn; 
    } 
    registerOnTouched(fn: any): void { 
     this.propagateTouch = fn; 
    } 
} 

şablon dosyasını:

<div class="form-field-input-component"> 
    <input id="{{id}}" 
      type="{{type}}" 
      class="form-field-input" 
      [value]="data" 
      (change)="onChange($event)" 
      (keyup)="onChange($event)" /> 
    <span class="context-icon fa fa-lock"></span> 
    <span class="info-icon fa fa-info-circle"></span> 
    <!-- I will have an NGIF here to check for errors before rendering the error --> 
    <div class="form-error"> 
     {{ error }} 
    </div> 
</div> 

cevap

1

İşte

henüz tam değil ama ben başarmaya çalışıyorum ne temel fikir vermelidir, cari kodu Bunu bir çeşit bağımlılık enjeksiyonu ya da bildirim tarzıyla yapmayı umuyordum. Bu yöntemleri kullanarak hiçbir şey bulamadığım için, bunu davasım için nasıl düzelttiğimi paylaşacağım.

Yalnızca formGroup bileşenlerini bir girdi parametresi olarak ekledim, bununla birlikte formControlName başlamasıyla birlikte kontrole başvuruda bulunabiliyorum. Eğer ControlValueAccessor ve özel denetim için tüm bu uygulamayı neden ihtiyaç İşte

, benim bileşeni

//Typescript code file for component 
/// ... necessary imports 
@Component({ 
    selector: 'form-field-input', 
    templateUrl: './form-field-input.component.html', 
    styleUrls: ['./form-field-input.component.less'], 
    providers: [{ 
     provide: NG_VALUE_ACCESSOR, 
     useExisting: forwardRef(() => FormFieldInputComponent), 
     multi: true 
    }] 
}) 
export class FormFieldInputComponent implements ControlValueAccessor { 

    private propagateChange = (_: any) => { }; 
    private propagateTouch = (_: any) => { }; 

    @Input('label') label: string; 
    @Input('type') type: string; 
    @Input('id') id: string; 
    @Input('contextIconName') contextIconName: string; 

    //Here I take in both the parent form and the form control name 
    //in ngOnInit I throw if there is no parent form passed 
    @Input('formControlName') formControlName: string; 
    @Input('parentForm') parentForm: FormGroup; 

    @Input('classes') classes: any; 
    @Input('errorDefs') errorDefs: any; 

    private error: string; 
    private value: string; 
    private data: any; 
    private control: AbstractControl; 

    constructor() {} 

    ngOnInit() { 
     if (!this.parentForm) { 
      throw "Form Field input component must be a part of a form group" 
     } 

     //It ain't pretty but here we get access to the control and all of it's errors 
     this.control = this.parentForm.get(this.formControlName); 
     if (!this.control) { 
      throw "Form Field input component must be a part of a form group" 
     } 
    } 

    private setError() { 
     if (this.errorDefs && this.control.errors) { 
      var errorKeys = Object.keys(this.control.errors).filter(x => !!x); 
      if (errorKeys) { 
       var errorKey = errorKeys[0]; 
       var error = this.errorDefs[errorKey] || null; 
       this.error = error; 
       return; 
      } 
     } 
     this.error = null; 
    } 

    //Now on our on change event we can propagate the events 
    //To the registered handlers, which should set the form field errors 
    //and at the end we can check the reference to the control for those errors 
    //so that we can display the appropriate messages 
    private onChange(event) { 
     this.data = event ? event.target.value : this.data; 
     this.propagateChange(this.data); 
     this.propagateTouch(this.data); 
     this.setError(); 
    } 

    writeValue(obj: any): void { 
     this.data = obj; 
    } 
    registerOnChange(fn: any): void { 
     this.propagateChange = fn; 
    } 
    registerOnTouched(fn: any): void { 
     this.propagateTouch = fn; 
    } 
} 

//HTML template file 
<div class="form-field-input-component"> 
    <input id="{{id}}" 
      type="{{type}}" 
      class="form-field-input {{class}}" 
      [value]="data" 
      (change)="onChange($event)" 
      (keyup)="onChange($event)" 
      (blur)="onChange($event)" /> 
    <span class="context-icon fa {{contextIconName || 'fa-cog'}}"></span> 
    <span class="info-icon fa fa-info-circle" *ngIf="error"></span> 
    <div class="form-field-error" *ngIf="error"> 
     {{ error }} 
    </div> 
</div> 

//EXAMPLE USAGE: 
<form novalidate [formGroup]="myFormGroup"> 
    <form-field-input 
         formControlName="firstName" 
         [parentForm]="myFormGroup" 

         <!-- example: When the Validators.required sets it's error message we can map that to a user friendly error --> 
         [errorDefs]="{ 
          'required': 'this field is required' 
         }" 
         <!-- Other inputs and stuff--> 
         > 
    </form-field-input> 
</form> 
+0

size özel denetime formGroup geçiyoruz sonucudur? FormGroup'un referansını, özel denetimde ana şablon dalışınıza iletin ve tamamlayın. Hiçbir ControlValueAccessor ve uygulamaları gerek yok. –

İlgili konular