Angular 11 Template Driven Form – NgModel for Two-Way Data Binding

Angular 11 Template Driven Form – NgModel for Two-Way Data Binding

In the tutorial, we show how to develop an Angular Form using Template-Driven Form approach with ngModel for two-way data binding, visual feedback and handling error messages.

Related posts:
How to Integrate Bootstrap with Angular (Angular 11)
Angular 11 Component – How to create & integrate New Angular 11 Component
Angular NgFor Repeater Directive – Loop over a Collection (Angular 11)

Angular 11 Form Validation example – Template-driven Forms

Technologies

  • Node.js – version v10.4.0
  • Angular – version 6
  • Bootstrap 4
  • Visual Studio Code – version 1.24.0

Objectives

We build an Angular project as below structure:

angular-6-template-driven-form +project-structure

Workflow

-> Initial form:

angular-6-template-driven-form +customer-form-initial

Input Name, Age and select Nationality option:
-> Valid data form:

angular-6-template-driven-form +customer-form-all-valid

-> Press Submit button to submit form:

angular-6-template-driven-form +submitted-customer

– Press Edit button, The Data Form will be appeared again.
– Delete text of Name input, and select bank value for Nationality input.
-> Error Alert Message appeares:

angular-6-template-driven-form + message-error-for-required-fields

Angular Template Driven Form

Project Setup

– Generate Angular project:

angular-6-template-driven-form +create-project

– Generate:

  • Customer Component
  • Customer Class

angular-6-template-driven-form +generate-customer-component-and-class-customer

– Install Bootstrap 4:

npm install bootstrap jquery --save

-> Configure installed Bootstrap & JQuery in angular.json file:


...
 
"styles": [
  "src/styles.css",
  "node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
  "node_modules/jquery/dist/jquery.min.js",
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
]
 
...

Add the selector app-customer of CustomerComponent to the template app.component.html of AppComponent as below:

<div class="container">
  <div class="row">
    <div class="col-sm-4">
      <app-customer></app-customer>
    </div>
  </div>
</div>

-> Now the project setting is ready for coding.

Forms Module

Template-driven forms are in the module FormsModule, so we need to import FormsModule to AppModule:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { FormsModule }   from '@angular/forms';

import { AppComponent } from './app.component';
import { CustomerComponent } from './customer/customer.component';

@NgModule({
  declarations: [
    AppComponent,
    CustomerComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Data Model

Create Customer data model to capture data-entry from form in file ./customer.ts :

export class Customer {
    constructor(
        public id: number,
        public name: string,
        public nationality: string,
        public age?: number
    ) {}
}

-> age property is optional.

Form Component

Implement CustomerComponent class customer.component.ts as below:

import { Component, OnInit } from '@angular/core';

import { Customer }    from '../customer';

@Component({
  selector: 'app-customer',
  templateUrl: './customer.component.html',
  styleUrls: ['./customer.component.css']
})
export class CustomerComponent {
  nationalities = ['', 'United States', 'United Kingdom', 'Iceland', 'South Korea'];

  customer = new Customer(1, '', '', 23);

  submitted = false;

  onSubmit() { 
    this.submitted = true; 
  }
}

Initial HTML Form Template

Modify template file customer.component.html as below :

<div>
  <h2>Customer Form</h2>
  <form>

    <div class="form-group">
      <label for="name">Name</label>
      <input type="text" class="form-control" id="name">
    </div>

    <div class="form-group">
      <label for="age">Age</label>
      <input type="text" class="form-control" id="age">
    </div>

    <div class="form-group">
      <label for="nationality">Nationality</label>
      <select class="form-control" id="nationality" 
              required>
        <option *ngFor="let nationality of nationalities" [value]="nationality">{{nationality}}</option>
      </select>
    </div>

    <button type="submit" class="btn btn-success">Submit</button>
  </form>
</div>

angular-6-template-driven-form +form-not-yet-setup-angular-control

How to bind data between form in template customer.component.html with customer data model in customer.component.ts?

-> We can use Angular ngModel directive for two-way data binding.

NgModel Directive

Modify the template customer.component.html as below:

<div>
    <h2>Customer Form</h2>
    <form #customerForm="ngForm">
  
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name" 
               required
               [(ngModel)]="customer.name" name="name">  
      </div>
  
      <div class="form-group">
        <label for="age">Age</label>
        <input type="text" class="form-control" id="age"
                [(ngModel)]="customer.age" name="age">
      </div>
  
      <div class="form-group">
        <label for="nationality">Nationality</label>
        <select class="form-control" id="nationality" 
                required
                [(ngModel)]="customer.nationality" name="nationality">
          <option *ngFor="let nationality of nationalities" [value]="nationality">{{nationality}}</option>
        </select>
      </div>
  
      <button type="submit" class="btn btn-success">Submit</button>
    </form>
</div>

<div>
  {{customer | json}}
</div>

Highlight Points:

  • For each input tag, we use ngModel directive to bind data with syntax: [(ngModel)]="customer.name" (customer is the data model in customer.component.ts) class
  • Added a name attribute to the input tag. It is a requirement when using [(ngModel)] in combination with a form.
  • Declare a template variable for the form: #customerForm="ngForm". The variable customerForm is now a reference to the NgForm directive which holds the controls on the elements with a ngModel directive and name attribute

{{customer | json}} is just evidence for two-way data binding between input tag and customer model.

-> Result:

angular-6-template-driven-form + form-setup-angular-ngForm-two-way-data-binding

When having any change in the input tags, we can track the affect of the two-way data binding by the evidence {{customer | json}} :

angular-6-template-driven-form + two-way-data-binding-angular-ngmodel

Visual Validation Feedback

NgModel directive also controls:

  • when the user touched the control
  • if the value changed
  • if the value became invalid

NgModel uses special Angular CSS classes to update the control:

  • ng-touched & ng-untouched -> control has been visited or NOT
  • ng-dirty -> the value of control has been changed. Otherwise ng-pristine
  • ng-valid & ng-invalid -> the value of control is valid or invalid

Really?

-> We can use an reference variable to check it:

<div class="form-group">
	<label for="name">Name</label>
	<input type="text" class="form-control" id="name" 
		   required
		   [(ngModel)]="customer.name" name="name" 
         #name>  
	{{name.className}}
</div>

-> results:

angular-6-template-driven-form +form-angular-css-control-class

Now we can change the value of ng-valid & ng-invalid for more visual feedback,
-> Modify the style file customer.component.css as below:

.ng-valid[required], .ng-valid.required  {
    border-left: 5px solid rgba(32, 77, 32, 0.623);
}

.ng-invalid:not(form)  {
    border-left: 5px solid rgb(148, 27, 27);
}

-> results:

angular-6-template-driven-form +visual-feedback-using-angular-css

Handle Error Messages

Modify the template customer.component.html as below:

<div>
    <h2>Customer Form</h2>
    <form #customerForm="ngForm">
  
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name" 
               required
               [(ngModel)]="customer.name" name="name" #name="ngModel">
        <div [hidden]="name.valid || name.pristine"
             class="alert alert-danger">
            Name is required
        </div>

      </div>
  
      <div class="form-group">
        <label for="age">Age</label>
        <input type="text" class="form-control" id="age"
                [(ngModel)]="customer.age" name="age">
      </div>
  
      <div class="form-group">
        <label for="nationality">Nationality</label>
        <select class="form-control" id="nationality" 
                required
                [(ngModel)]="customer.nationality" name="nationality" #nationality="ngModel">
          <option *ngFor="let nationality of nationalities" [value]="nationality">{{nationality}}</option>
        </select>
        <div [hidden]="nationality.valid || nationality.pristine" class="alert alert-danger">
            Nationality is required
        </div>
      </div>
  
      <button type="submit" class="btn btn-success">Submit</button>
    </form>
  </div>
  <div>
	{{customer | json}}
  </div>

-> results:

angular-6-template-driven-form +message-error

How to achieve it?

We need a template reference variable to access the input box’s Angular control.
-> With name input tag, we use name variable to reference ngModel: #name="ngModel".

Check below code:

<input type="text" class="form-control" id="name" 
	   required
	   [(ngModel)]="customer.name" name="name" #name="ngModel">
<div [hidden]="name.valid || name.pristine"
	 class="alert alert-danger">
	Name is required
</div>

-> Alert-Message is hidden if the name input’s value is valid or pristine (pristine means the value is not changed since it was displayed).

Form Submission

Up to now, the Submit button is useless. We finally modify the template file CustomerComponent as below:

<div [hidden]="submitted">
    <h2>Customer Form</h2>
    <form  (ngSubmit)="onSubmit()" #customerForm="ngForm">
  
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name" 
               required
               [(ngModel)]="customer.name" name="name" #name="ngModel">
        <div [hidden]="name.valid || name.pristine"
             class="alert alert-danger">
            Name is required
        </div>
  
      </div>
  
      <div class="form-group">
        <label for="age">Age</label>
        <input type="text" class="form-control" id="age"
                [(ngModel)]="customer.age" name="age">
      </div>
  
      <div class="form-group">
        <label for="nationality">Nationality</label>
        <select class="form-control" id="nationality" 
                required
                [(ngModel)]="customer.nationality" name="nationality" #nationality="ngModel">
          <option *ngFor="let nationality of nationalities" [value]="nationality">{{nationality}}</option>
        </select>
        <div [hidden]="nationality.valid || nationality.pristine" class="alert alert-danger">
            Nationality is required
        </div>
      </div>
  
      <button type="submit" class="btn btn-success" [disabled]="!customerForm.form.valid">Submit</button>
    </form>
</div>
  
<div [hidden]="!submitted">
  <h2>Submitted Customer</h2>
  <hr>
  <div><span class="badge badge-info">Id</span> -> {{customer.id}}</div>
  <div><span class="badge badge-info">Name</span> -> {{customer.name}}</div>
  <div><span class="badge badge-info">Age</span> -> {{customer.age}}</div>
  <div><span class="badge badge-info">Nationality</span> -> {{customer.nationality}}</div>
  <br>
  <button class="btn btn-primary" (click)="submitted=false">Edit</button>
</div>

-> Results:

– Initial Form, Submit is disable:

angular-6-template-driven-form + initial-form-submit-button-hidden

– Enter valid data, Submit button is enable:

angular-6-template-driven-form +submit-button-enable

– Press Submit -> Forward to the detail submitted Customer form:

angular-6-template-driven-form +press-submit-valid-data-results

– Press Edit button -> Go back to data-entry form:

angular-6-template-driven-form +press-edit-button-data-entry-form

How to achieve it?

-> Solution is at the highlight code lines: {1,3,35,39,47}

Revise the form tag:

<form  (ngSubmit)="onSubmit()" #customerForm="ngForm">

We had defined a reference variable customerForm to ngForm & add ngSubmit directive.
-> Now Submit button already can do the data submission. And It also uses customerForm to control the enable or disable:

<button type="submit" class="btn btn-success" [disabled]="!customerForm.form.valid">Submit</button>

Check the highlight code at lines {1, 39, 47}. -> We use submitted flag to toggle the data-entry form and a div form for showing submitted-data.

Source Code

How to run the below sourcecode:


1. Download Angular-6-Template-Driven-Form.zip file -> Then extract it to Angular-6-Template-Driven-Form folder.
2. Go to Angular-6-Template-Driven-Form folder
3. Run the app by commandline: ng serve --open

-> Source Code:

Angular-6-Template-Driven-Form

80 thoughts on “Angular 11 Template Driven Form – NgModel for Two-Way Data Binding”

  1. Thank you, I’ve just been looking for info about this topic for ages and yours is the best I have discovered till now. But, what about the conclusion? Are you sure about the source?

  2. One other issue is that if you are in a problem where you will not have a cosigner then you may actually want to try to exhaust all of your financial aid options. You could find many grants and other free college funding that will present you with funds that can help with education expenses. Thanks alot : ) for the post.

  3. Does your website have a contact page? I’m having trouble locating it but, I’d like to send you an email. I’ve got some suggestions for your blog you might be interested in hearing. Either way, great website and I look forward to seeing it improve over time.

  4. Hi there, I found your site by the use of Google even as looking for a
    similar matter, your site got here up, it looks
    good. I have bookmarked it in my google bookmarks.

    Hi there, just became alert to your weblog via Google, and found that it is truly informative.
    I am gonna watch out for brussels. I will be
    grateful in the event you proceed this in future.
    Lots of other people will probably be benefited out of your
    writing. Cheers!

  5. I’ve been absent for some time, but now I remember why I used to love this website. Thank you, I抣l try and check back more often. How frequently you update your website?

  6. I’m now not positive where you’re getting your
    info, but great topic. I must spend a while
    studying much more or working out more. Thanks for excellent information I was in search of this information for my mission.

  7. excellent post, very informative. I ponder why the other
    experts of this sector do not notice this. You must proceed your writing.
    I’m confident, you have a huge readers’ base already!

  8. I’ll right away seize your rss as I can’t find your email subscription hyperlink
    or e-newsletter service. Do you’ve any? Kindly permit me know
    so that I may subscribe. Thanks.

  9. I like the valuable info you provide in your articles.
    I will bookmark your blog and check again here frequently.
    I’m quite certain I’ll learn a lot of new stuff right here!
    Good luck for the next!

  10. Heya i’m for the first time here. I came across
    this board and I find It truly useful & it helped me out much.
    I hope to give something back and aid others like you aided me.

  11. My family members always say that I am wasting my time here at
    web, except I know I am getting know-how every day by reading such fastidious posts.

  12. The Citizen Nighthawk CA295-58E does not boast of elegant technology, apart from Eco-Drive modern technology. If you are brand-new to this, the watch records any kind of type of light and also converts it into power.

  13. Wow! This could be one particular of the most helpful blogs We’ve ever arrive across on this subject. Actually Great. I am also an expert in this topic so I can understand your effort.

  14. We’re a group of volunteers and starting a new scheme in our community.
    Your website offered us with valuable info to
    work on. You have done an impressive job and our whole community will be thankful to you.

  15. hi!,I really like your writing so a lot! proportion we keep up a correspondence more about your article on AOL? I require an expert in this house to solve my problem. May be that is you! Looking forward to see you.

  16. Hello, Neat post. There is a problem together with your site in web explorer, would check this… IE still is the marketplace leader and a large section of other folks will pass over your fantastic writing because of this problem.

  17. You actually make it appear so easy with your presentation but I in finding this topic to be
    really one thing that I feel I might by no means understand.
    It seems too complex and very broad for me. I’m looking ahead on your next put up, I
    will attempt to get the grasp of it!

  18. Terrific article! This is the kind of info that are meant to beshared across the internet. Shame on Google for not positioning this post higher!Come on over and discuss with my web site . Thank you =)

  19. I believe what you composed made a bunch of sense.
    But, what about this? what if you added a little information? I ain’t suggesting your information is not good.,
    however suppose you added a post title that grabbed folk’s
    attention? I mean ozenero | Mobile & Web Programming Tutorials is kinda plain.
    You could look at Yahoo’s home page and see how they create article headlines to grab people to open the links.

    You might add a related video or a related picture or two to get readers interested about everything’ve got to say.

    In my opinion, it might make your blog a little bit more interesting.

  20. Have you ever considered writing an e-book or guest authoring on other blogs? I have a blog based upon on the same ideas you discuss and would love to have you share some stories/information. I know my subscribers would value your work. If you are even remotely interested, feel free to shoot me an email.

  21. An intriguing discussion is worth comment. I do think that you need to publish more on this topic, it may not be a taboo matter but generally people
    do not speak about such topics. To the next! Kind regards!!

  22. Oh my goodness! Amazing article dude! Thank you so much, However I am going through problems with your RSS.

    I don’t know the reason why I am unable to subscribe to it.

    Is there anybody else having identical RSS issues?
    Anyone who knows the solution can you kindly respond? Thanx!!

  23. Greetings! Very helpful advice within this article! It’s the little changesthat produce the most significant changes. Many thanks for sharing!

  24. I do trust all of the ideas you have presented on your
    post. They’re very convincing and can certainly work.
    Nonetheless, the posts are very short for starters.

    May just you please lengthen them a bit from subsequent time?
    Thanks for the post.

  25. Today, I went to the beach with my children. I found a sea shell and gave it to my 4 year old
    daughter and said “You can hear the ocean if you put this to your ear.” She placed
    the shell to her ear and screamed. There was a hermit crab
    inside and it pinched her ear. She never wants to go
    back! LoL I know this is entirely off topic but I had to
    tell someone!

  26. Have you ever considered creating an e-book or guest authoring on other websites?
    I have a blog based upon on the same topics you discuss and would love to have you share some stories/information. I know
    my audience would value your work. If you are even remotely interested, feel free to shoot me an e mail.

  27. Hello! This is my first visit to your blog! We
    are a collection of volunteers and starting a new project in a
    community in the same niche. Your blog provided us beneficial information to work on. You have done a wonderful job!

  28. Hi would you mind letting me know which webhost you’re using?
    I’ve loaded your blog in 3 completely different internet browsers
    and I must say this blog loads a lot faster then most.
    Can you recommend a good hosting provider at a fair
    price? Cheers, I appreciate it!

  29. Just wish to say your article is as astounding.
    The clarity in your post is just great and i could assume you’re an expert on this subject.
    Well with your permission let me to grab your RSS feed to keep updated
    with forthcoming post. Thanks a million and please carry
    on the rewarding work.

  30. Currently it seems like WordPress is the best blogging
    platform out there right now. (from what I’ve read) Is that what you’re using
    on your blog?

  31. Hi there everybody, here every person is sharing these knowledge, so it’s fastidious
    to read this webpage, and I used to pay a visit this weblog everyday.

  32. Very nice post. I simply stumbled upon your weblog and wanted
    to mention that I’ve truly loved browsing your weblog posts.
    After all I will be subscribing on your feed and
    I am hoping you write once more very soon!

  33. I blog frequently and I truly appreciate your content.
    This article has truly peaked my interest. I’m
    going to take a note of your blog and keep checking for new
    information about once a week. I opted in for your
    Feed as well.

  34. Hey there! Would you mind if I share your blog with my twitter group?
    There’s a lot of people that I think would really enjoy your content.
    Please let me know. Thanks

  35. naturally like your website but you need to test the spelling on quite a few of your posts. A number of them are rife with spelling problems and I find it very troublesome to tell the reality nevertheless I’ll certainly come back again.

  36. Very nice post. I just stumbled upon your blog and wanted to say
    that I’ve truly enjoyed browsing your blog posts. After all
    I’ll be subscribing to your rss feed and I
    hope you write again soon!

  37. Excellent read, I just passed this onto a friend who was doing some research on that. And he actually bought me lunch because I found it for him smile Thus let me rephrase that: Thanks for lunch! “We steal if we touch tomorrow. It is God’s.” by Henry Ward Beecher.

  38. Politechnika Częstochowska

    ul. J.H. Dąbrowskiego 69
    42-201 Częstochowa
    NIP: 573-011-14-01
    Informacje

    bip.svgBiuletyn Informacji Publicznej

    Zamówienia Publiczne

    Informacje o cookies

    Deklaracja dostępności

    Inspektor Ochrony Danych

    SARS-CoV-2
    Wydziały

    Wydział Budownictwa

    Wydział Elektryczny

    Wydział Inżynierii Mechanicznej i Informatyki

    Wydział Inżynierii Produkcji i Technologii Materiałów

    Wydział Infrastruktury i Środowiska

    Wydział Zarządzania

    logo ePUAP

    Adres skrytki podawczej Politechniki Częstochowskiej w systemie ePUAP: /PolitechnikaCzestochowska/SkrytkaESP

  39. Keep up the great piece of work, I read few blog posts on this site and I think that your weblog is real interesting and contains bands of fantastic information.

  40. I do consider all the ideas you’ve introduced to your post. They’re really convincing and can definitely work. Still, the posts are too brief for novices. May you please prolong them a little from subsequent time? Thanks for the post.

  41. When some one searches for his required thing, so he/she wishes
    to be available that in detail, therefore that thing is maintained over here.

  42. Spot on with this write-up, I truly think this fabulous website needs far more consideration. I’ll more likely again you just read far more, thank you for that information.

Leave a Reply

Your email address will not be published. Required fields are marked *