首页 文章

Angular2- RC6自定义表单控件不起作用

提问于
浏览
2

我已将Angular2 RC5应用程序更新为RC6 . 我根据来自Whiletram的this教程开发了一些自定义表单控件 .

一切都工作到RC5,但是经过一些调查后更新后验证不再起作用我发现控件的值没有反映在相关模型中 .

你可以从Whiletram的教程here找到原始的plunker .

要重现此问题,请更新 systemjs.config.js 文件中的版本信息

var ngVer = '@2.0.0-rc.5'; 
var routerVer = '@3.0.0-rc.1'; 
var formsVer = '@0.3.0'; 
var routerDeprecatedVer = '@2.0.0-rc.2';

var ngVer = '@2.0.0-rc.6'; 
var routerVer = '@3.0.0-rc.2'; 
var formsVer = '@2.0.0-rc.6';

版本更新后,您将看到控件值未更新,因此验证不起作用 .

但是,如果我将角度版本更新为 @2.0.0-rc.6 并保持表单版本完整,即 @0.3.0 ,则可以正常工作 .

UPDATE 1: 组件的代码是

import { Component, OnInit, forwardRef, Input, OnChanges } from '@angular/core';
import { FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';


export function createCounterRangeValidator(maxValue, minValue) {
  return (c: FormControl) => {
    let err = {
      rangeError: {
        given: c.value,
        max: maxValue || 10,
        min: minValue || 0
      }
    };

  return (c.value > +maxValue || c.value < +minValue) ? err: null;
  }
}

@Component({
  selector: 'counter-input',
  template: `
    <button (click)="increase()">+</button> {{counterValue}} <button (click)="decrease()">-</button>
  `,
  providers: [
    { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CounterInputComponent), multi: true },
    { provide: NG_VALIDATORS, useExisting: forwardRef(() => CounterInputComponent), multi: true }
  ]
})
export class CounterInputComponent implements ControlValueAccessor, OnChanges {

  propagateChange:any = () => {};
  validateFn:any = () => {};

  @Input('counterValue') _counterValue = 0;
  @Input() counterRangeMax;
  @Input() counterRangeMin;

  get counterValue() {
    return this._counterValue;
  }

  set counterValue(val) {
    this._counterValue = val;
    this.propagateChange(val);
  }

  ngOnChanges(inputs) {
    if (inputs.counterRangeMax || inputs.counterRangeMin) {
      this.validateFn = createCounterRangeValidator(this.counterRangeMax, this.counterRangeMin);
    }
  }

  writeValue(value) {
    if (value) {
      this.counterValue = value;
    }
  }

  registerOnChange(fn) {
    this.propagateChange = fn;
  }

  registerOnTouched() {}

  increase() {
    this.counterValue++;
  }

  decrease() {
    this.counterValue--;
  }

  validate(c: FormControl) {
    return this.validateFn(c);
  }
}

主模块如下:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { CounterInputComponent } from './counter-input.component.ts';

@NgModule({
  imports: [BrowserModule, FormsModule, ReactiveFormsModule],
  declarations: [AppComponent, CounterInputComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

我正在使用我的app.component中的组件

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { createCounterRangeValidator } from './counter-input.component';

@Component({
  selector: 'my-app',
  template: `
    <h2>Inside Form</h2>
    <div>
      <label>Change min value:</label>
      <input [(ngModel)]="minValue">
    </div>
    <div>
      <label>Change max value:</label>
      <input [(ngModel)]="maxValue">
    </div>
    <form [formGroup]="form">
      <p>Control value: {{form.controls.counter.value}}</p>
      <p>Min Value: {{minValue}}</p>
      <p>Max Value: {{maxValue}}</p>
      <p>Form Value:</p>
      <pre>{{ form.value | json }}</pre>

      <counter-input
        formControlName="counter"
        [counterRangeMax]="maxValue"
        [counterRangeMin]="minValue"
        [counterValue]="50"
        ></counter-input>
    </form>

    <p *ngIf="!form.valid">Form is invalid!</p>


    <h2>Standalone</h2>
    <counter-input
      counterMinValue="0"
      counterMaxValue="3"
      [counterValue]="counterValue"></counter-input>
  `
})
export class AppComponent {

  form:FormGroup;
  counterValue = 3;
  minValue = 0;
  maxValue = 12;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.form = this.fb.group({
      counter: this.counterValue
    });
  }

}

Update 2 :我在Github上报告了这个问题here

2 回答

  • 0

    感谢您更新来源 . 我有一个控件,基本上你在这里做的也是:

    这是子组件微调器:

    @Component({
      selector: 'kg-spinner',
      templateUrl: './app/shared/numberSpinner/kgSpinner.component.html',
      styleUrls: ['./app/shared/numberSpinner/kgSpinner.component.css']
    })
    
    export class KgSpinnerComponent implements OnInit {
      @Input('startValue') curValue: number;
      @Input() range: number[];
      @Input() increment: number;
      @Input() spinName;
      @Input() precision: number;
      @Input() theme: string;
    
      @Output() onChanged = new EventEmitter<SpinnerReturn>();
    
      lowerLimit: number;
      upperLimit: number;
      name: string;
      curTheme: Theme;
      errorMessage: string;
      sr: SpinnerReturn;
      appPageHeaderDivStyle: {};
      unit: string = "(g)";
    
      constructor(private ts: ThemeService) {
        this.sr = new SpinnerReturn();
      }
    
      ngOnInit() {
        this.lowerLimit = this.range[0];
        this.upperLimit = this.range[1];
        this.appPageHeaderDivStyle = this.ts.getAppPageHeaderDivStyle();
        if(this.spinName === "carbGoal") {
          this.unit = "(g)";
        } else if (this.spinName === "proteinGoal") {
          this.unit = "(g)";
        } else {
          this.unit = "(%)";
        }
      }
    

    html:

    <div class="ui-grid-col-8 spinnerMargin">
                          <kg-spinner spinName="proteinGoal" [range]=[.6,1.2] [increment]=.1 [startValue]=.6 [precision]=1 (onChanged)="onChanged($event)"></kg-spinner>
                        </div>
    

    父组件onChanged:

    onChanged(sr: SpinnerReturn) {
            if (sr.spinName === "carbGoal") {
                (<FormControl>this.macroForm.controls['carbGoal']).setValue(sr.spinValue);
            } else if (sr.spinName === "proteinGoal") {
                (<FormControl>this.macroForm.controls['proteinGoal']).setValue(sr.spinValue);
            } else if (sr.spinName === "calorieDifference") {
                (<FormControl>this.macroForm.controls['calorieDifference']).setValue(sr.spinValue);
            }
    

    这一切都完美适用于RC6 . 希望这可以帮助您解决问题 .

  • 0

    在RC.6中为验证器指令引入了一个可选的registerOnChange()函数,并且在controlValueAccessor中已经存在一个具有相同名称的函数,这导致了冲突,并且我的组件中的registerOnChange被调用了两次 .

    这已在issue下修复 .

    建议的临时解决方法是

    @Component({
      ...
      ...
    })
    export class CounterInputComponent implements ControlValueAccessor, OnChanges {
    
      isPropagate: boolean = false;
    
      /*Rest of the class implementation
      ...
      ...
      */
    
      registerOnChange(fn) {
        if (this.isPropagate) {
          return;
        }
    
        this.propagateChange = fn;
        this.isPropagate = true;
      }
    
      //.....
    }
    

相关问题