Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will using Angular Reactive Forms .get() method in template cause unnecessary method calls like a component method?

I know that if I use a method call in a template it will get executed over and over (not ideal). I have solved that by using a combination of pure pipes and memoized methods. But I am also using reactive forms and in my template using the myFormGroup.get('myFormControl').value to get the values. Will this too get executed repeatedly like the method in my component or does Angular have a strategy in place to prevent this? A usage example is using *ngIf and having the conditional be based on a value of the form.

Also, I am not experiencing any performance degradation currently, but I would like to implement this in the best way possible before getting too far down the road with this application (and am just curious).

I can easily update it to reference the properties on the form object directly, I just prefer the syntax of the method call. Any insight would be helpful, thanks!

like image 239
MrUnderhill Avatar asked May 26 '20 23:05

MrUnderhill


People also ask

How do I use reactiveforms in angular?

With reactiveforms, you create a tree of Angular form control objects in the component class and bind them to native form control elements in the component template, using techniques described in this guide. You create and manipulate form control objects directly in the

What is the difference between reactive and template driven forms in angular?

Template-driven forms are the default way to work with forms in Angular. With template-driven forms, template directives are used to build an internal representation of the form. With reactive forms, you build your own representation of a form in the component class.

How to build a form in angular?

There are 2 ways to build a form in Angular — Template-Driven and Reactive approaches. Here we will focus on the Reactive approach. The Reactive approach is supposed a two-step process. First, we need to create a model programmatically and then link HTML elements to that model using directives in the template.

What are the fundamentals of angular application design?

Angular application-design fundamentals, as described in Angular Concepts The form-design concepts that are presented in Introduction to Forms Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time.


1 Answers

This is what happens when you call AbstractControl.get(...):

get(path: Array<string|number>|string): AbstractControl|null {
  return _find(this, path, '.');
}

Source.

And the _find function looks like this:

function _find(control: AbstractControl, path: Array<string|number>|string, delimiter: string) {
  if (path == null) return null;

  if (!Array.isArray(path)) {
    path = path.split(delimiter);
  }
  if (Array.isArray(path) && path.length === 0) return null;

  // Not using Array.reduce here due to a Chrome 80 bug
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1049982
  let controlToFind: AbstractControl|null = control;
  path.forEach((name: string|number) => {
    if (controlToFind instanceof FormGroup) {
      controlToFind = controlToFind.controls.hasOwnProperty(name as string) ?
          controlToFind.controls[name] :
          null;
    } else if (controlToFind instanceof FormArray) {
      controlToFind = controlToFind.at(<number>name) || null;
    } else {
      controlToFind = null;
    }
  });
  return controlToFind;
}

Source.

As you noticed, you can get descendants that reside deeper in the form control tree.

For instance:

form.get('a.b.c')

// Or

form.get(['a', 'b', 'c'])

This whole logic involes an iteration, because it's iterating over each element from path.


Will this too get executed repeatedly like the method in my component

I'd say it will.

I've created a StackBlitz demo to illustrate this:

@Component({
  selector: 'my-app',
  template: `
    <form [formGroup]="form">
      <input formControlName="name" type="text">  
    </form>

    <hr>

    <p>
      Getter value: {{ name.value }}
    </p>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup;
  name2: FormControl

  get name (): FormControl {
    console.log('getter invoked!')

    return this.form.get('name') as FormControl;
  }

  constructor (private fb: FormBuilder) { }

  ngOnInit () {
    this.form = this.fb.group({ name: '' });

    this.name2 = this.form.get('name') as FormControl;
  }
}

If you're using a getter, you should see getter invoked! logged twice for each character you type in the input(and the get method called multiple times as well).

If you're using {{ form.get('name').value }}, the AbstractControl.get method will be called multiple times, more than expected.

You can test this by opening the dev tools, typing forms.umd.js and placing a log breakpoint at this line path.forEach(function (name) {...}, inside _find function's body.

And if you're using this.name2 = this.form.get('name') as FormControl;, you should see nothing logged as you're typing.

In my opinion, it's less likely that the visible performance will decrease if you're using a getter or .get(), but I'd go with the third approach, creating a separate property for the control I'll be using in the view.

like image 145
Andrei Gătej Avatar answered Sep 19 '22 14:09

Andrei Gătej