【问题标题】:Two way binding in reactive forms反应形式的两种方式绑定
【发布时间】:2023-03-12 21:57:01
【问题描述】:

使用 Angular 2,在模板驱动的表单中双向绑定很容易——您只需使用香蕉盒语法。您将如何以模型驱动的形式复制这种行为?

例如,这是一个标准的反应形式。让我们假设它比看起来要复杂得多,有大量不同的输入和业务逻辑,因此更适合模型驱动的方法而不是模板驱动的方法。

    export class ExampleModel {
        public name: string;
        // ... lots of other inputs
    }

    @Component({
        template: `
            <form [formGroup]="form">
                <input type="text" formControlName="name">
                ... lots of other inputs
            </form>

            <h4>Example values: {{example | json}}</h4>
        `
    })
    export class ExampleComponent {
        public form: FormGroup;
        public example: ExampleModel = new ExampleModel();

        constructor(private _fb: FormBuilder) {
            this.form = this._fb.group({
                name: [ this.example.name, Validators.required ]
                // lots of other inputs
            });
        }

        this.form.valueChanges.subscribe({
            form => {
                console.info('form values', form);
            }
        });
    }

subscribe() 中,我可以将各种逻辑应用于表单值并根据需要映射它们。但是,我不想映射表单中的每个输入值。我只想查看整个 employee 模型更新时的值,其方法类似于 [(ngModel)]="example.name",并显示在模板中的 json 管道中。我怎样才能做到这一点?

【问题讨论】:

标签: angular angular2-forms angular2-formbuilder


【解决方案1】:

注意:@Clouse24"Using Reactive Froms with ngModel is deprecated in angular 6 and will be removed in a future version of Angular" 所述(这意味着将来将不再支持以下答案)。请阅读链接以了解弃用的原因以及您将拥有哪些替代方案。

您可以将[(ngModel)] 与响应式表单一起使用。

模板

<form [formGroup]="form">
  <input name="first" formControlName="first" [(ngModel)]="example.first"/>
  <input name="last" formControlName="last" [(ngModel)]="example.last"/>
</form>

组件

export class App {
  form: FormGroup;
  example = { first: "", last: "" };

  constructor(builder: FormBuilder) {
    this.form = builder.group({
      first: "",
      last: ""
    });
  }
}

Plunker

这将是一个完全不同于没有formControlName 的指令。对于反应形式,它将是FormControlNameDirective。如果没有formControlName,将使用NgModel 指令。

【讨论】:

  • 在 Angular/Angular 中提出了类似的问题,两位核心开发人员告诉我混合使用 ngModel 和 Reactive 表单是个坏主意。您应该能够使用 {{this.form.get('first').value}} 获取值
  • @Zuriel 但是如何将我的本地模型数据直接绑定到我的表单,以便我的模型数据有两种数据绑定方式?因为没有上述解决方案,我需要将我的表单数据映射回我的模型数据
  • angular.io/guide/reactive-forms 与响应式范式保持一致,组件保留了数据模型的不变性,将其视为原始值的纯粹来源。该组件不是直接更新数据模型,而是提取用户更改并将它们转发到外部组件或服务,后者对它们执行某些操作(例如保存它们)并将新的数据模型返回给反映更新的模型状态的组件。
  • 在 Angular 6 中不推荐将 Reactive Froms 与 ngModel 一起使用,并将在 Angular 7 中删除。angular.io/api/forms/FormControlName#use-with-ngmodel
  • 那么,简而言之:双向绑定不适用于FormBuilder? (即当 formControl 值改变时自动更新模型)
【解决方案2】:

有时您可能需要将 [(ngModel)] 与响应式表单结合使用。我可能是一些你不需要的输入控件作为表单的一部分,但你仍然需要将它绑定到控制器。然后你可以使用:[(ngModel)]="something" [ngModelOptions]="{standalone: true}"

【讨论】:

  • 这在 Angular v6 中已被弃用,并将在 Angular v7 中删除。您可以查看此以获取更多详细信息next.angular.io/api/forms/FormControlName#use-with-ngmodel
  • 我很高兴地报告这在 Angular 9 中有效。我希望他们已经改变了主意并会保留它。
  • @N8allan 不,它仍然被弃用,看看你的控制台。 It looks like you're using ngModel on the same form field as formControlName. Support for using the ngModel input property and ngModelChange event with reactive form directives has been deprecated in Angular v6 and will be removed in a future version of Angular.
  • @Shadoweb,这就是它所说的,但我认为他们应该重新考虑。在某些情况下,没有使用绑定来支持表单,并且取消此功能会导致不必要的迂回。像这样的限制就是你所说的非正交限制,它们使框架和语言变得不优雅和繁琐。
【解决方案3】:

如果您只想显示输入值,只需在输入中创建一个变量并在模板中使用。

<form [formGroup]="form">
  <input type="text" formControlName="name" #name>
  ... lots of other inputs
</form>

<h4>Example values: {{ name.value }}</h4>

【讨论】:

  • 澄清一下,这种技术是对 FormControl 本身的纯绑定,而不是对模型的绑定。
【解决方案4】:
    // Allow two way binding on the [(name)] from the parent component
    private nameValue: string;
    @Input()
    get name() {
        return this.nameValue;
    }
    set name(values) {
        this.nameValue = values;
        this.nameChange.emit(this.nameValue);
    }
    @Output() nameChange = new EventEmitter<string>();

    ngOnInit() {
        // Update local value and notify parent on control value change
        this.formControl.valueChanges.forEach(value => this.name = value));
    }

    ngOnChanges() {
        // Update local value on parent change
        this.formControl.setValue(this.expression);
    }

【讨论】:

    【解决方案5】:

    解决方法如下:

    为了得到two-way-binding的结果

    我使用本地“模板变量”并为两个字段使用相同的 formControl。

    <form [formGroup]="formGroup">
      <input #myInput (input)="mySlider.value = myInput.value" type="number" formControlName="twoWayControl">
    
      <mat-slider #mySlider (input)="myInput.value = mySlider.value" formControlName="twoWayControl" min="1" max="100">
      </mat-slider>
    
    </form>
    

    当我想以编程方式更改模型的值时,我使用 setValue(),正如其他人所宣称的那样。

    setTo33() {
      this.formGroup.get('twoWayControl').setValue(33);
    }
    

    【讨论】:

    • 这对我有用!由于 Angular 7 响应式表单不支持 ngModel
    • 对于阅读本文的 Angular 开发人员。我对 ngModel 和 formControlName 并不感到困惑!这实际上非常方便,制作了一个响应式表单并绑定了一个模型。如果我们可以同时做两件事呢?像这样:formGroup = new FormGroup({ name: new FormControl(['Michelangelo', {modelBinding: true}]) }) 所以我们实际上可以将它绑定到模型而不是手动更新它。
    【解决方案6】:

    ngModel 或模板驱动表单和响应式表单(模型驱动表单)可以混合在一起。例如,当您使用 TDF 时,无需订阅即可轻松读取数据,另一方面,您可以使用 MDF 提供一些验证。但我宁愿只选择其中一个。

    TDF 的最大缺点是您不能对它们应用单元测试,另一方面,使用 TDF 时模板更脏。

    【讨论】:

      【解决方案7】:

      Angular 6+ 解决方案...

      我也希望在使用双向数据绑定的同时进行反应式表单验证。我想出的最佳解决方案是将表单组的valueChanges 事件与debounce 计时器挂钩以更新模型。这是一个例子:

      <form [formGroup]="form">
        <input class="form-control" type="date" name="myDate" formControlName="myDate">
      </form>
      
      public myModel = {
        myDate: '2021-01-27'
      };
      
      public form = this.builder.group({
        myDate: [this.myModel.myDate, [Validators.required]],
      });
      
      // Don't update the model with every keypress, instead wait 1s and then update
      this.form.valueChanges.pipe(debounceTime(1000)).subscribe((changes) => {
        for (let key of Object.keys(changes)) {
          this.myModel[key] = values[key];
        }
      });
      

      为了更好地帮助复制/意大利面,我将使用给定的更改更新穆德尔的所有属性的值。如果你只想用双向数据绑定更新一个属性,你应该使用类似的东西:

      this.form.get('myDate').valueChanges.pipe(debounceTime(1000)).subscribe((changes) => {
        this.myModel.myDate = changes.myDate;
      });
      

      【讨论】:

      • 我真的很喜欢这个。我只是想知道为什么更新模型不像模板驱动的表单那样自动?似乎他们不希望我们不这样做,否则默认情况下会发生。他们经常指出我们将响应式表单作为最佳实践而不是模板驱动的表单,并强调他们不希望我们这样做。加上从反应式表单中删除ngModel。我阅读了弃用文档,其中说这是因为它令人困惑。但这对我来说似乎更方便。
      【解决方案8】:

      您可以使用响应式表单实现两种方式的绑定

      constructor(private fb: FormBuilder)
      
      this.formData= fb.group({
              variable: new FormControl(value,Validators.required)
            })
            
            //the 'value' attribute carries the value you want to bind
      var value="Eamanpreet Singh"
      
      <form [formGroup]="formData" (ngSubmit)="submit();">
            <mat-form-field>
                  <input matInput placeholder="Name" formControlName="variable">
            </mat-form-field>

      【讨论】:

        猜你喜欢
        • 2018-10-26
        • 2021-07-03
        • 2020-02-01
        • 2016-02-27
        • 2017-09-05
        • 2014-02-13
        • 1970-01-01
        • 2012-01-14
        • 1970-01-01
        相关资源
        最近更新 更多