Sunteți pe pagina 1din 367

1

● Install nodejs using nodejs sit.


● To install angular cli in windows use commend - npm install -g @angular/cli (g is
to install globally)
● To install angular cli in mac use command - sudo npm install -g @angular/cli
● Check node version using node -v
● Check angular cli version using ng --version(ng stands for angular cli itself)


● To create a new project skipping the test files .

● To open project in visual studio. Go to your project and type code .

● Once you import project to visual studio code, in package.json you can see
that you are using angular version 8.2.4
2

● Angular CLI tutorial for beginners.


a. Manual setting application from scratch is laborious and boring.
There are several steps create the application folder and add the
configuration files.
b. If you see the application that is being created there are
several files most important being package.json as shown below.
3

i.
2. Package.json contains the angular dependencies.
3. We also have files like tslint.json, bs-config.json
4. Next step in manually setting the angular configuration is installing
the package.
5. In package.json you can find all the dependencies that needs to be
installed using the node package installer.

a.
6. Create the root application component. I.e. appComponent
7. Root component is present in app.component.ts
4

a.
b.
8. By convention we call the root component as AppComponent.
9. Every angular application should have one root component.
10. This rootComponent i.e. AppComponent bootstraps the angular application.
11. Next step is to create root application module.i.e. app.module.ts
12. By convention root module is called AppModule.
5

a.

b.
13. Next step is to create main.ts file.
14. It is the entry point of our application
15. In main.ts we have code to load application module. i.e.AppModule
6

16. If we go to root application module i.e. AppModule, over there you can
see as we have specified AppComponent as the bootstrap component.
17. And then angular creates an instance of this component. As, appcomponent
has html template, so angular will insert this html template as a part
of our host template which is generally index.html.
18. Thereby, our next step is to create index.html.
19. Next question is where in index.html, angular is going to insert the
html template defined in AppComponent.
20. In AppComponent if we you see we have a selector as my-app

21. In index.html, we are using selector my-app as component in index.html


7

22.
23. So, within this component our Appcomponent html view template will be
inserted.
24. Due to above reasons setting angular code manually takes up a lot of
time.
25. Everytime we create a component, service, directive etc, we have to
write the same boiler plate code.
26. Angular cli helps you to create app component modules services pipes
directive services etc. with great speed and consistency while
practicing angular best practices which will not be the case if we go
with manual project creation, as there will be a number of developers
who will be writing such boiler plate code for eg while creating a
component you will be declaring a class, export the same, decorate with
the component annotation import the component from @angular/core. So
there are chances you can’t maintain the consistency.
8

27.
28. Once you have Node and NPM installed. Run Command Prompt as an administrator
and execute the following command. Flag -g installs Angular CLI globally on your
machine.
29. npm install -g @angular/cli

30. You can also use i as shortcut for install. So the above command can also be
rewritten as shown below
31. npm i -g @angular/cli

32. If you see a tree structure, you have Angular CLI installed successfully.

33. To verify the version of Angular CLI installed, execute the following command
34. ng -v

35. At the time of this recording, I have Angular CLI version 1.4.2 installed on my
machine.

36. If you run into any problems installing Angular CLI, follow these steps and hopefully
Angular CLI will be installed successfully.
37. Step 1 : Delete "npm" folder from the following path
38. C:\Users\Your_UserName\AppData\Roaming
9

39. Please note : If you cannot find "AppData" folder, make sure in your windows
operating system, you have "Show hidden files, folders, and drives" option is turned
on. "AppData" is a hidden folder.

40. Step 2 : Once you have the "npm" folder deleted, uninstall node.js. On a windows
machine you can uninstall node.js from Control Panel\All Control Panel
Items\Programs and Features. Right click on "Node.js" and select "uninstall" from the
context menu.

41. Step 3 : Reinstall Node.js by downloading the appropriate installer for your operating
system from the following link.
42. https://nodejs.org/en/download/

43. Step 4 : Run Command Prompt as an Administrator and try to install Angular CLI
again using the following command. Hopefully this time it installs successfully. If
not, please leave the problem you are facing as a comment on this video and we will
try to help as soon as we can. Also, if you had a problem and you solved it yourself,
please let us know what the problem is and how you solved it by leaving a comment,
so it could help others with a similar problem. After all, it's all about sharing and
learning from each other.
44. npm install -g @angular/cli
45. To create a new Angular Project, open Command Prompt as an Administrator and
execute the following command. This command creates all the required files and
also installs all the required packages. Depending on your computer and internet
connection speed, this command takes a few minutes to complete.

46. ng new MyFirstApp

47. ng is the Angular CLI


new for creating a new application
MyFirstApp is the name of your angular application

48. There are several options that we can use with "ng new". We will discuss all these
options in our next video.

49. Once the above command has completed successfully you will see the following
messages.
Installed packages for tooling via npm.
Project 'MyFirstApp' successfully created.

50. So what did this "ng new" command do


A new folder with name MyFirstApp is created
All the required configuration and source files are created.
All the npm dependencies are installed in node_modules folder
10

Unit and end-to-end tests are created


The Karma unit test runner is configured
The Protractor end-to-end test framework is configured

51. Please note that all these code and configuration files are created by the Angular CLI
out of the box while still following the angular teams best practices and conventions.

52. Now, go to the folder (MyFirstApp) that contains our angular project, by executing the
following command. cd stands for change directory
cd MyFirstApp

53. Now execute the following command at the command prompt to open the project
folder in Visual Studio Code. Notice there is a single space and a DOT after the word
code.
code .

54. At this point in Visual Studio Code you will see all the Angular project files. Also
notice node_modules folder, that conatins all the installed packages.

55. To run the project using Angular CLI, type the following command at the command
prompt. This command builds the application and opens it in our default browser.
The flag --open, launches our default browser and runs the application. By default the
application runs on port 4200. We can change this port number if required.
ng serve --open

56. In mac you can change path as -

57. Also, run the serve for the first time as below :
11

58. At the moment, the angular development server is running in watch mode, meaning
when a file changes, those changes are automatically detected, compiled and the
browser reloads to reflect the changes. This is called live reload. We can turn this live
reload functionality off, if required.

59. To stop the server, press CTRL + C while you are on the command prompt and then
"Y" and ENTER key. This will stop the server. In mac you need to press command + Q

60. To run all the unit tests, use the following command
ng test

61. To run all the end-to-end tests, use the following command
ng e2e

62. As far as Angular CLI is concerned, the most useful command I think is, ng --help.
When we type this command in the command prompt window and hit enter key, it
displays all the Angular CLI commands and the options we can use with these
commands.

63. As we scroll up in the command prompt window, notice we are not able to see all the
commands and their associated options. To fix this we have to increase the buffer
size. To increase the buffer size there are 2 simple steps

64. Step 1 : Right click on the "title bar" of the command window and select "Properties"
from the context menu
12

65. Step 2 : Click on the "Layout" tab and set the "Height" property to 9999 under "Screen
Buffer Size" section, and click "OK"

66. With this change in place, execute that same command ng --help. Notice now we can
see all the commands and their associated options.

67. There is lot of help text displayed on the screen. If you want to find a specific
command you can use the search feature of the command window. To use the
search feature, right click on the "title bar" of the command window and select "Edit"
from the context menu. You can then use the "Find" window to search for the
command you are looking for.
68. If you want the help with any specific command, you just need to type the name of
command and give it as ng generate --help for example.

69. To redirect the output of ng --help command to the windows clipboard, use the CLIP
command as shown below.
70. ng --help | clip

71. Once you have the output copied in the clipboard you can paste it anywhere you want
it. For example in a notepad, word document etc.

72. You can also redirect the output directly to a text document using the following
command. This command creates a text document with name MyTextDoc.txt in the
folder where you have executed the command. This text documents will have the
output of the command ng --help.
ng --help > MyTextDoc.txt

73. Similarly you can also redirect the output to a word document.
ng --help > MyWordDoc.doc
13

74. From the properties window of the command prompt you can also change the cursor
size, font size, font colour and background colour.
75. In this video we will discuss some of the common options that we can use with ng
new command.
76. Ng new --help (list all the options that you can use with new command)

77. Ng new app1 --dry-run(it says don’t create a new project, just report to me what all
files will be created)
78. Dry-run is of extreme use when you don’t know which command it is going to
generate what. So you can use dry-run command with any to check that before
executing the command.
79. We also have alias for --dry-run which is ng new app1 -d

80. --skip-install it is going to create the file and folders but it will skip installing the
packages. This means that after we create the project we have to manually install
the package using npm install.Most time consuming task while generating a project
is installing packages. When we go to the project node_module folder will be missing
as we have not installed the packages.

81. Alias for the same is ng new app1 --si

82. --skip-tests it skip creating the test files. We have unit testing files such as spec file,
main being the app.component.specs.ts
14

This screenshot is only to show specs file. Here we see red lines because in last step
we did --skip-install
83. Alias for skip test is --st
84. Generally when we create a new project, it creates an external html template and that
we can see as app-component.html and in app.component.ts we see the html
template mentioned as in templateUrl. Now what if you want an inline html template
while creating a new project.
85. Ng new app4 --inline-template or you can give it as ng new app4 --it

86. Ng new app4 --inline-style or -is


15

87. Use inline styles when generating the new application. This will not create an external
style sheet instead it will provide you with an internal style sheet.

88.

89. In this video we will discuss the significance of the Angular CLI configuration file
(.angular-cli.json). This is the configuration file that the Angular CLI uses.
90. tsconfig.app.json contains compiler configuration for angular application whereas
tsconfig.spec.json contains compiler configuration for unit test.
91. If you want to change style of an application to less and scss you can give it like ng
new app1 --style scss or ng new app1 --style less

92. The settings from this file are used when we


1. Generate angular features likes components, pipes, services etc
2. Run unit and end-to-end tests
3. Build the application etc.

93. The table below shows some of the settings and their purpose. We will discuss the
other settings and their purpose as we progress through the course.

94. project : name Name of the project

95. apps: root - The root directory of the application. Default is src. We can change this
using the "source-dir" option when generating a new angular project using the "ng
new" command

96. apps: outDir - The output directory for build results. Default is dist

97. apps: assets - List of application assets that you want to copy when building your
project. By default, the src/assets/ folder and src/favicon.ico are copied over

98. apps: index - The name of the start HTML file which is index.html by default

99. apps: main - The name of the main entry-point file. main.ts by default

100. To generate new component ng generate component app1 or ng g c app1

101. It generates the component and update the app.module.ts file with newly
generated component.
16

102. apps: polyfills - The name of the polyfills file. Angular is built on the latest
standards of the web platform. Targeting such a wide range of browsers is
challenging because not all browsers support all features of modern browsers. This
can be compensated by using polyfill scripts that implement missing features in
JavaScript

103. apps: styles - Global styles to be included in the build. Default is styles.css.
We can also use less or scss. To change to less or scss, use the "style" option when
generating a new angular project using the "ng new" command

104. apps: prefix - The selector prefix to apply for the generated components.
Default is "app". This can be changed by using the "prefix" option when generating a
new angular project using the "ng new" command

105. The important point to take away is that the values in the Angular CLI
configuration file depends on the options that you have used with the "ng new"
command when generating a new angular project. For example, if you do not use the
--prefix option with the "ng new" command, then the default value "app" is stored in
the configuration file for "prefix" setting. So the root component (AppComponent)
that is created at the application generation time has "app" as the selector prefix.

106. Instead if you want "pragim" as the prefix, use --prefix flag along with "ng
new" command. When you do this several things happen
1. "pragim" is stored as the "prefix" setting value in .angular-cli.json configuration file

2. "pragim" is used as the selector prefix for the root component that the "ng new"
command generates

3. Any new component that you generate in the future using the following command
will also have "pragim" as the selector prefix

ng generate component componentName

4. If you want to override the prefix setting in the angular cli configuration file, you
can use --prefix option with the generate command as shown below. This will
generate the component "xyz" with the prefix "tech" instead of "pragim"ng generate
component xyz --prefix tech

5. Some of the options like --prefix can be used with several commands like ng new
and ng generate
17

107. Please note : If you generate a project with --skip-install flag and when you try
to generate a new component using "ng new" command before executing the "npm
install" command you will get the following error
108. node_modules appears empty you may need to run npm install

109. To fix this, please first execute "npm install" to install the required npm
packages and then generate the component.

110. One of the easiest ways to create a working angular project, is by using the
Angular CLI. The following Angular CLI command creates a working Angular Project
out of the box
ng new AngularProject

111. As you can see there are several files in the project. The table below shows
the purpose of each file/folder.

112. package.json : This file contains the packages to build and run our
application. It contains two sets of packages, dependencies and devDependencies.
The dependencies are essential for running the application. The devDependencies
are only required to develop the application. Devdependencies are not required on
production servers. These packages are installed into the node_modules folder by
the Node Package Manager (npm), when npm install commaned is excuted. You can
also add your own custom scripts here.

113. "scripts" property in package.json file contains the useful npm commands.
Notice we have "start": "ng serve". This means when we execute npm start it
executes ng serve which builds and starts the server. In addition if you also want to
launch the browser and open the application CHANGE "start": "ng serve" TO "start":
"ng serve --open".

114. node_modules : The packages specified in package.json file(both


dependencies and dev dependencies) are installed into this folder when we run npm
install command

115. e2e Folder : Contains end-to-end tests and their configuration files. We will
discuss end-to-end tests in our upcoming videos.

116. .angular-cli.json : This is the Angular CLI configuration file. We discussed the
use of this file in our previous video.

117. .editorconfig : Configuration file for Visual Studio Code. The settings in this
file let you set certain code style guidelines. For example what indent_style do you
want - spaces or tabs and what should be the indent size etc. You can share this
18

editorconfig file with other developers to maintain consistent coding styles. To read
more about editor configuration please visit http://editorconfig.org

118. .gitignore : This file is used to determine files and folders you don't want to
check in to source control. For example one of the folders we do not want to check in
to source control is /dist folder which is auto generated when we build the
application. So this folder is listed in this file. So, all the files and folders listed in this
file are ignored, when a change set is checked in to source control.

119. karma.conf.js : Karma is the unit test runner for angular applications. As the
name implies, karma.conf.js is the configuration file for Karma.

120. protractor.conf.js : Protractor is an end-to-end test framework for Angular


applications. As the name implies, protractor.conf.js is the configuration file for
Protractor.

121. README.md : This is a README file which contains the commonly used
Angular CLI commands out of the box. You may enhance it with your own project
documentation so that anyone checking out the repo knows the commands to use to
build, run and test your app.

122. tsconfig.json : This is the TypeScript compiler configuration file. This file has
several TypeScript compiler configuration settings. For example, to compile
TypeScript to JavaScript on saving a TypeScript file set compileOnSave setting to
true. If you do not want .map files to be generated, set sourceMap to false. .map files
are used for debugging your application. Generally when we build our project for
production we set these sourceMap to false.

123. tslint.json - Angular has a linting tool that checks our TypeScript code for
programmatic and stylistic errors as well as non-adherence to coding standards and
conventions. tslint.json is the configuration file for linting. We will discuss the
settings in this file when we discuss linting in our upcoming videos.

124. src folder : As the name implies, this folder contains all our angular project
source code. Components, templates, pipes, services, images, styles etc that our
angular application needs are present in this folder. The rest of the files and folders
that are present outside this folder, are there to support building our angular
application.thereby we won’t need any of those files when we deploy our code to
production.

125. assets : As the name implies, the assets folder contains the assets of your
application like images and anything else to be copied when you build your
application.
19

126. environments : This folder contains the environment files. By default we have
2 environment files. environment.ts is for for development environment. Notice
production property in this file is set to false. environment.prod.ts is for production.
Notice in this file production property is set to true as expected. The build system
defaults to the dev environment which uses `environment.ts`, but if we do a
production build environment.prod.ts will be used. The file and environment mapping
is in Angular CLI configuration file (.angular-cli.json)

127. favicon.ico : This is the favorite icon for your application which is typically
displayed in the browser address bar and next to the page name in a list of
bookmarks. Angular CLI provides this favorite icon out of the box. You may replace
this favicon with your own company favicon.

128. index.html : The main HTML page that is served when someone visits your
site.

129. main.ts : The main entry point for the application. This file contains the code
to bootstrap the application root module (AppModule)

130. polyfills.ts : This is the polyfills file. Angular is built on the latest standards of
the web platform. Targeting such a wide range of browsers is challenging because
not all browsers support all the features of modern browsers. This can be
compensated by using polyfill scripts as they implement the missing features in
JavaScript. So these polyfills allow us to use an API regardless of whether it is
supported by a browser or not.

131. styles.css : This file contains the global styles of our application. Styles that
are local and specific to a component are often defined with in the component itself
for easier maintenance.

132. test.ts : This file is the main entry point for unit tests and loads all the .spec
and framework files

133. tsconfig.app.json : TypeScript compiler configuration for the Angular app

134. tsconfig.spec.json : TypeScript compiler configuration for the unit tests

135. typings.d.ts : This is the TypeScript typings file. Many JavaScript libraries,
such as jQuery, Angular etc extend the JavaScript environment with features and
syntax that the TypeScript compiler doesn't recognize natively. When the typeScript
compiler doesn't recognize something, it throws an error. So, we use TypeScript type
definition files to tell the compiler about those libraries. These TypeScript type
definition files have the extension d.ts. TypeScript editors leverage these type
definition files to display type information.
20

136. Many libraries include type definition files in their npm packages. Angular is
one such library. For example, if you look inside node_modules/@angular/core/
folder in an Angular application, it already contains the type definition files. All the
files that have the extenstion d.ts are the type definition files.

137. app.component.{ts,html,css,spec.ts} : The root component (AppComponent)


TypeScript, HTML template, StyleSheet and Spec files.

138. app.module.ts : This is the root application module (AppModule)

139. You must have the npm packages installed to be able to generate
components using Angular CLI. Otherwise when we try to generate components
using the ng generate command we will get the following error.

140. node_modules appears empty, you may need to run 'npm install'

141. The following command creates a new Angular project with name
"myProject" but it does not install the npm packages as we have used -si flag. The -si
flag as we know skips installing the npm packages.
ng new myProject -si

142. At this point if we try to generate a new component using the following ng
generate command, it reports an error - node_modules appears empty, you may need
to run 'npm install'
ng generate component abc

143. We will have to first execute npm install command to install the required
packages. Once this is done we will be able to generate components.

144. To generate a component use the following Angular CLI command


ng generate component ComponentName

145. OR the shortcut as shown below. In the following command the letter g
stands f for generate and the letter c stands for component
ng g c ComponentName

146. When we execute this command (ng g c abc) , several things happen
1. A folder with name abc is created
2. The component files (Component class, View template, CSS file and the spec
file ) are created and placed inside the folder "abc"
3. The root module file (app.module.ts) is also updated with our new component
i.e the required import statement to import the abc component from the component
21

file is included and the component is also declared in the declarations array of the
@NgModule() decorator

147. Placing the generated component folder in a different folder : By default a


separate folder is created for every new component that we generate, and the
component files (.ts, .css, .html & .spec) are placed in this folder. This newly created
folder is placed in the app folder by default. If you want the newly created folder to be
placed in a different folder other than the app folder, simply include the folder name
in the ng generate command

Here abc is the path of folder where we want to create our new component.

148. Generating a new component without a folder : To create a component


without a folder, use --flat option with the ng generate command

It will place files directly in app folder not a specific pqr folder will be created in this
case.
22

149. Placing the flat component files in a different folder other than app : A flat
component is a component that is created with --flat option. This component does
not have it's own folder. By default the flat component files are placed in the "app"
folder. If you want to place them in a different folder instead, specify the folder name
along with the ng generate command.

150. Using --dry-run flag with component generation : Just like how we can use the
--dry-run flag with "ng new" command, we can also use it with ng generate command.
The --dry-run flag reports the files and folders that will be generated, without actually
generating them. Once you are happy with what it is going to generate, you can
remove the --dry-run flag and execute the command.
23

151. If you want an inline template and styles instead of an external template and
stylesheet, use -it flag for inline template and -is flag for inline styles. Along the same
lines, if you do not want a spec file use --spec=false. Notice we are also using the -d
flag.

152. To use sass instead of CSS with your component, use the --style=scss flag
with ng generate command. If you want less use --style=less

153. To generate a component we use


ng generate component componentName or ng g c componentName

154. Similarly to generate a service we use


ng generate service serviceName or ng g s serviceName

155. For example. to generate a customer service we use the following command.
ng generate service customer

156. The above command generates the service and the spec file. What it does
not do is register the service. Remember for us to be able to use the service, we must
register the service.

157. We can do it manually after creating the service or we can tell Angular CLI to
register our service with a module, using --module option. We can also use it's alias -
m
Manually as below:
24

As we are registering it with root module, it is available throughout our module.

158. The following command not only generates employee service, it also
registers our service with the AppModule
ng generate service employee -module=app.module(name of the module where you
want to register your service)

159. The above command can also be rewritten using aliases


25

ng g s employee -m=app.module

160. We can also use the --dry-run flag or it's alias -d to see what Angular CLI
generates. Notice in the following command we are using -d option, so Angular CLI
simply report the files it is going to generate
ng g s student -d

161. The above command generates the service and the spec file. If you do not
want the spec file, simply set --spec=false
ng g s student -d --spec=false

162. When generating a component, Angular CLI by default creates a folder for the
component and places all the component files in that folder. A service on the other
hand will not have it's own folder. If you want a folder of it's own for a service that the
Angular CLI is generating, set --flat option to false as shown below.
ng g s student -d --spec=false --flat=false

163. to generate a module use


ng generate module moduleName or ng g m moduleName

164. For example. to generate a students module we could use


ng generate module students -d or ng g m students -d

165. Please note : Since we are using the --dry-run flag, the module file and folder
is not actually created. We only get the report of the files and folders that will be
created.

166. The above command generates the students module inside students folder.
Remember for us to be able to use this newly generated module, we must import it in
the root module.
26

167. We can do it manually after creating the module or we can tell Angular CLI to
import our newly generated module into the root module using --module option. We
can also use it's alias -m
Manually :

168. The following command not only generates students module, it also imports
it into the root module (AppModule)
ng g m students -d -m=app.module

By default module is generated inside its own folder.

169. By default a spec file is not generated. If you also want a spec file to be
generated use the --spec option
ng g m students -d -m=app.module --spec=true

Use --flat = true in order to place it in app folder not its own folder
27

170. When generating a module, Angular CLI by default creates a folder for the
module and places the module files in that folder. If you do not want a dedicated
folder for the module you are generating, use --flat option.
ng g m students -d -m=app.module --spec=true --flat=true

171. Unitil now, we have been using the --dry-run option. Now let's remove the -d
option and execute the command so the module is actually created.
ng g m students -m=app.module --spec=true --flat=true

172. The above command not only creates the students module, it also imports it
into the root module (AppModule). If we look inside app.module.ts file, notice
1. The required import statement to import students module is included
2. The students module is also included in the "imports" array

173. Generating directives, pipes, routing guards and other angular features is very
similar to generating component and services. We discussed generating
components and services using the Angular CLI in our previous videos in this course.

174. To generate a directive use


ng generate directive directiveName OR ng g d directiveName

175. To generate a pipe use


ng generate pipe pipeName OR ng g p pipeName
28

176. To generate a routing guard use


ng generate guard guardName OR ng g g guardName

177. Please note : When you try to generate a directive, pipe or a component, and
if you have multiple modules in your angular project you may get the following error
More than one module matches. Use skip-import option to skip importing the
component into the closest module.

178. The reason we are getting this error is we have more than one module in our
angular project, so Angular CLI does not know with which module the newly
generated directive, pipe or component should be registered. So we have 2 options
here.

1. Use --skip-import option to tell Angular CLI not to import and register the generated
component, directive or pipe
ng g d directiveName --skip-import -d

2. Use --module option or it's alias -m to tell Angular CLI the module with which we
want our newly generated component, directive or pipe should be registered.
ng g d directiveName -m=app.module -d

179. If you have just one module in your Angular project, then you wouldn't get this
error, as the angular cli will automatically import and register the newly generated
component, directive or pipe with that one existing module.
29

180. When genearting certain angular features like services or routing guards, you
will not get this error, even when you have multiple modules in your project, because
by default, Angular CLI does not try to import and register these features.

181. Please note that we can always use the following options along with ng
generate command to customise the generation of directives, pipes and routing
guards using the Angular CLI.

182. flat - Specifies if a dedicated folder should be created


183. module - Specifies the module with which the newly generated angular
feature should be registered
184. spec - Specifies if a spec file should be generated

185. As you have seen throughout this course, Angular CLI provides consistent set
of commands for generating features.

186. To generate a class use


ng generate class className or ng g cl className

187. For example, to generate an employee class use


ng g cl employee

188. The above command places the employee class directly in the "app" folder.
Instead if you want the employee class in a different folder, simply prefix the name of
the folder. The command below creates a folder with name "employee" and then
creates the "employee" class in it.
ng g cl employee/employee

189. By default, a spec file is not created for the class. If you want a spec file to be
generated set --spec option to true.
ng g cl employee/employee --spec=true
30

190. To generate an interface use


ng generate interface interfaceName or ng g i interfaceName

191. To generate an enum use


ng generate enum enumName or ng g e enumName

192. Angular has a linting tool that checks our TypeScript code for programmatic
and stylistic errors as well as non-adherence to coding standards and conventions.
tslint.json is the configuration file for linting. This file contains all the default rules for
linting our code.

193. For the purpose of this demo I have created a brand new Angular project
using the following command.
ng new AngularProject

194. Use the following command to lint the code


ng lint

195.
196. Since we have just generataed a new angular project and all the code in the
project is auto-generated, we do not have any linting errors and we get the message -
All files pass linting.

197. We also see the following warning


Warning: The 'no-use-before-declare' rule requires type checking

198. Basically this warning is saying, if 'no-use-before-declare' rule is enabled we


need to use --type-check option with the ng lint command
ng lint --type-check
31

Now modify the code in sayHello() function as shown below.


sayHello() {
var message = 'Hello';
message = message + ' Pragim';
console.log(message);
}
32

At this point, execute ng lint command again with --type-check option.

ERROR: C:/AngularProject/src/app/app.component.ts[12, 17]: variable 'message'


used before declaration

ERROR: C:/AngularProject/src/app/app.component.ts[13, 5]: Forbidden 'var' keyword,


use 'let' or 'const' instead
Lint errors found in the listed files.

Out of the box, "no-var-keyword" rule is also enabled by default. Turn this rule off by
setting it's value to false in tslint.json
"no-var-keyword": false
33

199. Run ng lint command again with --type-check option


Notice, now we only get 1 linting error
variable 'message' used before declaration
'no-use-before-declare' rule is enabled out of the box and it disallows usage of
variables before their declaration. To understand what this means, place the
following sayHello() function in AppComponent class in app.component.ts file.
sayHello() {
console.log(message);
var message = 'Hello';
message = message + ' Pragim';
}

200. Variables declared with let keyword are not accessible before they are
declared. So this rule 'no-use-before-declare' can be safely disabled, if you have 'no-
var-keyword' rule enabled. When 'no-use-before-declare' rule is disabled and when we
34

run ng lint command without --type-check option, we will no longer get the below
warning
The 'no-use-before-declare' rule requires type checking

201. Variables declared with var keywords can be easily used before they are
declared, but this is not the case with let keyword.

202. quotemark rule specifies whether you want single or double quotes

By default all the strings are wrapped in single quotes in angular.


203. no-trailing-whitespace rule disallows trailing whitespace at the end of a line
204. semicolon rule specifies that a line should be terminated with a semicolon
205. comment-format rule specifies that all single-line comments must begin with
a space
35

206. component-class-suffix rule enforces that a component class should end


with the suffix Component

Here class name should end with a suffix Component.


207. use-life-cycle-interface rule enforces that you add the implements keyword
for every lifecycle hook you use
To use lifecycle hook in our project we have 3 simple steps:
1. Import lifecycle hook interface, here interface is Onit
2. Then make your class implement the interface.
3. Provide implementation for Oninit
ngOnInit(){
}
36

Even if don’t implement the class Oninit in our class we are going to get the
same output, there will be on error. But as practice it is good to implement
the same because it will give the required compilation errors.

But when we use use-lifecycle-interface and in that case we are not


implementing the interface, logically it wont throw an error but the lint would
do so. Throw a linting error.

For all above lint rule voilation we have it like.

208. Some of the linting errors support automatic fix. To have these linting errors
fixed automatically, run ng lint command with the --fix option.
ng lint --fix

209. To see the options that can be used with ng lint command, use
37

ng lint --help

210. At the moment, Visual Studio Code is not able to show any linting rule
violations. In our next video, we will discuss how to display linting errors in Visual
Studio Code so we can fix them as we are writing code.

211. At the moment, our editor Visual Studio Code does not show linting errors. It
would be nice if Visual Studio Code can display these linting errors so we can fix
them as we are writing code. To achieve this install Visual Studio Code extension -
TSLint.

212. To install this extension


1. Click on the "View" menu in "Visual Studi Code" and select "Extensions"
from the context menu
2. In the "Search Extensions in Marketplace" textbox type TSLint
3. Click the "install" button
4. Once installed, restart Visual Stduio Code to activate TSLint

213. At this point, in Visual Studio Code we will be able to see linting errors and we
have the opportunity to fix them as we are developing our application.

214. Once you click on the line where you see a linting error, a light bulb appears
on the left margin and when you click on the light bulb you will see 1 to 3 options.

215. You can click on the respective options


1. To have the linting errors fixed automatically depending on whether the
issue supports automatic fix or not
2. To disable that specific rule
3. To get documentation of the rule

To disable linting in VS code


1. Click on the "View" menu in "Visual Studi Code" and select "Extensions"
from the context menu
2. In the "EXTENSIONS" window, expand "INSTALLED" section
38

3. Click the "SETTINGS" icon against TSLint extension


4. Select "Disable (Always)" option
5. Restart Visual Studio Code

216. Implementing routing in an Angular application involves many small steps.


Angular CLI does a pretty good job in having some of these routing steps
implemented out of the box by just using --routing option.

217. Before we discuss, how we can use Angular CLI to implement routing let's
setup routing manually so we understand all the moving parts as far as
implementing routing is concerned.

218. Using the following command, first create a brand new Angular project using
the Angular CLI.
ng new employeeManagement

219. We named it employeeManagement. Let's assume we are using this


application to manage employees. Out of the box, Angular CLI has created the root
component - AppComponent. In addition let's create the following 3 components
home : ng g c home
employees : ng g c employees
pageNotFound : ng g c pageNotFound

220. Steps to implement routing in Angular


Step 1 : Set base href in the application host page - index.html. It basically indicates
the root of our application.

Step 2 : Import the RouterModule into the application root module AppModule. The
Router Module contains the Router service and Router directives such as
(RouterLink, RouterLinkActive, RouterOutlet etc). So for us to be able to implement
routing, we first need to import the Router Module in our AppModule.
39

Step 3 : Configure the application routes.

import { RouterModule, Routes } from '@angular/router';

// Each route maps a URL path to a component


// The 3rd route specifies the route to redirect to if the path
// is empty. In our case we are redirecting to /home
// The 4th route (**) is the wildcard route. This route is used
// if the requested URL doesn't match any other routes already defined
const appRoutes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'employees', component: EmployeesComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];

// To let the router know about the routes configured above,


// pass "appRoutes" constant to forRoot(appRoutes) method
// We also have forChild() method. We will discuss the difference
// and when to use one over the other in our upcoming videos
@NgModule({
declarations: [...
],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes)
],
providers: [],
bootstrap: [AppComponent]
})
40

export class AppModule { }

Route is an array of routes object.

Step 4 : Specify where you want the routed component view template to be displayed
using the router-outlet directive

In appComponent.html, have the code as below.

Step 5 : Tie the routes to application menu.


41

To install bootstrap execute the following npm command


npm install bootstrap@3 --save

Installed in node_modeules folder.

221. Once Bootstrap is installed, open .angular-cli.json file and specify the path to
the Bootstrap stylesheet in the styles property as shown below.

At this point Routing should be working as expected.

222. The following are the directives provided by the RouterModule


223. routerLink:
Tells the router where to navigate when the user clicks the navigation link
224. routerLinkActive :
When a route is active the routerLinkActive directive adds the active CSS class. When
a route becomes inactive, the routerLinkActive directive removes the active CSS
class.
42

225. The routerLinkActive directive can be applied on the link element itself or it's
parent. In this example, for the active route styling to work correctly, routerLinkActive
directive must be applied on the list item element and not the anchor element.
226. router-outlet:
Specifies the location at which the routed component view template should be
displayed

227. At the moment routing is implemented in the root module - AppModule.


However, for separation of concerns and maintainability, it is better to implement
routing in a separate Routing module and then import that routing module in the
AppModule. In a later video, we will discuss how to move routing into it's own routing
module.

228. As we have seen throughout this video, there are many moving parts that we
have to remember, to implement routing correctly in an angular application. In our
next video, we will discuss, the routing workflow and how routing actually works in
Angular, by connecting all these little moving parts.

229. As you have seen in our previous video, there are many small steps that you
have to remember, to implement routing correctly in an angular application. Let's
quickly recap those steps.

230. Step 1 : Set base href in index.html.

231. Step 2 : Import the RouterModule into the application root module
AppModule.

232. Step 3 : Configure the application routes.

233. Step 4 : Specify where you want the routed component view template to be
displayed using the router-outlet directive

234. Step 5 : Create a navigation menu and tie the configured routes with the
menu using the routerLink directive. Optionally, use the routerLinkActive directive to
style the current route that is active, so the user knows the page that he is on, in the
application.

235. Now, let's connect all these small steps and see how routing actually works.

1. We have built the "Home" and "Employees" links using the RouterLink directive.
The RouterLink directive tells the angular router where to navigate when the
respective links are clicked. So for example, when we click on the "Home" link, the
angular Router includes '/home' in the URL.
43

2. When the URL changes the angular router looks for the corresponding route in the
route configuration. In this case the URL changed to /home, so the router looks for
the home route. We have the 'home' route already configured. In the route
configuration, we have specified to use the HomeComponent.
3. So the angular router knows to display the HomeComponent view template, but
the question is where should the HomeComponent view template be displayed.

4. At this point, the Angular router looks for the router-outlet directive. The home
component view template is then displayed at the location where we have the router-
outlet directive. In our case, we placed the router-outlet directive in the root
component (AppComponent) because that is the top level component where we
want our routed component templates to be displayed.

5. We specified 'app-root' as the selector for the root component (AppComponent).


This selector (app-root) is used as a directive in the application host page i.e
index.html. So along with the navigation menu HTML that we already have in the root
component, the HomeComponent view template is also display in index.html page.

6 . Now when we click on the "Employees" link, Steps 1 to 5 happen in the order
specified and the HomeComponent view template is replaced with the
EmployeesComponent view template.
44

236. Hope you are now able to connect all the dots and have a good
understanding of all the small steps of implementing routing in an angular
application.

237. Please note : When configuring routes in our previous video, we imported
Routes type from '@angular/router'. If you look at the definition of Routes type, it is
actually an array of Route objects. This Routes type is not required for the application
to work. Even if we remove the Routes type declaration from appRoutes as shown
below, the application routing works exactly the same way as before. However, using
it provides us compile time checking if we mis-spell the properties of the Route
object.

238. Notice the type declaration : Routes is removed from appRoutes constant
const appRoutes = [
{ path: 'home', component: HomeComponent },
{ path: 'employees', component: EmployeesComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];

239. At the moment routing is implemented in the root AppModule. However, for
separation of concerns and maintainability, it is better to implement routing in a
separate Routing module and then import that routing module in the AppModule. In
our next video, we will discuss how to move routing into a separate routing module.

240. At the moment, all the routing code is implemented in the root AppModule.
However, for separation of concerns and maintainability, it is better to implement
routing in a separate module and then import that routing module in the AppModule.
If routing is in it's own module, it is easier to find and change routing code if required.

241. Moving routing code into it's own module is easy and straight forward.
Step 1 : Create a new file in the 'app' folder. Name it app-routing.module.ts

Step 2 : Copy and paste the following code in it. The code is commented and
self-explanatory.

// Import NgModule decorator to decorate AppRoutingModule class


import { NgModule } from '@angular/core';
// Import RouterModule and Routes type from angular router library
import { RouterModule, Routes } from '@angular/router';

// Import the following 3 components as we will reference


// them in the route definitions below
import { HomeComponent } from './home/home.component';
45

import { EmployeesComponent } from './employees/employees.component';


import { PageNotFoundComponent } from './page-not-found/page-not-
found.component';

// Configure the routes. The Routes type and the


// referenced components are imported above
const appRoutes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'employees', component: EmployeesComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];

// The NgModule decorator is imported above


// Pass the configured routes to the forRoot() method
// to let the angular router know about our routes
// Export the imported RouterModule so it is available
// to the module that imports this AppRoutingModule
@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule],
})
export class AppRoutingModule { }

Step 3 : Modify the code in the root AppModule in app.module.ts. The code is
commented and self-explanatory.

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


import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';


import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { EmployeesComponent } from './employees/employees.component';
import { PageNotFoundComponent } from './page-not-found/page-not-
found.component';

@NgModule({
declarations: [
AppComponent,
HomeComponent,
EmployeesComponent,
PageNotFoundComponent
],
46

// Import AppRoutingModule which contains our routing code


// AppRoutingModule has also exported angular RouterModule, so
// all the RouterModule features are also available to this module
// including the router-outlet directive used in the AppComponent
// If AppRoutingModule module did not export RouterModule we get
// 'router-outlet' is not a known element error
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Remove all codes from our app.module.ts


And import our app.routing.module in app.module.ts
47

242. Save all changes and run the project using the following command
ng serve --open

243. Till here we get the error as below :

To fix this error we have to make features of router module in app module because in
app.component.html we are using the <router-outlet></router-outlet> which is the
property of routerModule. So, in our root module i.e. AppModule, we have to include
RouterModule.

Export the router module from app-routing.module.ts using exports.So, now any
module that is going to import the AppRoutingModule will also import the
routerModule.
48

244. Notice routing in our angular application works exactly the same way as
before. We now have routing implemented in it's own module.

245. To quickly recap, here are the steps to implement routing in a separate
module.

Step 1 : Set base href in index.html.

Step 2 : Create a separate routing module file. You can name it anything you
want. I named it app-routing.module.ts.

Step 3 : Import the angular RouterModule into your application routing


module (app-routing.module.ts). Also don't forget to re-export RouterModule.

Step 4 : Configure the application routes.

Step 5 : Import the application routing module (app-routing.module.ts) in the


root AppModule.

Step 6 : Specify where you want the routed component view template to be
displayed using the router-outlet directive
49

Step 7 : Create a navigation menu and tie the configured routes with the
menu using the routerLink directive. Optionally, use the routerLinkActive directive to
style the current route that is active, so the user knows the page that he is on, in the
application.

246.

247.
50

248.

249.
51

250.

251.
252. In this video we will discuss
1. How to compile and run an angular application locally on your development
machine
2. What happens behind the scenes when we compile and run an angular application
3. What is bundling and why is it important for performance

253. So far in this video series we have been using the following command to
build and run our angular application.
ng serve --open
52

254. Have you ever thought about what happens behind the scenes when we
execute this command. Behind the scenes, Angular CLI runs Webpack to build and
bundle all JavaScript and CSS code. In most cases the following are the bundles.
Bundle File What it contains
inline.bundle.js WebPack runtime. Required for WebPack to do it's job
main.bundle.js All our application code that we write(COMPONENT, FILE,
DIRECTIVES, PIPES ETC)
polyfills.bundle.js Browser Polyfills(Angular is build on latest web platform, targeting
all the browser is challenging, because all browser doesn’t support modern feature of
web platform.This can be compensated by implementing these polyfill features,
which implement those missing features in javascript.

styles.bundle.js Styles used by the application


vendor.bundle.js Angular and 3rd party vendor files

255. What is bundling and why is it important for performance


A typical real world angular application is made up of many components. Each
component code is in it's own .ts file which gets transpiled to JavaScript i.e to a .js
file.(because at end of the day java doesn’t understand typescript, it only understand
javascript) Along the same lines, a component may also have it's own .css file for
styles. So our angular application code is in many small files. In addition to our
application code files, we also have vendor code files like Angular, jQuery etc.

256. Web browsers have a limit on how many scripts or CSS files they can
download simultaneously. Because of this browser limitation, your application may
suffer from performance perspective, if it has many JavaScript and CSS files to
download.

257. Bundling can solve this problem by combining many small application and
library files into a few bundles. As mentioned before, Angular CLI runs WebPack for
building and bundling angular applications.

258. There are several ways to see these generated bundles.


53

1. If you have executed the "ng serve --open" command in a command prompt
window, upon build completion you can see the generated bundles in the command
prompt window

2. If you have executed the "ng serve --open" command in Visual Studio Code
Integrated Terminal, upon build completion you can see the generated bundles in the
integrated terminal window

3. "ng serve --open" command builds and runs the application. By default the
application runs at port number 4200. You can change this default port number if you
want to. We will discuss how to do that in our upcoming videos. When the
application is served in the browser you can see the generated bundles on the
"Elements" tab in Browser Developer Tools.

4. You can also see these bundles on the "Sources" tab in Browser Developer Tools.
54

5. To see the bundles along with their sizes click on the Network tab. If you don't see
the bundles, refresh the browser window by pressing F5.

259. In addition to bundling, we can also use other optimisation techniques like
Ahead-of-Time (AOT) Compilation, Minification, Uglification and TreeShaking to
improve performance. We will discuss all these techniques and how to implement
them in our upcoming videos.

260. The ng serve command builds and serves the application from memory for a
faster development experience. It does not write the build artifacts to the disk, so we
cannot use this command if you want to deploy the build to another server. For
55

example, if you want to deploy your angular application to a test server for testing, or
to your production server we cannot use ng serve. We instead use ng build. This
command writes the build artifacts to the specified output folder, so the application
can be deployed elsewhere. We will discuss ng build in our upcoming videos.

261. To customise the in-memory builds that the "ng serve" command produces,
there are several options that we can use along with this command. We will discuss
these options in our next video.
262. To see the list of all options that we can use with "ng serve" command use --
help option
ng serve --help

263. The following page also shows all the options that can be used with ng serve
https://github.com/angular/angular-cl...

264. The following command, builds and launches the application in your default
browser.
ng serve --open

265. Many of our channel subscribers have sent me emails saying their
application is using Internet Explorer, but they want to use Google chrome instead.
So their question is how to change my default browser. Well that's simple and it
really depends on the operating system you have. For example, on a Windows 7
operation system here are the steps to change your default browser.
1. Click on the Windows Start Button and in the "Search programs and files" text box
type: Control
2. Control Panel would appear in the list. Click on it.
3. In the "Control Panel" window, click on "Default Programs"
4. In the "Default Programs" window, click on "Set your default programs"
5. In the list of programs that appear, select the "Browser" that you want to be the
default browser and then click on the link that says "Set this program as default"

266. That's it. At this point, execute "ng serve --open" command and you will have
your application launched in your specified default browser.

267. Instead of using the full option name --open, you can also use it's alias -o

268. The following table shows the common options, alias, default value and their
purpose
Option Alias Default Purpose
--watch -w true Run build when files change
We can turn off watch mode explicitly by saying --watch -w false
56

--live-reload -lr true Whether to reload the page on change(As soon as we have few
changes done in our files they are picked up and compiled, but the web browser
doesn’t reload itself to show the changes. It needs to be explicitly reloaded.)

--open -o false Opens the url in default browser


--port -p 4200 The port on which the server is listening

--extract-css -ec Extract css from global styles onto css files instead of js ones

We don’t have styles under body section anymore, it has been placed under head
section.
57

It got .css extension instead of .bundle

269. In Parts 21 and 22 of Angular CLI tutorial we discussed ng serve command.


This command builds and serves the application from memory for faster
development experience. ng serve command does not write the build files to the disk,
so we cannot use it for deploying our application on a different server. For example, if
we want to deploy our application to a test, staging or production server we cannot
use ng serve command. For this we use a different command and that is ng build
command.

270. When we execute ng build command it creates a folder with name "dist" and
copies all the build files into that folder. Now the question that comes to our mind is,
why is the folder named "dist". The folder is named "dist" because that is what is
specified as the output directory for the build in the Angular CLI configuration file.
Notice the "outDir" property is set to "dist".
58

271. By default the ng build command does a development build, not a production
build. The development build is not optimised for production use. The development
build is typically used for testing. With a development build it is easier to debug as
the development build contains source map files.

272. As you can see in the "dist" folder we have


1. The favicon
2. Glyphicon files
3. Our host page index.html
4. Bundle files and their corresponding source map files

273. Please note : Both the following commands are equivalent and does the
same thing, i.e they produce a development build
ng build or ng build --dev
Production build files are smaller in size.
Dev build files :

Ng build --dev

Ng build --prod
59

Notice the size of both.

274. If you want to deploy the application to a server, copy the contents of the
"dist" folder to a folder on the server. We will discuss deployment in detail in a later
video.

275. The bundle files (inline, main, polyfills,styles, & vendor) generated by the
development build are not optimised, meaning the bundles are not minified or
treeshaked to remove the code that is not being used. A production build on the
other hand will have all the performance optimisation techniques like Ahead-of-time
(AOT) compilation, minification, uglification and treeshaking implemented. So the
sizes of the bundles that the production build produces will be significantly less than
the sizes of the bundles that a dev build produces.

276. To do a production build use --prod option with the ng build command.

277. Notice the file sizes in the production build are significantly less than the file
sizes in the development build.
With the production build, by default, we do not get the source map files because we
usually do not need them on a production server.
Also notice, Production build extracts css from global styles into a css file instead of
js ones.

278. in addition to these 3 differences between a dev build and a production build,
there are several other differences as well. We will discuss them in detail in our next
video.

ng serve vs ng build

279. ng serve
Compiles and serves the application from memory
Does not write the build files to the disk
Typically used to run the application on local development machine
Cannot be used for deploying the build to another server (Ex. Testing, Staging or
Production server)
60

280. ng build
Compiles the application to the "dist" folder
Can be used to produce both development & production builds
Typically used to deploy the application on another server

281. In production build we generally don’t need the source map files, it’s like we
don’t need to debug the files on production build. However, we can generate the
source map files on production build as well.
282.
Production build :

283.
284. To generate a development build we can use either
ng build
OR
ng build --dev

285. To generate a production build we use


ng build --prod

286. Here are some of the differences between a development build and a
production build in angular.

287. Source Maps : Development build generate Source Maps where as


production build does not.
61

288. What are Source Maps


To improve the performance, the application's JavaScript and CSS files are
combined and compressed. It is extremely difficult to debug those compressed files.
A source map holds information about the original files and can be used to map the
code within a compressed file back to it’s original position in a source file. So with
the help of these source maps we can easily debug our applications even after the
the files are compressed and combined.

289. By default, a development build produce source maps where as a production


build does not. However, we can change this default behaviour by using --
sourcemaps option along with the ng build command. It's alias is -sm.

290. The following command produces a development build without source maps
as we have set -sm option to false
ng build --dev -sm false

291. On the other hand, if you want source maps along with your production build
set -sm option to true as shown below.
ng build --prod -sm true
62

292. Extracts CSS : With the development build global styles are extracted to .js
files where as with the production build they are extracted to .css files. To change
this default behaviour use --extract-css option or it's alias -ec with the ng build
command.
63

293. The following command produces a development build with global styles
extracted to .css file(s) instead of .js ones.
ng build --dev -ec true

294. Minification & Uglification : A Prod Build is both minified and uglified, where
as a Dev Build is not.

295. What is Minification


The process of removing excess whitespace, comments, and optional tokens like
curly brackets and semicolons is called Minification.

296. What is Uglification


The process of transforming code to use short variable and function names is called
uglification.
64

* In main.js we still have our comment myConstructor over there when we had a dev
build.
65

*code is readable, didn’t move any whitespaces.


*function parameter first name and last name, variable results are not renamed.

Now, let’s execute the command with prod options.

*Now the main.js code is not very much readable.


Removed the while space, don’t have the comment MyConstructor.

*here we have addNumber function, notice the parameters are minified. And in
function body we don’t have result variable, it directly returns the sum of parameters.
66

297. The minified and uglified version of the file is smaller in size than the full
version, resulting in faster response times and lower bandwidth costs.

298. If you look at the bundles generated by the prod build, you will notice that
they are minified and uglified. Notice, extra whitespaces, comments, and optional
tokens like curly brackets and semicolons are removed. Also notice, the code is
transformed by using short variable and function names. On the other hand, the
bundles generated by the dev build, are not minified and uglified.

299. Tree Shaking : A Prod build is Tree Shaked, where as a Dev build is not.

300. What is Tree Shaking


Tree shaking is the process of removing any code that we are not actually using in
our application from the final bundle. It's one of the most effective techniques to
reduce the application size.

* at the moment we are not using the xxx.html anywhere.But in our final bundle
(main.ts) we still have our component.
67

But when we do a prod build


Ng build --prod, we don’t have the unreferenced component.

301. If you look at the bundles generated by the production build, they are
significantly less in size compared with the bundles generated by the development
build. This is beacause with the production build the code is tree shaked to remove
dead code i.e the code that is not referenced by the application.

302. Ahead-of-Time (AOT) Compilation : With a production build we get AOT


(Ahead-of-Time) compilation, i.e the Angular component templates are pre-compiled,
68

where as with a development build they are not. We will discuss Ahead-of-Time
compilation in detail in our next video.

303. The following table summarises the differences between a development


build and a production build

304. In Angular we have 2 models of compilation


JIT - Just-in-Time Compilation : JIT compilation as the name implies, compiles the
application Just-in-Time in the browser at runtime.
AOT - Ahead-of-Time Compilation : AOT compilation compiles the application at
build time.

305. By default, with the development build we get JIT compilation. This is how it
works. The application code along with the angular compiler is downloaded by the
browser. At runtime, when a request is issued to the application, the JIT-compiler in
69

the browser compiles the application code before it is executed. This means our user
who made that first request has to wait for the application to compile first.

306. In our previous videos we have seen that, when we build our angular
application, the following JavaScript bundles are generated.
Inline
Main
Polyfills
Styles
Vendor

307. The vendor bundle contains the compiler along with the angular framework.
The compiler code is roughly half of the Angular framework.

308. There is a tool called source-map-explorer that we can use to inspect the
JavaScript bundles. This tool analyzes the source map generated with the bundle
and draws a map of all dependencies.

309. To be able to use this tool we have to install it first. To install this tool,
execute the following command
npm install source-map-explorer --save-dev

310. Once we have the tool installed, if you have not done the development build
yet, do the development build using the following command.
ng build

311. Once the build is complete, you will have the JavaScript bundles along with
the source map files. Now execute the following command.
node_modules\.bin\source-map-explorer dist\vendor.bundle.js

312. The above command runs the source-map-explorer against the vendor
bundle and we see the graph of it. Notice the angular compiler is around 45% percent
of the bundle size. As this is development build and not optimised, notice the total
size of the bundle is 2.19 MB. Half of the size is angular compiler.
70

313. With AOT compilation the angular application is pre-compiled. So this means
the browser loads executable code so it can render the application immediately,
without waiting to compile the application first.

314. This also mean with AOT, as the application is already pre-compiled, there is
also no need for the browser to download the Angular compiler. As we already know,
the compiler code is roughly half of the Angular framework, so omitting it
dramatically reduces the application size.

315. By default, the production build is Ahead-of-Time compiled. So there is no


need to bundle up the angular compiler code in the vendor bundle. This brings down
the vendor bundle size by almost 50%. In addition it is also minified, uglified and tree-
shaked to remove any code that we are not referencing in our application. So the
bundler size is further reduced.

316. Now, execute the following command to generate a production build. Notice I
have also turned on sourcemap option. Without the sourcemap we will not be able to
use the source-map-explorer tool.
ng build --prod --sourcemap true

317. Once the production build is complete, execute the following command.
Vendor bundle name in your production build may be slightly different. Change it
accordingly and execute the command.
node_modules\.bin\source-map-explorer
dist\vendor.7e385ef294695236ffd1.bundle.js
71

No compiler downladed

318. The AOT compiler also detects and reports template binding errors at build
time itself. Let us understand this with an example.

319. Include the following function HomeComponent class in home.component.ts


file
getText(): string {
return 'Hello Pragim';
}

320. In home.component.html include the following [div] element. Notice I have


deliberately mis-spelled the getText() function name.
[div [innerText]='getTex()']

321. Save changes, and execute the following command. This command does a
development build in-memory. At the moment we are not using AOT, so we will not
know about the template binding error that is introduced above. Notice at build time
we do not see any errors.
ng serve

Even if we run our application it will work. As it is coming from memory location.
72

But it is not displaying the dic component string which is being returned by the
function

We have error as

Here are the steps

Step 1 : Build your angular application.

If you want to deploy a development build do a development build using the following
Angular CLI command. The base-href option on the build command sets the base-
73

href element in index.html to "/ePortal/" instaed of "/". In the IIS server, we will create
an application with name "ePortal" in just a bit.
ng build --base-href /ePortal/

If you want to deploy a production build do a production build using the following
Angular CLI command.
ng build --prod --base-href /ePortal/

In our case let's deploy a production build. After the build is complete, you will notice
a folder with name "dist" in your Angular project folder. This folder contains all the
build files. These build files need to be copied to a folder on the server where we
have IIS installed.

Step 2 : Create a folder on the server where you have IIS installed. You can name the
folder anything you want. I am going to name the folder "ProductionBuild" and I am
creating it in C:\ drive.

Step 3 : Now copy all the contents of the "dist" folder into "ProductionBuild" folder

Step 4 : Open IIS. There are several ways to do this. One way is to type "inetmgr" in
the "Run" window and click "OK"

Step 5 : Create an application in IIS. Name it "ePortal". This name has to match the
value we have specified for the --base-href option in Step 1.

a) Exapand the root IIS node


b) Expand Sites
c) Right click on "Default Web Site" and select "Add Application" from the context
menu
d) In the "Alias" textbox, type "ePortal"
e) Set the "Physical Path" to folder that contains the build files. In our case it is
"ProductionBuild" folder in C:\ drive

At this point, if you launch the browser and navigate to


http://localhost/ePortal/home, you will see the "home works" message as expected.
When you click on the "Employees" tab it also works as expected.

However, when you "Refresh" the page by pressing F5, you will see HTTP 404 error

Step 6 : To fix this Page Refresh issue in Angular, include the following URL rewrite
rule in you web.config file. This web.config file should be in copied the
"ProductionBuild" folder where we have the rest of the build files. Please replace [
with LESS_THAN symbol and ] with GREATER_THAN symbol.
74

[?xml version="1.0" encoding="utf-8"?]


[configuration]
[system.webServer]
[rewrite]
[rules]
[rule name="AngularJS Routes" stopProcessing="true"]
[match url=".*" /]
[conditions logicalGrouping="MatchAll"]
[add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /]
[add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /]
[add input="{REQUEST_URI}" pattern="^/(api)" negate="true" /]
[/conditions]
[action type="Rewrite" url="/ePortal" /]
[/rule]
[/rules]
[/rewrite]
[/system.webServer]
[/configuration]

Please note : You may also point the IIS application directly to the "dist" folder in
RoutingDemo project folder. The downside of this is every time you rebuild your
application, the "dist" folder is deleted and recreated. This means you will loose the
web.config file and you have to create it again.
75

ANGULAR CRUD :

In order to include reference to bootstrap style sheet.

Let us understand implementing the READ operation with an example. We want to


display list of employees as shown below.
76

At the moment, we do not have Employee model. First, let's create the Employee
model.

Creating Employee model :

1. In Visual Studio Code, expand the "src" folder


2. Right click on the "App" folder, and select "New Folder" from the context menu
3. Name the folder "models". We will place all our models in this folder
4. Now add a new file in the "models" folder
77

5. Name it "employee.model.ts"
6. Copy and paste the following code in it

Here ? indicates the field is optional.

Place the images that you gonna use in your asset folder as below.

Next, create a component to display the list of employees. Name it


ListEmployeesComponent.

Creating ListEmployeesComponent : Use the following AngularCLI command to


create ListEmployeesComponent. We will place all employee CRUD components in
"employees" folder. This is the reason we prefixed the "employees" folder name in the
78

command. Also, notice we have set --flat option to true as we do not want to place the
ListEmployeesComponent files in it's own dedicated folder.

ng g c employees/listEmployees --spec false --flat true

The above command not only creates the ListEmployeesComponent, it also updates
the AppModule. In the app.module.ts file it has imported ListEmployeesComponent and
included it in the declarations array. So the Angular CLI has generated lot of boiler plate
code, that we would have to write manually otherwise.
79

This is also created by angular cli.

Creating images folder : We will place all the images that we are going to use in
"images" folder. We will have the images folder in the "assets" folder. So add a new
folder in the "assets" folder and name it "images" and copy the following 3 images.
Name the images mark.png, mary.png and john.png.
80
81

Changes in list-employees.component.ts : The changes are commented and self-


explanatory. As of now we are hardcoding the employee values, later we’ll see how to
fetch the value from database.

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


// import Employee Model
import { Employee } from '../models/employee.model';

@Component({
selector: 'app-list-employees',
templateUrl: './list-employees.component.html',
styleUrls: ['./list-employees.component.css']
})
export class ListEmployeesComponent implements OnInit {
// Hard code the employee data. In a later video we will discuss
// how to retrieve this employees data from a database table
employees: Employee[] = [
{
id: 1,
name: 'Mark',
gender: 'Male',
contactPreference: 'Email',
email: 'mark@pragimtech.com',
dateOfBirth: new Date('10/25/1988'),
department: 'IT',
isActive: true,
photoPath: 'assets/images/mark.png'
},
{
id: 2,
name: 'Mary',
gender: 'Female',
contactPreference: 'Phone',
phoneNumber: 2345978640,
dateOfBirth: new Date('11/20/1979'),
department: 'HR',
isActive: true,
photoPath: 'assets/images/mary.png'
},
{
82

id: 3,
name: 'John',
gender: 'Male',
contactPreference: 'Phone',
phoneNumber: 5432978640,
dateOfBirth: new Date('3/25/1976'),
department: 'IT',
isActive: false,
photoPath: 'assets/images/john.png'
},
];
constructor() { }

ngOnInit() {
}
}

Changes in list-employees.component.html : Replace the existing HTML, with the


following HTML. Notice we are using Bootstrap for styling.

<div class="panel panel-primary" *ngFor="let employee of employees">


<div class="panel-heading">
<h3 class="panel-title">{{employee.name}}</h3>
</div>
<div class="panel-body">

<div class="col-xs-10">

<div class="row vertical-align">

<div class="col-xs-4">
<img class="imageClass" [src]="employee.photoPath" />
</div>
<div class="col-xs-8">

<div class="row">
<div class="col-xs-6">
Gender
</div>
<div class="col-xs-6">
83

: {{employee.gender}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Date of Birth
</div>
<div class="col-xs-6">
: {{employee.dateOfBirth | date}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Contact Preference
</div>
<div class="col-xs-6">
: {{employee.contactPreference}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Phone
</div>
<div class="col-xs-6">
: {{employee.phoneNumber}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Email
</div>
<div class="col-xs-6">
: {{employee.email}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Department
</div>
84

<div class="col-xs-6">
: {{employee.department}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Is Active
</div>
<div class="col-xs-6">
: {{employee.isActive}}
</div>
</div>

</div>

</div>
</div>
</div>
</div>

Changes in list-employees.component.css : Include the following CSS classes

.imageClass{
width:200px;
height:200px;
}
.vertical-align{
display: flex;
align-items: center;
}

Changes in app.component.html : Include the ListEmployeesComponent selector


(app-list-employees) as a directive in the root component (app.component.html)
<div class="container">
<app-list-employees></app-list-employees>
</div>

At this point, save all the changes and run the angular project using the following
command. This command not only compiles the angular application, it also launches
your default browser and displays the list of employees as expected.
ng serve -o
85

Step 1 : At the moment, in our application we have only one component


(ListEmployeesComponent). Let's create another component. In our upcoming videos
we will discuss how to create a new employee. So let's add
CreateEmployeeComponent. Use the following Angular CLI command to generate the
component.

ng g c employees/createEmployee --spec false --flat true

Step 2 : Set <base href="/"> in the application host page which is index.html. The <base
href> tells the angular router how to compose navigation URLs. This is already done for
us by the Angular CLI, when we created this project.

<base href="/">

We will discuss the significance of this base href element in detail in our next video.

Step 3 : Import the RouterModule into the application root module AppModule. The
Router Module contains the Router service and Router directives such as (RouterLink,
RouterLinkActive, RouterOutlet etc). So for us to be able to implement routing, we first
need to import the Router Module in our AppModule. So in app.module.ts make the
following changes. Notice the changes are commented and self-explanatory.

// Import RouterModule
import { RouterModule } from '@angular/router';

// Include RouterModule in the "imports" array of the @NgModule() decorator


@NgModule({
declarations: [...
],
imports: [
BrowserModule,
RouterModule
],
providers: [],
bootstrap: [AppComponent]
})

export class AppModule { }

Step 4 : Configure the application routes.


86

To configure routes, we first need to import Routes type from '@angular/router'. If you
look at the definition of Routes type, it is actually an array of Route objects. This Routes
type is not required for the application to work. However, using it provides us intellisense
and compile time checking. For example, mis-spelled properties of the Route object will
be reported as errors.

import { RouterModule, Routes } from '@angular/router';

// Each route maps a URL path to a component


// The 3rd route specifies the route to redirect to if the path
// is empty. In our case we are redirecting to /list

// pathMatch property value can be full or prefix. For now we


// will set it to full as we want to do a full match. In our upcoming videos,
// we will discuss the difference between prefix and full in detail.

const appRoutes: Routes = [


{ path: 'list', component: ListEmployeesComponent },
{ path: 'create', component: CreateEmployeeComponent },
{ path: '', redirectTo: '/list', pathMatch: 'full' }
];

// To let the router know about the routes configured above,


// pass "appRoutes" constant to forRoot(appRoutes) method
// We also have forChild() method. We will discuss the difference
// and when to use one over the other in our upcoming videos

@NgModule({
declarations: [...
],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes)
],
providers: [],
bootstrap: [AppComponent]
})

export class AppModule { }


87

Step 5 : Create the application menu and tie the routes to it. Notice we are using
routerLink directive. This directive tells the router where to navigate when the user clicks
the link. We are also using router-outlet directive. This directive specifies where you
want the routed component view template to be displayed. We want our navigation
menu to be always displayed, so the ideal location for it is the root component
AppComponent i.e app.component.html file. When the application first loads, it loads the
root AppComponent in index.html. So let's place the following HTML in
app.component.html file.

<div class="container">
<nav class="navbar navbar-default">
<ul class="nav navbar-nav">
<li>
<a routerLink="list">List</a>
</li>
<li>
<a routerLink="create">Create</a>
</li>
</ul>
</nav>
<router-outlet></router-outlet>
</div>

As you can see, in the HTML we have 2 things - Navigation menu and the <router-
outlet> directive. The navigation menu is always displayed. Below the navigation menu,
we have the <router-outlet> directive. This is the location where the routed component
view template is displayed.

For example, when we click on the "List" link in the navigation menu, the route changes
to "/list" and the ListEmployeesComponent view template is displayed at the location
where we have the <router-outlet> directive. At this point, if we click on "Create" link, 2
things happen

1. The route changes from "/list" to "/create"


2. ListEmployeesComponent view template is replaced with CreateEmployeeComponent
view template

Also notice, if we navigate to the root of the application i.e if we do not include "/list" or
"/create" in the URL, the router automatically redirects us to "/list". This is because of the
following empty route we have in our route configuration.
88

{ path: '', redirectTo: '/list', pathMatch: 'full' }

We are using Bootstrap navbar component to create the navigation menu. We discussed
Bootstrap navbar component in Part 28 of Bootstrap tutorial.

Since we are now routing to ListEmployeesComponent we no longer need it's selector.


So remove the selector specified in the @Component decorator of
ListEmployeesComponent

At the moment both the menu items, i.e the active and the inactive menu items are
styled the same way. As a result we don't know which page the user is on. We will
discuss how to address this in our upcoming videos.

In this short video we will discuss the significance of the base href element in
Angular.

When setting up routing in an angular application, the first step is to set the base path
using the base href element. The base path tells the angular router, how to compose the
navigation URLs. The browser uses the <base href> value to prefix relative URLs when
referencing CSS files, scripts, and images.

During development we usually set this to a single forward slash as shown below.
<base href="/">

This means all the URLs now will be relative to the root of the application. So when we
navaigate to "/list", the path "/list" will be appended to root URL and the complete URL
will be as shown below. Notice "/list" is relative to the root URL.
http://localhost:4200/list

Along the same lines, when we navigate to "/create", the complete URL is
http://localhost:4200/create

When we deploy our application to a server, we typically deploy it to a sub folder on the
server. For example, if we are deploying our application in a sub-folder called "emp",
then we set the base href element to /emp/ as shown below.
<base href="/emp/">

This means all the URLs now will be relative to the "emp" base path and will be as
shown below.
http://serverName/emp/list
http://serverName/emp/create
89

During development we usually set base href element to a single forward slash as
shown below.
<base href="/">

At this point, if we execute the following command, all the URLs will be relative to the
root URL "http://localhost:4200"
ng serve -o

Also, on the "sources" tab in the browser developer tools, you will find all the Script,
Images and Template files are relative to the root URL "http://localhost:4200" as
shown in the image below.
90

During development, if you want a different base path other than "/", simply execute the
"ng serve" command with --base-href option set to your desired base path as shown
below.
ng serve -o --base-href /emp/

At this point all the URLs will be relative to "http://localhost:4200/emp" as we have set
the --base-href to /emp/. You can confirm this by looking at the URLs in the address bar
and the "Sources" tab in the browser developer tools.
91

On your local development machine, if you set the base href element in index.html to
"/emp/" instead of a single "/" and if you run ng serve -o command without the "base-
href" option you will not see anything on the browser. When you open the browser
developer tools, you will see the JavaScript bundle files failed to load. To fix this execute
ng serve command along with the base href option as shown below.
ng serve -o --base-href /emp/

Error on running command without base-href :


92

On your local development machine, if you set the base href element in index.html to a
single forward slash and if you want to deploy your application on a server on sub-folder
called "emp", then you will have to remember to update the base href element value in
index.html to "/emp/". There are 2 ways we can do this.

1. Manually update the index.html file OR


2. Use the --base-href option along with the ng build command as shown below. This will
update the "base href" element value index.html.ng build --base-href /emp/

Index.html file in dist folder will be as below : base-href will be set to /emp/

In Part 2 of Angular CRUD tutorial, we discussed performing the READ operation. In this video
and in the next few videos we will discuss performing the CREATE operation. To understand
the CREATE operation, let us build a form that help us create a new employee. For this we will
use the createEmployee component that we already created in one of our previous videos in
this series. Along the way, we will also discuss performing validation and displaying meaningful
error messages to the user.

There are 2 ways to create forms in Angular


93

1. Template Driven Forms


2. Model Driven Forms (Commonly called Reactive Forms)

Both these approaches have their own pros and cons. For example, Template Driven forms
are generally used to create simple forms. On the other hand, Reactive forms are used to create
complex forms. For example, if you want to add form controls dynamically or perform cross-field
validation we use the Reactive forms approach. There are several other differences, between
Template driven and Reactive forms. We will discuss those differences in detail, in a later video.

In this video, we will use the Template driven approach to build the "Create Employee" form.
As the name implies, template driven forms are heavy on the template. This means we do most
of the work in the view template of the component.

We want to design our "Create Employee" form as shown below. To keep this simple, at the
moment we only have 2 fields (Full Name & Email). We will add the other fields like Gender,
Department, Phone Number etc.. later. Also, at the moment, we only have textboxes on our
form. In our upcoming videos we will discuss working with radio buttons, checkbox, dropdownlist
etc

Replace the HTML in "create-employee.component.html" file with the following HTML


94

<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employeeForm)">

<div class="panel panel-primary">

<div class="panel-heading">

<h3 class="panel-title">Create Employee</h3>

</div>

<div class="panel-body">

<div class="form-group">

<label for="fullName">Full Name</label>

//for tag is used to shift the focus to input box on clickin the fullName label.

<input id="fullName" type="text" class="form-control"

name="fullName" [(ngModel)]="fullName">

</div>

<div class="form-group">

<label for="email">Email</label>

<input id="email" type="text" class="form-control"

name="email" [(ngModel)]="email">

</div>

</div>
95

<div class="panel-footer">

<button class="btn btn-primary" type="submit">Save</button>

</div>

</div>

</form>

Angular Generated Form Model : {{employeeForm.value | json}}

Code Explanation:

We are using Bootstrap CSS classes like panel, panel-primary, panel-heading, panel-title etc to
style the form. There is no Angular here.

Consider the following line of code

<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employeeForm)">

#employeeForm is called the template reference variable. Notice we have assigned "ngForm"
as the value for the template reference variable employeeForm. So employeeForm variable
holds a reference to the form. When Angular sees a form tag, it automatically attaches the
ngForm directive to it. The ngForm directive supplements the form element with additional
features. It holds all the form controls that we create with ngModel directive and name attribute,
and monitors their properties like value, dirty, touched, valid etc. The form also has all these
properties. We will discuss these properties at the individual control level and at the form level in
detail in our upcoming videos.

The ngSubmit directive submits the form when we hit the enter key or when we click the
Submit button. When the form is submitted, saveEmployee() method is called and we are
passing it the employeeForm. We do not have this method yet. We will create it in the
component class in just a bit.
96

The ngForm directive is provided by Angular FormsModule. So for us to be able to use it, we
will have to import the FormsModule in our AppModule file (app.module.ts). So please make
sure to include the following import statement. Also include "FormsModule" in the imports array
of @NgModule decorator.

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

If "FormsModule" is not imported you will see the following error in the browser developer tools
there is no directive with exportas set to ngform

If we use the [(ngModel)] in the form attribute, we must set the name attribute in the html
element. It’s name can differ from ngModel and id of that element.

Consider the following block of code

<div class="form-group">

<label for="fullName">Full Name</label>

<input id="fullName" type="text" class="form-control"

name="fullName" [(ngModel)]="fullName">

</div>

1. To style the "Full Name" field and it's associated label, we are using Bootstrap. So "form-
group" and "form-control" are Bootstrap CSS classes used for styling. There is no
Angular here.
97

2. The "for" attribute on the label, is used to link the label with it's associated "fullName"
input control. With the "for" attribute in place, when we click on the label, it's associated
input element automatically receives the focus. Again there is no Angular here. It's all
standard HTML.
3. The ngModel directive is used for creating two-way data binding i.e to keep the HTML
element value and it's corresponding component property in sync.

4. Notice we have set ngModel directive to "fullName". We do not have "fullName"


property in the component class. Angular automatically creates "fullName" property
using the value of the "name" attribute of the HTML input element. This is why "name"
attribute is also required when we use ngModel directive. If we remove the "name"
attribute, we get the following error.
If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions

5. So the bottom line is, if you want an input element to be tracked by the form make sure
to include both the name attribute and ngModel directive. Otherwise that input element
will not be part of the Form model created by Angular.

Consider the following piece of code : We are using the value property of the employeeForm
to display fullName and email property values of the Form Model that angular automatically
generates for us. We are using the Angular "json" pipe to format the JSON data.

Angular Generated Forom Model : {{employeeForm.value | json}}

Finally in the CreateEmployeeComponent class include the following saveEmployee() method.


At the moment we are simply logging the value of the Angular generated Form model to the
console. In our upcoming videos, we will discuss how to save the new employee to a database
table.

saveEmployee(employeeForm: NgForm): void {

console.log(employeeForm.value);

}
98

Please note : Make sure to import NgForm type from '@angular/forms'

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

Remember we discussed, The ngForm directive supplements the form element with additional
features and properties like value, dirty, touched, valid etc. To see all these properties, knock of
the value property and log just the employeeForm as shown below.

saveEmployee(employeeForm: NgForm): void {

console.log(employeeForm);

At this point, if you fill in the Full Name and Email text boxes and when you submit the form
either by click the "Save" button or by pressing the "Enter" key you will see the form logged to
the browser console and you can see all these properties.
99

These properties are greatly useful for performing form validation. We will discuss them in detail
in our upcoming videos.

We want to include "Gender" radio buttons in the Create Employee form as shown below.
When we select employee "Gender" using the radio buttons, the selected gender value should
reflect in the Angular generated form model as shown in the image below. Also, we we click the
"Save" button we want the selected gender value to be logged to the console.
100

To achieve this all you have to do is include the following HTML in create-
employee.component.html file

<div class="form-group">
<label>Gender</label>
<div class="form-control">
<label class="radio-inline">
<input type="radio" name="gender" value="male" [(ngModel)]="gender">
Male
</label>
<label class="radio-inline">
<input type="radio" name="gender" value="female" [(ngModel)]="gender">
Female
</label>
</div>
</div>

Code Explanation
101

● The name attribute is required to group the radio buttons as one unit and make the
selection mutually exclusive. Make sure both the radio buttons have the same value for
the "name" attribute. Otherwise the radio button selection won't be mutually exclusive.

That is to say you can easily use both of the radio button at a time.

● It is also important that you set the "value" attribute for each radio button. This value is
posted to the server when the form is submitted.

While we are here, let's also include a textbox to capture "Phone Number" and "Contact
Preference" radio button. So the form should now look as as shown below.

For your reference, here is the complete HTML in create-employee.component.html

<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employeeForm)">


102

<div class="panel panel-primary">


<div class="panel-heading">
<h3 class="panel-title">Create Employee</h3>
</div>
<div class="panel-body">

<div class="form-group">
<label for="fullName">Full Name</label>
<input id="fullName" type="text" class="form-control" name="fullName"
[(ngModel)]="fullName">
</div>

<div class="form-group">
<label for="email">Email</label>
<input id="email" type="text" class="form-control" name="email"
[(ngModel)]="email">
</div>

<div class="form-group">
<label for="phoneNumber">Phone Number</label>
<input id="phoneNumber" type="text" class="form-control" name="phoneNumber"
[(ngModel)]="phoneNumber">
</div>

<div class="form-group">
<label>Contact Preference</label>
<div class="form-control">
<label class="radio-inline">
<input type="radio" name="contactPreference" value="email"
[(ngModel)]="contactPreference">
Email
</label>
<label class="radio-inline">
<input type="radio" name="contactPreference" value="phone"
[(ngModel)]="contactPreference">
Phone
</label>
</div>
</div>
103

<div class="form-group">
<label>Gender</label>
<div class="form-control">
<label class="radio-inline">
<input type="radio" name="gender" value="male" [(ngModel)]="gender">
Male
</label>
<label class="radio-inline">
<input type="radio" name="gender" value="female" [(ngModel)]="gender">
Female
</label>
</div>
</div>
</div>

<div class="panel-footer">
<button class="btn btn-primary" type="submit">Save</button>
</div>
</div>
</form>

Angular Generated Form Model : {{employeeForm.value | json}}

Working with a checkbox in Angular is very similar to working with a radio button. We
want to include "Is Active" checkbox in the Create Employee form as shown below. When we
check the checkbox, "isActive" property should reflect in the Angular generated for model as
shown in the image below. Also, when we click the "Save" button we want the "isActive"
property value to be logged to the console.
104

To achieve this all you have to do is include the following HTML in create-
employee.component.html file

<div class="form-group">
<div class="form-control">
<label class="checkbox-inline">
<input type="checkbox" name="isActive" [(ngModel)]="isActive">Is Active
</label>
</div>
</div>
105

If we include "checked" attribute on a checkbox, we expect checkbox to be checked by default


when the form initially loads. But you will discover that is not the case.

<input type="checkbox" name="isActive" [(ngModel)]="isActive" checked>Is Active

However, if you remove the "ngModel" directive from the checkbox, then it gets checked as
expected. Notice the "ngModel" directive is removed from the checkbox.

<input type="checkbox" name="isActive" checked>Is Active

With Angular Template Driven forms, we use "ngModel" directive for two-way data binding. So
the moment we put it back in place the "checked" attribute does not work. To make it work
include "isActive" property in the component class and initialise it to true.

isActive = true;

At this point you will have "Is Active" checkbox checked by default when the form loads. Now,
even if we remove the "checked" attribute from the checkbox it is still checked by default when
the form loads. This is because of the two-way data binding that we get with "ngModel"
directive. For our form we do not want the checkbox to be checked by default, so remove the
"checked" attribute and the "isActive" property from the component class.

How to disable a checkbox : To disable a checkbox, use the disabled attribute


106

<input type="checkbox" name="isActive" [(ngModel)]="isActive" disabled>Is Active

Another important point to keep in mind. By default, disabled form controls are not included in
the Angular auto generated form model. Since, the "Is Active" checkbox is disabled, it will not be
included in the Angular generated form model.

We don’t have IsActive included in our value object as it is disabled.

In our form, we do not want the checkbox to be disabled, so please remove the disabled
attribute.

Let us understand working with a select element in Angular with a simple example. We
want to include "Department" select list as shown in the image below.

Here is the HTML for the "Department" select list


<div class="form-group">
107

<label for="department">Department</label>
<select id="department" name="department"
[(ngModel)]="department" class="form-control">
<option value="1">Help Desk</option>
<option value="2">HR</option>
<option value="3">IT</option>
<option value="4">Paroll</option>
</select>
</div>

At the moment, we have hard coded the select list options in the HTML. In our next video we will
discuss, how to get the select list options from the component class. Notice each option also
has a corresponding value. The value is the department id which is what we want to save in the
database table when the form is submitted. We will discuss, saving the data to a database table
in a later video.

At this point, when we select an option, notice the corresponding option value is included
against the "department" property in the Angular auto-generated form model.

Also notice, when we click the "Save" button, the "department" property along with the
selected option value is logged to the console in browser developer tools.
108

How to have one of the select list option selected by default

If we include "selected" attribute on one of the options of the select list, we expect that option
to be selected by default when the form initially loads. In the example below, we have included
the "selected" attribute on the "IT" option, but when the form reloads, the "IT" option is not
selected.

<option value="3" selected>IT</option>

If you remove the "ngModel" directive from the select list, then the the "IT" option gets selected
as expected. Notice the "ngModel" directive is removed from the select list.

<div class="form-group">
<label for="department">Department</label>
<select id="department" name="department" class="form-control">
<option value="1">Help Desk</option>
<option value="2">HR</option>
<option value="3" selected>IT</option>
<option value="4">Paroll</option>
</select>
</div>

In Angular, we use "ngModel" directive for two-way data binding. So the moment we put it back
in place the "selected" attribute does not work. To make it work include "department" property
in the component class and initialise it with one of the option value which you want to have
selected by default. In our case, we want the "IT" option to be selected by default. The "IT"
option value is "3". So, I have initialised "department" property with a value of '3'

department = '3'
109

At this point you will have "IT" option selected by default when the form loads. Now, even if we
remove the "selected" attribute from the "IT" option, it is still selected by default when the form
loads. This is because of the two-way data binding that we get with "ngModel" directive.

How to disable a select list : To disable a select element, use the disabled attribute

<select id="department" name="department" [(ngModel)]="department"


class="form-control" disabled>

Another important point to keep in mind. By default, disabled form controls are not included
in the Angular auto generated form model. Since, the "department" select element is
disabled, it will not be included in the Angular generated form model.

In our form, we do not want the select element to be disabled, so please remove the disabled
attribute. Also, we do not want any option to be selected by default, so remove the
"department" property from the component class.

In our next video, we will discuss, how to get the select list options from the component
class, instead of having them hard-coded in the HTML.

Step 1 : Create the Department class.

Add a TypeScript file to the models folder. Name it department.model.ts. Copy and paste
the following code. Notice the Department class has 2 properties - id and name of the
department.

export class Department {


id: number;
name: string;
}

Step 2 : Import the Department class

Include the following import statement in create-employee.component.ts file


import { Department } from '../models/department.model';
110

Step 3 : Include the following array of departments in CreateEmployeeComponent class


in create-employee.component.ts file

departments: Department[] = [
{ id: 1, name: 'Help Desk' },
{ id: 2, name: 'HR' },
{ id: 3, name: 'IT' },
{ id: 4, name: 'Payroll' }
];

Please note : The "Department" type is not required for the application to work, but it
adds great value during development. Using it provides us intellisense, error checking
and type saftey.

Step 4 : In create-employee.component.html file, modify the HTML that displays the


"Department" dropdownlist as shown below.

<div class="form-group">
<label for="department">Department</label>
<select id="department" name="department" [(ngModel)]="department"
class="form-control">
<option *ngFor="let dept of departments" [value]="dept.id">
{{dept.name}}
</option>
</select>
</div>

Code explanation :

● On the "option" element we are using ngFor structural directive to loop over the
array of departments we have in the "departments" property of the component
class
● Structural directive has * before there name and they have property to modify the
DOM, i.e. add or remove the DOM.
● For each "Department" object in the "departments" array, we get an option.
● The option value is the department id and the display text is the department name
● Notice the square brackets around the [value] property. This is property binding in
Angular. We discussed property binding in detail in Part 9 of Angular 2 tutorial. If
111

you remove the square brackets the value for each option will be the literal text
"dept.id" instead of the department id (1 or 2 or 3 etc.)
● To display the deprtment name we are using interpolation. We discussed
interpolation in Part 8 of Angular 2 tutorial.
● Since ngFor is a structural directive there is an asterisk before it.
● Structural directives modify the DOM, i.e they add or remove the elements from
DOM. Adding and removing elements from DOM is different from showing and
hiding. We will discuss all these in detail in our upcoming videos.

At this point, when we select a department, the respective department id is included in


the Angular generated form model. Along the same lines, when we click the "Save"
button the respective department id is logged to the console.

Please note : It is important that we include the ngFor directive on the element that we
want to be repeated. In our case we want an option element for each department we have
in the array. So we included the ngFor directive on the option element. If we instead
include the ngFor directive on the "div" element that has the bootstrap "form-group"
class as shown below.

<div class="form-group" *ngFor="let dept of departments">


<label for="department">Department</label>
<select id="department" name="department" [(ngModel)]="department"
class="form-control">
<option [value]="dept.id">
{{dept.name}}
</option>
</select>
</div>

We get 4 department dropdownlists. That is one for each department in the array. So it is
important we include the ngFor directive on the right element.
112

We have each Department with different value.


We need to property bind the element value attribute

Why is not a good practice to use the browser built-in DatePicker control : This is
because the implementation of datepicker is different from browser vendor to vendor.
This means our end users may have different experience depending on the browser they
use. Let us understand this with an example.
113

On our "Create Employee" form we want to capture Date of Birth of an employee.


Datepicker control is very useful in capturing dates from users. When we use the HTML5
input type date, the browser automatically displays it's built-in datepicker control.
Include the following piece of HTML on "create-employee.component.html" file just
below the "Department" field HTML

<div class="form-group">
<label for="dateOfBirth">Date of Birth</label>
<input id="dateOfBirth" name="dateOfBirth" [(ngModel)]="dateOfBirth"
type="date" class="form-control" />
</div>

Notice we have set the input element type to date. At this point if we run the project and
navigate to http://localhost:4200/create in Google chrome, we see the date-picker as
shown below.

Now, if we navigate to the same url in firefox, we see a date-picker control that is very
different from the date-picker control that is on Google chrome browser.
114

So, this means our end users have different experience depending on the browser they
use. What we want here is consistency. There are many third party Date-picker controls
that we can use, to provide consistent experience to our end users. ngx-bootstrap
datepicker control is one of them.

Please refer to the UI components section on the following page, to see the list of all
third party UI components that we can use in Angular
https://angular.io/resources

Installing ngx-bootstrap : The following are the steps to install ngx-bootstrap

Step 1 : Execute the following command to npm install ngx-bootstrap


npm install ngx-bootstrap --save

Step 2 : If you do not have Bootstrap installed, please install it using the following npm
command. If you are following along we have already installed bootstrap in Part 1 of this
Angular CRUD tutorial. So I am not going to execute this command again.
npm install bootstrap@3 --save

Please note : We are usng Bootstrap 3. We can also use Bootstrap 4 with ngx-bootstrap.
Please refer to the documentation available at the following link on how to use Bootstrap
4 with ngx-bootstrap.
https://valor-software.com/ngx-bootstrap/#/getting-started
115

Step 3 : Once Bootstrap is installed, open .angular-cli.json file and specify the path to the
Bootstrap stylesheet (bootstrap.min.css) in the styles property as shown below. Again,
we have already done this in Part 1 of Angular CRUD tutorial.
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
"styles.css"
]

Using ngx-bootstrap datepicker in Angular : The following are the steps to use ngx-
bootstrap datepicker in Angular

Step 1 : In app.module.ts file, include the following import statement to import


BsDatepickerModule
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';

Also, include BsDatepickerModule in the imports array of @NgModule decorator as


shown below
@NgModule({
imports: [BsDatepickerModule.forRoot(),...]
})

Step 2 : In "create-employee.component.html" file, make the following 2 changes to the


HTML that displays the "Date of Birth" field

● Change the "type" attribute value from "date" to "text"


● Include "bsDatepicker" directive on the input element

<div class="form-group">
<label for="dateOfBirth">Date of Birth</label>
<input id="dateOfBirth" name="dateOfBirth" [(ngModel)]="dateOfBirth"
type="text" bsDatepicker class="form-control" />
</div>

Step 3 : Include a reference to the bs-datepicker.css file in .angular-cli.json file.

"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
"../node_modules/ngx-bootstrap/datepicker/bs-datepicker.css",
"styles.css"
]
116

At this point when you view the page in Google chrome or Firefox, you get the same
datepicker and hence the same experience.

When we select a date from the date-picker control, the "Date of Birth" textbox is
automatically populated with the selected date and it is also captured by the angular
generated form model.

With this datepicker control, it is also very easy to capture a date range. For example,
you have an open job role, and you want to capture a date range for accepting CV's, we
can very easily do this. All we have to do is use bsDaterangepicker directive instead of
bsDatepicker directive on the input element as shown below.

<div class="form-group">
<label for="dateOfBirth">Date of Birth</label>
<input id="dateOfBirth" name="dateOfBirth" [(ngModel)]="dateOfBirth"
type="text" bsDaterangepicker class="form-control" />

</div>
117

The above simple change, will display Daterange picker as shown below. When we select
a date range, the corresponding input field is automatically populated with the selected
date range and it is also captured by the angular generated form model.

● At the moment, the Datepicker is using the default green theme. We want to
change it to dark-blue theme, so it matches with the rest of the form.
● The date is captured in the textbox in mm/dd/yyyy format. We want to change it to
dd/mm/yyyy format
● At the moment there is no default date. We want to set a default date
● The input element is spanning across the entire width of the form. We want to limit
it's width

Datepicker is a highly configurable component. We will discuss how to do all of the


above in our next video.
118

In this video we will discuss customising the ngx-bootstrap datepicker component with
an example. This is continuation to Part 11. Please watch Part 11 from Angular CRUD
tutorial before proceeding.
119

At the moment we have date range picker. Use bsDatepicker directive instead of
bsDaterangepicker directive on the input element so we can capture single date i.e the
Date of Birth of the employee.

<div class="form-group">
<label for="dateOfBirth">Date of Birth</label>
<input id="dateOfBirth" name="dateOfBirth" [(ngModel)]="dateOfBirth"
class="form-control" type="text" bsDatepicker />
</div>

Changing ngx-bootstrap datepicker theme : At the moment, the Datepicker is using the
default green theme. We want to change it to dark-blue theme, so it matches with the rest
of the form. As of this recording ngx-bootstrap datepicker component has the following 6
color schemes.

1. theme-default
2. theme-green
3. theme-blue
4. theme-dark-blue
5. theme-red
6. theme-orange

We can change the default colour-scheme, by manipulating containerClass property in


bsConfig object. Here are the steps.

Step 1 : Make the following changes in CreateEmployeeComponent class (i.e create-


employee.component.ts file)

// Import BsDatepickerConfig type. This is the Config object for datepicker. Using this
// config object we can set minDate, maxDate, whether to show/hide week numbers and
// change the color theme using the containerClass property.
import { BsDatepickerConfig } from 'ngx-bootstrap/datepicker';

// In the CreateEmployeeComponent class make the following changes

export class CreateEmployeeComponent implements OnInit {


// create a property of type Partial<BsDatepickerConfig>
datePickerConfig: Partial<BsDatepickerConfig>;
120

// In the constructor set containerClass property to the preferred theme


constructor() {
this.datePickerConfig = Object.assign({}, { containerClass: 'theme-dark-blue' });
}

// Rest of the code...


}

Please note :
We are using the TypeScript partial type here to set only the "containerClass" property of
BsDatepickerConfig object. To learn more about the partial type please refer to the
following article.
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

Object.assign() copies the property values from one or more source objects to a target
object. The target object is the first parameter and the rest are the sources.
Object.assign() is useful for merging objects or cloning them shallowly.

Step 2 : In the view template (i.e in create-employee.component.html file) bind the


"datePickerConfig" property in the component class we created above in Step 1, to the
bsConfig input property.

<div class="form-group">
<label for="dateOfBirth">Date of Birth</label>
<input id="dateOfBirth" name="dateOfBirth" [(ngModel)]="dateOfBirth"
class="form-control" type="text" bsDatepicker [bsConfig]="datePickerConfig" />
</div>

At this point, you should see the Datepicker using the dark-blue theme colour as shown
below.
121

Showing or hiding week numbers : By default, the weeknumber are displayed. If you
want to hide them, all you have to do is set "showWeekNumbers" boolean property to
false in the config object as shown below.

constructor() {
this.datePickerConfig = Object.assign({},
{
containerClass: 'theme-dark-blue',
showWeekNumbers: false
});
}

You can find all the properties of the config object at the following page.
https://github.com/valor-software/ngx-bootstrap/blob/development/src/datepicker/bs-
datepicker.config.ts

Along the same lines we can also set the min and max dates. Please note that the month
numbers start from 0 and not 1. So for January it is 0, February it is 1, so on and so forth.

constructor() {
this.datePickerConfig = Object.assign({},
122

{
containerClass: 'theme-dark-blue',
showWeekNumbers: true,
minDate: new Date(2018, 0, 1),
maxDate: new Date(2018, 11, 31),
});
}

To change the date format, use dateInputFormat property of the config object.

constructor() {
this.datePickerConfig = Object.assign({},
{
containerClass: 'theme-dark-blue',
showWeekNumbers: true,
minDate: new Date(2018, 0, 1),
maxDate: new Date(2018, 11, 31),
dateInputFormat: 'DD/MM/YYYY'
});
}

To set a default date, create a property (dateOfBirth) in the component class and set it to
the default value you want. Since we are using 2 way databinding, the defualt date is
displayed in the corresponding input field when them form loads. In this case we have
set default date to January 30, 2018.

dateOfBirth: Date = new Date(2018, 0, 30);

At the moment, the "Date of Birth" input element is spanning across the entire width of
the form. There are sevral options to limit it's width. One option is to use the Bootstrap
row and grid classes (Example: col-md-4, col-md-5, etc...)

<div class="row">
<div class="form-group col-md-4">
<label for="dateOfBirth">Date of Birth</label>
<input id="dateOfBirth" name="dateOfBirth" [(ngModel)]="dateOfBirth"
class="form-control" type="text" bsDatepicker
[bsConfig]="datePickerConfig" />
</div>
123

</div>

To control the placement of the Datepicker use the placement property. The allowed
values are "top" | "bottom" | "left" | "right". The default is "bottom".

For our form we do not want a default date to be set. So please remove the dateOfBirth
property from the component class. We also do not want minDate and maxDate, so
delete these properties as well from the datePickerConfig object. Also delete,
showWeekNumbers property as it is set to true by default. This means our
datePickerConfig object in the constructor has just 2 properties (dateInputFormat and
containerClass)

constructor() {
this.datePickerConfig = Object.assign({},
{
containerClass: 'theme-dark-blue',
dateInputFormat: 'DD/MM/YYYY'
});
}
124

● Angular ngIf structural directive with an example


● How to prevent a button from submitting form

Here is what we want to do : When the Create Employee form first loads, we want to
display a field to enter "Photo Path" and "Show Preview" button

For now, we will assume employee photo is already available in the assets/images folder.
We will discuss uploading files in a later video in this series.

Once the user has typed the photo path in the respective field, and when they click
"Show Preview" button, we want to display the photo and the text on the button should
change to "Hide Preview".
125

At this point when the employee clicks "Hide Preview" button, the photo should be
hidden and the text on the button should change again back to "Show Preview".

Here are the steps to achieve this

Step 1 : First include an input field for capturing employee photo path. As we have set
both the name property and ngModel directive to photoPath, Angular generated form
model will create a property with name "photoPath" and keeps track of what is typed in
the photoPath textbox.

<div class="form-group">
<label for="photoPath">Photo Path</label>
<input id="photoPath" type="text" class="form-control"
name="photoPath" [(ngModel)]="photoPath">
</div>
126

Step 2 : Include image element to preview the employee photo. Notice we have set height
and width to 200 pixles. Also notice we are binding the img element src property to the
photoPath property.

<div class="form-group">
<img [src]="photoPath" height="200" width="200" />
</div>

With the above 2 changes in place, view the page in the browser and launch browser
developer tools. On the console tab, you will see the following error. This is because,
when the form loads, photoPath property is null and we have bound it to the src property
of the img element.
Failed to load resource: the server responded with a status of 404 (Not Found)

At this point, as you start to type in the "Photo Path" textbox, you will see a 404 error
logged to the console every time you type a character. This is because every time a
character is typed, angular tries to bind the src property of the image element to the
photoPath property, Since we have not completed typing the full valid photo path,
Angular is not able to find the image and it logs a 404 error to the console. Once we
complete typing the valid photo path, the photo is displayed.

Step 3 : We do not want to render the image element when the form first loads. So create
a boolen property with name previewPhoto in the CreateEmployeeComponent class and
initialise it to false.

previewPhoto = false;

Step 4 : In the view template (i.e in create-employee.component.html) file, include *ngIf


structural directive on the image element. Notice the expression assigned to *ngIf
127

directive. It is the boolean property (previewPhoto) we created in the component class. If


the value of the expression is truthy then the image element is rendered in the DOM
otherwise it is not. Since we have initialised previewPhoto with false, the image element
will not be rendered when the form is initially loaded.

<img [src]="photoPath" height="200" width="200" *ngIf="previewPhoto"/>

At this point, view the page in the browser and launch browser developer tools. On the
console tab, you will not see any errors on the initial form load. Also, when you start to
type in the Photo Path field, you do not see any 404 errors in spite of having the img
element bound to photoPath property. This is because the *ngIf structural directive
prevented the img element from being added to the DOM as it's value is falsy.

Step 5 : Now we need to include a button to Show and Hide Image Preview. In the view
template, include the following HTML.

<div class="form-group">
<button (click)="togglePhotoPreview()" class="btn btn-primary">
{{previewPhoto ? "Hide " : "Show " }} Preview
</button>
</div>

Code explanation :

● On the button click, we are calling "togglePhotoPreview()" method. This is event


binding. We discussed Event Binding in Part 14 of Angular 2 tutorial.
● We have not created togglePhotoPreview() method. We will create it in out next
step.
● We are using the Bootstrap btn and btn-primary classes for styling
● We are using interpolation to dynamically change the button text.

Step 6 : In the component class, create togglePhotoPreview() method. Notice this method
toggles the value of previewPhoto property.

togglePhotoPreview() {
this.previewPhoto = !this.previewPhoto;
}
128

● At this point, view the page in the browser and launch browser developer tools.
Type a valid photo path and click "Show Preview" button.
● The image will be displayed and the text on the button changes to "Hide Privew"
as expected.
● If you look on the console tab, you will see that the Angular generated form model
is logged to the console. We did not expect this to happen.
● The code to log the employee form values is in the saveEmployee() method and
this method should only be called when we click the "Save" button.
● So the question that comes to our mind is, why is the form being submitted when
we click "Show Preview" or "Hide Preview" button.
● This is because of the way we have created the button. If we do not explicitly
specify the button type attribute, the button behaves like the "Submit" button and
hence the code in the "saveEmployee()" method is also executed.
● To prevent this, explicitly set the type attribute of the button to "button". This
prevents the button from behaving as a Submit button.

<div class="form-group">
<button type="button" (click)="togglePhotoPreview()" class="btn btn-primary">
{{previewPhoto ? "Hide " : "Show " }} Preview
</button>
</div>
129

In this video we will discuss the following

● By default Angular 4 and later versions disable browser native validation. How to
enable browser validation using ngNativeValidate directive
● How to explicitly disable the native browser validation using the novalidate
attribute if you are using Angular 2.
● Why is it better to disable browser built-in validation and use Angular to validate
instead
130

To understand browser native validation,

1. On the "Create Employee" view template, include required attribute on FullName


input field
2.
Navigate to the "Create Employee" form and launch browser developer tools.
3.
Do not type anything in the "Full Name" input field click the "Save" button.
4.
Notice we do not get any validation, in spite of having required attribute on the
"Full Name" input field.
5.
This is because by default, Angular 4 and later versions disable browser
validation by including novalidate attribute on the form tag.
6.
To confirm this, click on the "Elements" tab in the browser developer tools and
you will see "novalidate" attribute on the form tag
7.
If you want to enable browser validation, include ngNativeValidate directive on the
form tag in create-employee.component.html file

<form #employeeForm="ngForm" ngNativeValidate


(ngSubmit)="saveEmployee(employeeForm)">
8.
At this point, if you click the "Save" button without typing anything in the Full
Name field, the native browser validation kicks in and you will see "Please fill in
this field" validation error. At the moment I am using Google Chrome browser.
131

9.
Now if you view the same page in a different browser like Firefox for example, you
will have a different experience. The browser validation is displayed in Firefox as
shown below.

10.
Because of this inconsistency it is better to disable browser native validation and
use Angular instead to validate form fields. We will discuss validaing form fields
using Angular in our upcoming videos.
11.
By default, browser built-in validation is disabled if you are using Angular 4 or any
later version.
12.
At the moment, we are using Angular version 5 and want to keep browser
validation disabled, so remove the ngNativeValidate directive from the form tag.
13.
If you are using Angular 2, you will have to explicitly disable browser validation by
using novalidate attribute on the form tag.
132

Why is it better to disable browser built-in validation and use Angular to validate instead
Different browser vendors implement browser validation differently and as a result, the
end users have different experience depending on the browser they use. Because of this
inconsistency it is better to disable browser native validation and use Angular instead to
validate form fields.

Next video : We will discuss validating form fields using Angular

In this video and in the next few videos we will discuss Form Validation in Angular with
examples. Along the way we will discuss validating textboxes, check boxes, radio
buttons, dropdownlists etc. We will also discuss, how to fix one of the common error that
we get when exporting NgModel into a local variable. The error that we get is, cannot
assign to a reference or variable. We will discuss what causes this error and how to fix it.

To understand validation in Angular, we need to understand the following 3 sets of


properties in Angular.

touched pristine valid


untouched dirty invalid

These 6 properties are available at each individual form control level and also at the form
level. Let's look at these properties in action at an individual form control level. Consider
the following "Full Name" field on our CreateEmployee page.

<input id="fullName" required type="text" class="form-control" name="fullName"


[(ngModel)]="fullName" #fullName="ngModel">

Please note :

● We have made the Full Name input field required by including required attribute
on the input field. required is HTML 5 attribute. This attribute specifies that a field
is required. Besides required, there are other HTML 5 validation attributes like
maxlength, pattern, min, max etc. The following page has the HTML 5 validation
133

attributes list.https://developer.mozilla.org/en-
US/docs/Web/Guide/HTML/HTML5/Constraint_validation

We will discuss most of these attributes with examples, in our upcoming videos.
Angular uses these HTML 5 validation attributes, for validating form fields and
displaying meaningful error messages to the end user.

We are exporting NgModel into a local variable called fullName. To do this we are
using #fullName="ngModel". This variable fullName is called with different names
- local variable, template variable and template reference variable.

At this point if you view the page in the browse you will see the following
errorCannot assign to a reference or variable!

We get this error because, Angular generated form model creates a property with
name "fullName" and we are also creating a local reference variable with the same
name by exporting ngModel to #fullName. Hence we get the error - Cannot assign
to a reference or variable.

One way to fix this error is, by giving our local template reference variable a
different name other than fullName. So if we change #fullName="ngModel" to
#fullNameControl="noModel" the error goes away.

The other way to fix this error is by using our own model instead of using the
Angular auto generated form model. Notice we have our own employee model in
employee.model.ts file in models folder. We will discuss using our own employee
model in our upcoming videos. For now let's continue discussing the validation
properties provided by Angular.

Include the following HTML table, just after the Full Name field in create-
employee.component.html. Notice we are using the local template variable
fullNameControl to access the 6 validation properties provided by Angular

<table border=1 style="border-collapse:collapse; font-family:Arial; table-layout: fixed">


<tr style="background-color:rgb(170, 120, 12); font-weight: bold">
<td colspan="3" style="padding:3px; white-space:nowrap; width:100%">
<h4>Full Name Field</h4>
134

</td>
</tr>
<tr style="background-color:rgb(212, 149, 13); font-weight: bold">
<td style="padding:10px; white-space:nowrap; width:33%">
<div>touched : {{ fullNameControl.touched }}</div>
<div>untouched : {{ fullNameControl.untouched }}</div>
</td>
<td style="padding:10px; white-space:nowrap; width:33%">
<div>pristine : {{ fullNameControl.pristine }}</div>
<div>dirty : {{ fullNameControl.dirty }}</div>
</td>
<td style="padding:10px; white-space:nowrap; width:33%">
<div>valid : {{ fullNameControl.valid }}</div>
<div>invalid : {{ fullNameControl.invalid }}</div>
</td>
</tr>
</table>

At this point, view the page in the browser and notice the following

● touched is false and untouched is true as we did not touch the Full Name field yet.
Notice when we click in the Full Name field, touched is still false and untouched is
still true. These 2 properties change when the field loses focus. So when we tab
out of the control, notice both the properties change as expected.


Notice the pristine and dirty properties. pristine means the form control value has
not been changed and dirty means the value has been changed. Notice when we
135

type something in the Full Name field the properties change as expected. Even
after we delete everything we typed, the dirty property remains true because we
have changed the value in the form control.

Finally notice valid and invalid properties. Since we have required attribute on the
Full Name field, valid is false, if we do not have anything typed in the field. The
moment we type something, valid is true and invalid is false. If we delete
everything we have typed, valid becomes false and invalid becomes true as
expected.

We also have these same 6 properties at the form level. Notice the form tag, we are
already exporting ngForm to a local template variable - employeeForm.

<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employeeForm)">

We can use this template reference variable employeeForm to access the validation
properties at the form level. Copy and paste the following HTML in create-
employee.component.html

<table border=1 style="border-collapse:collapse; font-family:Arial; table-layout: fixed">


<tr style="background-color:silver; font-weight: bold">
<td colspan="3" style="padding:3px; white-space:nowrap; width:100%">
<h4>Employee Form</h4>
</td>
</tr>
<tr style="background-color:silver; font-weight: bold">
<td style="padding:10px; white-space:nowrap; width:33%">
<div>touched : {{ employeeForm.touched }}</div>
<div>untouched : {{ employeeForm.untouched }}</div>
</td>
<td style="padding:10px; white-space:nowrap; width:33%">
<div>pristine : {{ employeeForm.pristine }}</div>
<div>dirty : {{ employeeForm.dirty }}</div>
</td>
<td style="padding:10px; white-space:nowrap; width:33%">
<div>valid : {{ employeeForm.valid }}</div>
<div>invalid : {{ employeeForm.invalid }}</div>
</td>
</tr>
136

</table>

Also, make the Email input field required by placing the required attribute

<input id="email" required type="text" class="form-control"


name="email" [(ngModel)]="email">

Save the changes and view the page in the browser.

● Notice when we type "Venkat" in the Full Name input field,


● touched property both at the input field level and form level is set to true
● untouched property both at the input field level and form level is set to false
● Same is true for dirty and pristine properties
● However, notice valid property of the Full Name field is true but the form valid
property is false. This is because, Emal field is also required and we did not enter
anything in the email field, so email field is invalid and as a result the form is
invalid.
137

● The moment we type something in the email field, the form valid property turns
true.
● So this brings us to an important conclusion - If all the form fields are valid, then
the form is valid. If any of the form field is invalid the form is also invalid.
● Along the same lines, if any of the form field is touched, the form is also marked
touched and if any of the form field is dirty the form is also marked dirty.

So to check if a form field is valid

● Include the HTML 5 validation attributes such as required for example on the input
field you want to validate.
● Export ngModel directive to local template variable
● Finally use the template reference variable to access the validation properties like
touched, dirty, valid etc.

Along the same lines, to check if the form is valid

● Export ngForm directive to a local template reference variable


● Then use the template reference variable to access the validation properties at the
form level.

At the moment, we are not displaying any validation error messages to the user. We will
discuss how to do this in our next video.

On our form, we do not want to display the validation properties and their values. So to
keep out form clean, please remove the HTML from the form, that displays the validation
properties and their values.
138

In this video we will discuss

● How to display validation error messages to the user


● Style the error messages using Bootstrap
● How to disable Submit button if the form is not valid
139

This is continuation to Part 15. Please watch Part 15 from Angular CRUD tutorial before
proceeding.

Here is what we want to do. If the "Full Name" field is not valid we want to style the input field
with a red border. The associated label text should also turn red and "Full Name is required"
validation error message should be displayed.

Once we type something in the Full Name field, and when it becomes valid, the validation
message and the red broder should disappear and also the label text should return to it's normal
black colourcolour.

We will be using the Bootstrap framework for styling validation error messages. If you are new
to Bootstrap, please check out our Bootstrap tutorial by clicking here.

We discussed Bootstrap form validation states in Part 23 of Bootstrap tutorial. We will use the
following Bootstrap classes for styling validation error messages.

● has-error
● control-label
● help-block

Modify the "Full name" input filed as shown below.

<div class="form-group" [class.has-error]="fullNameControl.invalid">


<label for="fullName" class="control-label">Full Name</label>
<input id="fullName" required type="text" class="form-control" name="fullName"
140

[(ngModel)]="fullName" #fullNameControl="ngModel">
<span class="help-block" *ngIf="fullNameControl.invalid">
Full Name is required
</span>
</div>

Code explanation :

● [class.has-error]="fullNameControl.invalid. This is class binding in angular. If the invalid


property returns true, then the Bootstrap class has-error is added to the div element, if it
is false then the class is removed.

On the "Full Name" label element we applied control-label Bootstrap class. This class
turns the label text to red if there is a validation error.

*ngIf="fullNameControl.invalid". The *ngIf structural directive on the span element adds
or removes the validation error message depending on the invalid property value. If the
invalid property is true, then the validation error message is displayed, otherwise it is
removed. Also, notice we are using the Bootstrap help-block class on the span element
for styling.

At this point, save the changes and view the page in the browser. Notice when the form initially
loads, we see the validation error message Full Name is required and it is also styled as
expected. As we soon as we start typing, the error goes away. When we delete everything that
we have typed, the error appears again. So, it's working as expected.

Let's enhance this a bit more. Some users does not like to see the validation error
messages, even before they had the opportunity to touch the form field. So what we want
to do is,

● Do not display any validation error messages when the form is initially loaded.
● When the user touches the field, and if he leaves the field without typing in the value,
then we want to display the validation error message.

This is easy. You might have already guessed we could use touched property to achieve this.
So modify the Full Name field HTML as shown below. With this change, the validation error
message is displayed only when the Full Name field is invalid and touched.

<div class="form-group"
[class.has-error]="fullNameControl.invalid && fullNameControl.touched">
141

<label for="fullName" class="control-label">Full Name</label>


<input id="fullName" required type="text" class="form-control" name="fullName"
[(ngModel)]="fullName" #fullNameControl="ngModel">
<span class="help-block"
*ngIf="fullNameControl.invalid && fullNameControl.touched">
Full Name is required
</span>
</div>

To take this to the next level, we can style a valid field with a different colour. Here is what I
mean.

● When the form first loads, the Full Name and it's label are black in colour and the
validation error message is not displayed
● When the user touches the field and leaves it without typing anything, the colour
changes to red and the validation error message is displayed
● If the user types something, the field is valid, so we want a green border and the label
text should also turn green.

To achieve this we can use the Bootstrap has-success class as shown below. As you can see,
the has-success class is added when valid property is true and it is removed when it is false.

<div class="form-group"
[class.has-error]="fullNameControl.invalid && fullNameControl.touched"
[class.has-success]="fullNameControl.valid">

As you can see these angular validation properties (valid, touched, dirty etc.) provide lot of
power and flexibility when validating form fields and displaying validation error messages.

How to disable Submit button if the form is not valid : To disable the "Save" button when
the form is not valid, bind the invalid property of the employeeForm template variable to the
disabled property of the button.

<button class="btn btn-primary" type="submit"


142

[disabled]="employeeForm.invalid">Save</button>
143

n this video we will discuss

● Binding Angular form to our own model class


● We will also discuss, how to fix one of the common error that we get when exporting
ngModel into a local variable. The error that we get is, cannot assign to a reference or
variable.

At the moment, in CreateEmployeeComponent we are using the Angular Auto-generated form


model. Instead of using the Angular generated form model, we can use our model class.

In employee.model.ts file in the models folder, we have Employee class. We want to use this
class as the model when creating a new employee. Here are the steps.

Step 1 : In create-employee.component.ts file, import the Employee model


import { Employee } from '../models/employee.model';

Step 2 : In CreateEmployeeComponent class, include employee property. Notice we have set


the type to Employee and initialised all properties with NULL value.

export class CreateEmployeeComponent implements OnInit {


144

employee: Employee = {
id: null,
name: null,
gender: null,
contactPreference: null,
phoneNumber: null,
email: null,
dateOfBirth: null,
department: null,
isActive: null,
photoPath: null
};

Step 3 : In the view template, bind the ngModel directive of an input field to it's corresponding
property on the employee object. The employee property we created in Step 2 returns an
employee object, which is the model for our form.

For example, bind ngModel directive on the email input field to the email property on the
employee object.
[(ngModel)]="employee.email"

Except fullName, bind the ngModel directive of the rest of the input fields with the corresponding
properties on the employee object.

In the employee class we do not have fullName property. we have name instead. On the view
template, the corresponding input field name is fullName. To keep things consistent let's change
fullName to name on the label and the input field as shown below.

<div class="form-group" [class.has-error]="name.invalid && name.touched">


<label for="name" class="control-label">Name</label>
<input id="name" required type="text" class="form-control" name="name"
[(ngModel)]="name" #name="ngModel">
<span class="help-block" *ngIf="name.invalid && name.touched">
Name is required
</span>
</div>

At this point, if you view the page in the browser, you will see the following error.
Cannot assign to a reference or variable

We get this error because, Angular generated form model creates name property and we are
also creating a local template variable with the same name by exporting ngModel to #name.
Hence we get the error - Cannot assign to a reference or variable.
145

One way to fix this error is, by giving our local template reference variable a different name
other than name. So if we change #name="ngModel" to #nameControl="noModel" the error
goes away. We discussed this in detail in Part 15 of Angular CRUD tutorial.

The other way to fix this error is by using our own model. Using the ngModel directive, bind the
name property of the employee object to the name input field

[(ngModel)]="employee.name"

At this point, if you view the page in the browser and notice the error is gone and all the
properties in the Angular generated form model are NULL as expected.

To see our own employee model, include the following code in the view template file (create-
employee.component.html)

Angular Generated Forom Model : {{employeeForm.value | json}}


<br/>
<br/>
Our Employee Model : {{ employee | json}}

At this point, on the browser we should see both - Angular generated form model and our own
employee model. Notice as we change the values in the input fields, the respective properties in
both the models are updated as expected.

At the moment, when we click the "Save" button, we are logging the employeeForm.value to
the console. We instead want to log our employee model object. To do this
In the view template, pass the employee object to the saveEmployee() method.

<form #employeeForm="ngForm" (ngSubmit)="saveEmployee(employee)">

Modify saveEmployee() method in create-employee.component.ts file as shown below.

saveEmployee(newEmployee: Employee): void {


console.log(newEmployee);
}

At this point, when we click the Save button, the employee object is logged to the console as
expected.

In this video we will discuss

● Validating Email form field in Angular


146

● Using multiple validators on a single input field


● Angular safe navigation operator

Email validation in Angular : There are 2 different ways to validate email form field in Angular.
We can either use pattern validator or email validator. Email validator is introduced in Angular
4. So if you are using Angular 4 or later version you may use email validator or pattern validator
to validate email. If you are using Angular 2, then your only choice is to use Pattern validator.

In this video we will discuss using the Angular built-in Email validator and in our next video we
will discuss using the Pattern validator.

Consider the following HTML. Notice we are using Bootstrap classes for styling.

<div class="form-group">
<label for="email">Email</label>
<input id="email" type="text" class="form-control" name="email"
[(ngModel)]="employee.email">
</div>

The above HTML would produce the following Email input field

We want to validate this email input field for 2 things

● Email is required and


● Valid email must be provided

To make email, a required field modify the HTML as shown below

<div class="form-group" [class.has-error]="email.invalid && email.touched">


<label for="email" class="control-label">Email</label>
<input id="email" required type="text" class="form-control" name="email"
[(ngModel)]="employee.email" #email="ngModel">
<span class="help-block" *ngIf="email.invalid && email.touched">
Email is required
</span>
</div>
147

Code Explanation :

● [class.has-error]="email.invalid && email.touched". This is class binding in angular. If the


email field is touched and invalid, then the Bootstrap class has-error is added to the div
element, else the class is removed.
● On the label that displays "Email" text, we applied control-label Bootstrap class. This
class turns the label text to red if there is a validation error.
● *ngIf="email.invalid && email.touched". Notice the *ngIf structural directive on the span
element. If the email field is touched and invalid the span element is added to the DOM,
else it is removed. The Bootstrap help-block class on the span element is for styling.

At this point, if you touch the email field and leave it without typing in anything, you will see the
validation error message "Email is required"

We also want to make sure the user enters a valid email. If someone types ABC, that is not a
valid email. Angular 4 has built-in email validator, that we can use to validate if the user has
entered a valid email. Here are the steps.

Step 1 : On the email input field, place the email directive

<input id="email" required email

Step 2 : Use the following HTML, to display the validation error message. If the email is invalid,
angular attaches email key to the errors collection. On the other hand, if the email field is valid,
the key email will not be in the errors collection. The question mark here is called the safe
navigation operator. It protects against null and undefined values in property paths. It is
generally used when we are not sure if a property exists or not. It safely handles null and
undefined values, and very useful to prevent null-reference exceptions.

<span class="help-block" *ngIf="email.errors?.email && email.touched">


Email is Invalid
</span>

Here is the complete HTML that makes the email filed required and also checks if the email has
a valid format
148

<div class="form-group" [class.has-error]="email.invalid && email.touched">


<label for="email" class="control-label">Email</label>
<input id="email" required email type="text" class="form-control" name="email"
[(ngModel)]="employee.email" #email="ngModel">
<span class="help-block" *ngIf="email.errors?.required && email.touched">
Email is required
</span>
<span class="help-block" *ngIf="email.errors?.email && email.touched">
Email is Invalid
</span>
</div>

As of this recording, email validator provided by Angular does not allow null or empty values.
When we leave the email field empty, the email validator is still fired. This is wrong. Checking
NULL and empty values should be the job of the required validator. The following is the work
around.

Bind email directive to a boolean expression. The email validator is only added when the email
field value is not an empty string. This ensures that, when we type something in the email field,
the email validator is attached to the input field and it validates if the email format is valid or not.

<input id="email" required [email]="employee.email!==''"

Please note : Do not forget to initialise the email property in the employee object to an empty
string.
149

In this video we will discuss using pattern validator in angular to meet most of your application
complex validation requirements.

With the pattern validator we use a regular expression. Regular expressions are extremely
useful when you want to validate if a given string conforms to a specified pattern.

For example, you can use regular expressions to check if a given email conforms to a a valid
email format. Similarly you can also check if provided postcode conforms to a specific country
postcode format.

Apart from checking conformity with a pattern, they can also be used to extract sub-strings from
a given input string.

To validate if the provided email has a valid email pattern we can use the pattern validator in
angular. To use the pattern validator use the pattern attribute along with the regular expression
on the input field you want to validate.

<input pattern="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$" type="text"


name="email" [(ngModel)]="employee.email" #email="ngModel">

It is easy to learn regular expressions. Initially they may appear complicated, but if you get the
basics right it is very easy to understand them. However, you can also find the commonly used
150

regular expressions on the internet. For example, if you want to find a regular expression to
validate email address, simply search the internet with the following string
Regular expression for email validation

Use the following HTML, to display the validation error message. If the email is invalid, angular
attaches pattern key to the errors collection. On the other hand, if the email field is valid, the key
pattern will not be in the errors collection. The question mark here is called the safe navigation
operator. We discussed this operator in detail in our previous video. If you are new to this
operator, please check out our previous video.

<span class="help-block" *ngIf="email.errors?.pattern && email.touched">


Email is Invalid
</span>

The following example, shows both required and pattern validators on the Email input field.

<div class="form-group" [class.has-error]="email.invalid && email.touched">


<label for="email" class="control-label">Email</label>
<input required pattern="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
id="email" type="text" class="form-control" name="email"
[(ngModel)]="employee.email" #email="ngModel">
<span class="help-block" *ngIf="email.errors?.required && email.touched">
Email is required
</span>
<span class="help-block" *ngIf="email.errors?.pattern && email.touched">
Email is Invalid
</span>
</div>

Let's take this pattern validation to the next level. I want to validate emails against a specific
domain. For example pragimtech.com is the only valid domain that I want to allow. Any other
domain should be considered invalid. This can be very easily achieved with the following regular
expression.

^[a-zA-Z0-9_.+-]+@(?:(?:[a-zA-Z0-9-]+\.)?[a-zA-Z]+\.)?(pragimtech)\.com$
151

In this video we will discuss radio button validation in Angular with example.

Example : Gender is a required field. If one of the gender radio button is not checked, we want
to validate and display "Gender is required" validation error message.

As soon as one of the "Gender" radio button is selected, the validation error message should
disappear.

Here is the HTML that makes this possible.

<div class="form-group" [class.has-error]="gender.invalid">


<label class="control-label">Gender</label>
<div class="form-control">
152

<label class="radio-inline">
<input type="radio" name="gender" required #gender="ngModel"
value="male" [(ngModel)]="employee.gender"> Male
</label>
<label class="radio-inline">
<input type="radio" name="gender" required #gender="ngModel"
value="female" [(ngModel)]="employee.gender"> Female
</label>
</div>
<span class="help-block" *ngIf="gender.invalid">
Gender is required
</span>
</div>

Code Explanation :

● Notice we have required attribute on both the radio buttons (Male and Female). This
attribute makes the "Gender" field required.
● #gender="ngModel". This creates a template reference variable. We can now this
variable (gender) to check if the field is invalid. Notice #gender="ngModel" is placed on
the both the radio buttons.
● [class.has-error]="gender.invalid". This class binding adds the has-error bootstrap css
class when the field is invalid and removes it when the field is valid. This class is used
for styling the validation error messages.
● On the label element that displays the static text "Gender" we have "control-label" class.
This class turns the text "Gender" to red when there is a validation error.
● *ngIf="gender.invalid". Notice the *ngIf structural directive on the span element. If the
gender field is invalid the span element is added to the DOM, else it is removed. The
Bootstrap help-block class on the span element is for styling.

The span element that displays the validation error message can also be coded as shown
below. Notice, instead of using "gender.invalid" as the expression for *ngIf, we are using
"gender.errors?.required". When the required validation fails, Angular attaches the required key
to the errors collection property of the gender field. The key is removed if the field passes
validation. So we can check for the existence of this key, to control the display of the validation
error message.

<span class="help-block" *ngIf="gender.errors?.required">


Gender is required
</span>
153

n this video we will discuss, how to add required attribute dynamically in template driven
forms. In our upcoming videos we will discuss how to do the same in reactive forms.

Example : Consider the following 3 fields.

If "Email" is selected as the "Contact Preference", then "Email" input field is required.
154

If "Phone" is selected as the "Contact Preference", then "Phone" input field is required.

Contact Preference radio buttons HTML :


<div class="form-group" [class.has-error]="contactPreference.invalid">
<label class="control-label">Contact Preference</label>
<div class="form-control">
<label class="radio-inline">
<input type="radio" required #contactPreference="ngModel" name="contactPreference"
value="email" [(ngModel)]="employee.contactPreference"> Email
</label>
<label class="radio-inline">
<input type="radio" required #contactPreference="ngModel" name="contactPreference"
value="phone" [(ngModel)]="employee.contactPreference"> Phone
</label>
</div>
<span class="help-block" *ngIf="contactPreference.errors?.required">
155

Contact Preference is required


</span>
<!-- Delete the below line after you see
the selected value in the browser -->
Contact Preference Selected Value : {{ contactPreference.value }}
</div>

Code Explanation :

● We made the contact preference field required, by including required attribute on both
email and phone radio buttons.
● At this point view the page in the browser and notice the selected radio button value.
When email radio button is selected we see the selected value as email, and when
phone radio button is selected, we see the selected value as phone.
● We will use this selected contact preference radio button value
(contactPreference.value) to dynamically add or remove required attribute to phone and
email input fields.

Email input field HTML :

<div class="form-group" [class.has-error]="email.invalid">


<label for="email" class="control-label">Email</label>
<input id="email" [required]="contactPreference.value=='email'"
type="text" class="form-control"
pattern="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
[(ngModel)]="employee.email" #email="ngModel" name="email">
<span class="help-block" *ngIf="email.errors?.required">
Email is required
</span>
<span class="help-block" *ngIf="email.errors?.pattern && email.touched">
Email is Invalid
</span>
</div>

Code explanation :

● Notice the required attribute. We assigned it a boolean expression


(contactPreference.value=='email').
● This boolean expression dynamically adds or removes required attribute to the email
field depending on whether email contact preference radio button is selected or not.
156

Phone Number input field HTML : Notice the boolean expression assigned to the required
attribute. This boolean expression dynamically adds or removes required attribute to the phone
number field depending on whether phone contact preference radio button is selected or not.

<div class="form-group" [class.has-error]="phoneNumber.invalid">


<label for="phoneNumber" class="control-label">Phone Number</label>
<input id="phoneNumber" [required]="contactPreference.value=='phone'"
#phoneNumber="ngModel" class="form-control" type="text"
name="phoneNumber" [(ngModel)]="employee.phoneNumber">
<span class="help-block" *ngIf="phoneNumber.errors?.required">
Phone Number is required
</span>
</div>

n this video we will discuss checkbox validation in Angular with example.

We want to make the following "Is Active" check box a required field. If the checkbox is not
checked, we want to validate and display "Is Active is required" validation error message. As
soon as the checkbox is checked, the validation error message should disappear.
157

Consider the following HTML

<div class="form-group" [class.has-error]="isActive.invalid && isActive.touched">


<div class="form-control">
<label class="checkbox-inline control-label">
<input type="checkbox" required name="isActive"
#isActive="ngModel" [(ngModel)]="employee.isActive">
Is Active
</label>
</div>
<span class="help-block"
*ngIf="isActive.errors?.required && isActive.touched">
Is Active is required
</span>
</div>

Code Explanation :

● The required attribute makes "Is Active" field required.


● #isActive="ngModel". This creates a template reference variable. We can now this
variable (isActive) to check if the field is invalid, touched, dirty etc.
● [class.has-error]="isActive.invalid && isActive.touched". This class binding adds the has-
error bootstrap css class when the field is invalid and touched and removes it when the
field is valid. This class is used for styling the validation error messages.
● On the label element that displays the static text "Is Active" we have "control-label" class.
This class turns the text "Is Active" to red when there is a validation error.
● *ngIf="isActive.errors?.required && isActive.touched". Notice the *ngIf structural directive
on the span element. If the "Is Active" field fails required validation and touched, the
span element is added to the DOM, else it is removed. The Bootstrap help-block class
on the span element is for styling.

At this point,
158

● If you tab into the checkbox control and leave it, without checking it, you will see the
validation error message
● If you select the checkbox box, the error goes away
● If you unselecet the checkbox, the required validation error appears again

This implementation of the checkbox validation is useful, when you want to force the user to
select a checkbox. For example, on many web sites, you might have seen a checkbox with the
following text. Only when you agree by checking that checkbox, you will be able to proceed.
Otherwise you will have to cancel that specific action.
I Agree to the terms and conditions

What if the employee is terminated or resigned? In that case we do not want the checkbox
to be checked. But at the moment, the required validator is forcing us to have the checkbox
checked. To fix this modify the required attribute as shown below. Notice, we are binding a
boolean expression to the required attribute. If the expression is true the required validator is
attached, otherwise it is removed.

[required]="employee.isActive==null"

With this change

● When the form first loads, isActive property on the employee object is null. So the
required attribute is attached to the checkbox.
● If we tab into the checkbox and levae it without selecting it, we see the required
validation error message as expected
● If we select the checkbox box, the error goes away
● If we unselecet the checkbox, notice we don't get the required validation. This is
because, when the checkbox is unchecked, the value of isActive property on the
employee object is false and not NULL. So the boolean expression bound to the
required attribute returns false. Hence the required attribute is removed from the
checkbox field and we do not see the required validation error.

At the moment, the user interface is confusing. If the employee you are creating is not active,
you have to first check the (Is Active) checkbox and then un-check it. To make this less
confusing there 2 options for us.

● Remove the required validator on the (Is Active) checkbox, and treat NULL as false.
● Use 2 radio buttons (Yes or No), instead of a single checkbox.
159

In this video, we will discuss Dropdown list validation in Angular with example.

Example : We want to make "Department" Dropdownlist a required field. If a department is not


selected, we want to validate and display "Department is required" validation error message. As
soon as a department is checked, the validation error message should disappear.
160

Consider the following HTML :

<div class="form-group"

[class.has-error]="department.touched && department.invalid">

<label for="department" class="control-label">Department</label>

<select id="department" required #department="ngModel"

name="department" [(ngModel)]="employee.department"

class="form-control">

<option *ngFor="let dept of departments" [value]="dept.id">

{{dept.name}}

</option>

</select>

<span class="help-block"

*ngIf="department.touched && department.invalid">

Department is required

</span>

</div>

Code Explanation :

● The required attribute makes "Department" field required.


161

● #department="ngModel". This creates a template reference variable. We can now this


variable (department) to check if the field is invalid, touched, dirty etc.
● [class.has-error]="department.touched && department.invalid". This class binding adds
the has-error bootstrap css class when the field is invalid and touched and removes it
when the field is valid. This class is used for styling the validation error messages.
● On the label element that displays the static text "Department" we have "control-label"
class. This class turns the text "Department" to red when there is a validation error.
● *ngIf="department.touched && department.invalid". Notice the *ngIf structural directive
on the span element. If the "Department" field fails required validation and touched, the
span element is added to the DOM, else it is removed. The Bootstrap help-block class
on the span element is for styling.

At this point, the dropdown list validation works as expected. However, in most of the real world
applications, you might see one of the following options as the first option in a dropdown list.

● Please select
● Select Department
● etc...

Modify the HTML to include "Select Department" as the first option. Notice the value of this
option is set to '-1', to indicate that it is not a valid department selection. The change is
highlighted in YELLOW.

<div class="form-group"

[class.has-error]="department.touched && department.invalid">

<label for="department" class="control-label">

Department

</label>

<select id="department" required #department="ngModel" name="department"

[(ngModel)]="employee.department" class="form-control">
162

<option value="-1">Select Department</option>

<option *ngFor="let dept of departments" [value]="dept.id">

{{dept.name}}

</option>

</select>

<span class="help-block"

*ngIf="department.touched && department.invalid">

Department is required

</span>

</div>

In the component class (create-employee.component.ts), initialise department property with a


value of '-1'. This will ensure that, when the "Department" dropdownlist is loaded, the first
default option 'Select Department' is selected.

employee: Employee = {

id: null,

name: null,

gender: null,

contactPreference: null,

phoneNumber: null,
163

email: '',

dateOfBirth: null,

department: '-1',

isActive: null,

photoPath: null

};

At this point, view the page in the browser. The dropdownlist REQUIRED validation does not
work as expected. The default first option, 'Select Department' is treated as a valid department
selection. We will discuss, how to fix this in our next video.
164

In this video we will discuss how to validate a select element if it has a custom option like one of
the following.

● Please Select
● Select Department
● Etc...

Consider the following example :

<div class="form-group"

[class.has-error]="department.touched && department.invalid">

<label for="department" class="control-label">Department</label>

<select required #department="ngModel" name="department"

[(ngModel)]="employee.department" id="department"

class="form-control">

<option [ngValue]="null">Select Department</option>

<option *ngFor="let dept of departments" [value]="dept.id">

{{dept.name}}

</option>

</select>

<span class="help-block"

*ngIf="department.touched && department.errors?.required">


165

Department is required

</span>

</div>

Code explanation :

● <option [ngValue]="null">Select Department</option>. Notice we are using ngValue


instead of value. If you use value, null is treated as a string and not as a null. Hence the
required validation does not work. Along with using ngValue, also make sure you set the
department property on the employee model object to null.
● <option *ngFor="let dept of departments" [value]="dept.id">{{dept.name}}</option>. Here
we are using value instead of ngValue, because we just want the selected department id
as a string. If you want the department object itself instead of just the department id
string, then use ngValue.
● <option *ngFor="let dept of departments" [ngValue]="dept">{{dept.name}}</option>. In
this example we are using ngValue and binding it to the dept object. If we select a
department now, we get the selected department object.
"department": { "id": 3, "name": "IT" }
● Use the disabled attribute, if you do not want the user to be able to select the "Select
Department" option.
<option disabled [ngValue]="null">Select Department</option>

Please note : The built-in required validator will only work with the SELECT element, if the
default option value is null.

<option disabled [ngValue]="null">Select Department</option>

In a real world application, most of the time we load the SELECT list options from a database
table. In some case we may also load the default option (like SELECT DEPARTMENT etc) also
from the database. In this case the default option value may not be NULL. Depending on your
166

use case it could be -1, or SELECT or something else. So in scenarios like this, built-in required
validator does not work with the SELECT element. To make it work, we have to implement our
own custom required validator. We will discuss how to do this in our next video.

In this video, we will discuss creating a custom validator in angular with an example. We will
make this validator reusable and configurable. Along the way, we will also learn how to create a
custom directive in angular.

Example : When you have a default option like "Select Department" in a SELECT list, the
REQUIRED validation does not work, if the default option value is anything else other than
NULL. So if the default option value is not null, then that default option is also treated as a valid
selection, and we do not get to see the required validation error. If "Select Department" is
selected, we want the validation to fail, and display "Department is required" validation error
message. To make this work the way we want, we implement a custom validator.

Create a custom Directive

To use a custom validator in template driven forms, we have to create the validator as a
directive. Once we have the directive created, we can then use that directive as an attribute on
the select element that we want to validate. This is going to be a configurable and reusable
validator. We can use it with any SELECT list in an angular application. So create a "shared"
167

folder in the "app" folder. In the "shared" folder create a file with name "select-required-
validator.directive.ts". Copy and paste the folllowing code.

import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';

import { Directive } from '@angular/core';

@Directive({

selector: '[appSelectValidator]',

providers: [

provide: NG_VALIDATORS,

useExisting: SelectRequiredValidatorDirective,

multi: true

}]

})

export class SelectRequiredValidatorDirective implements Validator {

validate(control: AbstractControl): { [key: string]: any } | null {

return control.value === '-1' ? { 'defaultSelected': true } : null;

}
168

Code Exaplanation :

Since we are creating a directive, we decorate the class with @Directive decorator

NG_VALIDATORS is a collection of validators. It already contains all the built-in validators like
required, pattern etc. Before we can use our custom validator we have to add it to the list of
validators by adding it to NG_VALIDATORS token. To specify that we want to add our validator
to the list of validators, we set multi property to true

providers: [

provide: NG_VALIDATORS,

useExisting: SelectRequiredValidatorDirective,

multi: true

}]

Implement Validator interface as we are creating a custom validator

export class SelectRequiredValidatorDirective implements Validator

Since we are implementing validator interface, we have to provide implementation for the
interface validate() method. This method has one input parameter and it's type is
AbstractControl. AbstractControl extends both FormControl and FormGroup. In some cases
you may want to validate a Formgroup instead of a single FormControl. So to cater for both
scenarios, the parent type - AbstractControl is specified. This function returns an object if the
169

validation fails or null if the validation succeeds. The object that is returned when the validation
fails contains a key/value pair. The key is a string and the value can be anything.

validate(control: AbstractControl): { [key: string]: any } | null

If the selected value in the SELECT list is the default value (-1), then we return an object with
key 'defaultSelected' and value of true to indicate that the validation has failed. Otherwise we
return NULL to indicate validation succeeded. In the HTML we can use the "defaultSelected"
key to display the validation error specific to this custom validator.

return control.value === '-1' ? { 'defaultSelected': true } : null;

Import the custom directive in a module where you want to use it.

At the moment we only have one module - Root module. So in app.module.ts file include the
following import statement

import { SelectRequiredValidatorDirective } from './shared/select-required-validator.directive';

Also include SelectRequiredValidatorDirective in the declarations array of the NgModule()


decorator

Using the custom required validator on the SELECT element

Modify the "Department" SELECT element in create-employee.component.html file as shown


below.
170

<div class="form-group"

[class.has-error]="department.touched && department.errors?.defaultSelected">

<label for="department" class="control-label">Department</label>

<select id="department" #department="ngModel" name="department"

[(ngModel)]="employee.department" appSelectValidator

class="form-control">

<option value="-1">Select Department</option>

<option *ngFor="let dept of departments" [value]="dept.id">

{{dept.name}}

</option>

</select>

<span class="help-block"

*ngIf="department.touched && department.errors?.defaultSelected">

Department is required

</span>

</div>

Code Explanation :

● [class.has-error]="department.touched && department.errors?.defaultSelected". Notice,


in this conditional class binding we are using the key "defaultSelected" to style the
171

Department SELECT element and it's label when there is a validation error. This key is
set by the required custom validator when the validation fails.
● *ngIf="department.touched && department.errors?.defaultSelected". Notice here also we
are using the key "defaultSelected" to display the validation error message.
● appSelectValidator is the selector we gave for our custom validation directive and we are
using it as an attribute on the SELECT list that we want to validate.

At the moment, we can only use this custom validator with a SELECT list whose default option
value is -1. We will discuss how to make this custom validator configurable and reusable in our
next video.

In this video we will discuss, how to make the select list custom required validator
configurable and reusable. This is continuation to Part 25. Please watch Part 25 from Angular
CRUD tutorial before proceeding.

Here is the SELECT list custom required validator we implemented in our previous video
172

import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';

import { Directive } from '@angular/core';

@Directive({

selector: '[appSelectValidator]',

providers: [{

provide: NG_VALIDATORS,

useExisting: SelectRequiredValidatorDirective,

multi: true

}]

})

export class SelectRequiredValidatorDirective implements Validator {

validate(control: AbstractControl): { [key: string]: any } | null {

return control.value === '-1' ? { 'defaultSelected': true } : null;

Consider this line of code : Notice we have hard-coded the default option value '-1'. Because
of this hard-coded value, we will not be able to reuse this validator with another SELECT list if it
has a different default option value other than '-1'.
173

return control.value === '-1' ? { 'defaultSelected': true } : null;

To make this custom validator reusable, we want to be able to do pass the default option value
from the template to our custom validator as shown below. Notice we are using our custom
validator selector and passing it the default option value. In this case we are passing -101. If you
have another SELECT list, and if it's default option value is -1, you simply pass that value.

<select appSelectValidator="-101" #department="ngModel" ....>

For this to work, we have to create a corresponding input property in the custom validator class.
Modify SelectRequiredValidatorDirective as shown below. The changes are commented and
self-explanatory

import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';

// Import input from @angular/core package

import { Directive, Input } from '@angular/core';

@Directive({

selector: '[appSelectValidator]',

providers: [{

provide: NG_VALIDATORS,

useExisting: SelectRequiredValidatorDirective,

multi: true
174

}]

})

export class SelectRequiredValidatorDirective implements Validator {

// Create input property to receive the

// specified default option value

@Input() appSelectValidator: string;

validate(control: AbstractControl): { [key: string]: any } | null {

// Remove the hard-coded value and use the input property instead

return control.value === this.appSelectValidator ?

{ 'defaultSelected': true } : null;

Please note : Since this is a directive input property, the input property name and the selector
name must match.

@Input() appSelectValidator: string;

For some reason if you do not like the input property name, you can use an alias as shown
below.

@Input('appSelectValidator') defaultValue: string;


175

We are now able to specify the default option value using the directive input property. This
makes our custom validator configurable and reusable. We can now use this custom required
validator to validate any SELECT list in our Angular application.

n this video we will discuss how to compare password and confirm password fields and
validate if they are equal. If they are not equal we want to display "Password and Confirm
Password does not match" validation error.
176

This is also commonly called as cross field validation in angular. We cannot use any of the
buil-in angular validators to perform cross-field validation. So let's create a custom validator. To
use a custom validator in template driven forms, we create the validator as a directive. We
discussed creating custom validators and directives in Parts 25 and 26 of Angular CRUD
tutorial. If you are new to these concepts, please check out those videos.

We will make this custom validator a reusable validator, so we could use it to compare any 2
input fields for equality. For example, we can use this same custom validator to compare if
EMAIL and CONFIRM EMAIL fields are equal.

Create a custom Directive

Add a new TypeScript file to the "shared" folder. Name it confirm-equal-


validator.directive.ts.Copy and paste the folllowing code.

import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';


177

import { Directive, Input } from '@angular/core';

@Directive({

selector: '[appConfirmEqualValidator]',

providers: [{

provide: NG_VALIDATORS,

useExisting: ConfirmEqualValidatorDirective,

multi: true

}]

})

export class ConfirmEqualValidatorDirective implements Validator {

@Input() appConfirmEqualValidator: string;

validate(control: AbstractControl): { [key: string]: any } | null {

const controlToCompare = control.parent.get(this.appConfirmEqualValidator);

if (controlToCompare && controlToCompare.value !== control.value) {

return { 'notEqual': true };

return null;

}
178

Code Exaplanation :

Since we are creating a directive, we decorate the class with @Directive decorator

This selector will be used as a directive on one of the 2 input fields that we want to compare for
equality. In our case we will use it on the Confirm Password field.

selector: '[appConfirmEqualValidator]',

NG_VALIDATORS is a collection of validators. It contains all the built-in validators like required,
pattern etc. Before we can use our custom validator we have to add it to the list of validators by
adding it to NG_VALIDATORS token. To specify that we want to add our validator to the list of
validators, we set multi property to true

providers: [{

provide: NG_VALIDATORS,

useExisting: ConfirmEqualValidatorDirective,

multi: true

}]

Implement Validator interface as we are creating a custom validator

export class ConfirmEqualValidatorDirective implements Validator


179

Since our custom validator class is implementing validator interface, we have to provide
implementation for the interface validate() method. This method has one input parameter and
it's type is AbstractControl. AbstractControl extends both FormControl and FormGroup. In some
case you may want to validate a Formgroup instead of a single FormControl. So to cater for
both scenarios, the parent type - AbstractControl is specified. This function returns an object if
the validation fails or null if the validation succeeds. The object that is returned when the
validation fails contains a key/value pair. The key is a string and the value can be anything.

validate(control: AbstractControl): { [key: string]: any } | null

The following line creates an input property. Since this is a directive input property, the input
property name and the selector name must match.

@Input() appConfirmEqualValidator: string;

We will use this custom directive (appConfirmEqualValidator), as an attribute either on the


PASSWORD field or CONFIRM PASSWORD FIELD. If we use this on the CONFIRM
PASSWORD field, we will also pass the field that we want to compare with. In this case the
PASSWORD field.

So the input property that we have created above receives the control that we want to compare
CONFIRM PASSWORD field with. This input property prevents the need to hard code the name
of the control that we want to compare with. Hence it makes our custom validator reusable. We
can use it to compare any 2 input fields for equality.

<input name="confirmPassword" appConfirmEqualValidator="password"

● In the validate() method implementation, we first retrieve the control that we want to
compare CONFIRM PASSWORD field with. That field in our case is the PASSWORD
field.
180

● Both PASSWORD and CONFIRM PASSWORD fields are siblings. So to get the
PASSWORD field, we go one level up from the CONFIRM PASSWORD field using the
parent property. The parent property returns the root FormGroup.
● On the root FormGroup we call the get() method passing it, the input property. The input
property receives the name of the PASSWORD field
● Finally we check if the PASSWORD and CONFIRM PASSWORD filed values are equal.
If they are equal, we return NULL indication validation succeeded otherwise we return an
object with key=notEqual and value=true.
● In the HTML we can use this key (notEqual) to display the relevant validation error
message

validate(control: AbstractControl): { [key: string]: any } | null {

const controlToCompare = control.parent.get(this.appConfirmEqualValidator);

if (controlToCompare && controlToCompare.value !== control.value) {

return { 'notEqual': true };

return null;

Import the custom directive in a module where you want to use it.

At the moment we only have one module - Root module. So in app.module.ts file include the
following import statement

import { ConfirmEqualValidatorDirective } from './shared/confirm-equal-validator.directive';


181

Also include ConfirmEqualValidatorDirective in the declarations array of the NgModule()


decorator

Using the custom validator

Include the following HTML for Password and Confirm Password fields in create-
employee.component.html file as shown below.

<div class="form-group"

[class.has-error]="password.touched && password.invalid">

<label for="password" class="control-label">Password</label>

<input id="password" required type="text" class="form-control"

name="password" [(ngModel)]="employee.password"

#password="ngModel">

<span class="help-block"

*ngIf="password.touched && password.errors?.required">

Password is required

</span>

</div>

<div class="form-group"
182

[class.has-error]="confirmPassword.touched && confirmPassword.invalid">

<label for="confirmPassword" class="control-label">Confirm Password</label>

<input name="confirmPassword" appConfirmEqualValidator="password" required

id="confirmPassword" type="text" class="form-control"

[(ngModel)]="employee.confirmPassword" #confirmPassword="ngModel">

<span class="help-block"

*ngIf="confirmPassword.touched && confirmPassword.errors?.required">

Confirm Password is required

</span>

<span class="help-block"

*ngIf="confirmPassword.touched && confirmPassword.errors?.notEqual &&

!confirmPassword.errors?.required">

Password and Confirm Password does not match

</span>

</div>

Notice on the CONFIRM PASSWORD field we are using our custom directive and passing it the
name of the control (PASSWORD) that we want to compare with.

<input name="confirmPassword" appConfirmEqualValidator="password"


183

Notice the expression of *ngIf structural directive. We are using the key (notEqual) set by our
custom validator to display the validation error message.

<span class="help-block"

*ngIf="confirmPassword.touched && confirmPassword.errors?.notEqual &&

!confirmPassword.errors?.required">

Password and Confirm Password does not match

</span>

At the moment there are 2 problems with our custom validator

When the validation fails only the CONFIRM PASSWORD field is styled with red border and not
the PASSWORD field. We want the password field also to have the red border.

If you first change PASSWORD field and then the CONFIRM PASSWORD field, the validation
works as expected. Now if you go back and change the PASSWORD field, the validation will not
be triggered and you will not see the validation error even if the password donot match.
184

In this video we will discuss

1. How to add or remove validation styles to a group of elements in Angular


2. How to trigger validation manually in Angular using the updateValueAndValidity()
function

This is continuation to Part 27. Please watch Part 27 from Angular CRUD tutorial before
proceeding.

How to add and remove validation styles to a group of elements in Angular : Use the
ngModelGroup directive and group the elements. Now we can add or remove validation styles
185

from the group. This in turn adds or removes the validation styles from all the elements in that
group.

In our case, we want to group password and confirm password fields to be able to control
their validation styles. Notice in the example below, both password and confirm password fields
are grouped using the ngModelGroup directive. The bootstrap validation class has-error is
conditionally added or removed from the group.

<div ngModelGroup="passwordGroup"

[class.has-error]="confirmPassword.touched && confirmPassword.invalid">

<div "passwordFieldDiv"> ...

</div>

<div "confirmPasswordFieldDiv"> ...

</div>

</div>

Use of updateValueAndValidity() function : At the moment we have a problem with confirm


password field validation. It does not work in one of the scenarios. Here is the scenario.
186

If you first change PASSWORD field and then the CONFIRM PASSWORD field, the validation
works as expected. Now if you go back and change the PASSWORD field, the validation will not
be triggered and you will not see the validation error even if the passwords do not match.

This is because our custom validation directive is applied on the confirm password filed but not
on the password. So our custom validation is triggered only when the confirm password field is
changed and not when the password field is changed. To make this work, even when the
password field is changed, we have to tell confirm password field to run it's validation when
password field is changed.

So the obvious question that comes to our mind is, how to tell the confirm password field to
run it's validation?

Well updateValueAndValidity() function comes to the rescue. When this method is called on a
control, that control's validation logic is executed again. Notice the event binding in the example
below. The change event of the password field triggers a call to confirm password field's
updateValueAndValidity() function. This in turn runs the confirm password field validation.

<input name="password"

(change)="confirmPassword.control.updateValueAndValidity()" …>

The change event is fired only after the form control has lost focus. The input event is fired as
the user changes the value. So if you want the validation to trigger as the user is changing the
value, use the input event instead of change event.

In this video we will discuss how to validate a group of form controls in Angular. This is
continuation to Part 28. Please watch Part 28 from Angular CRUD tutorial before proceeding.
187

In our previous video we discussed one way to implement confirm password validation. There is
another way as well. We will discuss that in this video.

If the password and confirm password are not equal we want to validate and display "Password
and Confirm Password does not match" validation error as shown below.

HTML used in the demo

<div ngModelGroup="passwordGroup" #passwordGroup="ngModelGroup"

appConfirmEqualValidator [class.has-error]="passwordGroup.errors?.notEqual

&& !confirmPassword.errors?.required">

<div class="form-group"
188

[class.has-error]="password.touched && password.invalid">

<label for="password" class="control-label">Password</label>

<input name="password" required type="text" class="form-control"

[(ngModel)]="employee.password" #password="ngModel">

<span class="help-block"

*ngIf="password.touched && password.errors?.required">

Password is required

</span>

</div>

<div class="form-group"

[class.has-error]="confirmPassword.touched && confirmPassword.invalid">

<label for="confirmPassword" class="control-label">Confirm Password</label>

<input name="confirmPassword" required type="text" class="form-control"

[(ngModel)]="employee.confirmPassword" #confirmPassword="ngModel">

<span class="help-block"

*ngIf="confirmPassword.touched && confirmPassword.errors?.required">

Confirm Password is required

</span>

<span class="help-block" *ngIf="confirmPassword.touched &&

passwordGroup.errors?.notEqual && !confirmPassword.errors?.required">


189

Password and Confirm Password does not match

</span>

</div>

</div>

Custom Validator Code :

import { Validator, NG_VALIDATORS, AbstractControl } from '@angular/forms';

import { Directive } from '@angular/core';

@Directive({

selector: '[appConfirmEqualValidator]',

providers: [{

provide: NG_VALIDATORS,

useExisting: ConfirmEqualValidatorDirective,

multi: true

}]

})

export class ConfirmEqualValidatorDirective implements Validator {

validate(passwordGroup: AbstractControl): { [key: string]: any } | null {


190

const passwordField = passwordGroup.get('password');

const confirmPasswordField = passwordGroup.get('confirmPassword');

if (passwordField && confirmPasswordField &&

passwordField.value !== confirmPasswordField.value) {

return { 'notEqual': true };

return null;

NgModelGroup Directive

● Use to create a sub-group within a form


● Useful to validate a sub-group of elements on the form
● Useful to group properties of the form model in to a nested object
● The name of the ngModelGroup will become the key for the nested object in the form
model
● The ngModelGroup directive can only be used as a child of NgForm directive

Creating a service

Add a new TypeScript file to the "employees" folder and name it employee.service.ts. Copy
and paste the following code. At the moment we have the data hard-coded in the service
method. In a later video we will discuss retrieving data from a remote server using HTTP.
191

import { Injectable } from '@angular/core';

import { Employee } from '../models/employee.model';

// The @Injectable() decorator is used to inject other dependencies

// into this service. As our service does not have any dependencies

// at the moment, we may remove the @Injectable() decorator and the

// service works exactly the same way. However, Angular recomends

// to always use @Injectable() decorator to ensures consistency

@Injectable()

export class EmployeeService {

private listEmployees: Employee[] = [

id: 1,

name: 'Mark',

gender: 'Male',

contactPreference: 'Email',

email: 'mark@pragimtech.com',

dateOfBirth: new Date('10/25/1988'),

department: 'IT',
192

isActive: true,

photoPath: 'assets/images/mark.png'

},

id: 2,

name: 'Mary',

gender: 'Female',

contactPreference: 'Phone',

phoneNumber: 2345978640,

dateOfBirth: new Date('11/20/1979'),

department: 'HR',

isActive: true,

photoPath: 'assets/images/mary.png'

},

id: 3,

name: 'John',

gender: 'Male',

contactPreference: 'Phone',

phoneNumber: 5432978640,

dateOfBirth: new Date('3/25/1976'),


193

department: 'IT',

isActive: false,

photoPath: 'assets/images/john.png'

},

];

getEmployees(): Employee[] {

return this.listEmployees;

Registering the service

A service in angular can be registered in a component or in a module. When a service is


registered at a component level, then that service is available only to that component and any of
it's children. It's not available to the other components.

One the other hand, if we register a service at a module level, then that service is registered
with the root injector and available to all the components in our application. We want our
EmployeeService to be available in several components (like employee list component, edit
component etc). So let's register it in the root module - AppModule.
194

We discussed dependency injection and injectors in Parts 32, 33, 35 and 36 in Angular 2
tutorial. If you are new to injectors and dependency injection in Angular, please check out those
videos by clicking here.

Include the following import statement to import EmployeeService in app.module.ts file

import { EmployeeService } from './employees/employee.service';

Also make sure to include EmployeeService in the providers array of @NgModule decorator.

Injecting and using the service

We need the employee service we created above in ListEmployeesComponent. So let's import


and use the Employee service in ListEmployeesComponent. Modify the code in list-
employees.component.ts file as shown below.

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

import { Employee } from '../models/employee.model';

// Import EmployeeService

import { EmployeeService } from './employee.service';

@Component({

templateUrl: './list-employees.component.html',
195

styleUrls: ['./list-employees.component.css']

})

export class ListEmployeesComponent implements OnInit {

employees: Employee[];

// Inject EmployeeService using the constructor

// The private variable _employeeService which points to

// EmployeeService singelton instance is then available

// throughout the class and can be accessed using this keyword

constructor(private _employeeService: EmployeeService) { }

// Call the getEmployees() service method of EmployeeService

// using the private variable _employeeService

ngOnInit() {

this.employees = this._employeeService.getEmployees();

Important points to remember about Angular service

● A service in angular is a class


196

● Irrespective of whether a service has an injected dependency or not, always decorate


the angular service class with @Injectable() decorator for consistency and future proof
● If a service is registered at a component level, then that service is available only to that
component and to it's children
● If a service is registered at a module level, then that service is available to all the
components in the application
● To use a service in a component inject it into the component class constructor

In our next video, we will discuss creating a new Employee.

Modify EmployeeService to include save() method

Include the following save() method in EmployeeService in employee.service.ts file. At the


moment, the save() method just pushes the employee object into the employees array. In a later
197

video, we will discuss saving the employee to a database table by calling a web service over
http.

save(employee: Employee) {

this.listEmployees.push(employee);

Injecting and using EmployeeService in CreateEmployeeComponent

Make the following changes in create-employee.component.ts file

Include the following import statement to import EmployeeService

import { EmployeeService } from './employee.service';

Also, import Angular Router service. We will use this service's navigate() method to redirect the
user to the employee list page after saving the employee.

import { Router } from '@angular/router';

Inject EmployeeService and Router service into the CreateEmployeeComponent using it's
constructor.
198

constructor(private _employeeService: EmployeeService,

private _router: Router)

Modify saveEmployee() method as shown below. Notice the saveEmployee() method calls the
EmployeeService save() method passing it the employee object we want to save. Wait a
minute, the employee object has all it's properties initialised to NULL. How do we get the values
from the controls on the form to the properties of this employee object.

Well, we do not have to write any code to keep employee object properties and the form control
values in sync. Angular's two way data-binding does that for us automatically.

Finally, we redirect the user to the "list" route using the navigate() method of the angular
Router service.

saveEmployee(): void {

this._employeeService.save(this.employee);

this._router.navigate(['list']);

Also, modify the call to saveEmployee() method in create-employee.component.html file as


shown below. Notice we removed the employee object that was being passed as a paramter.

<form #employeeForm="ngForm" (ngSubmit)="saveEmployee()">


199

At the moment, there is a small issue with data on the employee list page. Notice for the existing
employees the department name is displayed where as for the new employeees department id
is displayed. We will discuss how to fix this in our next video.

In this video, we will discuss angular ngSwitch directive. Let us understand switch case with
an example. Switch case in angular is a combination of 3 directives

● ngSwitch directive
● ngSwitchCase directive
● ngSwitchDefault directive

Consider the following department data. Depending on the employee's department id we want
to display department name.

departments: Department[] = [

{ id: 1, name: 'Help Desk' },

{ id: 2, name: 'HR' },

{ id: 3, name: 'IT' },

{ id: 4, name: 'Payroll' }

];

ngSwitch example
200

<div [ngSwitch]="employee.department">

<span *ngSwitchCase="1"> Help Desk </span>

<span *ngSwitchCase="2"> HR </span>

<span *ngSwitchCase="3"> IT </span>

<span *ngSwitchCase="4"> Payroll </span>

<span *ngSwitchDefault> N/A </span>

</div>

Code Explanation

● [ngSwitch]="employee.department". The expression (employee.department) returns


department id (1, 2 , 3 etc)
● <span *ngSwitchCase="1"> Help Desk </span>. If the department id is 1, then this
switchcase is executed and it displays Help Desk
● <span *ngSwitchDefault> N/A </span>. The default switch case is executed if the
department id is not 1, 2, 3 and 4
● Jus like ngIf and ngFor, the directives ngSwicthCase and ngSwitchDefault are also
structural directives, hence they have an asterisk in front of them
● If multiple ngSwitchCases match the switch expression value, then all those
ngSwitchCases are displayed

n this video we will discuss how to pass data from the Parent component to Child
component using input properties. Let us understand this with an example.

At the moment the ListEmplyeesComponent displays the list of employees you see below.
201

At the moment, ListEmployeesComponent is doing 3 things

● Calls the Angular EmployeeService to retrieve employees data


● Loops through each employee
● Has all the display logic to display employee details
202

Now let's create another component and off load the responsibility of displaying employee
details to that component. Let's name this new component - DisplayEmployeeComponent.

Here is the Angular CLI command you can use to generate the component

ng g c employees\DisplayEmployee --no-spec --flat

Copy and paste the following HTML in display-employee.component.html file

<div class="panel panel-primary">

<div class="panel-heading">

<h3 class="panel-title">{{employee.name}}</h3>

</div>

<div class="panel-body">

<div class="col-xs-10">

<div class="row vertical-align">

<div class="col-xs-4">

<img class="imageClass" [src]="employee.photoPath" />

</div>

<div class="col-xs-8">
203

<div class="row">

<div class="col-xs-6">

Gender

</div>

<div class="col-xs-6">

: {{employee.gender}}

</div>

</div>

<div class="row">

<div class="col-xs-6">

Date of Birth

</div>

<div class="col-xs-6">

: {{employee.dateOfBirth | date}}

</div>

</div>

<div class="row">

<div class="col-xs-6">

Contact Preference

</div>
204

<div class="col-xs-6">

: {{employee.contactPreference}}

</div>

</div>

<div class="row">

<div class="col-xs-6">

Phone

</div>

<div class="col-xs-6">

: {{employee.phoneNumber}}

</div>

</div>

<div class="row">

<div class="col-xs-6">

Email

</div>

<div class="col-xs-6">

: {{employee.email}}

</div>

</div>

<div class="row">
205

<div class="col-xs-6">

Department

</div>

<div class="col-xs-6" [ngSwitch]="employee.department"> :

<span *ngSwitchCase="1"> Help Desk </span>

<span *ngSwitchCase="2"> HR </span>

<span *ngSwitchCase="3"> IT </span>

<span *ngSwitchCase="4"> Payroll </span>

<span *ngSwitchDefault> N/A </span>

</div>

</div>

<div class="row">

<div class="col-xs-6">

Is Active

</div>

<div class="col-xs-6">

: {{employee.isActive}}

</div>

</div>

</div>
206

</div>

</div>

</div>

</div>

Notice in the HTML above we are binding to the employee property. We need to pass the
employee object from the Parent component (ListEmployeesComponent) to this Component
(DisplayEmployeeComponent). To make this happen we create an input property in
DisplayEmployeeComponent. So modify the code in display-employee.component.ts file as
shown below.

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

import { Employee } from '../models/employee.model';

@Component({

selector: 'app-display-employee',

templateUrl: './display-employee.component.html',

styleUrls: ['./display-employee.component.css']

})

export class DisplayEmployeeComponent implements OnInit {

// Parent component will use this Input property to pass


207

// the employee object to which the template binds to

@Input() employee: Employee;

constructor() { }

ngOnInit() {

Copy and paste the following 2 style classes in display-employee.component.css file. Delete
these 2 classes from list-employees.component.css. We do not need them there anymore.

.imageClass{

width:200px;

height:200px;

.vertical-align{

display: flex;

align-items: center;

}
208

Make the following changes in the parent component (list-employees.component.html). Notice,


the child component selector (app-display-employee) is nested in the Parent component. The
parent component, loops through each employee and passes the employee object to the child
component (DisplayEmployeeComponent) using it's Input property.

<div *ngFor="let employee of employees">

<app-employee-display [employee]="employee"></app-employee-display>

</div>

In this video we will discuss how to detect and react when component input property value
changes. This is continuation to Part 33. Please watch Part 33 from Angular CRUD tutorial
before proceeding.

To detect and react when an input property value changes, we have 2 options. We can either
use
209

● Property Setter
● ngOnChanges Life Cycle Hook

If you are new to Angular Life Cycle hooks, please check out Part 24 from Angular 2 tutorial.

To understand these 2 approaches, let's modify ListEmployeesComponent as shown below.


Notice instead of displaying all the employees at once, we have "View Next Employee" button
which cycles through the list of employees, displaying one employee at a time.

To achieve this modify the code in list-employees.component.html file as shown below.

<button (click)="nextEmployee()" class="btn btn-primary">

View Next Employee

</button>

<br/><br/>
210

<app-employee-display [employee]="employeeToDisplay">

</app-employee-display>

Modify the code in list-employees.component.ts as shown below.

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

import { Employee } from '../models/employee.model';

import { EmployeeService } from './employee.service';

@Component({

templateUrl: './list-employees.component.html',

styleUrls: ['./list-employees.component.css']

})

export class ListEmployeesComponent implements OnInit {

employees: Employee[];

employeeToDisplay: Employee;

private arrayIndex = 1;

constructor(private _employeeService: EmployeeService) { }

ngOnInit() {
211

this.employees = this._employeeService.getEmployees();

this.employeeToDisplay = this.employees[0];

nextEmployee(): void {

if (this.employeeToDisplay.id <= 2) {

this.employeeToDisplay = this.employees[this.arrayIndex];

this.arrayIndex++;

} else {

this.employeeToDisplay = this.employees[0];

this.arrayIndex = 1;

At this point view the page in the browser. Notice only one employee is displayed. Click "View
Next Employee" button to see the next employee. It is the child component
(DisplayEmployeeComponent) that displays the employee details. For the child component to
be able to display the employee details, the parent component is passing the employee object
to the child component using the child component input property.

So every time we click the "View Next Employee" button, the INPUT property value changes.
When this happens we want to detect the change and react. Let us say for example, we want to
212

log to the console the previously displayed employee name and the currently displayed
employee name.

We can achieve this either by using ngOnChanges life cycle hook or Property Setter. Let's
look at both the ways.

Detecting and reacting to Input property changes using ngOnChanges life cycle hook

// This life cycle hook receives SimpleChanges as an Input parameter

// We can use it to retrieve previous and current values as shown below

ngOnChanges(changes: SimpleChanges) {

const previousEmployee = <Employee>changes.employee.previousValue;

const currentEmployee = <Employee>changes.employee.currentValue;

console.log('Previous : ' + (previousEmployee ? previousEmployee.name : 'NULL') );

console.log('Current : ' + currentEmployee.name);

Next Video : Detecting and reacting to Input property changes using Property Setter
213

In this video we will discuss how to detect and react when component input property value
changes using a property setter. This is continuation to Part 34. Please watch Part 34 from
Angular CRUD tutorial before proceeding.

In our previous video we discussed, detecting and reacting to INPUT property changes using
ngOnChanges life cycle hook. In this video we will discuss doing the same using a Property
Setter instead.

Detecting and reacting to Input property changes using Property Setter

// Private backing field for the property

private _employee: Employee;

// This property setter is called anytime the input property changes

// Notice the code here logs the previous and current employee names

@Input()

set employee(val: Employee) {


214

console.log('Previous : ' + (this._employee ? this._employee.name : 'NULL'));

console.log('Current : ' + val.name);

this._employee = val;

// This getter is called when reading and displaying data

get employee(): Employee {

return this._employee;

At this point you might be thinking, there are 2 ways to detect and react to Input property
changes (Property Setter and ngOnChanges life cycle hook). What is the difference
between the two and when to use one over the other. We will answer these 2 questions in our
next video.
215

In our previous 2 videos we discussed 2 approaches (ngOnChanges and Property Setter) to


detect and react to input property changes in Angular. In this video we will discuss the
difference between these 2 approaches and when to use one over the other.

Both these approaches have their own use cases. Your software requirement determines which
approach to choose. Let us understand this with an example.

Let us say your child component has 5 input properties. If any of the input properties change,
then your requirement is to log those changes. This can be very easily achieved using
ngOnChanges life cycle hook. The ngOnChanges life cycle hook is invoked when any of the
input properties change.

Each input property that has changed will be attached to the SimpleChanges object using the
property name as the key. So if you have 5 input properties, and if 3 out of those 5 properties
change, then those 3 properties will be attached to the SimpleChanges object using the
property name as the key.

So in short, with ngOnChanges you have access to all input property changes at one place.

The following code logs all the input property changes to the browser console.

ngOnChanges(changes: SimpleChanges) {
216

for (const propName of Object.keys(changes)) {

const change = changes[propName];

const from = JSON.stringify(change.previousValue);

const to = JSON.stringify(change.currentValue);

console.log(propName + ' changed from ' + from + ' to ' + to);

To achieve this exact same thing (i.e logging if any of the 5 input properties change) with a
property setter, is a bit tedious because you have to have that logging code in every property
setter. So if you want to capture multiple property changes, I prefer ngOnChanges life cycle
hook as we get all the changes instead of just the changes related to a single property. On the
other hand, if you are interested in a single property, then I would use a property setter instead.

private _employeeId: number;

@Input()

set employeeId(val: number) {

console.log('employeeId changed from ' + JSON.stringify(this._employeeId)

+ ' to ' + JSON.stringify(val));


217

this._employeeId = val;

get employeeId(): number {

return this._employeeId;

private _employee: Employee;

@Input()

set employee(val: Employee) {

console.log('employee changed from ' + JSON.stringify(this._employee)

+ ' to ' + JSON.stringify(val));

this._employee = val;

get employee(): Employee {

return this._employee;

ngOnChanges

● We get all the changes instead of just the changes related to a single property
● Useful when multiple properties change
218

Property Setter

● Property setter is specific to a given property, so we only get changes of that specific
property
● Useful when you want to keep track of a single property

In this video we will discuss how to call child component methods and properties from
parent component. One way to do this is by using output properties. We discussed output
properties in Part 37 of Angular CRUD tutorial. Another way, to pass data from the child
component to the parent component is by using a template reference variable.

Let us understand this with an example. We want to do the same thing that we did with Output
properties in Part 37. The child component DisplayEmployeeComponent is responsible for
displaying each Employee details in a Bootstrap Panel.

Here is what we want to do. When we click on any of the employee panel we want to display
that employee name and gender as shown in the image below. So we need to pass employee
name and gender from the child component (DisplayEmployeeComponent) to parent
component (ListEmployeesComponent).
219

Here is the child component class. Notice the class has employee property and
getNameAndGender() method. We will be calling both of these from the parent component
(ListEmployeesComponent) using a template reference variable.

export class DisplayEmployeeComponent {

@Input() employee: Employee;

getNameAndGender(): string {

return this.employee.name + ' ' + this.employee.gender;

}
220

Code in Parent Component View Template (list-employees.component.html)

<h1 #h1Variable></h1>

<div *ngFor="let employee of employees">

<div (click)="h1Variable.innerHTML = childComponent.getNameAndGender()">

<app-employee-display [employee]="employee" #childComponent>

</app-employee-display>

</div>

</div>

Code Explanation :

● #childComponent is the template reference variable to the child component. Using this
template variable we can call child component public property (employee) and method
(getNameAndGender())
● <div (click)="handleClick(childComponent.getNameAndGender())">. Using the template
reference variable we are calling the child component method getNameAndGender().
The value this method returns is assigned to the innerHTML property of the <h1>
element. #h1Variable is the template reference variable for <h1> element.

At this point when you click on an employee panel, you will see that employee's name and
gender displayed by the <h1> element.
221

Calling the child component property using template reference variable : Notice in the
example below, we are calling the child component public property employee using the template
reference variable childComponent.

<h1 #h1Variable></h1>

<div *ngFor="let employee of employees">

<div (click)="h1Variable.innerHTML = childComponent.employee.name + ' ' +


childComponent.employee.gender">

<app-employee-display [employee]="employee" #childComponent>

</app-employee-display>

</div>

</div>

Even now, when you click on an employee panel, you will see that employee's name and
gender displayed by the <h1> element exactly the same way as before.

Summary : There are 2 ways to pass data from Child Component to Parent Component

● Output Properties
● Template Reference Variable

With Output properties there are several moving parts. In the child component we need to
create a custom event. Raise the custom event. From the parent component bind to the child
component event and handle it. With the template reference variable approach, we do not have
222

so many moving parts. We just declare a template reference variable and use it to call the child
component public properties and methods.

Component Input Property

● In Part 33 of Angular CRUD tutorial we discussed how to pass data from parent
component to child component.
● To pass data from parent component to child component we use input properties.
● In the child component we create a property with @Input decorator.
● The parent component then binds to the child component's input property.

Component Output Property

● On the other hand, to pass data from child component to parent component we use
output properties.
● The child component raises an event to pass data to the parent component.
● To create and raise an event, we use EventEmitter object.
223

● So the output property is an event defined using EventEmitter object and decorated
with @Output decorator.
● To specify the type of data that we want to pass from the child component to parent
component we use the EventEmitter generic argument.

In the example below, the notify event is used to pass string data from the child component to
parent component. This event data is commonly called event payload.

@Output() notify: EventEmitter<string> = new EventEmitter<string>();

If you want to pass a number instead of a string as event data, then you specify the generic
argument type as number instead of string.

@Output() notify: EventEmitter<number> = new EventEmitter<number>();

If you want to be able to pass any type of data, you can use 'any' as the generic argument type.

@Output() notify: EventEmitter<any> = new EventEmitter<any>();

You can only pass one value using EventEmitter. If you want to pass more than one value using
EventEmitter use a custom type like Employee as the generic argument.

@Output() notify: EventEmitter<Employee> = new EventEmitter<Employee>();

We want the "notify" custom event to be raised when we click on any of the "Employee" panel
shown below.
224

The child component DisplayEmployeeComponent is responsible for displaying Employee


details in a Bootstrap Panel. In (display-employee.component.html) file, wire up the click event
handler on the <div> element that has Bootstrap panel css classes panel and panel-primary.

<div class="panel panel-primary" (click)="handleClick()">

To raise the custom event, use the emit() method of the EventEmitter object. The event payload
is passed as the argument to the emit() method. So in display-employee.component.ts file
include handleClick() method as shown below.

handleClick() {
225

this.notify.emit(this.employee);

Here is the complete code in display-employee.component.ts file

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

import { Employee } from '../models/employee.model';

@Component({

selector: 'app-display-employee',

templateUrl: './display-employee.component.html',

styleUrls: ['./display-employee.component.css']

})

export class DisplayEmployeeComponent implements OnInit {

@Input() employee: Employee;

@Output() notify: EventEmitter<Employee> = new EventEmitter<Employee>();

constructor() { }

ngOnInit() {

}
226

handleClick() {

this.notify.emit(this.employee);

Now, in the parent component (list-employees.component.html) bind to the custom event raised
by the child component. Notice we are using event binding to bind to the notify event. We are
handling the notify event in the parent component using handleNotify($event) method. $event is
the data from the child component.

<h1 *ngIf="dataFromChild">

{{ dataFromChild?.name + ' ' + dataFromChild?.gender }}

</h1>

<div *ngFor="let employee of employees">

<app-display-employee [employee]="employee"

(notify)="handleNotify($event)">

</app-display-employee>

</div>

In list-employees.component.ts file, include handleNotify() method as shown below. This event


handler method assigns the event data received from the child component to dataFromChild
227

property. The view template (list-employees.component.html) binds to this dataFromChild


property to display the employee name and gender.

handleNotify(eventData: Employee) {

this.dataFromChild = eventData;

}
228

In this video we will discuss how to call child component methods and properties from
parent component. One way to do this is by using output properties. We discussed output
properties in Part 37 of Angular CRUD tutorial. Another way, to pass data from the child
component to the parent component is by using a template reference variable.

Let us understand this with an example. We want to do the same thing that we did with Output
properties in Part 37. The child component DisplayEmployeeComponent is responsible for
displaying each Employee details in a Bootstrap Panel.

Here is what we want to do. When we click on any of the employee panel we want to display
that employee name and gender as shown in the image below. So we need to pass employee
name and gender from the child component (DisplayEmployeeComponent) to parent
component (ListEmployeesComponent).
229

Here is the child component class. Notice the class has employee property and
getNameAndGender() method. We will be calling both of these from the parent component
(ListEmployeesComponent) using a template reference variable.

export class DisplayEmployeeComponent {

@Input() employee: Employee;

getNameAndGender(): string {

return this.employee.name + ' ' + this.employee.gender;

}
230

Code in Parent Component View Template (list-employees.component.html)

<h1 #h1Variable></h1>

<div *ngFor="let employee of employees">

<div (click)="h1Variable.innerHTML = childComponent.getNameAndGender()">

<app-employee-display [employee]="employee" #childComponent>

</app-employee-display>

</div>

</div>

Code Explanation :

● #childComponent is the template reference variable to the child component. Using this
template variable we can call child component public property (employee) and method
(getNameAndGender())
● <div (click)="handleClick(childComponent.getNameAndGender())">. Using the template
reference variable we are calling the child component method getNameAndGender().
The value this method returns is assigned to the innerHTML property of the <h1>
element. #h1Variable is the template reference variable for <h1> element.

At this point when you click on an employee panel, you will see that employee's name and
gender displayed by the <h1> element.
231

Calling the child component property using template reference variable : Notice in the
example below, we are calling the child component public property employee using the template
reference variable childComponent.

<h1 #h1Variable></h1>

<div *ngFor="let employee of employees">

<div (click)="h1Variable.innerHTML = childComponent.employee.name + ' ' +


childComponent.employee.gender">

<app-employee-display [employee]="employee" #childComponent>

</app-employee-display>

</div>

</div>

Even now, when you click on an employee panel, you will see that employee's name and
gender displayed by the <h1> element exactly the same way as before.

Summary : There are 2 ways to pass data from Child Component to Parent Component

● Output Properties
● Template Reference Variable

With Output properties there are several moving parts. In the child component we need to
create a custom event. Raise the custom event. From the parent component bind to the child
component event and handle it. With the template reference variable approach, we do not have
232

so many moving parts. We just declare a template reference variable and use it to call the child
component public properties and methods.

In this video we will discuss CanDeactivate route guard in Angular. We will also discuss how
to access angular template reference variable in component class.

First let's understand, why route guards are required. Consider the following "Create
Employee" data entry form. This is a pretty big form. Let's say you have filled 90% of the form
fields and you accidentally clicked on the "List" navigation link. The application immediately
redirects you to the "list" route and all your data on the "Create Employee" form is lost. Wouldn't
it have been nice if there was an alert asking the user if he really wants to discard his changes
and navigate away from the Create Employee form.
233

A routing guard called CanDeactivate can help us achieve this very easily. The following table
has the common routing guards and their purpose.

Route Guard Use

Guard navigation away from the current route


CanDeactivate

CanActivate Guard navigation to a route


CanActivateChild Guard navigation to a child route
CanLoad Guard navigation to a feature module loaded asynchronously
Resolve Perform route data retrieval before route activation
234

We will discuss each of these routing guards with an example in our upcoming videos. In this
video we will discuss CanDeactivate guard.

There are 3 steps to use a routing guard in Angular.

● Build the route guard


● Register the guard with angular dependency injection system
● Tie the guard to a route

Building the route guard : A route guard can be implemented as a function or a service. In this
video let's create a service. In our upcoming videos we will discuss creating a function.

Create a new file, in the employees folder. Name it create-employee-can-deactivate-


gaurd.service.ts. Copy and paste the following code.

import { Injectable } from '@angular/core';

import { CanDeactivate } from '@angular/router';

import { CreateEmployeeComponent } from './create-employee.component';

@Injectable()

export class CreateEmployeeCanDeactivateGuardService

implements CanDeactivate<CreateEmployeeComponent> {

constructor() { }
235

canDeactivate(component: CreateEmployeeComponent): boolean {

if (component.createEmployeeForm.dirty) {

return confirm('Are you sure you want to discard your changes?');

return true;

Code Explanation :

● Since we are implementing the routing guard as a service, decorate the guard class with
@Injectable() decorator.
● Since we want to implement CanDeactivate routing guard, make the guard class
implement CanDeactivate interface.
● CanDeactivate interface supports generics. In our case, since we are creating a guard
for CreateEmployeeComponent, we have specified CreateEmployeeComponent as the
argument for the generic type of CanDeactivate interface.
● CanDeactivate interface has one method called canDeactivate(). Our routing guard class
needs to provide implementation for this interface method.
● canDeactivate() method returns true or false. True to allow navigation away from the
route. False to prevent navigation.
● The first parameter that is passed to the canDeactivate() method is the
CreateEmployeeComponent. We are using this parameter to check if the component is
dirty. If it is dirty, we are triggering JavaScript confirm dialog to the user.
● If the component is not dirty we simply return true, to allow navigation away from the
"create" route.

How to check if the form is dirty : Include the following line of code in
CreateEmployeeComponent class
236

@ViewChild('employeeForm') public createEmployeeForm: NgForm;

@ViewChild() decorator provides access to the NgForm directive in the component class.
employeeForm which is passed as the selector to the @ViewChild() decorator is the form
template reference variable.

The public property "createEmployeeForm" is used to check if the form is dirty.

Register the guard with angular dependency injection system : Since the routing guard is
implemented as a service, we need to register it in a module. At the moment we have only one
module in our application and that is the root module AppModule. Import and register
CreateEmployeeCanDeactivateGuardService in app.module.ts file using the providers
property.

@NgModule({

declarations: […

],

imports: […

],

providers: [CreateEmployeeCanDeactivateGuardService],

bootstrap: [AppComponent]

})

export class AppModule { }


237

Tie the guard to a route : Using the route guard, we want to prevent navigating away from the
"create" route, so tie the route guard with the "create" route in app.module.ts file as shown
below.

const appRoutes: Routes = [

path: 'list', component: ListEmployeesComponent

},

path: 'create',

component: CreateEmployeeComponent,

canDeactivate: [CreateEmployeeCanDeactivateGuardService]

},

path: '', redirectTo: '/list', pathMatch: 'full'

];

At this point, if you try to navigate away from the "create" route when the form is dirty you get
the alert as expected. The browser back and forward buttons also prevent the navigation away
from the "create" route.
238

CanDeactivate limitations : CanDeactivate guard does not prevent route deactivation

● If you type a different url in the address bar directly OR


● If you close the tab or the browser window OR
● If you navigate to an external URL

n this video we will discuss CanDeactivate route guard in Angular. We will also discuss how
to access angular template reference variable in component class.

First let's understand, why route guards are required. Consider the following "Create
Employee" data entry form. This is a pretty big form. Let's say you have filled 90% of the form
fields and you accidentally clicked on the "List" navigation link. The application immediately
redirects you to the "list" route and all your data on the "Create Employee" form is lost. Wouldn't
it have been nice if there was an alert asking the user if he really wants to discard his changes
and navigate away from the Create Employee form.
239

A routing guard called CanDeactivate can help us achieve this very easily. The following table
has the common routing guards and their purpose.

Route Guard Use

Guard navigation away from the current route


CanDeactivate

CanActivate Guard navigation to a route


CanActivateChild Guard navigation to a child route
CanLoad Guard navigation to a feature module loaded asynchronously
Resolve Perform route data retrieval before route activation
240

We will discuss each of these routing guards with an example in our upcoming videos. In this
video we will discuss CanDeactivate guard.

There are 3 steps to use a routing guard in Angular.

● Build the route guard


● Register the guard with angular dependency injection system
● Tie the guard to a route

Building the route guard : A route guard can be implemented as a function or a service. In this
video let's create a service. In our upcoming videos we will discuss creating a function.

Create a new file, in the employees folder. Name it create-employee-can-deactivate-


gaurd.service.ts. Copy and paste the following code.

import { Injectable } from '@angular/core';

import { CanDeactivate } from '@angular/router';

import { CreateEmployeeComponent } from './create-employee.component';

@Injectable()

export class CreateEmployeeCanDeactivateGuardService

implements CanDeactivate<CreateEmployeeComponent> {

constructor() { }
241

canDeactivate(component: CreateEmployeeComponent): boolean {

if (component.createEmployeeForm.dirty) {

return confirm('Are you sure you want to discard your changes?');

return true;

Code Explanation :

● Since we are implementing the routing guard as a service, decorate the guard class with
@Injectable() decorator.
● Since we want to implement CanDeactivate routing guard, make the guard class
implement CanDeactivate interface.
● CanDeactivate interface supports generics. In our case, since we are creating a guard
for CreateEmployeeComponent, we have specified CreateEmployeeComponent as the
argument for the generic type of CanDeactivate interface.
● CanDeactivate interface has one method called canDeactivate(). Our routing guard class
needs to provide implementation for this interface method.
● canDeactivate() method returns true or false. True to allow navigation away from the
route. False to prevent navigation.
● The first parameter that is passed to the canDeactivate() method is the
CreateEmployeeComponent. We are using this parameter to check if the component is
dirty. If it is dirty, we are triggering JavaScript confirm dialog to the user.
● If the component is not dirty we simply return true, to allow navigation away from the
"create" route.

How to check if the form is dirty : Include the following line of code in
CreateEmployeeComponent class
242

@ViewChild('employeeForm') public createEmployeeForm: NgForm;

@ViewChild() decorator provides access to the NgForm directive in the component class.
employeeForm which is passed as the selector to the @ViewChild() decorator is the form
template reference variable.

The public property "createEmployeeForm" is used to check if the form is dirty.

Register the guard with angular dependency injection system : Since the routing guard is
implemented as a service, we need to register it in a module. At the moment we have only one
module in our application and that is the root module AppModule. Import and register
CreateEmployeeCanDeactivateGuardService in app.module.ts file using the providers
property.

@NgModule({

declarations: […

],

imports: […

],

providers: [CreateEmployeeCanDeactivateGuardService],

bootstrap: [AppComponent]

})

export class AppModule { }


243

Tie the guard to a route : Using the route guard, we want to prevent navigating away from the
"create" route, so tie the route guard with the "create" route in app.module.ts file as shown
below.

const appRoutes: Routes = [

path: 'list', component: ListEmployeesComponent

},

path: 'create',

component: CreateEmployeeComponent,

canDeactivate: [CreateEmployeeCanDeactivateGuardService]

},

path: '', redirectTo: '/list', pathMatch: 'full'

];

At this point, if you try to navigate away from the "create" route when the form is dirty you get
the alert as expected. The browser back and forward buttons also prevent the navigation away
from the "create" route.
244

CanDeactivate limitations : CanDeactivate guard does not prevent route deactivation

● If you type a different url in the address bar directly OR


● If you close the tab or the browser window OR
● If you navigate to an external URL
245
246

In this video we will discuss

● Creating a route with parameters and


● Activating the route

Create a route with parameters : To create a route with parameter include a FORWARD
SLASH, a COLON and a place holder for the parameter. The example below, creates a route
with parameter id.

{ path: 'employees/:id', component: EmployeeDetailsComponent }

You can have more than one parameter in a route. The following route definition creates a route
with 2 parameters - id and name.
247

{ path: 'employees/:id/:name', component: EmployeeDetailsComponent }

Activating the route with parameters : One way to activate the route is by using the
routerLink directive.

<a [routerLink]="['employees',2]">

Get Employee with Id 2

</a>

Code Explanation:

● Notice in this example we are binding routerLink directive to an array.


● This array is called link parameters array.
● The first element in the array is the path of the route to the destination component.
● The second element in the array is the route parameter, in our case the employee Id.

Another way to activate the route is by using the Angular Router service naviaget() method.
Notice to the navigate method also we are passing the same link parameter array.

constructor(private _router: Router) { }

onClick(employeeId: number) {
248

this._router.navigate(['/employees', employeeId]);

Angular CLI command to generate EmployeeDetailsComponent

ng g c employees/employee-details --no-spec --flat

list-employees.component.css

.pointerCursor {

cursor: pointer;

list-employees.component.ts

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

import { Employee } from '../models/employee.model';

import { EmployeeService } from './employee.service';

import { Router } from '@angular/router';

@Component({
249

templateUrl: './list-employees.component.html',

styleUrls: ['./list-employees.component.css']

})

export class ListEmployeesComponent implements OnInit {

employees: Employee[];

constructor(private _employeeService: EmployeeService,

private _router: Router) { }

ngOnInit() {

this.employees = this._employeeService.getEmployees();

onClick(employeeId: number) {

this._router.navigate(['/employees', employeeId]);

list-employees.component.html

<div *ngFor="let employee of employees">

<div (click)="onClick(employee.id)" class="pointerCursor">


250

<app-display-employee [employee]="employee">

</app-display-employee>

</div>

</div>

app.module.ts

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

import { NgModule } from '@angular/core';

import { RouterModule, Routes } from '@angular/router';

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

import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';

import { SelectRequiredValidatorDirective } from './shared/select-required-validator.directive';

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

import { ListEmployeesComponent } from './employees/list-employees.component';

import { CreateEmployeeComponent } from './employees/create-employee.component';

import { ConfirmEqualValidatorDirective } from './shared/confirm-equal-validator.directive';

import { EmployeeService } from './employees/employee.service';

import { DisplayEmployeeComponent } from './employees/display-employee.component';


251

import { CreateEmployeeCanDeactivateGuardService } from './employees/create-employee-can-


deactivate-guard.service';

import { EmployeeDetailsComponent } from './employees/employee-details.component';

const appRoutes: Routes = [

{ path: 'list', component: ListEmployeesComponent },

path: 'create',

component: CreateEmployeeComponent,

canDeactivate: [CreateEmployeeCanDeactivateGuardService]

},

path: 'employees/:id', component: EmployeeDetailsComponent

},

{ path: '', redirectTo: '/list', pathMatch: 'full' }

];

@NgModule({

declarations: [

AppComponent,

ListEmployeesComponent,
252

CreateEmployeeComponent,

SelectRequiredValidatorDirective,

ConfirmEqualValidatorDirective,

DisplayEmployeeComponent,

EmployeeDetailsComponent

],

imports: [

BrowserModule,

FormsModule,

BsDatepickerModule.forRoot(),

RouterModule.forRoot(appRoutes)

],

providers: [EmployeeService, CreateEmployeeCanDeactivateGuardService],

bootstrap: [AppComponent]

})

export class AppModule { }


253

In this video we will discuss, reading route parameter values in Angular 2 and later versions.

To read the route parameter value use Angular ActivatedRoute service.

● There are 2 ways to read the route parameter value.


● We can either use the snapshot approach or observable approach.
● Snapshot approach works fine, if you navigate to another component before navigating
from the current employee to the next employee. In our case we are always navigating
back to the ListEmployeesComponent before navigating to view another employee
details.
● If you expect users to navigate from employee to employee directly, without navigating
to another component first, then you need to use the observable approach. We will
discuss the observable approach in our next video.

constructor(private _route: ActivatedRoute) { }


254

ngOnInit() {

const id = +this._route.snapshot.params['id'];

Since Angular 4, params have been deprecated in favor of the new interface paramMap. So if
you are using Angular 4 or above, use paramMap instead of params.

ngOnInit() {

const id = +this._route.snapshot.paramMap.get('id');

Introduce getEmployee() method in employee.service.ts file

getEmployee(id: number): Employee {

return this.listEmployees.find(e => e.id === id);

employee-details.component.html
255

<div class="panel panel-primary">

<div class="panel-heading">

<h3 class="panel-title">{{employee.name}}</h3>

</div>

<div class="panel-body">

<div class="col-xs-10">

<div class="row vertical-align">

<div class="col-xs-4">

<img class="imageClass" [src]="employee.photoPath" />

</div>

<div class="col-xs-8">

<div class="row">

<div class="col-xs-6">

Gender

</div>

<div class="col-xs-6">

: {{employee.gender}}

</div>

</div>

<div class="row">
256

<div class="col-xs-6">

Date of Birth

</div>

<div class="col-xs-6">

: {{employee.dateOfBirth | date}}

</div>

</div>

<div class="row">

<div class="col-xs-6">

Contact Preference

</div>

<div class="col-xs-6">

: {{employee.contactPreference}}

</div>

</div>

<div class="row">

<div class="col-xs-6">

Phone

</div>

<div class="col-xs-6">

: {{employee.phoneNumber}}
257

</div>

</div>

<div class="row">

<div class="col-xs-6">

Email

</div>

<div class="col-xs-6">

: {{employee.email}}

</div>

</div>

<div class="row">

<div class="col-xs-6">

Department

</div>

<div class="col-xs-6" [ngSwitch]="employee.department">

<span *ngSwitchCase="1">Help Desk</span>

<span *ngSwitchCase="2">HR</span>

<span *ngSwitchCase="3">IT</span>

<span *ngSwitchCase="4">Payroll</span>

<span *ngSwitchDefault>N/A</span>
258

</div>

</div>

<div class="row">

<div class="col-xs-6">

Is Active

</div>

<div class="col-xs-6">

: {{employee.isActive}}

</div>

</div>

</div>

</div>

</div>

</div>

<div class="panel-footer">

<a class="btn btn-primary" routerLink="/list">

Back to List

</a>

</div>

</div>
259

employee-details.component.ts

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

import { Employee } from '../models/employee.model';

import { ActivatedRoute } from '@angular/router';

import { EmployeeService } from './employee.service';

@Component({

selector: 'app-employee-details',

templateUrl: './employee-details.component.html',

styleUrls: ['./employee-details.component.css']

})

export class EmployeeDetailsComponent implements OnInit {

employee: Employee;

constructor(private _route: ActivatedRoute,

private _employeeService: EmployeeService) { }

ngOnInit() {

const id = +this._route.snapshot.paramMap.get('id');

this.employee = this._employeeService.getEmployee(id);
260

employee-details.component.css

.imageClass{

width:200px;

height:200px;

.vertical-align{

display: flex;

align-items: center;

}
261

In this video we will discuss how to subscribe to angular route parameter changes and then
execute some code in response to those changes. In our previous video, we discussed there
are 2 ways to read the route parameter values. We can either use the snapshot approach or
observable approach. We discussed the snapshot approach in our previous video. In this video
we will discuss the observable approach.

Snapshot approach works fine, if you navigate to another component before navigating from the
current employee to the next employee. In our case we are always navigating back to the
ListEmployeesComponent before navigating to view another employee details.
262

If you expect users to navigate from employee to employee directly, without navigating to
another component first, then you need to use the observable approach. Notice, on the
EmployeeDetailsComponent we now have "View Next Employee" button.

Here is what we want to do : Every time when we click this "View Next Employee" button, we
want to display the next employee details on the page. Notice in this workflow, we are not
navigating to another component before navigating from the current employee to the next
employee. So the snapshot approach will not work. Let's try to use the snapshot approach and
see what happens.

Modify the HTML in employee-details.component.html file to include "View Next Employee"


button. Include the HTML for the button, in the panel-footer <div>

<div class="panel-footer">

<a class="btn btn-primary" routerLink="/list">Back to List</a>


263

<button class="btn btn-primary pull-right" (click)="getNextEmployee()">

View Next Employee

</button>

</div>

Modify the code in employee-details.component.ts file as shown below

export class EmployeeDetailsComponent implements OnInit {

employee: Employee;

// Include a private field _id to keep track of the route parameter value

private _id;

constructor(private _route: ActivatedRoute,

private _employeeService: EmployeeService,

private _router: Router) { }

// Extract the route parameter value and retrieve that specific

// empoyee details using the EmployeeService

ngOnInit() {

this._id = +this._route.snapshot.paramMap.get('id');

this.employee = this._employeeService.getEmployee(this._id);

}
264

// Everytime this method is called the employee id value is

// incremented by 1 and then redirected to the same route

// but with a different id parameter value

getNextEmployee() {

if (this._id < 3) {

this._id = this._id + 1;

} else {

this._id = 1;

this._router.navigate(['/employees', this._id]);

At this point, run the project and notice that, every time we click "View Next Employee" button
the route parameter value changes as expected, but the employee details displayed on the
page does not change. This is because the code in ngOnInit() is executed only when the
component is first loaded and initialised.

After that, every time we click "View Next Employee" button, only the route parameter value
changes and the component is not reinitialised and hence the code in ngOnInit() is not
executed. If you want to react to route parameter changes and execute some code every time
265

the route parameter value changes, then you have to subscribe to the route parameter changes.
So modify the code in ngOnInit() method as shown below.

// The paramMap property returns an Observable. So subscribe to it

// if you want to react and execute some piece of code in response

// to the route parameter value changes

ngOnInit() {

this._route.paramMap.subscribe(params => {

this._id = +params.get('id');

this.employee = this._employeeService.getEmployee(this._id);

});

With the above change in place, when we click "View Next Employee" button, the application
works as expected.

Please note : paramMap is introduced in Angular 4. So if you are using Angular 4 or any
version beyond it, then the above code works. If you are using Angular 2, then use params
instead of paramMap as shown below.

ngOnInit() {

this._route.params.subscribe(params => {
266

this._id = +params['id'];

this.employee = this._employeeService.getEmployee(this._id);

});

Another important point to keep in mind : When we subscribe to an observable in a


component, we also must write code to unsubscribe from the observable when the component
is destroyed. However, for some observables we do not have to explicitly unsubscribe. The
ActivatedRoute observable is one such observable. The angular Router destroys a routed
component when it is no longer needed and the injected ActivatedRoute dies with it.

When to use snapshot over observable while reading route parameter values : Use
snapshot approach if the route parameter value does not change and you only want to read the
initial route parameter value. On the other hand, if you know the route parameter value
changes, and if you want to react and execute some code in response to that change, then use
the Observable approach.

Whenever we subscribe to an observable we must unsubscribe from it after the task is done.
However, we have some exception to it. One such exception is the ActivatedRoute.
267

In this video we will discuss optional route parameters in Angular with a simple example.

On some routes, the route parameters are required and on some routes they are optional.

For example, when we navigate from Employees list route to employee details route, the
employee id is a required route parameter. Without it, we wouldn't know which employee details
to retrieve and display.
268

Let us understand one use case for optional route parameter. If we have just viewed the details
of the employee whose ID is 2 and if we navigate back to the employees LIST route, we want to
pass the employee ID 2 in the URL as a route parameter to the LIST route, so that specific
employee can be styled with a different colour indicating that, we have just viewed his details.

Notice in the example below, the second employee "Mary" panel is styled with green
background colour, indicating that we have just viewed her details.
269
270

On the other hand, if we navigate from the CREATE route to the LIST route, we do not have an
employee ID to pass to the LIST route. So the ID has to be an optional route parameter on the
LIST route. If the employee ID route parameter is present on the LIST route, then that specific
employee panel will be styled with a colour different from the rest of the employees in the list. If
the ID route parameter is not present, then all the employee panels will have the same colour.

The following route is activated when we navigate from Employee LIST to employee DETAILS.
To view a specific employee details, we need his or her ID. Hence in the following route, id is a
required route parameter.

{ path: 'employees/:id', component: EmployeeDetailsComponent }

A required route parameter is part of the route definition and is used in route pattern matching.
When defining routes, in the route definition, we include a place holder for required route
parameter. In the above route definition, ":id" is the placeholder for employee id required route
parameter.

Now if we want to view the details of an employee whose ID is 2, we will navigate to


localhost:4200/employees/2

On the other hand, an optional route parameter is not part of the route definition and is not used
in route pattern matching. When defining routes, we do not include a place holder for an
optional route parameter like we do for the required route parameter. The following is the route
for employee LIST.
271

{ path: 'list', component: ListEmployeesComponent }

When navigating back to the employee LIST from employee DETAILS, we want to pass the ID
of the employee whose DETAILS we have just viewed to the LIST route. So the LIST url, looks
as shown below. Notice we are passing 2 as the value for id. In this url id is an optional route
parameter.

http://localhost:4200/list;id=2

To pass an optional route parameter you use the LINK parameters array as shown below.
Notice we are using an object with an optional id parameter. The LIST route works perfectly fine
without the id parameter value. If we have the id parameter value, then we style that specific
employee with a different colour to indicate he is the employee whose details we have just
viewed.

<a class="btn btn-primary" [routerLink]="['/list',{id:employee.id}]">

Back to List

</a>

Reading optional route parameter is very similar to reading required route parameter. We use
the same ActivatedRoute service.

constructor(private _route: ActivatedRoute) { }


272

ngOnInit() {

this.selectedEmployeeId = +this._route.snapshot.paramMap.get('id');

In the view template (display-employee.component.html), we want to add panel-success


bootstrap css class to the employee panel, if the id of the employee being displayed by that
panel IS EQUAL TO the value we have in selectedEmployeeId property.

<div class="panel panel-primary"

[class.panel-success]="selectedEmployeeId === employee.id">

In our next video, we will discuss the differences between optional route parameters and
required route parameters and when to use one over the other.
273

In this video we will discuss some of the common differences between required route
parameters and optional route parameters in Angular. This is continuation to Part 43.
Please watch Part 43 from Angular CURD tutorial.

● Required route parameters are part of route configuration where as optional route
parameters are not.
274

Here employees/:id is a required parameter, thereby it is in route configuration. We have


optional router parameter for list, thereby that is not the part of route configuration.

● Required route parameters are used in pattern matching i.e when mapping an incoming
URL to a named route in our application. Optional route parameters are not used in
pattern matching.

Suppose we have the parameter as below:


275

The only difference we have is the route parameter in above two path and lines

So, now when you have route parameter, EmployeeDetails component is called and
when you don’t have route parameter ListEmployeeComponent is called. Thereby, if we
have more route parameter more complex the pattern matching gets.

● Optional route parameters must be passed after the required route parameters if any.
● In general, prefer a required route parameter when the value is simple and mandatory.
For example, to view a specific employee details, the ID parameter is mandatory and it is
a simple integer. On the other hand, prefer an optional route parameter when the value
is optional and complex. For example you want to send many search parameters like
NAME, AGE, DEPARTMENT, DOB etc...from the SEARCH PAGE to LIST PAGE.
276

Here if we have required parameter in this way, then there will be two issues.

1. You don’t know which value is for which param


2. You don’t know, it’s a search form and it’s not the case always where you’ll be getting
the value for each param.

In this video we will discuss, why and how to rest a form in angular.

Sometimes we must reset the form, before we can redirect the user to another route. Let me
explain this with an example.

1. We have a form to create a new employee


2. After we fill the form and when we click the "Save" button, the employee data must be
saved and the user should be redirected to the "LIST" route.
3. But canDeactivate guard is added on the "CREATE" route
4. This means if the form is dirty, the canDecativate guard will prevent us from leaving the
"CREATE" route
5. So there is a need to to RESET the form, which will reset all the form control values to
their intial state and also resets the form flags like dirty, pristine, valid, touched etc.
6. Once the form is REST, the form is no longer dirty, so the canDeactivate guard will not
prevent use from leaving the CREATE route
277

Angular form reset

● Clears all the form controls


● Optionally the form controls can be initialised with default values
● Resets the form state and individual controls state like dirty, pristine, valid, touched etc.

To RESET the form in HTML

<form #employeeForm="ngForm"

(ngSubmit)="saveEmployee(employeeForm); employeeForm.reset()">

There are 2 ways to RESET the form in code. One way is to pass the form (NgForm) to the
Submit() method and then call reset() method.

saveEmployee(empForm: NgForm): void {

this._employeeService.save(this.employee);

empForm.reset();

this._router.navigate(['list']);

The other way is to use the @ViewChild decorator to access form in code and then call reset()
method.

@ViewChild('employeeForm') public createEmployeeForm: NgForm;


278

saveEmployee(): void {

this._employeeService.save(this.employee);

this.createEmployeeForm.reset();

this._router.navigate(['list']);

Please note : @ViewChild() decorator provides access to the NgForm directive in the
component class. employeeForm which is passed as the selector to the @ViewChild() decorator
is the form template reference variable.

To reset and initialise the form controls with some default values, pass an object to the reset()
method with key/value pairs. The key is the form control name and the value is the default
value.

this.createEmployeeForm.reset({

name: 'Test Value',

email: 'kudvenkat@pragimtech.com'

});
279

This is continuation to Part 45, please watch Part 45 from Angular CRUD tutorial before
proceeding.

The following is the reason the submitted data becomes NULL when the angular form is reset
280

1. The "Create Employee Form" is bound to the Employee object.


2. This same employee object is passed to the save method of the Employee Service.
this._employeeService.save(this.employee);
3. When the form is reset, the employee object which is bound to the form is also reset.
This happens because of the Angular 2 way data-binding.
4. As the employee object that is bound to the form, and the employee object that is
passed to the save() method are the same, when the form is reset, the bound employee
object is reset and as a result, the employee object passed to the save() method is also
reset.

To solve this create a new employee object and copy over all the values of the employee object,
and then pass the newly created employee object to the save() method.

Notice in the example below, we are using Object.assign() method to copy the employee object
property values into a new employee object

saveEmployee(): void {

const newEmployee: Employee = Object.assign({}, this.employee);

this._employeeService.save(newEmployee);

this.createEmployeeForm.reset();

this._router.navigate(['list']);

Regarding reference variables the key point to keep in mind is, object reference variables are
pointers to objects, they are not objects themselves.
281

n this video we will discuss implementing a filter pipe in Angular. Angular team recommends
not to use pipes to filter and sort data. This is because a filtering and sorting pipe can
significantly impact the performance of the application if not implemented carefully. To better
understand the performance implications, let's implement a custom pipe to filter data. We
discussed creating custom pipes in detail in Part 19 of Angular 2 tutorial.

We want to include Search by Name textbox on ListEmployeesComponent


282

list-employees.component.html

<div class="form-group">

<input type="text" class="form-control" placeholder="Search By Name"

style="width:300px" [(ngModel)]="searchTerm" />

</div>

<div *ngFor="let employee of employees | employeeFilter:searchTerm">

<div (click)="onClick(employee.id)" class="pointerCursor">

<app-display-employee [employee]="employee" #childComponent>

</app-display-employee>

</div>

</div>
283

In list-employees.component.ts file, include the following searchTerm property

searchTerm: string;

employee-filter.pipe.ts

import { PipeTransform, Pipe } from '@angular/core';

import { Employee } from '../models/employee.model';

@Pipe({

name: 'employeeFilter'

})

export class EmployeeFilterPipe implements PipeTransform {

transform(employees: Employee[], searchTerm: string): Employee[] {

if (!employees || !searchTerm) {

return employees;

return employees.filter(employee =>


284

employee.name.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1);

Also do not forget to register EmployeeFilterPipe in a module.

As you can see we implemented filtering using a pipe and everything seems to be working fine.
So the question that comes to our mind is,

Why did the Angular team recommend not to use pipes to filter and sort data

Well, that's because a filtering and sorting pipe can significantly impact the performance of the
application.

We will discuss these performance implications and the recommended approach to filter and
sort data in our next video.
285

n this video we will discuss

● What is a pure pipe


● Why is it not recommended to use pipes to filter and sort data in Angular

This is continuation to Part 47, please watch Part 47 from Angular CRUD tutorial before
proceeding.

In Angular, there are two categories of pipes

● Pure Pipes
● Impure Pipes

When you create a new pipe, it is pure by default. To make a pipe impure, set it's pure flag to
false. Impure pipes can significantly affect the performance of the application. So you have to
think very carefully, before you use an impure pipe in your angular application. We will discuss
impure pipes and why they are bad from performance perspective in our next video.

@Pipe({
name: 'employeeFilter',
pure: false
})
export class EmployeeFilterPipe implements PipeTransform {
286

On each panel we have a name displayed. What al we want to do is to convert these names to
upper case

Name are converted to uppercase as expected.


287

Now we have a button to change employee name as below formmark to Jordan

Name changes and the pipe is exeuted.


288

Now suppose we have it with the object refernce.

Input to our filter pipe is an array which is the reference type. And when we click change name,
we are changing the reference type.

Now before changing the name when we are executing the filter by typing J we only see JOhn
over there. Now,as soon as we click the button to chnage the name, we see both Jordan and
JOhn this is because change in refernce is considered as the pure change and custom filter is
being fired.
289

Pure pipe

● Is fast
● Angular executes a pure pipe only when it detects a pure change to the input value
● A pure change is either a change to a primitive input value (String, Number, Boolean) or
a changed object reference (Array, Date, Object)
● A pure pipe is not executed if the input to the pipe is an object and only the property
values of the object change but not the reference

Now, if we have the code as :-

Now in filter if I type J, I see one employee with name John, and then click on change
employee name we don’t see both the employee JOhn and newly renamed employee
Jordan in filtered list. Filter pipe is not executed. As it is not the pure change, we are not
changing the reference.

Why are Pure pipes fast

● An input for a pipe can either be a value type (String, Number, Boolean etc) or a
reference type (like Array, Date, Object etc.)
● If the input to the pure pipe is a value type. Since value types store data directly in the
memory slot comparing if a value type value has changed is very quick.
290

● On the other hand, if the input to the pure pipe is a reference type, it is executed only if
the reference of the input object has changed. Checking if an object reference has
changed is much faster than checking if each of the object individual property values
have changed.

So pure pipes are fast, but using them for filtering data is not a good idea because, the filtering
may not work as expected if the source data is updated without a change to the object
reference.

One way to make this work is by making the pipe impure. Impure pipes can significantly impact
the performance of the application as they run on every change detection cycle.

We will discuss impure pipes and the performance impact they can have in our next video.

In this video we will discuss impure pipes and their performance implications.

This is continuation to Part 48, please watch Part 48 from Angular CRUD tutorial before
proceeding.

In our previous 2 videos we implemented a pure pipe to filter employee data. A pure pipe is only
executed when a pure change to the input value is detected.

Pure pipes are fast, but using them for filtering data is not a good idea because, the filtering may
not work as expected if the source data is updated without a change to the object reference.

One way to make this work is by making the pipe impure. Impure pipes can significantly impact
the performance of the application as they run on every change detection cycle. To make a pipe
impure, set it's pure property to false.

@Pipe({
name: 'employeeFilter',
pure: false
})
export class EmployeeFilterPipe implements PipeTransform {

You have to think very carefully when you make a pipe impure. This is because an impure pipe
is processed on every change, even when the source data does not change. They run
unnecessarily when not required.
291

If you have an array with thousands of objects and each object in turn has dozens of properties.
Now if we use an impure pipe to filter or sort this array and for some reason on the same page if
we are also listening to the mouse move or keystroke event, then the pipe is unnecessarily
executed for every mouse move or keystroke which can significantly degrade the performance
of the application.

This is the reason, Angular team recommends not to use pipes to filter and sort data. Angular
recommends to move the filtering and sorting logic into the component itself. We will discuss
how to implement filtering in a component in our next video.

For every mouse move, our filter pipe is executed. Processed on every change, even source
data doesn’t change.

n this video we will discuss implementing the data filter logic in an Angular component so
we have better control on when that code should and shouldn't execute.

list-employees.component.ts : The code is commented and self-explanatory

export class ListEmployeesComponent implements OnInit {


employees: Employee[];
// Use this property to stored filtered employees, so we do not
// lose the original list and do not have to make a round trip
// to the web server on every new search
filteredEmployees: Employee[];
292

private _searchTerm: string;

// We are binding to this property in the view template, so this


// getter is called when the binding needs to read the value
get searchTerm(): string {
return this._searchTerm;
}

// This setter is called everytime the value in the search text box changes
set searchTerm(value: string) {
this._searchTerm = value;
this.filteredEmployees = this.filterEmployees(value);
}

constructor(private _employeeService: EmployeeService,


private _router: Router,
private _route: ActivatedRoute) { }

ngOnInit() {
this.employees = this._employeeService.getActiveEmployees();
this.filteredEmployees = this.employees;
}

filterEmployees(searchString: string) {
return this.employees.filter(employee =>
employee.name.toLowerCase().indexOf(searchString.toLowerCase()) !== -1);
}
}

list-employees.component.html : To display the filtered list of employees, in the view template


bind to the filteredEmployees property instead of employees property.

<div *ngFor="let employee of filteredEmployees">


<div (click)="onClick(employee.id)" class="pointerCursor">
<app-display-employee [employee]="employee" #childComponent>
</app-display-employee>
</div>
</div>
293

Now suppose I filter by J and then click on change name button, I expect it to be displaying two
employees but it display only John not the Jordan name because in text box we didn’t change
the name and so setter method is not called, thereby in change name method we need to call
filterEmployee method also, after changing the name with the search text.

In this video we will discuss Query String Parameters in Angular. Creating and reading Query
String Parameters is somewhat similar to creating and reading required and optional route
parameters. We discussed required and optional route parameters in Parts 43 and 44 of
Angular CRUD tutorial.

Query parameters are usually used when you want the parameters on the route to be
optional and when you want to retain those parameters across multiple routes. For
example, on the LIST route, you search for a specific employee. You then click on one of the
employees in the search results to view that specific employee details on the DETAILS route. At
294

this point, when we navigate back to the LIST route we want to retain the search term used, so
we can display the filtered list instead of the full employee list.

Just like optional route parameters, query parameters are not part of the route configuration and
therefore they are not used in route pattern matching.

Passing query string parameters in code : We use the second argument of the Router
service navigate() method to pass query string parameters.

● Create an object with queryParams as the key.


● The value is an object with key/value pairs.
● The key is the name of the query parameter and the value is the value for the query
parameter.

this._router.navigate(['employees', employeeId], {
queryParams: { 'searchTerm': this.searchTerm, 'testParam': 'testValue' }
});

The query string parameters start with a question mark and are separated by & as you can see
below.
http://localhost:4200/employees/3?searchTerm=John&testParam=testValue

Passing query string parameters in the HTML


<a [routerLink]="['/employees']"
[queryParams]="{ 'searchTerm': 'john', 'testParam': 'testValue'}">
List
</a>

Preserve or Merge Query String Parameters : By default, the query string parameters are not
preserved or merged when navigating to a different route. To preserve or merge Query Params
set queryParamsHandling to either preserve or merge respectively.

Preserve query string parameters in code


this._router.navigate(['/employees', this._id], {
queryParamsHandling: 'preserve'
});

Please note : queryParamsHandling is available in Angular 4 and later versions. If you are
using Angular 2, you would not have queryParamsHandling. You will have to use
preserveQueryParams and set it to true. preserveQueryParams is deprecated since Angular 4.
295

Merge query string parameters in code :


this._router.navigate(['/employees', thi
s._id], {
queryParams: { 'newParam': 'newValue' },
queryParamsHandling: 'merge'
});

Preserve query string parameters in the HTML :


<a [routerLink]="['/list']"
queryParamsHandling="preserve">
Back to List
</a>

Merge query string parameters in the HTML :


<a [routerLink]="['/list']"
[queryParams]="{'newParam': 'newValue'}" queryParamsHandling="merge">
Back to List
</a>
296

We can also give merge inspite of preserve. We’ll see difference in a while. If we have any new
query string parameter that we are ading it will merge with existing query string parameter.
297

If we have queryStringParam as preserve, then this new query string parameter will nott be
added it will be only the existing query parameter that will be preserved.

In this video we will discuss, how to read query string parameters in Angular

To read query parameters we use ActivatedRoute service. We use this same service to read
required and optional route parameters.

Inject the ActivatedRoute service using the component class constructor


constructor(private _route: ActivatedRoute) { }

Depending on your project requirements, you can then use either the snapshot approach or
the observable approach. We discussed the difference between these 2 approaches and
when to use one over the other in Part 42 of this Angular CRUD tutorial.

In Angular there are 3 types of parameters.

● Required parameters
● Optional parameters
● Query parameters

When working with any of these parameters, the following properties and methods are very
useful.
298

Member Description

Returns true if the parameter is present and false if not. Very useful to check
has(name)
for the existence of optional route and query parameters

Returns the parameter value as a string if present, or null if not present in the
get(name)
map. Returns the first element if the parameter value is an array of values

Returns a string array of the parameter value if found, or an empty array if the
getAll(name) parameter is not present in the map. Use getAll when a single parameter
could have multiple values

keys Returns a string array of all the parameters in the map

Please note : For required and optional route parameters, we use the paramMap property of
the ActivatedRoute object and for Query Parameters, we use queryParamMap property.

Use the snapshot approach if the query params does not change during the lifetime of this
component.

if (this._route.snapshot.queryParamMap.has('searchTerm')) {
this.searchTerm = this._route.snapshot.queryParamMap.get('searchTerm');
} else {
this.filteredEmployees = this.employees;
}
299

For getAll we get the following attribute. As we have only one value for searchTerm

This is for Keys property :

Here we didn’t get the optional value, because here we are working with the queryParamMAp,
which has three functions to read. If we want the optional value also then we use ParamMap
Property as below. On param map we also see the same function.

Therefore we get our optional route parameter.

Now we include additional route parameter.

Now we get array with two element : both our optional route parameter.

On the other hand, if you expect the query parameter value to change during the life time of this
component, and if you want to react and execute some code in response to that change, then
use the Observable approach.
300

this._route.queryParamMap.subscribe((queryParams) => {
if (queryParams.has('searchTerm')) {
this.searchTerm = queryParams.get('searchTerm');
} else {
this.filteredEmployees = this.employees;
}
});

We retain our list when we go back from view employee, also the filtered list
AS in set searchTerm we are filtering the value.
301

In this video we will discuss, how to create an observable from static data in an array. Along
the way, we will discuss, the 2 most common issues that you will run into when working with
observables in your angular application.

There are several ways to create an observable. The simplest of all is to use Observable.of()
as shown below.

import { Injectable } from '@angular/core';


import { Employee } from '../models/employee.model';

import { Observable } from 'rxjs/Observable';


import 'rxjs/add/observable/of';

@Injectable()
export class EmployeeService {

private listEmployees: Employee[] = [


{ id: 1, name: 'Mark' },
{ id: 2, name: 'Mary' },
{ id: 3, name: 'John' },
];
302

getEmployees(): Observable<Employee[]> {
return Observable.of(this.listEmployees);
}
}

At this point, you might be wondering why do we have to return an Observable<Employee[]>


instead of just an Employee[] from getEmployees() method. Well, this is because in a real
world application, we would not have data hard coded like this in the angular service. We
retrieve it from a database by calling a server side service using the angular http service. The
angular http service returns an observable.

We discussed Observables and calling server side service using the angular HTTP service in
Part 27 of Angular 2 tutorial.

In our upcoming videos in this series, we will discuss calling a server side service. So in
preparation for that, we are creating and returning an Observable.

In a typical real world application, when a server side service is called over HTTP, there may be
some latency and we may not get the data immediately. So to simulate some artificial latency
and understand the implications it can have on the code that consumes this returned
Observable, import and use the delay operator as shown below. Notice we have a delay of
2000 milli-seconds.

import 'rxjs/add/operator/delay';

getEmployees(): Observable<Employee[]> {
return Observable.of(this.listEmployees).delay(2000);
}

//now on page load immediately that we are not going to see the data.

Consider the following code which calls getEmployees() method of EmployeeService. If you
are following along with this course, the following code is from ListEmployeesComponent class
in list-employees.component.ts. The code is commented and self explanatory.

ngOnInit() {
this._employeeService.getEmployees().subscribe((empList) => {
// This code executes asynchronously. When the data is returned
// after the 2 seconds delay, that's when the employees property is set. During those 2
seconds employees property is undefined.
this.employees = empList;
});

// This code will not wait for 2 seconds. After a call to the subscribe() method
303

// is issued, the application continues to process the below lines of code. So for
// those 2 seconds this.employees property is undefined, and within that time, the
// below code is executed which can have 2 serious implications
// 1. The list page will not display any data
// 2. Cannot read property 'length' of undefined error in the console
this._route.queryParamMap.subscribe(params => {
if (params.has('searchTerm')) {
this.searchTerm = params.get('searchTerm');
} else {
this.filteredEmployees = this.employees;
console.log(this.employees.length);
}
});
}

Another problem is if you wanna access the code of any employee eg name it will display
cannot read property of length undefined.

To fix these 2 issues, we want the second block of code to execute synchronously after the
employees property is populated with data returned from the service. To achieve this move the
second block of code, into the callback function passed as a parameter to the subscribe()
method.

ngOnInit() {
this._employeeService.getEmployees().subscribe((empList) => {
this.employees = empList;
304

this._route.queryParamMap.subscribe(params => {
if (params.has('searchTerm')) {
this.searchTerm = params.get('searchTerm');
} else {
this.filteredEmployees = this.employees;
console.log(this.employees.length);
}
});
});
}

At this point, when you navigate to the list route, only a part of the page loads first and after 2
seconds when the data becomes available that's when the page is updated with employee list.
You don't want to display a partial page to the user, while waiting for the data. We will discuss
how to fix this using the resolve guard in our next video.

Summary:
305

In this video we will discuss Angular resolve guard with an example. This is continuation to
Part 53. Please watch Part 53 from Angular CRUD tutorial before proceeding.
306

At the moment, when you navigate to the LIST route, only a part of the page loads first and after
2 seconds when the data becomes available that's when the page is updated with employee list.

You don't want to display a partial page to the user, while waiting for the data. Before we
activate the LIST route and display the view template associated with the LIST route, we want to
pre-fetch the data. Once the data is available, that's when we want to render the view template,
so the end user does not see a partial page.

So in short, we want to delay rendering the routed component view template until all necessary
data have been fetched.

To pre-fetch data for a route we use a route resolver. A route resolver can be implemented as
a function or a service. In this video, let's implement the route resolver as a service. In a later
video, we will discuss how to implement it as a function.

The following are the steps to implement a route resolve guard

Step 1 : Implement the Route Resolver Service. Add a new file to the "employees" folder. Name
it "employee-list-resolver.service.ts". Copy and paste the following code.

import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';


import { Employee } from '../models/employee.model';
import { Observable } from 'rxjs/Observable';
import { Injectable } from '@angular/core';
import { EmployeeService } from './employee.service';

@Injectable()
// Implement the Resolve interface, as we are implementing a route resolve guard
// Resolve interface supports generics, so specify the type of data that this
// resolver returns using the generic parameter
export class EmployeeListResolverService implements Resolve<Employee[]> {
// Inject the employeee service as we need it to retrieve employee data
constructor(private _employeeService: EmployeeService) {
}
// Resolve interface contains the following one method for which we need to
// provide implementation. This method calls EmployeeService & returns employee data
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<Employee[]> {
return this._employeeService.getEmployees();
}
}
307

Step 2 : Register the Route Resolver Service. At the moment we only have one module in our
application and that is the root module - AppModule in app.module.ts file.

import { EmployeeListResolverService } from './employees/employee-list-resolver.service';

@NgModule({
declarations: [...
],
imports: [...
],
providers: [EmployeeListResolverService],
bootstrap: [AppComponent]
})
export class AppModule {}

Step 3 : Add the Route Resolver Service to the route for which we want to pre-fetch data. In our
case we want to add the EmployeeListResolverService on the LIST route. To do this use the
resolve property of the route configuration as shown below. Notice, the value for resolve
property is an object with a key and a value. The key is employeeList. You can name it anything
you want. The value is the resolver service, which provides the employee data. When the
angular router sees this configuration, it knows it has to prefetch the employee list data, before it
can activate the LIST route and display it's associated view template.

const appRoutes: Routes = [


{
path: 'list',
component: ListEmployeesComponent,
resolve: { employeeList: EmployeeListResolverService }
},
// other routes
308

];

Step 4 : Read the pre-fetched data from the ActivatedRoute. In our case, modify the code in
ListEmployeesComponent class to read the data from the route. Since the route resolver
service is prefetching the data, we do not need EmployeeService reference in this component
anymore. So remove all the EmployeeService references from this component, including the
import statement.

export class ListEmployeesComponent implements OnInit {


// rest of the code
constructor(private _employeeService: EmployeeService,
private _router: Router,
private _route: ActivatedRoute) {

this.employees = this._route.snapshot.data['employeeList'];

if (this._route.snapshot.queryParamMap.has('searchTerm')) {
this.searchTerm = this._route.snapshot.queryParamMap.get('searchTerm');
} else {
this.filteredEmployees = this.employees;
}
}

ngOnInit() {
}
// rest of the code
}
309

At this point, when we navigate to the LIST route, the angular router, first pre-fetches the data,
before activating the route and rendering it's associated view template. At the moment, while we
are waiting for the route resolver to fetch data, the end user does not have any visual clue
indicating that the request is being processed. In our upcoming videos we will discuss, how to
display loading...icon.

In this video we will discuss the sequence of navigation events that are triggered by the
angular router, when navigating from one route to another route. These navigation events
range from when the navigation starts and ends to many points in between.

To see these navigation events in action, set enableTracing option to true as shown below.
Enabling tracing logs all the router navigation events to the browser console.

RouterModule.forRoot(appRoutes, { enableTracing: true })

When we click on create :-


310

First event triggered is navigation start.this event is triggered when the route navigation starts.
Then we have routeRecognized, again as name specifies this event is triggered when the
routes are recognized.
Followed by that we have gueardCheckStart, event is triggered when the route guard starts it’s
execution.
Some of the events also have their corresponding end events.
For eg: NavigationStart has NavigationEnd.
GuardCheckStart has GguardCheckEnd.

The following list shows some of the navigation events

● NavigationStart
● NavigationEnd
● RoutesRecognized
● GuardsCheckStart
● GuardsCheckEnd
● NavigationCancel-when navigation is cancelled by a route guard
● NavigationError-when there is an error while navigatiing from one route to another.
● ChildActivationStart
● ChildActivationEnd
● ActivationStart
● ActivationEnd
● ResolveStart
● ResolveEnd

Now suupose we have our guard canDeactivate an dour form is dirty, now when we try and
move from create to list it shows us that we may loose our data if we navigate cz of
canDeactivate guard.so at that time w ehave if we cancel the navigation by clicking cancel.

GuardCheckStart

GuardCheckEnd

● NavigationCancel-when
311

What are the use cases of these navigation events

● Monitor routes
● Troubleshoot when routing does not work as expected
● Display a loading message if there is a delay when navigating from one route to another
(We will discuss this in our next video)for eg whne we navigate to list tab from craete
there is a nd delay of 2seconds

In this video we will discuss how to display a loading indicator if there is a delay when
navigating from one route to another route in an angular application. This is continuation to Part
55. Please watch Part 55 from Angular CRUD tutorial before proceeding.

At the moment in our application, when we navigate to the LIST route, it will take 2 seconds to
pre-fetch data required for the LIST route. This is because, we have a route resolver configured
on the LIST route. We implemented this route resolver in Part 54 of Angular CRUD tutorial.

During the 2 seconds wait time, while the route resolve guard is busy retrieving the required
data, we want to display a loading indicator, so the user knows the application is busy
processing the request and he does not end up clicking on the link multiple times.

To implement the loading indicator, we are going to make use of the Angular Router
Navigation events. We discussed these events in our previous video. These navigation events
range from when the navigation starts and ends to many points in between. When the
navigation starts, we want to show the loading indicator, and when the navigation ends, hide the
loading indicator.

To be able to react and execute some code in response to the router navigation events,
subscribe to the Angular router events observable.

Step 1 : Modify the code in Root Component (AppComponent) in app.component.ts as shown


below.
Because of this._router.events.subscribe((routerEvent: Event), every time router emits a
navigation event we are notified. Because we are subscribed to it.

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


// Import the Router and navigation events
import {
Router, NavigationStart, NavigationEnd,
NavigationCancel, NavigationError, Event
} from '@angular/router';
312

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
// We will use this property to show or hide
// the loading indicator
showLoadingIndicator = true;

// Inject the Angular Router


constructor(private _router: Router) {
// Subscribe to the router events observable
this._router.events.subscribe((routerEvent: Event) => {

// On NavigationStart, set showLoadingIndicator to ture


if (routerEvent instanceof NavigationStart) {
this.showLoadingIndicator = true;
}

// On NavigationEnd or NavigationError or NavigationCancel


// set showLoadingIndicator to false
if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationError ||
routerEvent instanceof NavigationCancel) {
this.showLoadingIndicator = false;
}

});
}
}

Step 2 : Bind to the showLoadingIndicator property in the view template of our root
component i.e AppComponent in app.component.html file.

<div class="container">
<nav class="navbar navbar-default">
<ul class="nav navbar-nav">
<li>
<a routerLink="list" queryParamsHandling="preserve">List</a>
</li>
<li>
<a routerLink="create">Create</a>
313

</li>
<li>
<a [routerLink]="['employees',2]">
Get Employee with Id 2
</a>
</li>
</ul>
</nav>
<router-outlet>
</router-outlet>
<!-- Bind to showLoadingIndicator property in the component class -->
<div *ngIf="showLoadingIndicator" class="spinner"></div>
</div>

Step 3 : We are using CSS animations to get the effect of a loading spinner. Place the following
CSS in app.component.css file

.spinner {
border: 16px solid silver;
border-top: 16px solid #337AB7;
border-radius: 50%;
width: 80px;
height: 80px;
animation: spin 700ms linear infinite;
top:50%;
left:50%;
position: absolute;
}
@keyframes spin {
0% { transform: rotate(0deg) }
100% { transform: rotate(-360deg) }
}

The following website has different loading spinners.


https://loading.io/css/

We have to slightly modify some of the CSS properties to be able to use them on our list page. I
made the following changes.

● Removed display property


● Changed position property value from relative to fixed
● Add top and left properties and set them to 50%
● Changed background property value to #337AB7;

With the above changes I have a spinner as shown below.


314

n this video we will discuss how to display a loading indicator if there is a delay when
navigating from one route to another route in an angular application. This is continuation to Part
55. Please watch Part 55 from Angular CRUD tutorial before proceeding.

At the moment in our application, when we navigate to the LIST route, it will take 2 seconds to
pre-fetch data required for the LIST route. This is because, we have a route resolver configured
on the LIST route. We implemented this route resolver in Part 54 of Angular CRUD tutorial.

During the 2 seconds wait time, while the route resolve guard is busy retrieving the required
data, we want to display a loading indicator, so the user knows the application is busy
processing the request and he does not end up clicking on the link multiple times.

To implement the loading indicator, we are going to make use of the Angular Router
Navigation events. We discussed these events in our previous video. These navigation events
range from when the navigation starts and ends to many points in between. When the
navigation starts, we want to show the loading indicator, and when the navigation ends, hide the
loading indicator.

To be able to react and execute some code in response to the router navigation events,
subscribe to the Angular router events observable.

Step 1 : Modify the code in Root Component (AppComponent) in app.component.ts as shown


below.

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


// Import the Router and navigation events
import {
Router, NavigationStart, NavigationEnd,
NavigationCancel, NavigationError, Event
} from '@angular/router';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
// We will use this property to show or hide
315

// the loading indicator


showLoadingIndicator = true;

// Inject the Angular Router


constructor(private _router: Router) {
// Subscribe to the router events observable
this._router.events.subscribe((routerEvent: Event) => {

// On NavigationStart, set showLoadingIndicator to ture


if (routerEvent instanceof NavigationStart) {
this.showLoadingIndicator = true;
}

// On NavigationEnd or NavigationError or NavigationCancel


// set showLoadingIndicator to false
if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationError ||
routerEvent instanceof NavigationCancel) {
this.showLoadingIndicator = false;
}

});
}
}

Step 2 : Bind to the showLoadingIndicator property in the view template of our root
component i.e AppComponent in app.component.html file.

<div class="container">
<nav class="navbar navbar-default">
<ul class="nav navbar-nav">
<li>
<a routerLink="list" queryParamsHandling="preserve">List</a>
</li>
<li>
<a routerLink="create">Create</a>
</li>
<li>
<a [routerLink]="['employees',2]">
Get Employee with Id 2
</a>
</li>
</ul>
</nav>
316

<router-outlet>
</router-outlet>
<!-- Bind to showLoadingIndicator property in the component class -->
<div *ngIf="showLoadingIndicator" class="spinner"></div>
</div>

Step 3 : We are using CSS animations to get the effect of a loading spinner. Place the following
CSS in app.component.css file

.spinner {
border: 16px solid silver;
border-top: 16px solid #337AB7;
border-radius: 50%;
width: 80px;
height: 80px;
animation: spin 700ms linear infinite;
top:50%;
left:50%;
position: absolute;
}
@keyframes spin {
0% { transform: rotate(0deg) }
100% { transform: rotate(-360deg) }
}

The following website has different loading spinners.

With the above changes I have a spinner as shown below.


317

In this video we will discuss implementing CanActivate guard in Angular with an example.

As the name implies CanActivate guard determines if a route can be activated. There are
several use cases for CanActivate guard.

● We can use it to check if the user is authenticated before allowing him to access
protected areas of the application. If he is not authenticated, we redirect him to
AccessDenied component or Login component.
● Similarly we can use it to check, if a specific resource he is looking for exists. if the
resource does not exist we redirect him to the PageNotFound component. If the
resource exists, we redirect the user to navigate the resource.

In our example, we use a URL as shown below to access a specific employee details. The
number 1 in the URL is the employee ID.
http://localhost:4200/employees/1

There is nothing stopping the end user from typing an employee id in the URL that does not
exist. In this case it will show an empty design template. So here is what we want to do.

If the employee with the specified id in the URL exists, then the navigation should be allowed to
continue and view that specific employee details. On the other hand, if the employee does not
exist, then it should redirect the user to pageNotFound component.
318

Use the following Angular CLI command to generate pageNotFound component. This command
also updates the Root module file to import this newly generated component and add it to the
declarations array.

ng g c pageNotFound --flat

Include the following <h1> element in page-not-found.component.html file


<h1>The resource you are looking for cannot be found</h1>

Include the following style for <h1> element in page-not-found.component.css file


h1{
color: red
}

In the root module file, app.module.ts include a route for pageNotFound component.
const appRoutes: Routes = [
{
path: 'list',
component: ListEmployeesComponent,
resolve: { employeeList: EmployeeListResolverService }
},
{
path: 'create',
component: CreateEmployeeComponent,
canDeactivate: [CreateEmployeeCanDeactivateGuardService]
},
{ path: 'employees/:id', component: EmployeeDetailsComponent },
{ path: '', redirectTo: '/list', pathMatch: 'full' },
{ path: 'notfound', component: PageNotFoundComponent },
];

At this point if you navigate to http://localhost:4200/notfound, you will see pageNotFound


component.

Now let's implement CanActivate guard. This guard should check if the employee with the
specified id in the URL exists. If the employee does not exist, then it should redirect the user to
pageNotFound component. If the employee exists, then it should allow the navigation to
continue and view that specific employee details.

Step 1 : Add a new file to the "employees" folder. Name it employee-details-guard.service.ts.


Copy and paste the following code.
import {
CanActivate, Router,
ActivatedRouteSnapshot,
319

RouterStateSnapshot
} from '@angular/router';
import { Injectable } from '@angular/core';
import { EmployeeService } from './employee.service';

@Injectable()
// Make the class implement CanActivate interface as
// we are implementing CanActivate guard service
export class EmployeeDetailsGuardService implements CanActivate {
constructor(private _employeeService: EmployeeService,
private _router: Router) { }

// Provide implementation for canActivate() method of CanActivate interface


// Return true if navigation is allowed, otherwise false
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
const employeeExists = !!this._employeeService.getEmployee(+route.paramMap.get('id'));

if (employeeExists) {
return true;
} else {
this._router.navigate(['/notfound']);
return false;
}
}
}

Step 2 : Register the guard with angular dependency injection system : Since CanActivate
guard is implemented as a service, we need to register it in a module. At the moment we have
only one module in our application and that is the root module AppModule. Import and register
EmployeeDetailsGuardService in app.module.ts file using the providers property.
import { EmployeeDetailsGuardService } from './employees/employee-details-
guard.service';

@NgModule({
declarations: […
],
imports: […
],
providers: [EmployeeDetailsGuardService],
bootstrap: [AppComponent]
})
export class AppModule { }
320

Step 3 : Tie the guard to a route : We want to guard navigation to employee details, so tie the
route guard with the "employees/:id" route in app.module.ts file as shown below.
const appRoutes: Routes = [
{
path: 'list',
component: ListEmployeesComponent,
resolve: { employeeList: EmployeeListResolverService }
},
{
path: 'create',
component: CreateEmployeeComponent,
canDeactivate: [CreateEmployeeCanDeactivateGuardService]
},
{
path: 'employees/:id',
component: EmployeeDetailsComponent,
canActivate: [EmployeeDetailsGuardService]
},
{ path: '', redirectTo: '/list', pathMatch: 'full' },
{ path: 'notfound', component: PageNotFoundComponent },
];
321

In this video we will discuss different ways of passing data between components.

There are several techniques to pass data between components in angular. We discussed most
of these techniques in our previous videos in this series. If the components are nested, then
there is a parent child relationship between those components. To pass data from the parent to
child component we use input properties. To pass data from the child component to parent
component we can either use output properties or template reference variables.

Passing data from Parent Component to Child Component


322

Part 21 - Angular 2 tutorial


Input Properties
Part 33 - Angular CRUD tutorial

Passing data from Child Component to Parent Component

Part 22 - Angular 2 tutorial


Output Properties
Part 37 - Angular CRUD tutorial

Template Reference Variables Part 38 - Angular CRUD tutorial

Passing data from Component to Component (No parent child relation)

Angular Service Part 34 - Angular 2 tutorial

Required Route Parameters Part 40 - Angular CRUD tutorial

Optional Route Parameters Part 43 - Angular CRUD tutorial

Query Parameters Part 51 - Angular CRUD tutorial

Let's use some of these techniques to pass data between components. Along the way we will
refactor the code in the angular application that we have been working with so far in this video
series.
323

This will give us little more practice with component communication techniques. We are
refactoring code in our application in preparation for performing DELETE and UPDATE
operations in our upcoming videos. By the end of this video, our Employee List page should be
as shown below.
324

Changes in list-employees.component.html

● Remove the style property on "Search By Name" text box so the width spans 100%
● Remove "Change Employee Name" button along with the <div> element that surrounds
it
● Delete changeEmployeeName() method in the component class (list-
employees.component.ts)
● #childComponen template reference variable is not required on <app-display-employee>
child componenet. So delete it.
● Remove the <div> element that surrounds <app-display-employee> child component.
● Delete "onClick(employee.id)" method in the component class (list-
employees.component.ts)
● Delete (mousemove) event binding and the corresponding method in the component
class
● We want to pass the Search Term we type in the "Search By Name" textbox to the
child component DisplayEmployeeComponent. So notice on <app-display-employee>
we are binding to searchTerm input property. We do not have this searchTerm input
property DisplayEmployeeComponent yet. We will implement that in just a bit.

At this point, the HTML in list-employees.component.html file should be as shown below


<div class="form-group">
<input type="text" class="form-control"
325

placeholder="Search By Name" [(ngModel)]="searchTerm" />


</div>
<div *ngFor="let employee of filteredEmployees">
<app-display-employee [employee]="employee" [searchTerm]="searchTerm">
</app-display-employee>
</div>

In list-employees.component.ts file we are not using the Router service anymore. So remove
it from the import statement and the constructor. At this point the code in list-
employees.component.ts should be as shown below.
import { Component, OnInit } from '@angular/core';
import { Employee } from '../models/employee.model';
import { ActivatedRoute } from '@angular/router';

@Component({
templateUrl: './list-employees.component.html',
styleUrls: ['./list-employees.component.css']
})
export class ListEmployeesComponent implements OnInit {
employees: Employee[];
filteredEmployees: Employee[];

private _searchTerm: string;


get searchTerm(): string {
return this._searchTerm;
}
set searchTerm(value: string) {
this._searchTerm = value;
this.filteredEmployees = this.filtereEmployees(value);
}

filtereEmployees(searchString: string) {
return this.employees.filter(employee =>
employee.name.toLowerCase().indexOf(searchString.toLowerCase()) !== -1);
}

constructor(private _route: ActivatedRoute) {

this.employees = this._route.snapshot.data['employeeList'];

if (this._route.snapshot.queryParamMap.has('searchTerm')) {
this.searchTerm = this._route.snapshot.queryParamMap.get('searchTerm');
} else {
this.filteredEmployees = this.employees;
326

ngOnInit() {
}
}

Changes in display-employee.component.html file : Include a panel footer. The HTML


required for the panel footer is shown below. Paste this HTML just before the last closing </div>
element in display-employee.component.html file.
<div class="panel-footer">
<button class="btn btn-primary" (click)="viewEmployee()">View</button>
<button class="btn btn-primary">Edit</button>
<button class="btn btn-danger">Delete</button>
</div>

Changes in display-employee.component.css file : Include the following style for the View,
Edit and Delete buttons so all of them have the same width.
button.btn {
width: 70px;
}

Changes in display-employee.component.ts file : Include searchTerm input properrty. This is


the property to which the parent component (ListEmployeesComponent) is binding ans passing
the searchTerm we have typed in the "Search By Name" textbox.

Also, include viewEmployee() method. This is the method that is called when "View" button is
clicked.
import { Component, OnInit, Input } from '@angular/core';
import { Employee } from '../models/employee.model';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
selector: 'app-display-employee',
templateUrl: './display-employee.component.html',
styleUrls: ['./display-employee.component.css']
})
export class DisplayEmployeeComponent implements OnInit {
@Input() employee: Employee;
@Input() searchTerm: string;

private selectedEmployeeId: number;


327

constructor(private _route: ActivatedRoute, private _router: Router) { }

ngOnInit() {
this.selectedEmployeeId = +this._route.snapshot.paramMap.get('id');
}

viewEmployee() {
this._router.navigate(['/employees', this.employee.id], {
queryParams: { 'searchTerm': this.searchTerm }
});
}
}

Changes in employee-details.component.html file : Modify "Back to List" button as shown


below. Notice we have removed [queryParams] directive. We included this [queryParams]
directive to demonstrate merging query parameters. Also set queryParamsHandling to preserve
instead of merge.
<a class="btn btn-primary" [routerLink]="['/list',{id:employee.id}]"
queryParamsHandling="preserve">
Back to List
</a>

Changes in app.component.html file : In the menu we do not need "Get Employee with Id 2"
link. So remove the associated menu item.

In our upcoming videos we will discuss implementing UPDATE and DELETE operations.

In this video we will discuss editing and updating data in Angular. To set the expectations
right, we will be updating data on the client side. We will discuss persisting the data to a
database table in our upcoming videos when we implement the server side service.

We are going to modify "Create Employee Form" so it supports both

● Creating a new employee


● Editing and updating an existing employee
328

Changes in Root Module file (app.module.ts) : Change the path in the following route from
"create" to "edit/:id"

Existing Route

{
path: 'create',
component: CreateEmployeeComponent,
canDeactivate: [CreateEmployeeCanDeactivateGuardService]
}

Updated Route to support both creating a new employee and editing an existing employee. If
the id route parameter value is 0, then the form will be used to create a new employee. If the id
value is not 0, then the form will be used to edit an existing employee.

{
path: 'edit/:id',
component: CreateEmployeeComponent,
canDeactivate: [CreateEmployeeCanDeactivateGuardService]
}

Changes in create-employee.component.html : The value attribute of email and phone radio


buttons is set to Email and Phone respectively to match with the employee data. Otherwise
when we select to edit an existing employee, his saved contact preference value will not be
bound to the correct radio button. The change is bolded and italicized.

<div class="form-group" [class.has-error]="contactPreference.invalid


&& contactPreference.touched">
<label class="control-label">Contact Preference</label>
<div class="form-control">
<label class="radio-inline">
<input type="radio" required #contactPreference="ngModel"
name="contactPreference" value="Email"
[(ngModel)]="employee.contactPreference"> Email
</label>
<label class="radio-inline">
<input type="radio" required #contactPreference="ngModel"
name="contactPreference" value="Phone"
[(ngModel)]="employee.contactPreference"> Phone
</label>
</div>
<span class="help-block" *ngIf="contactPreference.errors?.required
&& contactPreference.touched">
329

Contact Preference is required


</span>
</div>

The required attribute value is updated to include capital E in Email for the validation to work
correctly.

<div class="form-group" [class.has-error]="email.invalid">


<label for="email" class="control-label">Email</label>
<input id="email" [required]="contactPreference.value=='Email'"
type="text" class="form-control" [(ngModel)]="employee.email"
#email="ngModel" name="email"
pattern="^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$">
<span class="help-block" *ngIf="email.errors?.required">
Email is required
</span>
<span class="help-block" *ngIf="email.errors?.pattern && email.touched">
Email is Invalid
</span>
</div>

The required attribute value is updated to include capital P in Phone for the validation to work
correctly.

<div class="form-group" [class.has-error]="phoneNumber.invalid">


<label for="phoneNumber" class="control-label">Phone Number</label>
<input id="phoneNumber" [required]="contactPreference.value=='Phone'"
#phoneNumber="ngModel" class="form-control" type="text"
name="phoneNumber" [(ngModel)]="employee.phoneNumber">
<span class="help-block" *ngIf="phoneNumber.errors?.required">
Phone Number is required
</span>
</div>

The value attribute of male and female radio buttons is set to Male and Female respectively to
match with the employee data. Otherwise when we select to edit an existing employee, his
saved gender value will not be bound to the correct radio button. The change is bolded and
italicized.

<div class="form-group"
[class.has-error]="gender.invalid && gender.touched">
<label class="control-label">Gender</label>
<div class="form-control">
<label class="radio-inline">
330

<input type="radio" name="gender" required #gender="ngModel"


value="Male" [(ngModel)]="employee.gender"> Male
</label>
<label class="radio-inline">
<input type="radio" name="gender" required #gender="ngModel"
value="Female" [(ngModel)]="employee.gender"> Female
</label>
</div>
<span class="help-block" *ngIf="gender.errors?.required && gender.touched">
Gender is required
</span>
</div>

Now when we change name of employee to Mary to MAry 1 and try and go to list component, it
shows us canDeactivate method stating if we navigate the form all our changes wills be
discarded. We click ok on popup, ideally the name of employee should be mary only but it
changes to Mary1 which is not the expected behaviour. We have not clicked the save button
mind it.

Notice, I have included ngIf to show the "Image Preview" button and the image, only if there is
a value in the photoPath field. This avoids unnecessary 404 errors in the browser console.

<div class="form-group">
<button type="button" (click)="togglePhotPreview()" class="btn btn-primary"
*ngIf="employee.photoPath !=='' && employee.photoPath !==null">
{{previewPhoto ? "Hide " : "Show " }} Preview
</button>
</div>

<div class="form-group">
<img [src]="employee.photoPath" height="200" width="200"
*ngIf="previewPhoto && employee.photoPath !=='' && employee.photoPath !==null"/>
</div>

Changes in create-employee.component.ts : Change ngOnInit() and saveEmployee()


employee methods as shown below. Also please do not forget to include the following
panelTitle property.

panelTitle: string;

// Subscribe to route parameter changes and react accordingly


ngOnInit() {
this._route.paramMap.subscribe(parameterMap => {
331

const id = +parameterMap.get('id');
this.getEmployee(id);
});
}

saveEmployee(empForm: NgForm): void {


const newEmployee = Object.assign({}, this.employee);
this._employeeService.save(newEmployee);
empForm.reset();
this._router.navigate(['list']);
}

This is cz this.employee = this._employeeService.getEmployee(id); and our form is bound to


this.employee form.so whenever we make a change to any field in our form, the object that
this.employee is pointing to is automatically updated. To solve this we have to copy the vallue of
object into the another object and assign that object as a value to this.employee property.Now
our value willnot be updated if we move from modify to list without saving the details.

Now we have one more issue, if we go and edit one of employee details, earlier we had contact
prefernce set as phone but now we want it to be email. So we select email and it starts showing
error for email field required. Now at this point i want to create a new employee and i click on
create tab. I get the blank page but still i have the email validation error on the blank new page,
which is not as expected.
We need to reset the form as well as below
332

private getEmployee(id: number) {


// If the id is 0, we want to create a new employee. So we intialise the employee
// property with an Employee object with all properties set to null. The template
// is bound to this employee property so all the form fields are displayed blank,
// to enter details of a new employee we want to create
if (id === 0) {
this.employee = {
id: null,
name: null,
gender: null,
contactPreference: null,
phoneNumber: null,
email: '',
dateOfBirth: null,
department: null,
isActive: null,
photoPath: null
};
// Resetting the form, resets any previous validation errors
this.createEmployeeForm.reset();
this.panelTitle = 'Create Employee';
} else {
// If the Id is not 0, then retrieve the respective employee using the employee
// service. Copy the values into a new object and assign that object as the value
// for the employee property. Otherwise the employee property holds a reference
// to the employee object in the array in the EmployeeService. This means any
// changes we make on the form are automatically saved, without we explicitly
// saving by clicking the Save button.
this.employee = Object.assign({}, this._employeeService.getEmployee(id));
this.panelTitle = 'Edit Employee';
}
}

Changes in employee.service.ts : Change save() method as shown below


One way is pushing the element in array like
333

Second way is using the reduce method.

save(employee: Employee) {
if (employee.id === null) {
// reduce() method reduces the array to a single value. This method executes
// the provided function for each element of the array (from left-to-right)
// When we implement the server side service to save data to the database
// table, we do not have to compute the id, as the server will assing it
const maxId = this.listEmployees.reduce(function (e1, e2) {
return (e1.id > e2.id) ? e1 : e2;
}).id;
employee.id = maxId + 1;

this.listEmployees.push(employee);
} else {
const foundIndex = this.listEmployees.findIndex(e => e.id === employee.id);
this.listEmployees[foundIndex] = employee;
334

}
}

Changes in display-employee.component.html : Bind editEmployee() method to the click


event of the "Edit" button

<button class="btn btn-primary" (click)="editEmployee()">Edit</button>

Changes in display-employee.component.ts : Include the following editEmployee() method

editEmployee() {
this._router.navigate(['/edit', this.employee.id]);
}

Changes in app.component.html : Change the "Create" menu item to point to our new route
"edit/0".

<li>
<a routerLink="edit/0">Create</a>
</li>

Dynamically set the title to create or edit


335
336

VIDEO - 60

For deleting an object :-

Create amethod delete in service class and pass employee id to the same.
In method first check do we have employee by that id or not?

Create a method delete in compoent class which will be binded to the html

We are just deleting the employee on client side which means when we reload the application
we will get back all the employees.

Delete doesn’t wrk as expected, cz when i filter the employee for eg John and click on delete
button it appears that nothing has happened.

Behind the scene it deleted john from our service but our ui wasn’t updated.
337

When we remove our filter within our list we don’t have John any more.

Becuse view template is binding to property filtered employeees

If you see here

in constructir we are storing the employeelist from route resolver to employees variable. And
when we don’t have serach term we are settig the filteredEmployee to this.employee.(To be
continued)

In this video we will discuss

● How to implement simple accordion type of functionality in Angular


● Difference between ngIf directive and hidden property in Angular 2 and later versions

Implementing accordion type of functionality in Angular 2 and later versions : When the
panel title is clicked, the panel body and the footer must be collapsed. Clicking on the panel title
again, must expand the collapsed panel body and footer.
338

There are several ways to do this. One way is to implement this accordion functionality in the
component itself where we need it. For example, in our case the display logic for the employee
is present in DisplayEmployeeComponent. So we include accordion functionality also in the
DisplayEmployeeComponent.

The benefit of this approach is, it is very easy to implement. The downside is, we cannot reuse
this accordion functionality in another component if we need it there. We have to re-implement
this same functionality again in that component.

In our next video we will discuss how to extract common accordion functionality into a separate
component using content projection, so it can be reused anywhere in the application where
we need that accordion type of functionality.

In this video let's implement accordion functionality in the DisplayEmployeeComponent itself.

Changes in display-employee.component.ts : Include the following panelExpanded boolean


property in the component class. Notice we have initialised it to true, so the employee panel is
expanded by default when the page first loads.

panelExpanded = true;

Changes in display-employee.component.css : Include the following "pointerCursor" class


to make the cursor, a pointer when hovered over panel title, so the end user knows, it is
clickable.

.pointerCursor {
cursor: pointer;
}

Changes in display-employee.component.html : To show and hide panel body and footer,


we are using ngIf structural directive. ngIf removes the element from the DOM completely when
the condition is false and adds the element back once the condition becomes true. So every
time, we click the panel title, the panel body and footer are either added to the DOM or removed
from the DOM depending on whether the condition is true or false.

<div class="panel panel-primary"


[class.panel-success]="selectedEmployeeId === employee.id">
<!-- pointerCursor class changes the cursor style to pointer when hovered over
the employee panel title. When clicked on the title, the panelExpanded
boolean property is toggled from true to false & vice-versa. We use this
property to toggle the visibility of panel body & footer -->
339

<div class="panel-heading pointerCursor"


(click)="panelExpanded = !panelExpanded">
<h3 class="panel-title">{{employee.name | uppercase}}</h3>
</div>
<!-- Render panel-body <div> only if panelExpanded property is true -->
<div class="panel-body" *ngIf="panelExpanded">
<!-- Rest of the HTML -->
</div>
<!-- Render panel-footer <div> only if panelExpanded property is true -->
<div class="panel-footer" *ngIf="panelExpanded">
<!-- Rest of the HTML -->
</div>
</div>

In our case, the user of the application may toggle the visibility several times. So from a
performance standpoint, it is better to show and hide the panel body and footer, rather than
removing from the DOM and adding them back again when the condition is true. To show and
hide we use the hidden property as shown below.

Div and footer are removed from the DOM.


When clicked again DIV elements are added to the DOM.

<div class="panel-body" [hidden]="!panelExpanded">


<!-- Rest of the HTML -->
</div>
<div class="panel-footer" [hidden]="!panelExpanded">
<!-- Rest of the HTML -->
</div>
340

We can also use hidden property to do so.

It adds the hidden attribute when we click on the accordion to hide it.

ngIf vs hidden in Angular

● ngIf adds or removes element from the DOM where as hidden property hides and
shows the element by adding and removing display: none style.
● If you frequently toggle the visibility of an element, it is better to use hidden property from
a performance standpoint
● If you know you will not need to show an element, then ngIf is better. For example, you
are logged in as a NON-Administrator and there is a report component on the page that
should be displayed only to the Administrators. Since you are logged in as a NON-
Administrator, using ngIf to hide the report component is better from a performance
standpoint. Since ngIf does not add the element to the DOM, it also does not execute
the code associated with that report component. If you use hidden property instead, the
report component will be constructed, all it's associated code is executed, the
component is added to the DOM, and to keep it hidden from the non-administrator it
uses display:none style. So in short, if you frequently toggle the visibility of an element, it
is better to use hidden property. On the other hand, if you know the element will remain
hidden and the user does not have the ability to toggle the visibility, then use ngIf
structural directive.

and the collapsed panel body and footer.


341

Another important requirement is, this accordion panel must be reusable with any other
component in our application. The component that uses this accordion panel component, must
be able to specify what content it wants in the accordion panel body and footer.

For example, if we use this accordion panel, with a ProductComponent that displays a
product, then in the accordion panel body, the ProductComponent may want to project and
display product image, price, weight etc. In the footer, the ProductComponent may want to
project and display buttons to customise the product or buy.

In our case we want to use this accordion panel, with DisplayEmployeeComponent. So in the
panel body we want to project and display, employee photo, gender, date of birth, email etc. In
the footer, we want to project and display buttons to View, Edit and Delete employee as shown
below.
342

So the important question that we need to answer is, how will the components that use this
accordion component be able to inject variable content into the acoordion panel body and
footer.
By using <ng-content> tag

As you can see in the image below, you can think of this <ng-content> as a place holder for the
variable content. In a bit we will understand, how a component that uses this accordion
component can project variable content depending on the requirements of your application.
343

First, let's create our reusable accordion component. This is a reusable component and can be
used by another component in our application. So, let's place this component in the "Shared"
folder. Use the following Angular CLI command to create the component.
ng g c shared/accordion --flat

accordion.component.ts : Notice, in the component class we have introduced 3 properties.


The code is commented and self-explanatory.

export class AccordionComponent implements OnInit {


// We use this property to set a different CSS class on the employee
// panel if we have just viewed his details
@Input() hasJustViewed: boolean;
// Sets the panel title, in our case the name of the employee
@Input() title: string;
// Controls hiding and showing panel body and footer
@Input() isHidden = false;

constructor() { }

ngOnInit() {
}
}

accordion.component.css : The "pointerCursor" class makes the cursor a pointer when


hovered over panel title, so the end user knows, it is clickable.
344

.pointerCursor {
cursor: pointer;
}

accordion.component.html : As you can see, we have defined the shell for the accordion
panel i.e accordion panel header, body and footer. We also have encapsulated the logic in this
component to show and hide the panel body and footer. But the content that goes in the panel
body and footer will be decided by the component that consumes this accordion component.
The consuming component will also need to bind and pass data for the 3 input properties (title,
isHidden and hasJustViewed).

<!-- Add panel-success class only if hasJustViewed property is true -->


<div class="panel panel-primary" [class.panel-success]="hasJustViewed">
<!-- pointerCursor class changes the cursor style to pointer when hovered
over the employee panel title. When clicked on the title, isHidden
boolean property is toggled from true to false & vice-versa. We use
this property to toggle the visibility of panel body & footer -->
<div class="panel-heading pointerCursor" (click)="isHidden = !isHidden">
<h3 class="panel-title">{{title | uppercase}}</h3>
</div>
<div class="panel-body" [hidden]="isHidden">
<!-- ng-content specifies the slot into which the content will be projected
by the component that consumes this accordion component -->
<ng-content select=".myPanelBody"></ng-content>
</div>
<div class="panel-footer" [hidden]="isHidden">
<!-- Another slot into which the content can be projected. Since we have more
than one slot into which the content can be projected, this is called
multi-slot content projection-->
<ng-content select=".myPanelFooter"></ng-content>
</div>
</div>

Changes in display-employee.component.html : The changes are commented and self-


explanatory.

<!-- Pass employee name as the value for title input property. Also set
isHidden input propety to false if you want the panel body and footer
to be collapsed on the initial page load.-->
<app-accordion [title]="employee.name" [isHidden]="true"
[hasJustViewed]="selectedEmployeeId === employee.id">
<!-- Notice myPanelBody css class is present on this <div>. This CCS class is
used as the selector on the <ng-content> tag in accordion component. So all this
345

content in this DIV will be projected at the location where we have <ng-content>
tag with css class selector .myPanelBody -->
<div class="col-xs-10 myPanelBody">
<div class="row vertical-align">
<div class="col-xs-4">
<img class="imageClass" [src]="employee.photoPath" />
</div>
<div class="col-xs-8">

<div class="row">
<div class="col-xs-6">
Gender
</div>
<div class="col-xs-6">
: {{employee.gender}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Date of Birth
</div>
<div class="col-xs-6">
: {{employee.dateOfBirth | date}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Contact Preference
</div>
<div class="col-xs-6">
: {{employee.contactPreference}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Phone
</div>
<div class="col-xs-6">
: {{employee.phoneNumber}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Email
346

</div>
<div class="col-xs-6">
: {{employee.email}}
</div>
</div>
<div class="row">
<div class="col-xs-6">
Department
</div>
<div class="col-xs-6" [ngSwitch]="employee.department">
:
<span *ngSwitchCase="1">Help Desk</span>
<span *ngSwitchCase="2">HR</span>
<span *ngSwitchCase="3">IT</span>
<span *ngSwitchCase="4">Payroll</span>
<span *ngSwitchDefault>N/A</span>
</div>
</div>
<div class="row">
<div class="col-xs-6">
Is Active
</div>
<div class="col-xs-6">
: {{employee.isActive}}
</div>
</div>

</div>
</div>
</div>
<!-- The content in the following DIV will be projected at the location
where we have <ng-content> tag with css selector .myPanelFooter -->
<div class="myPanelFooter">
<button class="btn btn-primary" (click)="viewEmployee()">View</button>
<button class="btn btn-primary" (click)="editEmployee()">Edit</button>
<span *ngIf="confirmDelete">
<span>Are you sure you want to delete ?</span>
<button class="btn btn-danger" (click)="deleteEmployee()">Yes</button>
<button class="btn btn-primary" (click)="confirmDelete=false">No</button>
</span>
<span *ngIf="!confirmDelete">
<button class="btn btn-danger" (click)="confirmDelete=true">Delete</button>
</span>
</div>
347

</app-accordion>

At the moment we are using class selector to match the projection content with the ng-content
slot. We can use any of the CSS selectors (class selector, element selector, attribute selector
etc)

In order to collapse accordion by default:


348

What is REST API


REST stands for Representational State Transfer. REST is an architectural pattern for creating
an API that uses HTTP as its underlying communication method.

The REST architectural pattern specifies a set of constraints that a system should adhere to.
Some of these constraints are Client Server constraint, Stateless constraint, Cacheable
constraint, Uniform Interface constraint etc. We discussed these constraints in Part 1 of
ASP.NET Web API tutorial. Let's quickly recap the Uniform Interface constraint.

Uniform Interface - The uniform interface constraint defines the interface between the client
and the server. To understand the uniform interface constraint, we need to understand what a
resource is and the HTTP verbs - GET, PUT, POST & DELETE.

In the context of a REST API, a resource typically represents a data entity like Product,
Employee, Customer etc. The HTTP verb (GET, PUT, POST, DELETE) that is sent with each
request tells the API what to do with the resource. Each resource is identified by a specific URI
(Uniform Resource Identifier) or URL (Uniform Resource Locator). The following table shows
some typical requests that you see in an API.
349

Resource Verb Outcome


/Employees GET Gets list of employees
/Employees/1 GET Gets employee with Id = 1
/Employees POST Creates a new employee
/Employees/1 PUT Updates employee with Id = 1
/Employees/1 DELETE Deletes employee with Id = 1

Depending on the server side technology you use, there are many frameworks that we can use
to build a REST API. For example, if your server side technology is Microsoft Dot Net, you can
use WCF or ASP.NET Web API to create a REST API.

Since this is an Angular course, and to stay focused on it, let's create a fake REST API using
JSON Server. In our upcoming videos, we will perform all the CRUD operations using this fake
REST API.

The following is the JSON Server Github page


https://github.com/typicode/json-server

Execute the following command to install JSON server


npm install -g json-server

Execute the following command to start the server


json-server --watch db.json

This automatically creates db.json file in the root project folder. Copy and paste the following
JSON data in db.json file.

{
"employees": [
{
"id": 1,
"name": "Mark",
"gender": "Male",
"contactPreference": "Email",
"email": "mark@pragimtech.com",
"dateOfBirth": "1988/10/25",
"department": "3",
"isActive": true,
"photoPath": "assets/images/mark.png"
350

},
{
"id": 2,
"name": "Mary",
"gender": "Female",
"contactPreference": "Phone",
"phoneNumber": 2345978640,
"dateOfBirth": "1979/11/20",
"department": "2",
"isActive": true,
"photoPath": "assets/images/mary.png"
},
{
"id": 3,
"name": "John",
"gender": "Male",
"contactPreference": "Phone",
"phoneNumber": 5432978640,
"dateOfBirth": "1976/3/25",
"department": "3",
"isActive": false,
"photoPath": "assets/images/john.png
}
]
}

At this point, fire up the browser and navigate to http://localhost:3000/employees/ to see the list
of all employees. You can test this REST API using a tool like fiddler.

In our upcoming videos we will discuss performing CRUD operation using this fake REST API.

In this video we will discuss how the client communicates with the server in an Angular
application. Along the way, we will understand the typical architecture of an angular
application. Finally, we will discuss the difference between HTTP POST, PUT and Patch verbs.
351

● When a browser issues a request, a route in our Angular application responds to that
request.
● There is a component associated with a route and the component code executes. If the
component needs data, it calls an angular service.
● The data access logic is usually encapsulated in an Angular service. If you are
wondering, why can't we include the data access logic in the component itself, rather
than an Angular service.
● Well, that's because, if the data access logic is encapsulated in a service, then the
service can be reused across all the components that needs that data access logic.
● Without the service we would have to repeat the data access code in each component
that needs it. Imagine the overhead in terms of time and effort required to develop,
debug, test and maintain the duplicated code across multiple places instead of having it
in one central place like a service and reusing that service where required.
● The Angular service calls the server side service over HTTP. The HTTP verb that is
sent with each request to the server, specifies what we want to do with the resource on
the server.
● The server side service talks to the database

HTTP Verb Purpose


GET To get data from the server
POST To post data i.e to create new item on the server
DELETE To delete data
PUT, To update data
PATCH

POST PUT
352

Create a Create a new item with a given ID if the item does not exit or update the item
new item with the given ID if the item already exists.
Not Idempotent
Idempotent

PUT is idempotent where as POST is not. So what does, Idempotent mean?


Well, since PUT is idempotent, no matter how many times you call it, you would have the same
effect. For example, when you use PUT with a specific ID and if a resource with that ID does not
exist, PUT creates it. Now if you issue the same PUT request again with the same ID, another
item with the same ID will not be created. Instead, that existing item will be updated. So it would
not matter how many times you call PUT, it would have the same effect.

Remember we use POST to create a new item. So, when you call POST multiple times,
multiple items will be created. So for example, if you have an employee object and when you
POST that same employee object 10 times, 10 objects will be created on the server. So POST
is not idempotent.

PUT PATCH
Replace an existing Resource entirely i.e update all Partial update i.e update only a sub-
the properties of a resource set of the properties of a resource
Updates the item with the given ID if the item already An item can only be patched if it
exists or creates a new item with a given ID if the item exists. We cannot patch an item if it
does not exit does not exist

Depending on the Angular version being used, we can either user the Angular Http service or
HttpClient service to call the server side service.

Angular Version Angular Service to use


Angular Version 4.3.x and later HttpClient
Angular Version < 4.3.x Http

Since Angular Version 4.3.x, the old Http service is deprecated. If you are using a version less
than 4.3.x, then your only choice is to use Http service. We discussed using the Http service in
Parts 27 and 28 of Angular 2 tutorial. HttpClient service has some great benefits over Http
service. We will discuss using the HttpClient service and it's benefits in our upcoming videos in
this series.

Summanry

● When a browser issues a request


● The request is mapped to route in the Angular application
353

● The component that is associated with the route calls the Angular Service
● The Angular service calls the server side service using HTTP
● The server side service talks to the database
● The database provides the data to the server side service
● The server side service then provides that data to the Angular service on the client side
● The Angular Service provides the data to the component
● The component displays the data to the user in the browser

In this video we will discuss how to call a server side service using Angular HttpClient
service. We will specifically discuss, issuing a GET request to retrieve data from the server.

Step 1 : Import Angular HttpClientModule : Before we can use HttpClient service, we need to
import Angular HttpClientModule. In most angular applications we do this in the root module
AppModule (app.module.ts)

import { HttpClientModule } from '@angular/common/http';

Include the HttpClientModule in the imports array of @NgModule() decorator of AppModule


class

Step 2 : Import and inject HttpClient service : We want to use HttpClient service in our
EmployeeService (employee.service.ts)

// Import HttpClient service


import { HttpClient } from '@angular/common/http';

// Inject the service using the service class constructor


@Injectable()
export class EmployeeService {
constructor(private httpClient: HttpClient) {
}

getEmployees(): Observable<Employee[]> {
return this.httpClient.get<Employee[]>('http://localhost:3000/employees');
}
}

Notice
354

● We are using the HttpClient service get() method to issue a GET HTTP request.
● In addition to get() method, we also have post(), put(), patch(), and delete() methods to
perform the respective HTTP operations.
● To the get() method we pass the URI of the server side service we want to call.
● Also notice we are using the get<T>() method, generic parameter to specify the type of
data we are expecting. In our case, we are expecting an Employee[] array back.
● If we were using the old Http service, we would have to use .json() method on the
response to get JSON data back.
● With the new HttpClient service, we no longer have to do that. JSON is now the default
response.

Step 3 : Subscribe to the angular observable service : To be able to use getEmployees()


method of EmployeeService, we need to subscribe to it as it returns an Observable. If we do not
subscribe, the service method will not be called.

However, there is an exception to this. If the observable service is being consumed by a


Resolver, the resolver service will subscribe to the Observable, we do not have to explicitly
subscribe. The resolver will automatically subscribe to the observable service. On the other
hand, if the getEmployees() method of the EmployeeService is consumed by a Component or
another service, then that component or service must explicitly subscribe to the Observable,
otherwise it will not be called.

Make sure the JSON server is running. If it is not running the list route does not display
anything. Use the following command to start the JSON server
json-server --watch db.json

At the moment, we are not handling errors. What happens if the request fails on the server, or if
a poor network connection prevents the request from even reaching the server. In this case,
HttpClient service returns an error object instead of a successful response. We will discuss error
handling in our next video.
355

In this video we will discuss error handling in Angular. When using HttpClient, to call a server
side service, errors may occur. When they do occur we want to handle these errors.

When an Angular component needs data, there are usually 3 players

● The Component itself


● The Angular Service and
● The Server Side Service

The first question that comes to our mind is, should we handle the service related errors in the
service itself or in the component that consumes the service. According to Angular style guide,
error inspection, interpretation, and resolution is something you want to do in the service, not in
the component.

If all goes well the server side service provides data to the client side angular service and the
Angular service provides it to the component, which then displays that data to the user.

However, sometimes the request may fail on the server or on the client. There are two types of
errors that can occur.

● The server may reject the request, returning an HTTP response with a status code such
as 404 or 500. These are error responses.
356

● Something could go wrong on the client-side such as a network error that prevents the
request from completing successfully or an exception thrown in an RxJS operator. These
errors produce JavaScript ErrorEvent objects.
● The HttpClient captures both kinds of errors in its HttpErrorResponse and you can
inspect that response to figure out what really happened.

A typical error handler method may look as shown below.

private handleError(errorResponse: HttpErrorResponse) {


if (errorResponse.error instanceof ErrorEvent) {
console.error('Client Side Error :', errorResponse.error.message);
} else {
console.error('Server Side Error :', errorResponse);
}
// return an observable with a meaningful error message to the end user
return new ErrorObservable('There is a problem with the service.
We are notified & working on it. Please try again later.');
}

Please do not forget the following imports

import { HttpClient, HttpErrorResponse } from '@angular/common/http';


import { ErrorObservable } from 'rxjs/observable/ErrorObservable';

● So, the important point to keep in mind is, if the HttpErrorResponse is an instance of
ErrorEvent, then it means that a client-side or network error occurred. If it's not an
instance of ErrorEvent, then it means a server error occurred.
● In a real world application, we may log the errors to a database table or a file for
debugging and fixing.
● Notice, in the error hanlder, we are logging the actual errors and returning an
ErrorObservable with a user-friendly error message.
● Consumers of the service expect service methods to return an Observable of some kind,
even a "bad" one.
● Displaying the actual raw errors to the end user is bad for two reasons - First they are
cryptic and does not make much sense to the end user and second they may contain
sensitive information that could be useful for a potential hacker. That is why we are
logging the actual error and returning a user friendly error message.

Finally, take the Observables returned by the HttpClient methods and pipe them through to the
error handler as shown below.

getEmployees(): Observable<Employee[]> {
return this.httpClient.get<Employee[]>('http://localhost:3000/employees1')
.pipe(catchError(this.handleError));
}
357

Please do not forget the following imports

import { Observable } from 'rxjs/Observable';


import { catchError } from 'rxjs/operators';

With the release of rxjs version 5.5, we have Pipeable Operators that can be used along with
the pipe() function. Before the introduction of pipeable operators, we only had chain operators
as shown below. The catch operator in the example below is called a patch operator.

import 'rxjs/add/operator/catch';

getEmployees(): Observable<Employee[]> {
return this.httpClient.get<Employee[]>('http://localhost:3000/employees1')
.catch(this.handleError);
}

So the point that I am, trying to make is

● There are 2 types of operators in rxjs - Pipeable Operators and Patch Operators
● Pipeable Operators are imported from rxjs/operators/
● Patch Operators are imported from rxjs/add/operator/
● Pipeable Operators have several benefits over Patch Operators. So if you have rxjs
version 5.5 or later use pipeable operators.
● Use the following link to read the benefits of pipeable operators
https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md

In our case, the angular service getEmployees() method is consumed by a Resolver service,
and this resolver service provides the data to the LIST route. If there is an exception, the
resolver fails and the target route, in our case the LIST route will not be activated. If a
component is directly consuming the angular service getEmployees() method, then it is easy to
catch the error observable and display the error message to the user.

However, when a resolver is involved, the target route is not activated if there is an error. So
displaying an error message to the end user is a bit more involved.

In our next video, we will discuss handling errors and displaying error messages when there is a
resolver between an angular service and the component.
n this video we will discuss handling errors and displaying meaningful error messages to the
user, when there is a resolver in between the Angular component and an Angular Service.

A resolver service is usually used to pre-fetch data for a route, before activating that route.
When there is a resolver service, between a component and an angular service and if either the
358

angular service or the resolver service throws an error, that target route will not be activated at
all and you will stay on the current route.

Now if your requirement is to navigate the user to that target route and then display a
meaningful error message to the user. The trick to this is to create a custom type which contains
2 things

● The data that you want to return when there is no error


● The error message that you want to display to the user if there is an error

As you can see in the custom type below we have 2 public properties

● employeeList property holds the array of employees


● error property holds the error message to display if there is an error
● Notice we have defaulted error to NULL

import { Employee } from '../models/employee.model';

export class ResolvedEmployeeList {


constructor(public employeeList: Employee[], public error: any = null) { }
}

In the resolver service modify the code as shown below.

import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';


import { Observable } from 'rxjs/Observable';
import { Injectable } from '@angular/core';
import { EmployeeService } from './employee.service';
import { ResolvedEmployeeList } from './resolved-employeelist.model';

import { catchError } from 'rxjs/operators/catchError';


import { map } from 'rxjs/operators/map';

@Injectable()
export class EmployeeListResolverService implements Resolve<ResolvedEmployeeList> {
constructor(private _employeeService: EmployeeService) {
}

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):


Observable<ResolvedEmployeeList> {
return this._employeeService.getEmployees()
.pipe(
map((employeeList) => new ResolvedEmployeeList(employeeList)),
catchError((err: any) => Observable.of(new ResolvedEmployeeList(null, err)))
359

);
}
}

Code explanation :

● In either cases we are returning our custom type - ResolvedEmployeeList


● If the resolver completes successfully, we populate the employeeList property of our
custom type with the array of employees we get from the service.
● If there is an error, we populate the error property of our custom type, and a value of null
is pass to the employeeList property.
● In the target component where this reolver data is consume check if the error property is
NULL or NOT and react accordingly.

In list-employees.component.ts file make the following changes

const resolvedEmployeeList: ResolvedEmployeeList


= this._route.snapshot.data['employeeList'];

if (resolvedEmployeeList.error == null) {
this.employees = resolvedEmployeeList.employeeList;
} else {
this.error = resolvedEmployeeList.error;
}

Code explanation :

● If resolvedEmployeeList.error si NULL, then we know the service call completed without


errors. So we set employees property of the component to the list of employees we have
in employeeList property of resolvedEmployeeList object.
● If resolvedEmployeeList.error is NOT NULL, then we know there is an error.

In the view template, display the error

<div *ngIf="error">
{{ error }}
</div>

If you do not want to create a separate type just for handling resolver errors, you may modify
the code in the Resolver Service as shown below.

@Injectable()
// The resolver returns a union type - either an Employee[] or string
// Employee[] will be returned if the resolver completes successfully
// else the string error message will be returned
360

export class EmployeeListResolverService implements Resolve<Employee[] | string> {


constructor(private _employeeService: EmployeeService) {
}

// The return type of the resolve() method matches with the above
// Resolve interface signtaure
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<Employee[] | string> {
return this._employeeService.getEmployees()
.pipe(catchError((err: string) => Observable.of(err)));
}
}

Modify the code in list-employees.component.ts file as shown below.

// resolvedData can either be a string or Employee[]


const resolvedData: string | Employee[] = this._route.snapshot.data['employeeList'];

// If the resolver completed without errors resolvedData is an Employee[]


if (Array.isArray(resolvedData)) {
this.employees = resolvedData;
} else {
this.error = resolvedData;
}

In this video we will discuss handling errors and displaying meaningful error messages to the
user, when there is a resolver in between the Angular component and an Angular Service.

A resolver service is usually used to pre-fetch data for a route, before activating that route.
When there is a resolver service, between a component and an angular service and if either the
angular service or the resolver service throws an error, that target route will not be activated at
all and you will stay on the current route.

Now if your requirement is to navigate the user to that target route and then display a
meaningful error message to the user. The trick to this is to create a custom type which contains
2 things

● The data that you want to return when there is no error


● The error message that you want to display to the user if there is an error

As you can see in the custom type below we have 2 public properties

● employeeList property holds the array of employees


361

● error property holds the error message to display if there is an error


● Notice we have defaulted error to NULL

import { Employee } from '../models/employee.model';

export class ResolvedEmployeeList {


constructor(public employeeList: Employee[], public error: any = null) { }
}

In the resolver service modify the code as shown below.

import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';


import { Observable } from 'rxjs/Observable';
import { Injectable } from '@angular/core';
import { EmployeeService } from './employee.service';
import { ResolvedEmployeeList } from './resolved-employeelist.model';

import { catchError } from 'rxjs/operators/catchError';


import { map } from 'rxjs/operators/map';

@Injectable()
export class EmployeeListResolverService implements Resolve<ResolvedEmployeeList> {
constructor(private _employeeService: EmployeeService) {
}

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):


Observable<ResolvedEmployeeList> {
return this._employeeService.getEmployees()
.pipe(
map((employeeList) => new ResolvedEmployeeList(employeeList)),
catchError((err: any) => Observable.of(new ResolvedEmployeeList(null, err)))
);
}
}

Code explanation :

● In either cases we are returning our custom type - ResolvedEmployeeList


● If the resolver completes successfully, we populate the employeeList property of our
custom type with the array of employees we get from the service.
● If there is an error, we populate the error property of our custom type, and a value of null
is pass to the employeeList property.
● In the target component where this reolver data is consume check if the error property is
NULL or NOT and react accordingly.
362

In list-employees.component.ts file make the following changes

const resolvedEmployeeList: ResolvedEmployeeList


= this._route.snapshot.data['employeeList'];

if (resolvedEmployeeList.error == null) {
this.employees = resolvedEmployeeList.employeeList;
} else {
this.error = resolvedEmployeeList.error;
}

Code explanation :

● If resolvedEmployeeList.error si NULL, then we know the service call completed without


errors. So we set employees property of the component to the list of employees we have
in employeeList property of resolvedEmployeeList object.
● If resolvedEmployeeList.error is NOT NULL, then we know there is an error.

In the view template, display the error

<div *ngIf="error">
{{ error }}
</div>

If you do not want to create a separate type just for handling resolver errors, you may modify
the code in the Resolver Service as shown below.

@Injectable()
// The resolver returns a union type - either an Employee[] or string
// Employee[] will be returned if the resolver completes successfully
// else the string error message will be returned
export class EmployeeListResolverService implements Resolve<Employee[] | string> {
constructor(private _employeeService: EmployeeService) {
}

// The return type of the resolve() method matches with the above
// Resolve interface signtaure
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<Employee[] | string> {
return this._employeeService.getEmployees()
.pipe(catchError((err: string) => Observable.of(err)));
}
}

Modify the code in list-employees.component.ts file as shown below.


363

// resolvedData can either be a string or Employee[]


const resolvedData: string | Employee[] = this._route.snapshot.data['employeeList'];

// If the resolver completed without errors resolvedData is an Employee[]


if (Array.isArray(resolvedData)) {
this.employees = resolvedData;
} else {
this.error = resolvedData;
}

In this video we will discuss updating data on the server using Angular HttpClient service.

We update data by issuing a PUT request. To issue a PUT request, we use HttpClient service
put() method.

In employee.service.ts file, include the following updateEmployee() method


baseUrl = 'http://localhost:3000/employees';

// When an update is peformed our server side service does not return anything
// So we have set the return type to void.
updateEmployee(employee: Employee): Observable<void> {
// We are using the put() method to issue a PUT request
// We are using template literal syntax to build the url to which
// the request must be issued. To the base URL we are appending
// id of the employee we want to update. In addition to the URL,
// we also pass the updated employee object, and Content-Type header
// as parameters to the PUT method
return this.httpClient.put<void>(`${this.baseUrl}/${employee.id}`, employee, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
})
.pipe(catchError(this.handleError));
}

Please note: When an item is updated, by default we get the http status code 204 no content

Method to get an employee by id


getEmployee(id: number): Observable<Employee> {
return this.httpClient.get<Employee>(`${this.baseUrl}/${id}`)
364

.pipe(catchError(this.handleError));
}

Method to add a new employee


addEmployee(employee: Employee): Observable<Employee> {
return this.httpClient.post<Employee>(this.baseUrl, employee, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
})
.pipe(catchError(this.handleError));
}

Modify canActivate() method in employee-details-guard.service.ts file as shown below.


canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
: Observable<boolean> {
return this._employeeService.getEmployee(+route.paramMap.get('id'))
.pipe(
map(employee => {
const employeeExists = !!employee;

if (employeeExists) {
return true;
} else {
this._router.navigate(['notfound']);
return false;
}
}),
catchError((err) => {
console.log(err);
return Observable.of(false);
})
);
}

Modify code in saveEmployee() method in create-employee.component.ts file as shown


below.
saveEmployee(empForm: NgForm): void {
if (this.employee.id == null) {
console.log(this.employee);
this._employeeService.addEmployee(this.employee).subscribe(
(data: Employee) => {
console.log(data);
empForm.reset();
365

this._router.navigate(['list']);
},
(error: any) => { console.log(error); }
);
} else {
this._employeeService.updateEmployee(this.employee).subscribe(
() => {
empForm.reset();
this._router.navigate(['list']);
},
(error: any) => { console.log(error); }
);
}
}

Also modify code in getEmployee() method in create-employee.component.ts file to


subscribe to the employee service
private getEmployee(id: number) {
if (id === 0) {
this.employee = {
id: null, name: null, gender: null, contactPreference: null,
phoneNumber: null, email: '', dateOfBirth: null, department: null,
isActive: null, photoPath: null
};
this.createEmployeeForm.reset();
this.panelTitle = 'Create Employee';
} else {
this._employeeService.getEmployee(id).subscribe(
(employee) => { this.employee = employee; },
(err: any) => console.log(err)
);
this.panelTitle = 'Edit Employee';
}
}

Modify ngOnInit() method in employee-details.component.ts


ngOnInit() {
this._route.paramMap.subscribe(params => {
this._id = +params.get('id');
this._employeeService.getEmployee(this._id).subscribe(
(employee) => this.employee = employee,
(err: any) => console.log(err)
);
});
366

On the edit page you may get the following error. We get this error because the template is
trying to bind to the name property before the server has returned the data, and the
employee object is initialised. So that is the reason we cannot read name property from an
undefined employee object.
Cannot read property 'name' of undefined

To fix this error, use the following *ngIf directive in create-employee.component.html file..
Now <div> element and it's children will be rendered only after the employee object is initialized.
<div class="panel panel-primary" *ngIf="employee">

Do the same thing in employee-details.component.html file.

In this video we will discuss deleting data on the server using Angular HttpClient service.

To issue a DELETE request, we use HttpClient service delete() method.

In employee.service.ts file, include the following deleteEmployee() method

baseUrl = 'http://localhost:3000/employees';

deleteEmployee(id: number): Observable<void> {


return this.httpClient.delete<void>(`${this.baseUrl}/${id}`)
.pipe(catchError(this.handleError));
}

Code Explanation:

● deleteEmployee() method takes the ID of the employee to delete as a parameter


● delete() method does not return anything so we have set the return type to void
● The URL that is passed as a parameter to the HttpClient delete() method has the ID of
the employee to delete

Delete button click event handler is in DisplayEmployeeComponent. So modify


deleteEmployee() method in display-employee.component.ts file as shown below.

deleteEmployee() {
this._employeeService.deleteEmployee(this.employee.id).subscribe(
() => console.log(`Employee with ID = ${this.employee.id} Deleted`),
(err) => console.log(err)
);
367

this.notifyDelete.emit(this.employee.id);
}

The success callback function logs the ID of the deleted employee to the console and the
error callback function logs the error to the console.

S-ar putea să vă placă și