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

Angular2-RC6自定义表单控件不工作,angular,angular2-forms,Angular,Angular2 Forms,我已将Angular2 RC5应用程序更新为RC6。我根据Thoughtram的教程开发了一些自定义表单控件 在RC5之前,一切都正常工作,但是在更新之后,验证不再有效。经过一点调查,我发现控件的值没有反映在相关模型中 你可以在Thoughtram的教程中找到原始的plunker 要重现问题,请从更新systemjs.config.js文件中的版本信息 var ngVer = '@2.0.0-rc.5'; var routerVer = '@3.0.0-rc.1'; var formsVer

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

在RC5之前,一切都正常工作,但是在更新之后,验证不再有效。经过一点调查,我发现控件的值没有反映在相关模型中

你可以在Thoughtram的教程中找到原始的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'; 

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

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

更新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 { 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
    });
  }

}
从'@angular/core'导入{Component};
从'@angular/forms'导入{FormBuilder,FormGroup};
从“./counter input.component”导入{createCounterRangeValidator};
@组成部分({
选择器:“我的应用程序”,
模板:`
内表
更改最小值:
更改最大值:
控件值:{form.controls.counter.value}

最小值:{{minValue}}

最大值:{{maxValue}}

表单值:

@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 = "(%)";
    }
  }
{{form.value | json}} 表单无效

独立的 ` }) 导出类AppComponent{ 表格:表格组; 计数器值=3; 最小值=0; 最大值=12; 构造函数(私有fb:FormBuilder){} 恩戈尼尼特(){ this.form=this.fb.group({ 计数器:这个。计数器值 }); } }

更新2:我已经在Github上报告了这个问题

感谢您更新您的源代码。我有一个控件,它基本上也执行您在这里所做的操作:

这是子组件微调器:

<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>
@组件({
选择器:“kg微调器”,
templateUrl:'./app/shared/numberSpinner/kgSpinner.component.html',
样式URL:['./app/shared/numberSpinner/kgSpinner.component.css']
})
导出类KgSpinnerComponent实现OnInit{
@输入('startValue')曲线值:数字;
@输入()范围:数字[];
@Input()增量:数字;
@输入()spinName;
@输入()精度:数字;
@Input()主题:字符串;
@Output()onChanged=neweventemitter();
lowerLimit:数字;
上限:数字;
名称:字符串;
科瑟姆:主题;
错误消息:字符串;
sr:喷丝头返回;
appPageHeaderDivStyle:{};
单位:string=“(g)”;
构造函数(专用ts:ThemeService){
this.sr=新喷丝头返回();
}
恩戈尼尼特(){
this.lowerLimit=this.range[0];
this.upperLimit=this.range[1];
this.appPageHeaderDivStyle=this.ts.getAppPageHeaderDivStyle();
如果(this.spinName==“carbGoal”){
this.unit=“(g)”;
}else if(this.spinName==“proteinGoal”){
this.unit=“(g)”;
}否则{
此.unit=“(%)”;
}
}
html:

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

父组件一旦更改:

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

  //.....
}
onChanged(sr:喷丝头返回){
如果(sr.spinName==“carbGoal”){
(this.macroForm.controls['carbGoal']).setValue(sr.spinValue);
}else if(sr.spinName==“proteinGoal”){
(this.macroForm.controls['proteinGoal']).setValue(sr.spinValue);
}else if(sr.spinName==“热量差异”){
(此.macroForm.controls['carriodifference']).setValue(sr.spinValue);
}

这一切在RC6上都能很好地工作。希望这能帮助您解决问题。

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

这一点已在下表中修复

建议的临时解决方法是


正如您所看到的,我在提供的源代码中没有使用任何不推荐的东西。因此,我相信这是另一个问题。这里有一个区别。我的组件是一个自定义表单控件,它可以根据值使表单有效/无效。在您的情况下,您正在编写自定义组件。我面临的问题只有在您编写自定义表单时才会出现控件。自定义表单控件的链接是可以的……但我看不出两者之间的区别。我只允许子组件中的有效值,因此它们的输出总是有效的。我非常感谢您为解决我的问题所做的努力。但正如我前面所说,我的组件是一个表单控件,这意味着您可以将其放入表单中并设置内置/自定义验证器,如果验证失败,表单将无效。如果您阅读我在上面的评论中发送的链接,您将了解整个情况。您没有理解我的观点。如果您正在实现微调器,请像我在示例中所做的那样为其提供有效范围。我在许多表单中使用了该选项,并且效果良好我只允许输入有效范围,并设置有效默认值。表单始终保持有效,因为这些控件始终有效。我不认为允许用户输入无效数据有什么意义。。。
  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);
        }
@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;
  }

  //.....
}