Angular One-Pager
Introduction
This is in a question-answer format.
If you use a spaced repetition learning software like Anki, you can enter them into it or import them:- Anki package(apkg) and Anki package(txt)
This post will constantly be updated. Check the latest updates in the Updates section.
Setup
-
Which
npm
command will install the angular cli for version 15?npm install -g @angular/cli@15
-
What does the following code do?
npm install -g @angular/cli@15
It will install the version 15 of the`angular/cli`.
-
Which angular cli command creates a new application?
ng new my-app
-
What does the following command do?
ng new my-app
It creates a new angular application called my-app.
-
What is
angular.json
?The main configuration file of the angular workspace.
-
What is the name of the main configuration file of the Angular cli workspace?
angular.json
-
Which angular cli command starts the angular application?
ng serve
-
What does ng serve do?
It starts the web application.
-
Which file is the main html file of the angular application?
index.html
-
Which file is the main entry point of the angular application?
main.ts
-
Why does this command do?
npx -p @angular/cli@15 ng generate environments
With this command, you can switch to any angular cli that you need to work with. You do not need to globally install angular.
Modules
-
Which file defines the main module of the angular application?
app.module.ts
-
What is the main module of angular called?
AppModule
-
In which file will you find
AppModule
?app.module.ts
-
What is the double curly braces syntax called?
interpolation
-
What are decorators?
Allows us to add metadata to class declarations.
-
How can you recognize a decorator?
The @ prefix.
-
How many types of decorators are there?
-
Class decorators
-
Property decorators
-
Method decorators
-
Parameter decorators
-
-
When does the class decorator execute?
The decorator statement is executed before the class gets instantiated.
-
Which feature does the
BrowserModule
provide to an angular application?It allows an angular application to run in the browser.
-
What properties does
NgModule
accept?-
declarations
-
imports
-
providers
-
bootstrap
-
-
Which angular artifacts can be added to the declarations collection of
NgModule
?Components, directives, and pipes.
-
What does the imports property of
NgModule
contain?Other modules and standalone components, directives and pipes.
-
What does the providers property of
NgModule
contain?Services
-
What does the bootstrap property of
AppModule
define?The component that loads on startup.
-
What are feature modules?
Modules that represent specific features of the application.
-
What features does the
CommonModule
provide to an angular application?It provides the template syntax features to an angular application.
-
What is the cli command to generate a module?
ng generate module module-name
-
How do you expose components from an Angular module?
Using the exports array of
NgModule
-
What features does the
HttpClientModule
provide an angular application?It provides features that enable http communication.
-
What is the
RouterModule
?Module that enables navigation in an Angular application.
-
What features does the
BrowserAnimationsModule
provide to an angular application?It provides features that enable UI animations in an Angular application.
Components
-
Which decorator would you use to create an angular component?
@Component
-
Which tag in index.html is used to load the main component of the application?
<app-root></app-root>
-
What is the
<app-root></app-root>
tag?It is used to load the main component of the angular application.
-
By convention, What is the main component of the angular application?
AppComponent
-
What is the
AppComponent
?It is the default component of an angular application.
-
Where is the
AppComponent
registered?In the
AppModule
defined in theapp.module.ts
file. -
Does every component created need to be registered in a module?
Yes except standalone components.
-
What is the point of the selector property in an angular component decorator?
Angular loads the component when it finds the selector tag in an html template.
-
What prefix does angular add to a component’s selector by default?
app-
-
What is the point of the
templateUrl
property in the angular component decorator?Defines the path of the component’s html template file.
-
What is the point of the
styleUrls
property of an angular decorator component?It defines the path to external style sheets of the component.
-
Can you register a component in more than one module?
A component must be registered with only one Angular module.
-
By default, in which module does the
ng generate component
command register a component?AppModule
-
What is a standalone component?
A component that does not need to be registered in an Angular module.
-
Which cli command creates a standalone angular component?
ng generate component component-name --standalone
-
Which additional properties do standalone components contain?
-
standalone
-
imports
-
-
Can standalone components import angular modules?
Standalone components can import Angular modules and vice versa.
-
In which property of an
NgModule
must you register a standalone component?imports array.
-
In which property of an
NgModule
must a standalone components not be added?The declarations array because standalone components cannot be registered to a single module.
-
What is the following angular syntax called?
<span [innerText]="title"></span>
Property Binding syntax.
-
What is
innerText
called and what istitle
called?<span [innerText]="title></span>
innerText
is called the target property.title
is called the template expression. -
What is the following angular syntax called?
<p [class.star]="isLiked"></p>`
Class Property Binding syntax
-
What is the output of the following code?
<p [style.color]="'greenyellow'"></p>
The paragraph element will have a
greenyellow
color. -
What is the output of the following code?
<p [style.width.px]="'100'"></p>"
The paragraph element will be 100 pixels long.
-
What is the
target event
and what is thetemplate statement
in the code below?<button (click)=""onClick()"">Click me</button>
(click)
is the target event.onClick()
is the template statement. -
What is
@Input
?A component input property decorator.
-
What does the following code define?
@Input() name = '"";
Defines a name property that is an
Input
to the component. -
Explain the following code in detail?
<app-product-detail [name]="selectedProduct"></app-product-detail>
It binds the name property of
app-product-detail
to theselectedProduct
property of the parent component. -
Explain the following code in detail?
<app-product-detail name="Webcam"></app-product-detail>
The string valueWebcam
is bound to thename
property ofapp-product-detail
. -
What is
@Output
?@Output
is an angular property decorator for emitting events. -
What does the following code declare?
@Output() bought = new EventEmitter();
A property called bought that emits events.
-
What does the following code do?
this.bought.emit();
It emits an event from the component.
-
Explain the following code?
<app-product-detail [name]="selectedProduct" (bought)="onBuy()"></app-product-detail>
The name property of
app-product-detail
is bound to theselectedProduct
of the parent component.
The bought event ofapp-product-detail
is bound to the onBuy() method of the parent component. -
What does the following code declare?
@Output() bought = new EventEmitter<string>();
A property called bought that emits string data.
-
What does $event mean in the following code do?
<app-product-detail [name]="selectedProduct" (bought)="onBuy($event)"></app-product-detail>
The $event object is a reserved keyword in Angular. It contains the payload data that is emitted by the
app-product-detail
. -
What is the term in angular for
#product
?<app-product-detail #product [name]="selectedProduct" (bought)="onBuy()"></app-product-detail>
template reference variable.
-
What is View Encapsulation?
View Encapsulation is how angular manages CSS scoping within a component.
-
How can you change the view encapsulation of a component?
By changing the encapsulation property of the
@Component
decorator. -
What are the three View Encapsulation enumeration value?
-
Emulated
-
Native
-
None
-
-
What are the most basic angular lifecycle hooks?
ngOnInit
,ngOnDestroy
,ngOnChanges
,ngAfterViewInit
-
What is the general advice towards using constructors in Angular?
Should be relatively empty and devoid of logic.
-
You need to initialize a component with data from an angular service.
Which lifecycle hook in the best place to do it?
The
OnInit
hook -ngOnInit
method. -
When is a component destroyed?
A component is destroyed when it is removed from the DOM.
-
What should we do in
ngOnDestroy
method?Perform cleanups:
-
Reset timers and intervals
-
Unsubscribe from observable streams
-
-
When does
ngOnChanges
get called?When the value of an input data binding has changed.
-
Can you change the selector prefix 'app' that angular adds by default to a component?
Yes in the
angular.json
file. -
Which flag of
ng generate component
should you use to create a standalone component?--standalone
-
What is class property binding?
Component property bound to
[class.class-name]
. -
What is style property binding?
Component property bound to
[style.style-name]
. -
Why would you use the
@Input
artifact?To pass data from one component down to another component.
-
Which class does the Output decorator use to emit events?
The
EventEmitter
class. -
How would you emit an event from a component?
Call the emit method -
this.bought.emit(1);
-
Why would you use a template reference variable like
#product
?In case you would like to access a property of the component elsewhere in the current template.
-
You would like to access a property of the component elsewhere in the current template.
How can you access it?
Declare a template reference variable.
-
Which
ViewEncapsulation
option is the default option?Emulated
-
What does the emulated
ViewEncapsulation
option provide?It simulates a Shadow DOM and encapsulates CSS rules.
-
Which
ViewEncapsulation
option simulates a Shadow DOM and encapsulates CSS rules?Emulated
-
What does the Native option of
ViewEncapsulation
do?It uses the native browser Shadow DOM.
-
Which
ViewEncapsulation
option simulates a native browser Shadow DOM?Native
-
What limitation does the Native option of
ViewEncapsulation
have?It works only on browsers that support Shadow DOM.
-
Which lifecycle hook gets called when angular detects that the value of an input data binding has changed?
ngOnChanges
-
When does the
ngOnChanges
lifecycle hook get called?Whenever the change detection cycle runs.
-
What is the first parameter of the
ngOnChanges
method?SimpleChanges
-
How do you exclude the changes that get triggered on first load of the component?
By calling
isFirstChange()
.ngOnChanges(changes: SimpleChanges): void { if (!changes['name'].isFirstChange()) { const oldValue = changes['name'].previousValue; const newValue = changes['name'].currentValue; } }
-
When is the
AfterViewInit
lifecycle hook of an angular component called?-
The html template has been initialized.
-
The html template of all child components have been initialized.
-
-
Which lifecycle hook gets called when the html template of the component has been initialized?
AfterViewInit
-
Which lifecycle hook gets called when the html template of all child components have been initialized?
ngAfterViewInit
-
When would you use the
@ViewChild
?To get a reference object of a child component in the template.
-
What does the following code do?
@ViewChild(ProductDetailComponent) productDetail:ProductDetailComponent | undefined;
Loads the first product-detail component it finds in the template. undefined if it does not find one.
-
What is the
@ContentChild
decorator?It helps an angular component have access to the elements inside the projected content i.e ng-content
-
When should you use
@ContentChild
decorator?To get access to elements inside ng-content projected by the parent component.
-
What does the following code do?
@ContentChild(CourseImageComponent) image: CourseImageComponent
It looks for the course-image component that should be projected by the parent into ng-content.
-
What does the following code do?
@ContentChild(CourseImageComponent, {read: ElementRef}) image: ElementRef
This gives us access to the element itself not the component object inside ng-content.
-
What does the following code do?
@ContentChildren(CourseImageComponent) images: QueryList<CourseImageComponent>
Selects all
<course-image>
component withinng-content
. -
What does the following code do?
<ng-template #blankImage> No Image </ng-template> <ng-content select="course-image" *ngIf="course.iconUrl; else blankImage"> </ng-content>
It displays "No Image" if there is not
iconUrl
. -
What does SimpleChanges contain?
It contains one key for each input property that changes.
-
What does the static property indicate?
@ViewChild('keyContainer', { static: true }) input: ElementRef |undefined;
It indicates whether the element we want to query will be available during component initialization.
-
How do you declare a standalone component?
By setting the standalone property of the
@Component
decorator totrue
. -
Do standalone components need to be registered with a module?
No
-
You would like to use standalone components instead of modules.
What steps do you need to do to enable that?
-
Delete
app.module.ts
-
In
main.ts
, typebootstrapApplication
passing inAppComponent
-
-
Can you build angular without using modules?
Yes by using standalone components.
-
When would you use the
ViewChild
decorator?When you would like your component to have access to an element in its template.
-
What does the following code do?
@ViewChild(CourseCardComponent) card: CourseCardComponent
Angular searches for a
course-card
component in the template and populatescard
with a reference to that component. -
What happens if you use the following code and there are 2
course-card
components in the template?@ViewChild(CourseCardComponent) card: CourseCardComponent
Angular selects the first matching
course-card
component. -
What does the following do?
@ViewChild('cardRef') card: CourseCardComponent
Selects a
course-card
component with acardRef
template reference variable. -
What does the following code do?
@ViewChild('container') containerDiv: ElementRef
Angular queries for the
container
template reference variable in the template file.ElementRef
is a type used to represent normal html elements. -
What does the following code do?
@ViewChild('cardRef1', {read: ElementRef}) cardComponent1: ElementRef
Angular queries the template for a
card
component with the template reference variablecardRef1
and returns the html element of that component. -
When will you use the following?
@ViewChild('cardRef1', {read: ElementRef})
When you would like to have a reference to the html element of an angular component used in the template.
-
When can you be sure that the
@ViewChild
objects are populated with the component objects and can be used safely?In the
ngAfterViewInit()
function. -
Is it advisable to do any data modification in the
NgAfterViewInit
lifecycle hook method?No you get an error.
-
What is the difference between
@ViewChild
and@ViewChildren
decorator?With
@ViewChild
you can select only one component. With@ViewChildren
you can select more than one component. -
What does the following do?
@ViewChildren(CourseCardComponent) cards: QueryList<CourseCardComponent>;
Creates a view children component for a list of
course-card
components. -
Which tag can you use for content projection?
ng-content
-
Explain what does the following do?
<ng-content select="img"><ng-content>
It selects all the
img
tags from the projected content. -
Explain what does the following do?
<ng-content select=".course-img"><ng-content>
It selects only tags with class .
course-img
from the content projection. -
Why did angular introduce standalone components, directives and pipes?
Components, directives and pipes need to be associated with a single Angular module in an application.<br>Standalone components, directives and pipes need not be associated with an Angular module and can be imported into multiple angular modules.
-
How can you import an existing angular module into a standalone component?
By adding the module to the imports array of the standalone component decorator.
-
Can you bootstrap an application using standalone components with no angular module?
Yes
-
What does the following code do?
import {bootstrapApplication} from '@angular/platform-browser'; import {PhotoAppComponent} from './app/photo.app.component'; bootstrapApplication(PhotoAppComponent);
Bootstraps an angular application with a standalone component. == Directives
-
When would you use a directive?
When you would like to extend the behavior of the html tag itself.
-
How many types of directives are there?
There are 3 types of directive:-
-
Components
-
Structural directives
-
Attribute directives
-
-
What is a Component directive?
It is a directive with an associated template.
-
Which type of directive has an associated template?
The Component directive.
-
What is a Structural Directive?
A directive that adds or removes elements from the DOM.
-
Which type of directive removes or adds elements to the DOM?
The Structural Directive.
-
Which type of directive can modify the appearance of or define a custom behaviour of a DOM element?
An Attribute Directive.
-
What are the 3 structural directives provided by angular?
-
ngIf
-
ngFor
-
ngSwitch
-
-
What does the
ngIf
structural directive do?Adds or removes a dom element based on an expression.
-
Which structural directive adds or removes a portion of the DOM tree based on an expression?
ngIf
-
What does the
ngFor
structural directive do?Iterates through a list of items and binds each item to a template
-
Which structural directive iterates through a list of items and binds each item to a template?
ngFor
-
Which structural directive switches between templates and displays each one depending on the condition?
ngSwitch
-
What does the asterisk in an
ngIf
directive indicate?<div *ngIf=""name"">
It indicates that it is a structural directive.
-
Can the
ngFor
directive observe for changes in the underlying collection?Yes
-
What does angular use to keep track of every item in the
ngFor
collection?An object identity
-
What is an object identity?
Angular uses it to keep track of every item in the
ngFor
collection. -
Can you change the object identity of an
ngFor
collection?Yes
-
How can you change the object identity of an
ngFor
collection?By using the
trackBy
property. -
Why does angular keep tracks of object in an
ngFor
collection?Because any changes to the collection must be synced in the DOM.
-
Is the
ngSwitch
directive prefixed by an asterisk?No
-
What are the three sets of directives related to
ngSwitch
?-
[ngSwitch]
-
*ngSwitchCase
-
*ngSwitchDefault
-
-
What does
*ngSwitchCase
do?It adds or removes a template based on the value of
[ngSwitch]
. -
What does
*ngSwitchDefault
do?Add a template if there is no match for
*ngSwitchCase
. -
What does the following code do?
<ng-template let-courseName="description"> {courseName} </ng-template>
It declares a variable
courseName
that can be used inside the template. -
What does
ngTemplateOutlet
do?<ng-template let-courseName="description"> {courseName} </ng-template> <div *ngTemplateOutlet="blankImage; context: {description: course.description}"> </div>
*ngTemplateOutlet
directive instantiates an ng-template. -
What does the
@HostBinding
function decorator allow you to do?It allows you to set the properties of the host element from the directive class.
-
Which angular decorator allows you to set the properties of the host element from the directives class?
@HostBinding()
-
When would you want to use the
@HostBinding
function decorator in a directive?If you would like to change the style properties like the height, width, color etc of the host element.
-
How will you use the
@HostBinding()
function decorator in a directive?You pass in the name of the host element property whose value you would like to change as the first parameter.
-
If you want to pass an input to a directive, what should be the name of that input property?
The name of the input property should be the same as the name of the directive selector.
-
If you would like to use more than one input property for a directive, what should you do?
Declare it as follows:
@Input('anotherProperty') propertyName
The directive should useproperyName
for internal purposes, whereas components that use the directive should useanotherProperty
. -
Which angular cli command generates a directive?
ng generate directive copyright
-
How do you declare a directive?
Using the
@Directive
decorator and defining its selector property. -
What is the
ElementRef
class?It gives us access to the host element of the directive.
-
Which class allows us to manipulate the underlying HTML element attached to the directive?
ElementRef
-
Which property of the
ElementRef
class gives us access to the html element?nativeElement
-
What does the following code do?
import { Directive, ElementRef } from '@angular/core'; @Directive({ selector: '[appCopyright]' }) export class CopyrightDirective { constructor(el: ElementRef) { const currentYear = new Date().getFullYear(); const targetEl: HTMLElement = el.nativeElement; targetEl.classList.add('copyright'); targetEl.textContent = 'Copyright ©${currentYear} All Rights Reserved.'; } }
It creates a directive that adds the class 'copyright' to an element.
-
What is the
@HostBinding
decorator?It binds a value to an attribute of the host element of the directive.
-
You would like to assign a value to a property of the host element.
Which decorator would you use?
@HostBinding
-
You would like to listen to events raised by the host element in a directive.
Which decorator will you use?
@HostListener
-
What is
$event
?@HostListener('keypress', ['$event']) onKeyPress(event:KeyboardEvent)
The event object of the triggered event.
-
What does the
ViewContainerRef
artifact give access to?It gives us access to the angular view container that hosts a directive.
-
What is the
ViewContainerRef
?It is a container where one or more views can be dynamically created and attached as a child of the host element.
-
When you would like to use a
ViewContainerRef
?When you would like to dynamically create and add a component and attach it to the view or when you would like to dynamically create an
ngTemplate
and attach it to the view. -
If the view container creates more than one view, where is the second view added?
The second view is added after the first view
-
How can you create a new component and add it to the view of a
ViewContainerRef
?By using its
createComponent
method. -
How can you create a new
ngTemplate
and add it to the view of aViewContainerRef
?By using its
createEmbeddedView
method. -
What is
TemplateRef
?TemplateRef
is a class and the way to reference theng-template
in the component or directive class. -
You need access to an
ngTemplate
that is declared in your html template.How can your directive access that template?
By injecting the
TemplateRef
class. -
What is
ngTemplateOutlet
?It is a structural directive that renders the template.
-
Which event gets called when Angular initializes the view of the current component and its child components?
ngAfterViewInit
-
When does
ngAfterViewInit
get called?This is called after angular initializes the view of the current component and its child components.
-
What is the diff between the following 2 lines of code?
<input class="demo" (keyup)="onKeyUp(titleInput.value)" #titleInput />
<input class="demo" (keyup)="onKeyUp($event.target.value)" />
They do the same thing. $event is the event object while
titleInput
is the template reference variable. -
Write the syntax for
ngFor
.*ngFor="let course of courses"
-
Write the syntax for
ngFor
with an index parameter.*ngFor="let course of courses;index as i"
-
Write the syntax for
ngFor
with an index and a first parameter*ngFor="let course of courses;index as i; first as isFirst"
-
Write the syntax for
ngFor
with an index and a first parameter and a last parameter.*ngFor="let course of courses;index as i; first as isFirst; last as isLast"
-
Write the syntax for
ngFor
with an index and a first parameter and a last parameter and an even parameter and an odd parameter.*ngFor="let course of courses;index as i; first as isFirst; last as isLast; even as isEven; odd as isOdd"
-
What happens to the element if the
ngIf
expression returns false?The element is removed from the page.
-
What happens to the element if the
ngIf
expression returns true?The element is added on the page.
-
What expressions are allowed in
ngIf
?Any variable, object or function that returns a truthy value.
-
What does
ngClass
do?It adds a class to the element.
-
What does the following do?
[ngClass]="'beginner'"
Adds the beginner class to the element.
-
What does the following do?
[ngClass]="['beginner', 'testClass']"
Adds the
beginner
andtestClass
to the element. -
What does the following do?
[ngClass]="{'beginner':true, 'course-card':true}"
It reads the object and applies the class only if true.
-
What does the following do?
[ngClass]=cardClasses()
It calls the function and the function must return a list of classes.
-
When should you use
ngClass
?When we want to add a style depending on the content of the data.
-
What does the following do?
[style.text-decoration]=""'underline'""
Adds a text-decoration style to the element.
-
What does the following do?
[ngStyle]="{'text-decoration': 'underline'}"
Add the text-decoration style to the element.
-
What is an
<ng-container>
?It is an angular element onto which you can apply a structural directive like
ngIf
orngSwitch
. -
When should you use an
<ng-container>
tag?If you find yourself in a situation where you don’t have a convenient html element to use with a structural directive.
Pipes
-
What do pipes do?
They transform the output of an expression in a template.
-
What are the various types of built-in angular pipes?
-
uppercase/lowercase
-
percent
-
currency
-
slice
-
date
-
json
-
async
-
keyvalue
-
-
When would you use the
async
pipe?When data that is handled asynchronously needs to be reflected in our view immediately.
-
Which pipe would you use when you need to manage data that is handled asynchronously by the component, and that the views promptly reflect those changes?
The
async
pipe. -
Which angular command helps generate a new pipe?
ng generate pipe pipe-name
-
What is the only required property of the
@Pipe
decorator?The name of the pipe.
-
"What does the following code do?
import { Pipe, PipeTransform } from '@angular/core';
Imports the
Pipe
andPipeTransform
artifact from '@angular/core'. -
Which method must a pipe implement?
It must always implement the transform method.
-
Which method must an angular pipe implement?
The transform method
-
What are the two parameters that the
PipeTransform
method accepts?-
The input value that needs to be transformed.
-
an optional arguments list.
-
-
What is an impure pipe?
An impure pipe is called for every change detection cycle no matter whether the value or parameter(s) change.
-
What is a pure pipe?
A pure pipe is only called when Angular detects a change in the value or the parameters passed to a pipe.
-
What is the difference between pure pipes and impure pipes?
Pure pipes execute when there is a change to the reference of the input variable.<br>Impure pipes execute whenever the change detection cycle executes.
Routing
-
What does the following code do?
bootstrapApplication(PhotoAppComponent, { (1) providers: [ {provide: BACKEND_URL, useValue: 'https://photoapp.looknongmodules.com/api'}, (2) provideRouter([/* app routes */]), (3) ] });
1 Bootstraps the application with PhotoAppComponent
2 Creates the config value BACKEND_URL that PhotAppComponent
can use3 Configures the routes. -
In the
bootstapApplication
function, how can you configure routes?Pass in the routes to the
provideRouter
function. -
What does the
loadComponent
function do?export const ROUTES: Route[] = [ { path: 'admin', loadComponent: () => import('./admin/panel.component').then(mod => mod.AdminPanelComponent) }, ];
It configures the admin route to lazy load the component.
-
Which function can you use to lazy load a standalone component?
loadComponent
-
What does
loadChildren
do in the following code?{ path: 'admin', loadChildren: () => import('./admin/routes').then(mod => mod.ADMIN_ROUTES) }
loadChildren
lazy loads all components defined in ADMIN_ROUTES. -
Explain in detail the following code-
import { NgModule } from '@angular/core'; (1) import { Routes, RouterModule } from '@angular/router'; (2) const routes: Routes = []; (3) @NgModule({ (4) imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } (5)
1 Imports NgModule
.2 Imports Routes
andRouterModule
.3 Adds routes array. 4 Configures NgModule
decorator.5 Exports the AppRoutingModule
class -
Explain in detail what does the following code do:-
const routes: Routes = [ { path: 'first-component', component: FirstComponent }, { path: 'second-component', component: SecondComponent }, ];
Defines the routes for first-component and second-component.
-
What is routerLink and router-outlet?
<a routerLink="/first-component" routerLinkActive="active">First Component</a> <a routerLink="/second-component" routerLinkActive="active">Second Component</a> <router-outlet></router-outlet>
-
routerLink
attribute adds the route. -
router-outlet
is where the view renders.
-
-
In what order should you add routes?
-
Static Path
-
Empty Path - default route.
-
Wildcard Path - not found route.
-
-
Which route should be last by default?
The wildcard route.
-
How can you add a wildcard route to your routes definition?
{ path: '**', component: <component-name> }
-
In the providers array, in the provideRouter method, how will you enable input binding with routing?
providers: [provideRouter(appRoutes, withComponentInputBinding()),]
-
What are eager-loaded modules?
These are loaded at application startup.
-
How do you declare eager-loaded modules?
In the imports array of another module.
-
What are lazy-loaded modules?
These are loaded on demand due to in-app navigation.
-
How do you declare lazy-loaded modules?
In the routing module.
-
Which package does angular provide for routing?
The
@angular/router
npm package. -
What is html5 pushstate?
HTML5 pushState allows in-app navigation without causing a full reload of a page.
-
Which html tag must be mandatorily set to enable pushState routing?
The
base href
tag in the index.html file. -
What value must be set in the
base href
tag of an angular application?The application root -
/
.
Or if your application resides in a different folder than the`src\app`, you should name it after that folder. -
Why use the forRoot pattern?
Why do we register our routes using the
RouterModule.forRoot(routes)
?If a module exposes services and you would like to register them once in the root injector so that only singleton instances of those services are created then you would use the
forRoot
pattern. -
What happens if we do not use the
RouterModule.forRoot
pattern when importing a module?If we try to import a module that exposes services normally without
forRoot
then we will end up with multiple instances of the same service, thereby violating the singleton pattern. -
What does
RouterModule.forRoot(routes)
return?-
Services to perform common routing tasks such as navigation.
-
Directives that we can use in our components to enrich them with navigation logic.
-
-
What does each route definition object contain?
-
A path property, which is the URL path of the route.
-
A component property, that defines which component will be loaded when the application navigates to that path.
-
-
Should the path property of a route object contain a leading slash?
The value of a path property should not contain a leading
/
. -
Why use a wildcard route?
If the user tries to navigate to a URL that does not match any route, Angular activates the wildcard route.
-
How doe you define a wildcard route?
The wildcard route has a path property with two asterisks which matches any URL.
-
What is a
router-outlet
and why would you use it?It is an Angular component and is used as a placeholder for components activated with routing.
-
Which angular component will you use as a placeholder for components that are activated with routing?
router-outlet
-
You would like to assign a route path to an anchor tag, which angular directive would you use?
The
routerLink
directive. -
Should the route path be assigned to the
routerLink
directive contain a leading/
?Yes it should contain a leading
/
. -
You would like to register routes in a feature module.
Which
RouterModule
method would you use?The
forChild
method is used when we want to register routes in a feature module. -
In which module must you call the
RoutingModule.forRoot
method?You should call the
forRoot
method only in the routing module of the main application module. -
Does the import order of routing modules matter?
The order that we import routing modules does matter. The router selects a route with a first-match-wins strategy.
-
In what order must you import routing modules?
Feature routing modules before the main application routing module.
We want to force the router to search through our specific route paths and then fall back to an application-specific one. -
In which module must you always define the wildcard route?
In the app-routing.module.ts file.
-
Should a wildcard route be tied to a specific feature module?
No a wildcard route must apply to the whole application and not be tied to a specific feature.
-
In a list of application routes, where must you place the wildcard route?
The wildcard route must be the last entry in the route list.
-
Why must the wildcard route be the last entry in the list of application routes?
Because the application should only reach it if there are no matching routes.
-
You would like to define a default route that matches
''
.How will you define such a route?
{ path: '', redirectTo: '/products', pathMatch: 'full' }
-
The path is set to ''.
-
It redirects to a specific route.
-
The pathMatch property tells the router how to match the URL to the route path property. In this case, an empty string.
-
-
You would like to navigate to a specific path inside a component class.
Which method would you use?
this.router.navigate(['/']);
We call the navigate method of the Router service to navigate into the root path of the application. -
You would like to bind the parameters of a link to an
@Input
property of a component.How can you do that?
By setting the
bindToComponentInputs
option ofRouterModule.forRoot
. -
You would like to add a parameter to a link using the
routerLink
directive.For eg:- add a dynamic
productId
at the end of/products
How can you achieve this feature?
<a [routerLink]="['/products', product.id]">{{product.name}}</a>
The routerLink directive uses property binding to set its value in a link parameters array. -
How can we retrieve information about the currently active route in a component?
The ActivatedRoute service povides the necessary information.
-
When would you like to use child routes for a component?
When we want to have a landing page component that will provide routing to other components in a feature module. It should contain a `<router- outlet>`element in which child routes will be loaded.
-
How can you define a child route for a component?
We use the children property of a route definition object to define child routes, which contain a list of route definition objects.
-
Why will you use a guard?
When we want to prevent unauthorized access to a particular route.
-
What are the various types of Angular guards?
An Angular guard can be of the following types:
-
canActivate: Controls whether a route can be activated.
-
canActivateChild: Controls access to child routes of a route.
-
canDeactivate: Controls whether a route can be deactivated. Deactivation happens when we navigate away from a route.
-
canLoad: Controls access to a route that loads a lazy-loaded module.
-
canMatch: Controls access to the same route path based on application conditions.
-
-
You would like to inject a service directly inside a method instead of a constructor.
Which angular method provides this feature?
const authService = inject(AuthService);
The inject method. -
You would like to cancel a previous navigation in a guard and redirect the user to the home page.
Which angular method provides this feature?
return this.router.parseUrl('/');
The parseUrl method returns a UrlTree object, which effectively cancels the previous navigation and redirects the user to the URL passed in the parameter. -
You would like to navigate to a url relative to the current active url path.
Which angular method must you call and which angular property must you set?
this.router.navigate(['./', product.id], { relativeTo: this.route });
The navigate method passing in an additional object after the link parameters array that defines the relativeTo property pointing to the current activated route. -
Why will you use a resolver?
If you would like to prefetch some data before activating a route and intializing a component.
-
Give the details of one popular use case for using resolvers.
A resolver can be useful when we want to handle possible errors before activating a route.
It would be more appropriate not to navigate to the product details component if the id we pass as a route parameter does not exist in the backend. -
What is lazy loading?
Lazy loading means that we don’t load all the modules of our application initially.
-
You would like to lazy load a few modules of your application?
Which property of the route object would you set?
The loadChildren property of a route definition object is used to lazy load Angular modules.
-
Do lazy-loaded modules and the non lazy-loaded modules share the same root injector?
Lazy-loaded modules create a separate injector that is an immediate child of the root application injector.
-
What happens if you use an angular service registered with the root injector in a lazy-loaded module?
If you use an Angular service registered with the root application injector in a lazy-loaded module, you will end up with a separate service instance in both cases.
-
To control access to a lazy-loded module, which guard should you use?
The canLoad guard.
-
What is a
RouterOutlet
?It is an angular component that acts as a placeholder that Angular dynamically fills based on the current router state.
-
What are named
RouterOutlets
?They are router outlet components that have the optional name property set.
<router-outlet name='left'></router-outlet>
-
How can you define a route object to use a named
RouterOutlet
?{path: <base-path>, component: <component>, outlet: <target_outlet_name>}
-
How do you provide
bootstrapApplication
with routing?In the providers option, call the
provideRouter
function providing theappRoutes
.
Change Detection
-
When is change detection triggered?
-
button clicks
-
async requests
-
setTimeout
-
setInterval.
-
-
Can we change the change detection strategy for a component?
Yes by changing the
changeDetection
property of the@Component
decorator. -
When does change detection cause a problem?
When large amounts of data change rapidly, it can cause a performance problem.
-
When does
ChangeDetectionStrategy.OnPush
get triggered?Only when the @Input property of the component changes.
-
Why does Angular use
Zone.js
?To detect any changes to the application state.
-
Which library helps angular detect changes to an application’s state?
Zone.js
-
What events does Zone.js listen for and capture in an angular application?
setTimeouts
,setIntervals
, network requests, and event listeners. -
What does
runOutsideAngular
do?ngOnInit() { this.ngZone.runOutsideAngular(() => setInterval(pollForUpdates), 500); }
It excludes the passed in function for change detection.
-
What does angular do on each change detection cycle? (2 steps)
-
Evaluates all template expressions in all non-onPush components of the application.
-
Executes the
ngDoCheck
,ngAfterContentChecked
,ngAfterViewChecked
, andngOnChanges
lifecycle hooks of each component.
-
-
Which lifecycle hooks does each change detection cycle execute?
-
ngDoCheck
-
ngAfterContentChecked
-
ngAfterViewChecked
-
ngOnChanges
-
-
Why should you make sure that the
ngDoCheck
,ngAfterContentChecked
,ngAfterViewChecked
, andngOnChanges
lifecycle hooks execute quickly?Because these methods run after every change detection cycle.
-
A component has
OnPush
change detection strategy, When does change detection run for this component?The component receives new inputs.
-
Which command generates an application with routing enabled?
ng new routing-app --routing --defaults
Services and Injector
-
Which decorator helps you identify an Angular Service?
The
@Injectable
decorator. -
What does
@Injectable
accept as its first parameter?An object with a single property named
providedIn
. -
What is the command to generate an Angular service?
ng generate service serviceName
-
By default, where is an angular service registered?
By default, it is registered with a root injector.
-
What is an Injector?
An object that maintains a list of all dependencies that an Angular application needs.
-
What does the Injector return?
A singleton instance of the registered type.
-
How does the Injector work or how does the Injector return an instance?
The Injector first checks to see if it has already created an instance of the dependency.
If no, a new one is created and returned.
If yes, the existing one is returned. -
What is a provider?
A recipe containing guidelines on creating a specific service.
-
What does the root injector do?
The root injector creates singleton services.
-
Where are the services created by the root injector available?
Globally available throughout the application.
-
Can we register services in the
@NgModule
decorator?Yes
-
Where can we register services in
@NgModule
?The providers property of the`@NgModule` decorator.
-
What is the difference between registering a service in the
AppModule
providers property and registering a service using theprovidedIn:root
configuration?The main difference between them is that the
providedIn
syntax is tree shakable. -
What is tree shaking?
Tree shaking is the process of finding dependencies that are not used in an application and removing them from the final bundle.
-
What does the Angular compiler do as a result of its tree shaking activity?
Detects Angular services that are not used and deletes them, resulting in a smaller bundle.
-
What is the suggested best practice for registering services - use the root injector or register them in an angular module?
You should always register services with the root injector unless you want to satisfy a particular case.
-
How does angular search for a service or a dependency when a component asks for it?
In two passes:
-
1st pass - It searches through the injectors of all the parent components up through the component tree.
-
2nd pass - It searches through the injectors of all the parent modules, including the root injector of the application.
-
-
Where will you register services in a component?
In the providers property of the
@Component
decorator. -
Which components have access to services that are registered with the component injector?
The component itself and the child components.
-
When a component is rendered, what type of service instance does it receive - new or singleton?
A new instance is created everytime the component is rendered.
-
What type of service instance registered in the parent component do child components receive?
They receive a new service instance per child component or they share the same parent component service instance?
Child components reuse the same instance of the service from the parent component.
-
You would like to restrict service dependency access to only those child components created in the view template of the component.
In which property of the
@Component
decorator would you register your service?viewProviders: [ProductsService]
-
You would like to restrict your component to look for a dependency just at the level of the parent component and not above that.
If it does not find the service in the parent component injector an error should be thrown.
Which decorator would help in this scenario?
Apply the @Host decorator to those dependency parameters as follows:-
constructor(@Host() private productService: ProductsService) {}
-
You would like to restrict your component to look for a dependency just at the level of the parent component and not above that.
If it does not find the service in the parent component injector an error should not be thrown.
Which decorator would help in this scenario?
You can use the
@Optional
decorator as follows:
constructor(@Host() @Optional() private productService: ProductsService) {}
-
You would like to restrict your component to look for a dependency just at the level of the current component and not above that.
Which decorator would help in this scenario?
You can use the
@Self
decorator as follows:
constructor(@Self() private productService: ProductsService) {}
-
You would like to restrict your component to look for a dependency at all levels except the current component level.
Which decorator would help in this scenario?
You can use the
@SkipSelf
decorator as follow:
constructor(@SkipSelf() private productService: ProductsService) {}
-
Give an example of a class provider syntax?
providers: [ProductsService]
-
Write the longer form of a class provider syntax?
{ providers: [ { provide: ProductsService, useClass: ProductsService } ] }
-
provide - This is the class that consumers of the dependency inject into their constructors.
-
useClass - This is the actual implementation of the class, the injector will provide. The property name will differ according to the implementation type provided. The type can be a useClass, useValue, or a useFactory.
-
-
When would you use the deps property in the class provider syntax?
providers: [ { provide: ProductsService, useFactory: favoritesFactory(true) } ]
If one of the services also injected other dependencies, the previous syntax would not suffice. You should use the deps property to add dependencies.
providers: [ { provide: ProductsService, useFactory: favoritesFactory(true), deps: [ProductViewService] }];
-
What is an interface?
Interfaces are syntactic sugar in TypeScript that are thrown away during compilation.
-
How can we make an interface object injectable?
By using an injection token as follows:
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
-
where AppConfig is the interface type
-
and
app.config
is the file name.
-
-
How do we inject an InjectionToken object into an angular component?
First, declare it in the providers property of the
@Component
decorator as follows:-
` providers: [{ provide: APP_CONFIG, useValue: appSettings }],`-
where
APP_CONFIG
is the injection token. -
and
appSettings
is the name of the instance of the interface objectSecond, Use the
@Inject
decorator:
constructor(@Inject(APP_CONFIG) config: AppConfig) {}
-
where
APP_CONFIG
is the injection token. -
and
config
is of the type AppConfig containing the values fromappSettings
object.
-
RxJs
-
What is an Observable?
It is a collection of items emitted over time.
-
Why use an Observable?
It is great for things that arrive over time like keypresses, events, an http response etc.
-
What is an Observer?
An observer watches an observable for notifications.
-
What notifications does an observer watch for?
next, error and complete.
-
What is a first order mapping?
It transforms each emitted value and emits the result.
map(x ⇒ x * 2);
-
What is a higher order mapping?
It transforms each emitted value to an Observable.
map(x ⇒ of(x*2)
-
If you encounter a situation where you have to perform an operation on a value published by an Observable of Observable eg:-
Observable<Observable<Product>>
.Which type of rxjs operators would you look at using?
Higher-order operators
-
When would you consider using an higher-order operator of
rxjs
?If you would like to perform an operation on a Observable of Observable.
Eg:-Observable<Observable<Product>>
-
Give 3 examples of higher-order operators in
rxjs
.switchMap
,mergeMap
andconcatMap
-
How do higher-order operators of
rxjs
operate?They map each value from a source (outer) observable to a new (inner) observable and they automatically subscribe to and unsubcribe from the inner observables for you and emit the resulting values to the output observable. Because remember an observable whether outer or inner does not execute unless subscribed to.
-
When would we need to define a Subject?
If we need to define our own observables.
-
How do you define a Subject whose stream will emit a number?
private subject = new Subject<number>();
subject$ = this.subject.asObservable();
-
How do you define a BehaviourSubject whose stream will emit a number?
private bhSubject = new BehaviorSubject<number>(1);
bhSubject$ = this.bhSubject.asObservable();
-
What is the difference between a BehaviourSubject and a Subject?
With a Subject, once you subscribe, you will not get a value until a new value is emitted.
With a BehaviorSubject, once you subscribe, you will get the last emitted value or the default value. -
How do you emit a value using a Subject?
this.subject.next(categoryId)
-
When would you use the
fromEvent
operator ofrxjs
?When you would like to create an observable from a DOM event of an html element.
-
You would like to create an observable from a DOM event.
Which
rxjs
operator would you use?The
fromEvent
operator. -
When would you use the
of
operator ofrxjs
?When you would like to create an observable from values such as numbers.
const values = of(1, 2, 3);
-
You would like to create an observable from values such as numbers.
Which
rxjs
operator would you use?The
of
operator. -
When would you use the
from
operator ofrxjs
do?When you would like to convert an array or a promise to an observable:
const values = from([1, 2, 3]);
-
You would like to convert an array or a promise to an observable.
Which
rxjs
operator would you use?The
from
operator. -
When would you use the
pipe
operator ofrxjs
?When you would like to chain multiple operators one after the other.
-
You would like to chain multiple operators one after the other.
Which
rxjs
operator would you use?The
pipe
operator. -
When would you use the
tap
operator ofrxjs
?When we would like to do something with the data emitted without modifying it.
-
You would like to do something with the data emitted without modifying it.
Which
rxjs
operator would you use?The
tap
operator. -
When would you use the
map
operator ofrxjs
?When you would like to perform an operation on each of the emitted value.
-
You would like to perform an operation on each of the emitted value.
Which
rxjs
operator would you use?The
map
operator. -
When would you use the
filter
operator ofrxjs
?When you would like to filter out emitted values based on a certain condition.
-
You would like to filter out emitted values based on a certain condition.
Which
rxjs
operator would you use?The
filter
operator. -
When would you use the
forkJoin
operator ofrxjs
?When you would like to wait for all of the inner observables to complete.
-
You would like to wait for all of the inner observables to complete.
Which operator would you use?
The
forkJoin
operator. -
What are higher order observables?
Observables that operate on other observables.
-
What are observables that operate on other observables called?
Higher-order observables.
-
What do higher-order observables contain?
They contain an inner observable that contains the actual values.
-
When will you use the
concatMap
operator ofrxjs
?If you would like to perform an operation on the values emitted by an observable in a sequential first-in first-out format, no matter how long the operation may take for each value.
-
What are the scenarios where a
concatMap
operator ofrxjs
would be useful?When you have to perform insert, update and delete operation and the order is important.
-
When will you use the
mergeMap
operator ofrxjs
?If you would like to perform an operation on the values emitted by an observable all at once and the order of the return values does not matter.
-
When will you use the
switchMap
operator ofrxjs
?If you would like to perform an operation on the values emitted by an observable but only on the latest emitted value. If the observable emits a new value, the operations on the previous values are cancelled.
-
What are the scenarios where a
switchMap
operator ofrxjs
would be useful?When you would like to perform an autocomplete but only on the latest value.
-
Why should we use the async pipe in Angular?
The async pipe automatically subscribes to and unsubscribes from an observable. Because remember an observable does not execute unless subscribed to
Things to Remember:-
-
An observable does nothing until a consumer subscribes.
-
You need to have a subscription before an observable can publish values.
-
A subscription is an object that represents the execution of an observable.
-
Ensure each Observable is subscribed and ensure each subscription is unsubscribed.
-
Don’t nest subscribes.
-
Use the async pipe.
To work with multiple streams, use a combination operator.
-
combineLatest - Emits a combined value when any of the Observables emit. It won’t emit until all Observables have emitted at least once.
-
merge - Emits one value when any of the observables emit.
-
forkJoin - When all of the observables complete, emit the last value from each Observable into an array.
Interceptors
-
What is an HTTP interceptor?
An Angular service that intercepts HTTP requests and responses that pass through the HttpClient.
-
What are the scenarios where we would like to use an HTTP Interceptor?
-
When we want to pass custom HTTP headers in every request, such as an authentication token.
-
When we want to display a loading indicator while we wait for a response from the server.
-
When we want to provide a logging mechanism for every HTTP communication.
-
-
You would like to pass a custom Http header for every request.
Which angular feature would you use?
An HTTP interceptor.
-
You would like to display a loading indicator while we wait for a response from the server.
Which angular feature would help with this?
An HTTP interceptor
-
You would like to log every HTTP communication.
Which angular feature can help with this?
An HTTP interceptor
-
In which module must an HTTP interceptor be provided?
It must be provided in the same Angular module that imports HttpClientModule.
-
How will you register an interceptor?
To register an interceptor with a module,
-
Import the HTTP_INTERCEPTORS injection token from the
@angular/common/http
namespace -
Add it to the providers array of the @NgModule decorator.
-
-
Write the command that creates an interceptor?
ng generate interceptor auth
-
Which interface must an angular interceptor implement?
An HttpInterceptor interface.
-
Which method of the HTTP interceptor must an angular interceptor implement?
It must implement the intercept method of the HttpInterceptor interface that accepts the following parameters:
-
request: An HttpRequest object that indicates the current request.
-
next: An HttpHandler object that denotes the next interceptor in the chain.
-
-
Which interceptor is the last interceptor in the pipeline before the request is sent to the server?
HttpBackend
Ngrx
-
What is Redux?
Redux is a global state of data that is outside your framework.
-
What is
Ngrx
?It is a bunch of libraries that helps build reactive applications in Angular and implements redux in the Angular way.
-
Why use
Ngrx
store for state management?When you have a lot of user interactions and services and managing state in services in no longer sufficient.
Ngrx
state management lifecycle-
-
A component triggers an action.
-
Action calls the service for new data.
-
Service returns with the new data.
-
Action passed the data to a reducer.
-
Reducer updates the store with the new data.
-
Store activates the selectors with the new data.
-
Components registered to the selectors update their state.
-
-
What is an action in
Ngrx
?An action is a simple interface with a property called
type
that is astring
. -
What is the purpose of the
createAction
function inNgrx
?It helps us create an action.
-
What can you pass as parameters to the
createAction
function inNgrx
?-
The global name of the action.
-
Additional properties that the action can accept as a parameter.
-
-
What is the standard format for naming
Ngrx
actions?Include a namespace in the name of your action
[namespace-name] action-name
e.g:-[Auth] Register
. -
Why must we include a namespace while naming our
Ngrx
actions?Because all actions are stored in a global state.
-
What does the following function do?
export const register = createAction( '[Auth] Register', props<{request: RegisterRequestInterface}>() );
It creates an action called
register
with the name[Auth] Register
and accepts aRegisterRequestInterface
object as a parameter. -
What does the following code do?
this.store.dispatch(register({request}));
It dispatches a
register
action with a request parameter to the store. -
What does
provideStore
do in the following application?bootstrapApplication(AppComponent, { providers: [provideRouter(appRoutes), provideStore()], });
It initializes the single store object for the application.
-
Which
Ngrx
library provides redux dev tools?@ngrx/store-devtools
REST
-
When must you use the
patch
method and when must you use theput
method?The
patch
method should be used when we want to update only a subset of an object.
Theput
method should be used when we want to update all of the object properties.
Reactive Forms
-
Detail the steps that go into creating a reactive form in angular?
-
Create the form in html.
-
Import
FormBuilder
,ReactiveForms
andValidators
from@angular/forms
. -
Create a form group using
this.fb.group
orthis.fb.nonNullable.group
. -
Make sure the objects in the form group are mapped to the html controls using
formControlName
property. -
Map the html form to the form group object in the component using
[formGroup]
. -
Create
ngSubmit
on the form tag.
-
-
What is
ReactiveFormsModule
?Exports the required infrastructure and directives for reactive forms.
-
What is a
Validator
?A validator is a function that processes a
FormControl
or collection of controls and returns. -
What does a
Validator
return?An error map in case of failure and null in case of success.
-
What is
updateValueAndValidity()
?When you add or remove a validator at run time, you must call`updateValueAndValidity()` function for the new validation to take effect.
Unit Tests
describe('Calculator', () => {
it('should add two numbers', () => {
expect(1+1).toBe(2);
});
});
-
the describe method defines a test suite and accepts a name and an arrow function as parameters.
-
The
it
method defines a single unit test. It accepts a name and an arrow function as parameters.
describe('Calculator', () => {
let total: number;
beforeEach(() => total = 1);
it('should add two numbers', () => {
total = total + 1;
expect(total).toBe(2);
});
it('should subtract two numbers', () => {
total = total - 1;
expect(total).toBe(0);
});
afterEach(() => total = 0);
});
-
The
beforeEach
method is used for the setup functionality and runs before every unit test. -
The
afterEach
method runs after every unit test.
Angular testing framework:-
-
Jasmine - It is a unit testing framework.
-
Karma - The test runner for running our unit tests.
-
Angular testing utilities - A set of helper methods that assist us in setting up our unit tests and writing assertions in the context of the Angular framework.
Angular testing utilities consists of the TestBed
class and various helper methods that can be found under the @angular/core/testing
namespace.
-
TestBed
-
It is a testing module that behaves like an Angular module.
-
When we test an Angular artifact, we detach it from the angular module it resides in and attach it to this testing module.
-
The
TestBed
class contains theconfigureTestingModule
method we use to set up the test module as needed.
-
-
ComponentFixture
-
A wrapper class around an Angular component instance.
-
It allows us to interact with the component and its corresponding HTML element.
-
-
DebugElement
-
A wrapper around the DOM element of the component.
-
It is an abstraction that operates cross-platform so that our tests are platform-independent.
-
Angular creates a spec file for every component you create using the CLI.
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
});
It uses the configureTestingModule method of the TestBed
class.
The declarations array contains the components that we want to test.
The teardown property defines tear-down options for the module.
The teardown property contains an object of the ModuleTeardownOptions that has the properties:
-
destroyAfterEach - Creates a new instance of the module at each test.
-
rethrowErrors - It throws any errors that occur when the module is destroyed.
The compileComponents
method compiles components configured in the testing module. It inlines external CSS files and templates.
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent); (1)
const app = fixture.componentInstance; (2)
expect(app).toBeTruthy(); (3)
expect(app.title).toEqual('my-app'); (4)
});
1 | createComponent returns a ComponentFixture instance of the AppComponent type. |
2 | The instance of the component can be obtained from the componentInstance property. |
3 | toBeTruthy() checks whether the resulting instance is valid. |
4 | The componentInstance can be used to read the properties of the component such as the title . |
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges(); (1)
const compiled = fixture.nativeElement as HTMLElement; (2)
expect(compiled.querySelector('.content span')?.textContent).toContain('my-app app is running!'); (3)
});
1 | The detectChanges method triggers the Angular change-detection mechanism, forcing the data bindings to be updated.
On first run, it executes |
2 | We can query the DOM element of the component using the nativeElement property. |
3 | querySelector can be used to select the html elements. |
Performing DOM unit testing in your Angular applications is recommended.
There are two ways to testing with dependencies:
-
Stubbing - This method tells the dependency injector to inject a stub of the dependency that we provide.
-
Spying - This method injects the actual dependency by attaching a spy to the method that we call in our component. We can either return mock data or let the method call through.
Using stubbing over spying is preferable when a dependency is complicated. |
A fake dependency or a stub can be created in the following ways:-
-
Create a constant variable that contains properties and methods of the real dependency.
-
Create a mock definition of the actual class of the dependency.
const stubService: Partial<CustomerService> = { (1)
name: 'Boothstomper'
}
await TestBed.configureTestingModule({
declarations: [ CustomerComponent ],
providers: [
{ provide: CustomerService, useValue: serviceStub } (2)
]
});
fixture = TestBed.createComponent(CustomerServiceComponent);
component = fixture.componentInstance;
msgDisplay = fixture.nativeElement.querySelector('span');
service = TestBed.inject(CustomerService); (3)
1 | A stub of the CustomerService service. |
2 | Injects the stub in our testing module. |
3 | The injected instance of the CustomerService class using the inject method of the TestBed class. |
Stubbing a dependency is not always viable, especially when the root injector does not provide it.
A service can be provided at the component injector level.
await TestBed.configureTestingModule({
declarations: [ StubComponent ],
})
.overrideComponent(StubComponent, { (1)
set: { (2)
providers: [
{ provide: StubService, useValue: serviceStub }
]
}
});
1 | The overrideComponent methods accepts the type of the component and a metadata object. |
2 | The metadata object contains the set property, which is used to provide services to the component. |
There are two ways to set up a spy dependency:
-
Inject the actual dependency and spy on its methods.
-
Use the Jasmine
createSpyObj
method to create a fake instance of the dependency.
describe('with spy object', () => {
let titleSpy: jasmine.SpyObj<Title>; (1)
beforeEach(() => {
titleSpy = jasmine.createSpyObj('Title', ['getTitle', 'setTitle']); (2)
titleSpy.getTitle.and.returnValue('My title'); (3)
TestBed.configureTestingModule({
declarations: [ SpyComponent ],
providers: [
{ provide: Title, useValue: titleSpy }
]
});
fixture = TestBed.createComponent(SpyComponent);
component = fixture.componentInstance;
});
it('should get the title', () => {
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('My title'); (4)
});
});
1 | Define the Title service as a spy object. |
2 | createSpyObj method to initialize the spy object, passing in the name of the service and an array of the method names that the component uses. |
3 | Attach a spy to the getTitle method and return a custom title. |
4 | Usage. |
There are two ways to tackle asynchronous testing scenarios:
-
waitForAsync - It is combined with
whenStable
method of ComponentFixture. -
fakeAsync - It is used in combination with the tick method.
// The Component class
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { AsyncService } from '../async.service';
@Component({
selector: 'app-async',
template:`
<p *ngFor="let hero of data$ | async">
{{hero}}
</p>`
})
export class AsyncComponent implements OnInit {
data$: Observable<string[]> | undefined;
constructor(private asyncService: AsyncService) { }
ngOnInit() {
this.data$ = this.asyncService.getData();
}
}
// The service
getData(): Observable<string[]> {
return of(heroes).pipe(delay(500));
}
it('should get data with waitForAsync', waitForAsync(async() => { (1)
fixture.detectChanges(); (2)
await fixture.whenStable(); (3)
fixture.detectChanges(); (4)
const heroDisplay: HTMLElement[] = fixture.nativeElement.
querySelectorAll('p');
expect(heroDisplay.length).toBe(5);
}));
1 | The body of the test is wrapped inside the waitForAsync method. |
2 | The detectChanges method triggers the ngOnInit lifecycle hook. |
3 | The whenStable method returns a promise that resolves once the data$ observable is complete. |
4 | Call detectChanges once more to trigger databinding and query the DOM accordingly. |
it('should get data with fakeAsync', fakeAsync(() => { (1)
fixture.detectChanges(); (2)
tick(500); (3)
fixture.detectChanges(); (4)
const heroDisplay: HTMLElement[] = fixture.nativeElement.
querySelectorAll('p');
expect(heroDisplay.length).toBe(5);
}))
1 | The body of the test is wrapped in a fakeAsync method. |
2 | The detectChanges() method triggers the ngOnInit lifecylce hook. |
3 | The tick method advances the time by 500ms. |
4 | Call detectChanges once more to trigger databinding and query the DOM accordingly. |
Testing how a component interacts when hosted from another component can be done in two ways:
-
We can verify that our input binding is correctly set.
-
We can verify that our output binding triggers correctly and that what it emits is received.
// Source Component
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-bindings',
template: `
<p>{{title}}</p>
<button (click)="liked.emit()">Like!</button>
`
})
export class BindingsComponent {
@Input() title = '';
@Output() liked = new EventEmitter();
}
//Parent Component that hosts the above component
@Component({
template: `<app-bindings [title]="testTitle" (liked)="isFavorite =
true"></app-bindings>`
})
export class TestHostComponent {
testTitle = 'My title';
isFavorite = false;
}
//beforeEach method to setup the tests
let component: TestHostComponent;
let fixture: ComponentFixture<TestHostComponent>; (1)
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
BindingsComponent, (2)
TestHostComponent
]
});
fixture = TestBed.createComponent(TestHostComponent); (3)
component = fixture.componentInstance;
fixture.detectChanges();
});
1 | Only the ComponentFixture of the parent component is declared. |
2 | Both the components are declared. |
3 | Only the ComponentFixture of the parent component is created. |
it('should display the title', () => {
const titleDisplay: HTMLElement = fixture.nativeElement.querySelector('p');
expect(titleDisplay.textContent).toEqual(component.testTitle);
});
it('should emit the liked event', () => {
const button: HTMLButtonElement = fixture.nativeElement.querySelector('button');
button.click();
expect(component.isFavorite).toBeTrue();
});
it('should emit the liked event using debugElement', () => {
const buttonDe = fixture.debugElement.query(By.css('button')); (1)
buttonDe.triggerEventHandler('click');
expect(component.isFavorite).toBeTrue();
});
1 | Use nativeElement if your app will only execute in the browser otherwise use debugElement . |
Angular CDK testing utilities enable us to access Angular Material components without relying on their internal implementation by using a component harness.
Using a harness consists of the following parts:
-
@angular/cdk/testing - The npm package that contains infrastructure for interacting with a component harness.
-
Harness Loader - A class used to load a component harness inside a unit test.
-
Component Harness - A class that gives the developer access to the instance of a component in the browser DOM.
// Parent Component
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-bindings',
template: `
<p>{{title}}</p>
<button mat-button (click)="liked.emit()">Like!</button>
`
})
export class BindingsComponent {
@Input() title = '';
@Output() liked = new EventEmitter();
}
// Testing the Component
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; (1)
import { HarnessLoader } from '@angular/cdk/testing'; (2)
import { MatButtonHarness } from '@angular/material/button/testing'; (3)
loader = TestbedHarnessEnvironment.loader(fixture); (4)
it('should emit the liked event using harness', async () => { (5)
const buttonHarness = await loader.getHarness(MatButtonHarness); (6)
await buttonHarness.click();
expect(component.isFavorite).toBeTrue();
});
1 | The TestbedHarnessEnvironment class represents the testing environment for running unit tests with Karma. |
2 | The HarnessLoader class is used to create a harness loader instance. |
3 | Almost all angular material components have a corresponding component harness. |
4 | The loader method accepts the ComponentFixture of the component under test and returns a HarnessLoader object. |
5 | The body of the test must be inside async as harnesses are promise based. |
6 | await the creation of the buttonHarness . |
There are 3 different types of testing we can perform in a service:
-
Testing a synchronous operation, such as a method that returns a simple array.
-
Testing an asynchronous operation, such as a method that returns an observable.
-
Testing services with dependencies, such as a method that makes HTTP requests.
import { TestBed } from '@angular/core/testing'; import { AsyncService } from './async.service'; describe('AsyncService', () => { let service: AsyncService; beforeEach(() => { TestBed.configureTestingModule({}); service = TestBed.inject(AsyncService); (1) }); it('should be created', () => { expect(service).toBeTruthy(); }); it('should set data', () => { const result = service.setData('Fake hero'); (2) expect(result.length).toBe(6); }); });
1 | AsyncService is configured in the root injector. If it was not, you need to add it to the providers array of the TestingModule. |
2 | Straightforward test case. |
it('should get data', (done: DoneFn) => { (2)
service.getData().subscribe(heroes => { (1)
expect(heroes.length).toBe(5);
done(); (3)
});
})
1 | getData returns an observable, so we subscribe to it. |
2 | Karma does not know when the observable will complete, so we provide the done method to signal completion. |
3 | Calling the done method. |
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule] (1)
});
service = TestBed.inject(ProductsService);
httpTestingController = TestBed.inject(HttpTestingController); (2)
});
it('should get products', () => {
service.getProducts().subscribe();
const req = httpTestingController.expectOne("https://fakestoreapi.com/products"); (3)
expect(req.request.method).toBe('GET'); (4)
});
it('should add a product', () => {
service.addProduct('Fake product', 100).subscribe(); (5)
const req = httpTestingController.expectOne('https://fakestoreapi.com/products');
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual({ (6)
title: 'Fake product',
price: 100
});
});
afterEach(() => {
httpTestingController.verify(); (7)
});
1 | HttpClientTestingModule replaces the real HttpClientModule . |
2 | HttpTestingController mocks the HttpClient service. |
3 | A fake request using the expectone method which creates a mock request object and asserts that only one request is made. |
4 | Validate that its method is GET . |
5 | Testing the addProduct method. |
6 | Verify that the request body contains the correct data. |
7 | Make sure that no unmatched requests are pending using the verify method. |
Testing pipes is pretty straightforward.
import { ListPipe } from './list.pipe';
describe('ListPipe', () => {
it('create an instance', () => {
const pipe = new ListPipe();
expect(pipe).toBeTruthy();
});
it('should return an array', () => {
const pipe = new ListPipe();
expect(pipe.transform('A,B,C')).toEqual(['A', 'B', 'C']);
});
});
No Angular testing utilities are involved. Instantiate the Pipe class and start calling methods.
Directives is used with components. So it makes sense to test them with components.
describe('CopyrightDirective', () => {
let container: HTMLElement;
beforeEach(() => {
const fixture = TestBed.configureTestingModule({
declarations: [
CopyrightDirective,
TestHostComponent
]
}).createComponent(TestHostComponent);
container = fixture.nativeElement.querySelector('span');
});
it('should have copyright class', () => {
expect(container.classList).toContain('copyright');
});
it('should display copyright details', () => {
expect(container.textContent).toContain(new Date().getFullYear().toString());
});
});
Testing Reactive Forms
// source component
export class SearchComponent {
get searchText() {
return this.searchForm.controls.searchText;
}
searchForm = new FormGroup({
searchText: new FormControl('', Validators.required)
});
search() {
if(this.searchForm.valid) {
console.log('You searched for: ' + this.searchText.value)
}
}
}
// Test imports
TestBed.configureTestingModule({
imports: [ReactiveFormsModule], (1)
declarations: [SearchComponent]
});
// Test methods
it('should set the searchText', () => {
const input: HTMLInputElement = fixture.nativeElement.querySelector('input');
input.value = 'Angular';
input.dispatchEvent(new CustomEvent('input')); (2)
expect(component.searchText.value).toBe('Angular');
});
it('should disable search button', () => {
component.searchText.setValue(''); (3)
expect(button.disabled).toBeTrue();
});
it('should log to the console', () => {
const spy = spyOn(console, 'log'); (4)
component.searchText.setValue('Angular');
fixture.detectChanges();
button.click();
expect(spy).toHaveBeenCalledWith('You searched for: Angular'); (5)
});
1 | Import the ReactiveFormsModule . |
2 | Triggers the input DOM event to that element. |
3 | You can also use setValue instead of the input element. |
4 | Use spyOn for console object. |
5 | Verify the result. |
In cases where you may have many controls, create a Page
object in the beforeEach
statement and access its properties and methods in our tests.
class Page {
get searchText() { return this.query<HTMLInputElement>('input'); }
get submitButton() { return this.query<HTMLButtonElement>('button'); }
private query<T>(selector: string): T {
return fixture.nativeElement.querySelector(selector);
}
}
Production Deployment
ng build --configuration=development
ng build --configuration=production
The output files of the production configuration contains hash numbers in the filename to invalidate cached files in the browser.
Steps to create different environments. Lets say staging.
Create a default environment file:
export const environment = {
apiUrl: 'https://my-default-url'
};
Create a staging environment file:
export const environment = {
apiUrl: 'https://my-staging-url'
};
Add the environment to AppComponent
import { Component } from '@angular/core';
import { environment } from '../environments/environment';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
apiUrl = environment.apiUrl;
}
In angular.json
, in the architect
property, in the configurations
property of the build
command, add the staging
configuration.
"staging": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.staging.ts"
}
]
}
Execute: ng build --configuration=staging
You can limit your application size by using the budget property in angular.json.
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
]
Angular CLI performs optimization techniques when building and application:
-
Minification - Converts multiline source files into a single line, removing white space and comments.
-
Uglification - Renames properties and methods to a non-human-readable form so that they are difficult to understand and use for maliciuous purposes.
-
Bundling - Concatenates all source files of the application into a single file, called the bundle.
-
Tree-Shaking - Removes unused files and Angular artifacts.
-
Font optimization - Inlines external font file. Currently supports Google and Adobe fonts.
-
Build cache - Caches the previous build state and restores it when we run the same build. Decreasing build time.
In case the final bundle is still large, use an external tool called source-map-explorer
to investigate why.
To deploy an angular application, you can copy the contents of the output folder to a path in a server.
In case you would like to change the output path, change the base-href
property.
ng build --base-href=/mypath/
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"main": "src/main.ts",
"baseHref": "/mypath/",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
A Global Error Handler
// app-error-handler.ts
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { ErrorHandler, Injectable } from '@angular/core'; (1)
@Injectable() (2)
export class AppErrorHandler implements ErrorHandler { (3)
handleError(error: any): void {
const err = error.rejection || error; (4)
if (err instanceof HttpErrorResponse) {
switch(err.status) {
case 0:
console.error('Client error:', error.error);
break;
case HttpStatusCode.InternalServerError:
console.error('Server error:', error.error);
break;
case HttpStatusCode.BadRequest:
console.error('Request error:', error.error);
break;
default:
console.error('Unknown error:', error.error);
}
} else {
console.error('Application error:', err)
}
}
}
// app.module.ts
import { NgModule, ErrorHandler } from '@angular/core'; (5)
import { AppErrorHandler } from './app-error-handler'; (6)
providers: [
{ provide: ErrorHandler, useClass: AppErrorHandler } (7)
]
1 | Imports the error handler. |
2 | Not added to the root injector. Will be added in the AppModule. |
3 | Implements the ErrorHandler |
4 | Errors that originate from Zone.js populate the rejection property. |
5 | Import the ErrorHandler in AppModule. |
6 | Import the AppErrorHandler in AppModule. |
7 | Add it to the providers array. |
A good place to handle 401 Unauthorized error
is in an interceptor.
A 401 Unauthorized error
can occur because:
-
The user does not provide the correct credential while logging into the application.
-
The authentication token provided when the user logged in to the application has expired.
export class AuthInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const authReq = request.clone({
setHeaders: { Authorization: 'myAuthToken' }
});
return next.handle(authReq).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === HttpStatusCode.Unauthorized) {
this.authService.logout();
return EMPTY; (1)
} else {
return throwError(() => error); (2)
}
})
);
}
}
1 | Return an EMPTY observable to stop returning data. |
2 | throwError which will bubble up to the global error handler. |
Updates
-
14-Jun-2023 - Updated Ngrx section. Added ReactiveForms section. Anki package(apkg) and Anki package(txt)
-
11-Jun-2023 - Added Ngrx and REST. Added new questions to Components, Routing and Setup. Anki package(apkg) and Anki package(txt)
-
10-Jun-2023 - Added components, directives, setup and pipes section. Refactored the existing sections. Anki package(apkg) and Anki package(txt)