5
votes

Hello I have recently ventured into angular and ionic development.

I understand that interpolation and property binding is to pass data from class to template, and that interpolation supports only strings, whereas property binding supports all types.

Event binding is used to pass data from template into class.

Two way binding is achieved using foll. 4 ways:

  1. With ngModel banana syntax:
<input [(ngModel)]="username">
<p>Hello {{username}}!</p>
  1. NgModel without banana syntax:
<input [ngModel]="username" (ngModelChange)="username = $event">
<p>Hello {{username}}!</p>
  1. Without ngModel:
<input [value]="username" (input)="username = $event.target.value">
<p>Hello {{username}}!</p>

Or

<input [value]="username" (input)="username = varTest.value" #varTest>
<p>Hello {{username}}!</p>
  1. We can implement custom two way binding (without ngModel) provided we implement appropriate functions:
<custom-counter [(counter)]="someValue"></custom-counter>
<p>counterValue = {{someValue}}</p>

We also have a concept of template reference variables. When you declare this on say an input field, the value of field is accessible in the template using interpolation. Also, if ngModel is assigned to template ref variable.. #varTref="ngModel", then various properties of the element like validation, dirty, pristine, touched, untouched is accessible in template using interpolation. All these can be passed to the code class file by passing the template ref variable into say for example a button click event OR we can make use of ViewChild concept.

My question is about ngForms and ngModel concept in case of forms (template driven forms):

  1. We use <form #f="ngForm"..... And then in each input element we use ngModel with a name and this makes it accessible as property of forms.value.fieldname. Can the same thing not be achieved just by using template reference variable and then passing this to the button click event, thus having access to the form elements in the code? Then why do we have to use ngForm concept?

  2. At element level we use ngModel. Is this same as attribute binding or event binding? Or is it just to make the element accessible to the #f? We could as well use template reference variable to achieve the same isnt it? To achieve two way binding we make use of banana syntax here too so what purpose does just using the keyword ngModel at every element level really serve in template driven forms?

  3. Is using [(ngModel)]=varName same as writing [(ngModel)] name=varName?

Please I need some clarity in this. Thanks.

1

1 Answers

12
votes

Yes, these concepts can be confusing at first. But some of the information you specified above regarding two-way binding is incorrect:

Two-way binding

[(ngModel)]="lastName"

Any modification to the component property is shown in the template and any change in the template is set in the component property. This is most often used on input boxes and for template driven forms.

The above is a "short-cut" version of this:

<input [ngModel]="lastName" (ngModelChange)="lastName = $event">

One way/property binding

[ngModel]="lastName"

The UI is kept in sync with the value of the component property. This one is similar to interpolation: {{lastName}}

One time binding

ngModel="lastName"

Only binds the initial value of the component property and won't change if the value changes.

Template Reference Variables

#lastNameVar="ngModel"    /* for a form model element eg input element*/
#f="ngForm"               /* for the form itself */

The primary purpose of a template reference variable is to provide a reference to an item in the template. You don't need to add it to every input element on a template driven form, only those that you want to access.

For example:

  <div class="form-group row mb-2">
    <label class="col-md-2 col-form-label"
           for="lastNameId">Last Name</label>
    <div class="col-md-8">
      <input class="form-control"
             id="lastNameId"
             type="text"
             placeholder="Last Name (required)"
             required
             maxlength="50"
             [(ngModel)]="customer.lastName"
             name="lastName"
             #lastNameVar="ngModel"
             [ngClass]="{'is-invalid': (lastNameVar.touched || lastNameVar.dirty) && !lastNameVar.valid }" />
      <span class="invalid-feedback">
        <span *ngIf="lastNameVar.errors?.required">
          Please enter your last name.
        </span>
        <span *ngIf="lastNameVar.errors?.maxlength">
          The last name must be less than 50 characters.
        </span>
      </span>
    </div>
  </div>

Notice how the above sets the template reference variable to #lastName and then uses it to set the style (with [ngClass]) and to check the errors collection to display the appropriate error message.

Or for your example with the form, you can check the form state to disable the Save button, for example:

      <button class="btn btn-primary"
              type="submit"
              [disabled]="!f.valid">
        Save
      </button>

If you have no need to access the state of the form in your template, you don't need the template reference variable.

You can also pass the form's template reference variable into the component to access the form value or state:

In the template:

<form novalidate
      (ngSubmit)="save(signupForm)"
      #signupForm="ngForm">

In the component:

  save(customerForm: NgForm) {
    console.log('Saved: ' + JSON.stringify(customerForm.value));
  }

You could instead pass in each individual control's template reference variable, but why? As you add controls over time, you'd have to remember to always add it to the method call. And you'd have to check each control's state instead of just the form's overall state. It is much easier/better/clearer to simply pass the reference to the form.

<form novalidate
      (ngSubmit)="save(lastName, firstName, phone, email)">