Templete driven forms

AngularJS handled forms using the ng-model directive, and it leveraged the power of two-way binding that made the lives of developers easier. Angular enables developers to build template-driven forms using ngModel, which is similar to ng-model in AngularJS.

The following is the implementation of template-driven forms:

  1. Let's create an app named First Template Form in Visual Studio Code (VS Code).
  2. Add the required packages and dependency details in package.json, and install them using the npm install command.

 

      {
"name":"first-template-form",
"version":"1.0.0",
"private":true,
"description":"First template form",
"scripts":{
"test:once":"karma start karma.conf.js --single-
run",
"build":"tsc -p src/",
"serve":"lite-server -c=bs-config.json",
"prestart":"npm run build",
"start":"concurrently "npm run build:watch" "npm
run serve"",
"pretest":"npm run build",
"test":"concurrently "npm run build:watch" "karma
start
karma.conf.js"",
"pretest:once":"npm run build",
"build:watch":"tsc -p src/ -w",
"build:upgrade":"tsc",
"serve:upgrade":"http-server",
"build:aot":"ngc -p tsconfig-aot.json && rollup -c
rollup-
config.js",
"serve:aot":"lite-server -c bs-config.aot.json",
"build:babel":"babel src -d src --extensions
".es6" --source-
maps",
"copy-dist-files":"node ./copy-dist-files.js",
"i18n":"ng-xi18n",
"lint":"tslint ./src/**/*.ts -t verbose"
},
"keywords":[
],
"author":"",
"license":"MIT",
"dependencies":{
"@angular/common":"~4.0.0",
"@angular/compiler":"~4.0.0",
"@angular/compiler-cli":"~4.0.0",
"@angular/core":"~4.0.0",
"@angular/forms":"~4.0.0",
"@angular/http":"~4.0.0",
"@angular/platform-browser":"~4.0.0",
"@angular/platform-browser-dynamic":"~4.0.0",
"@angular/platform-server":"~4.0.0",
"@angular/router":"~4.0.0",
"@angular/tsc-wrapped":"~4.0.0",
"@angular/upgrade":"~4.0.0",
"angular-in-memory-web-api":"~0.3.1",
"core-js":"^2.4.1",
"rxjs":"5.0.1",
"systemjs":"0.19.39",
"zone.js":"^0.8.4"
},
"devDependencies":{
"@types/angular":"^1.5.16",
"@types/angular-animate":"^1.5.5",
"@types/angular-cookies":"^1.4.2",
"@types/angular-mocks":"^1.5.5",
"@types/angular-resource":"^1.5.6",
"@types/angular-route":"^1.3.2",
"@types/angular-sanitize":"^1.3.3",
"@types/jasmine":"2.5.36",
"@types/node":"^6.0.45",
"babel-cli":"^6.16.0",
"babel-preset-angular2":"^0.0.2",
"babel-preset-es2015":"^6.16.0",
"canonical-path":"0.0.2",
"concurrently":"^3.0.0",
"http-server":"^0.9.0",
"jasmine":"~2.4.1",
"jasmine-core":"~2.4.1",
"karma":"^1.3.0",
"karma-chrome-launcher":"^2.0.0",
"karma-cli":"^1.0.1",
"karma-jasmine":"^1.0.2",
"karma-jasmine-html-reporter":"^0.2.2",
"karma-phantomjs-launcher":"^1.0.2",
"lite-server":"^2.2.2",
"lodash":"^4.16.2",
"phantomjs-prebuilt":"^2.1.7",
"protractor":"~4.0.14",
"rollup":"^0.41.6",
"rollup-plugin-commonjs":"^8.0.2",
"rollup-plugin-node-resolve":"2.0.0",
"rollup-plugin-uglify":"^1.0.1",
"source-map-explorer":"^1.3.2",
"tslint":"^3.15.1",
"typescript":"~2.2.0"
},
"repository":{
}
}
  1. Create a class book and add the following code snippet:
      export class Book {
constructor(
public id: number,
public name: string,
public author: string,
public publication?: string
) { }
}
  1. Create AppComponent and add the following code:
      import { Component } from '@angular/core';
@Component({
selector: 'first-template-form',
template: '<book-form></book-form>'
})
export class AppComponent { }

This AppComponent shown earlier is the root component of the application that will host the BookFormComponent. AppComponent is decorated with the first-template-form selector and template that has the inline HTML with the <book-form/> special tag. This tag will be updated with the actual template during runtime.

  1. Now, let's add the book-form.component.ts with the following code snippet:
      import { Component } from '@angular/core';
import { Book } from './book';
@Component({selector: 'book-form',
templateUrl: './book-form.component.html'
})
export class BookFormComponent {
model = new Book(1, 'book name','author
name','publication name
is optional');
onSubmit() {
// code to post the data
}
newBook() {
this.model = new Book(0,'','','');
}
}

Here, note that we have imported Book from book.ts. Book is the data model for this form. BookFormComponent is decorated with the @Component directive that was imported from @angular/core. Selector value is set to book-form and the templateUrl is assigned with the template HTML file. In the BookFormCompoent, we have instantiated Book model with the dummy data. We have two methods--onSubmit() and newBook()--one to post the data submitted to API and the other to clear the form.

  1. Now, let's add the book-form.component.html template file to the following HTML content:
      <div class="container">
<h1>New Book Form</h1>
<form (ngSubmit)="onSubmit()" #bookForm="ngForm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name"
required
[(ngModel)]="model.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="author">Author</label>
<input type="text" class="form-control" id="author"
required
[(ngModel)]="model.author" name="author"
#author="ngModel">
<div [hidden]="author.valid || author.pristine"
class="alert alert-danger">
Author is required
</div>
</div>
<div class="form-group">
<label for="publication">Publication</label>
<input type="text" class="form-control"
id="publication"
[(ngModel)]="model.publication" name="publication"
#publication="ngModel">
</div>
<button type="submit" class="btn btn-success"
[disabled]="!bookForm.form.valid">Submit</button>
&nbsp;&nbsp;
<button type="button" class="btn btn-default"
(click)="newBook()">Clear</button>
</form>
</div>
<style>
.no-style .ng-valid {
border-left: 1px solid #CCC
}
.no-style .ng-invalid {
border-left: 1px solid #CCC
}
</style>

This is a simple template form that has three input controls to key in the book, author, and publisher name, a submit button to submit the details, and a Clear button to clear the form. Angular implicitly applies the ngForm directive to the forms in the template. We assigned the ngForm directive to the #bookForm local variable.

Using the #bookForm local variable, we can track the form for errors and check whether they are valid or invalid, touched or untouched, and pristine or dirty. Here, the submit button will be enabled only if the valid property of ngForm returns true, as it is assigned to the button's disabled property.

The onSubmit function from BookFormComponent is assigned to the ngSubmit event of the form. So, when the submit button is clicked on, it will call the onSubmit function in BookFormComponent.

Note that all the input controls contain the ngModel event-cum-property attribute, and it is assigned with their respective model properties, such as model.name, model.author, and model.publication respectively. In this way, we can achieve the two-way binding so that the model properties in BookFormComponent will be updated with their respective values when they are keyed into the corresponding input controls:

  1. We have the required template and components in place. Now we need to create an AppModule to bootstrap the root component of our application, AppComponent. Create a file named app.module.ts and add the following code snippet:
      import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-
browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { BookFormComponent } from './book-
form.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
BookFormComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule { }

As we discussed in chapter 2 Angular Building Blocks - Part 1, any Angular application will have a root module that will be decorated with the NgModule directive along with the metadata details, such as imports, declarations, and bootstrap.

In the preceding code, note that we assigned the AppComponent class to bootstrap metadata to inform Angular that AppComponent is the root component of the application.

  1. Now that we have all the required templates and classes in place, we need to bootstrap the module. Let's create a file named main.ts with the following code snippet that bootstraps the module:
      import { platformBrowserDynamic } from 
'@angular/platform-
browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
  1. Finally, add the index.html file with the following content:
      <!DOCTYPE html>
<html>
<head>
<title>Book Form</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-
scale=1">
<link rel="stylesheet"
href="https://unpkg.com/[email protected]/
dist/css/bootstra p.min.cs
s">
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="forms.css">
<!-- Polyfills -->
<script src="node_modules/core-
js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js">
</script>
<script
src="node_modules/systemjs/dist/system.src.js">
</script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){
console.error(err);
});
</script>
</head>
<body>
<first-template-form>Loading...</first-template-
form>
</body>
</html>

Note that the <first-template-form/> special tag is added in the body. This tag will be updated with the actual template during runtime. Also, note that the required libraries are loaded during runtime using the System.js module loader. The systemjs.config.js file should have the instructions on mapping the npm packages and our application's starting point. Here, our application is bootstrapped in the main.ts, which will be transpiled to main.js after the application is built. The content of the systemjs.config.js is given as follows:

/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {// our app is within the app folder
'app': 'app',
// angular bundles
'@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js',
meta: {
'./*.js': {
loader: 'systemjs-angular-loader.js'
}
}
},
rxjs: {
defaultExtension: 'js'
}
}
});
})(this);
  1. Now, we have all that we need. Let's run the application by pressing F5, and the index page will be rendered with the template powered by BookFormComponent, as follows:
The output of the FIrstTemplateForm application
  1. Now remove the dummy texts assigned to input controls and note that the form validation got fired showing the validation error message keeping the Submit button in a disabled state:
Inspecting the console log to form submit

In this template-drive form, you would have noted that we have applied the required attribute to the input controls. Similar to this, we can also apply minimum length and maximum length validations. However, applying validations like these tightly couples the validation logic to the template, and we can only test these validations by writing browser-based, end-to-end tests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.190.219.65