Mukyuu
Mukyuu

Reputation: 6769

Manually assign value to formgroup vs patchValue

Assume I had the following form group:

this.cvForm = this._fb.group({
      name: ['', [Validators.required]],
      lastname: ['', [Validators.required]],
      nested: this._fb.group({
        level1: this._fb.group({
          level2: this._fb.group({
            checkbox: [this.checkbox, Validators.required] // this.checkbox is boolean value (initial value = false)
          })
        }),
      }),
    });

And then I assign the checkbox value to true (this.checkbox = true;).


When I assign manually to the form group without using patchValue:

this.cvForm.value.nested.level1.level2.checkbox = this.checkbox;

and observe the value: console.log('a',this.cvForm.value.nested.level1.level2.checkbox) // return true

It returns true but the checkbox wasn't checked.

But when I use patchValue:

this.cvForm.patchValue({
      nested: {
        level1: {
          level2: {
            checkbox:  this.checkbox
          }
        }
      }
    });

and observe the value: console.log('b',this.cvForm.value.nested.level1.level2.checkbox) // return true

It returns true and the checkbox was checked.


From patchValue() docs:

Patches the value of the FormGroup. It accepts an object with control names as keys, and does its best to match the values to the correct controls in the group.

Does this mean when you manually assign the value it wouldn't reflect to the form as patchValue does? What is the difference between the two?

Stackblitz Demo

Upvotes: 1

Views: 1656

Answers (1)

Andrei Gătej
Andrei Gătej

Reputation: 11979

This happens because patchValue(just like setValue) does a little more work under the hood.

patchValue

// For a `FormControl` instance
patchValue(value: any, options: {
  onlySelf?: boolean,
  emitEvent?: boolean,
  emitModelToViewChange?: boolean,
  emitViewToModelChange?: boolean
 } = {}): void {
  this.setValue(value, options);
}

setValue

setValue(value: any, options: {
    onlySelf?: boolean,
    emitEvent?: boolean,
    emitModelToViewChange?: boolean,
    emitViewToModelChange?: boolean
  } = {}): void {
    (this as{value: any}).value = this._pendingValue = value;
    if (this._onChange.length && options.emitModelToViewChange !== false) {
      this._onChange.forEach(
          (changeFn) => changeFn(this.value, options.emitViewToModelChange !== false));
    }
    this.updateValueAndValidity(options);
  }

Where onChange function will look like this

function setUpModelChangePipeline(control: FormControl, dir: NgControl): void {
  control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
    // control -> view
    dir.valueAccessor !.writeValue(newValue);

    // control -> ngModel
    if (emitModelEvent) dir.viewToModelUpdate(newValue);
  });
}

dir.valueAccessor !.writeValue(newValue); is what reflects the change of the model(e.g: FormControl) into the view(a DOM element).

CheckboxValueAccessor.writeValue

writeValue(value: any): void {
  this._renderer.setProperty(this._elementRef.nativeElement, 'checked', value);
}

I've recently written an article "A thorough exploration of Angular Forms" in which I try to explain how all things are set up when using forms.

Upvotes: 2

Related Questions