反应形式的双向结合

使用角度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 管道中。我该怎么做呢?

143742 次浏览

Note: as mentioned by @Clouse24, "Using Reactive Froms with ngModel is deprecated in angular 6 and will be removed in a future version of Angular" (which means that the answer below will no longer be supported in the future). Please read the link to see the reasoning for deprecation and to see what alternatives you will have.

You can use [(ngModel)] with Reactive forms.

template

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

component

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


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

Plunker

This will a completely different directive than the one that would be used without the formControlName. With reactive forms, it will be the FormControlNameDirective. Without the formControlName, the NgModel directive would be used.

Sometimes you might need to combine [(ngModel)] with Reactive forms. I could be some inputcontrol that you don't need as a part of the form, but you still need it to be binded to the controller. Then you can use: [(ngModel)]="something" [ngModelOptions]="{standalone: true}"

If you just want to show a input value just create a variable in your input and use in your template.

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


<h4>Example values: \{\{ name.value }}</h4>
    // 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);
}

Here is how you can solve it:

In order to have the result of two-way-binding

I use local "template variables" and use the same formControl for both fields.

<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>

When I programmatically want to change the value of the model I use setValue() as others have proclaimed.

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

ngModel or Template driven forms and reactive forms( model driven forms ) can be mixed together. for example, it's easy to read data without subscription when you use TDF and on the other hand, you can provide some validations using MDF. But i would prefer to choose only one of them.

The biggest disadvantage of TDF is that you can't apply unit tests on them and on the other hand it's much dirtier the template when you use TDF.

An Angular 6+ solution...

I too would like reactive form validation while also using two-way data binding. The best solution I've come up with was to hook the form group's valueChanges event with a debounce timer to update the model. Here's an example:

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

To better help copy/pasta I'm going to update the value of all properties of the moodel with the given changes. If you only wanted to update one property with two-way data binding you should use something like:

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

You can achieve two way binding by using Reactive forms

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>