Angular One-Pager

one-pagers typescript angular

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

  1. Which npm command will install the angular cli for version 15?

    npm install -g @angular/cli@15

  2. What does the following code do?

    npm install -g @angular/cli@15

    It will install the version 15 of the`angular/cli`.

  3. Which angular cli command creates a new application?

    ng new my-app

  4. What does the following command do?

    ng new my-app

    It creates a new angular application called my-app.

  5. What is angular.json?

    The main configuration file of the angular workspace.

  6. What is the name of the main configuration file of the Angular cli workspace?

    angular.json

  7. Which angular cli command starts the angular application?

    ng serve

  8. What does ng serve do?

    It starts the web application.

  9. Which file is the main html file of the angular application?

    index.html

  10. Which file is the main entry point of the angular application?

    main.ts

  11. 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

  1. Which file defines the main module of the angular application?

    app.module.ts

  2. What is the main module of angular called?

    AppModule

  3. In which file will you find AppModule?

    app.module.ts

  4. What is the double curly braces syntax called?

    interpolation

  5. What are decorators?

    Allows us to add metadata to class declarations.

  6. How can you recognize a decorator?

    The @ prefix.

  7. How many types of decorators are there?

    • Class decorators

    • Property decorators

    • Method decorators

    • Parameter decorators

  8. When does the class decorator execute?

    The decorator statement is executed before the class gets instantiated.

  9. Which feature does the BrowserModule provide to an angular application?

    It allows an angular application to run in the browser.

  10. What properties does NgModule accept?

    • declarations

    • imports

    • providers

    • bootstrap

  11. Which angular artifacts can be added to the declarations collection of NgModule?

    Components, directives, and pipes.

  12. What does the imports property of NgModule contain?

    Other modules and standalone components, directives and pipes.

  13. What does the providers property of NgModule contain?

    Services

  14. What does the bootstrap property of AppModule define?

    The component that loads on startup.

  15. What are feature modules?

    Modules that represent specific features of the application.

  16. What features does the CommonModule provide to an angular application?

    It provides the template syntax features to an angular application.

  17. What is the cli command to generate a module?

    ng generate module module-name

  18. How do you expose components from an Angular module?

    Using the exports array of NgModule

  19. What features does the HttpClientModule provide an angular application?

    It provides features that enable http communication.

  20. What is the RouterModule?

    Module that enables navigation in an Angular application.

  21. What features does the BrowserAnimationsModule provide to an angular application?

    It provides features that enable UI animations in an Angular application.

Components

  1. Which decorator would you use to create an angular component?

    @Component

  2. Which tag in index.html is used to load the main component of the application?

    <app-root></app-root>

  3. What is the <app-root></app-root> tag?

    It is used to load the main component of the angular application.

  4. By convention, What is the main component of the angular application?

    AppComponent

  5. What is the AppComponent?

    It is the default component of an angular application.

  6. Where is the AppComponent registered?

    In the AppModule defined in the app.module.ts file.

  7. Does every component created need to be registered in a module?

    Yes except standalone components.

  8. 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.

  9. What prefix does angular add to a component’s selector by default?

    app-

  10. What is the point of the templateUrl property in the angular component decorator?

    Defines the path of the component’s html template file.

  11. What is the point of the styleUrls property of an angular decorator component?

    It defines the path to external style sheets of the component.

  12. Can you register a component in more than one module?

    A component must be registered with only one Angular module.

  13. By default, in which module does the ng generate component command register a component?

    AppModule

  14. What is a standalone component?

    A component that does not need to be registered in an Angular module.

  15. Which cli command creates a standalone angular component?

    ng generate component component-name --standalone

  16. Which additional properties do standalone components contain?

    • standalone

    • imports

  17. Can standalone components import angular modules?

    Standalone components can import Angular modules and vice versa.

  18. In which property of an NgModule must you register a standalone component?

    imports array.

  19. 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.

  20. What is the following angular syntax called?

    <span [innerText]="title"></span>

    Property Binding syntax.

  21. What is innerText called and what is title called?

    <span [innerText]="title></span>

    innerText is called the target property. title is called the template expression.

  22. What is the following angular syntax called?

    <p [class.star]="isLiked"></p>`

    Class Property Binding syntax

  23. What is the output of the following code?

    <p [style.color]="'greenyellow'"></p>

    The paragraph element will have a greenyellow color.

  24. What is the output of the following code?

    <p [style.width.px]="'100'"></p>"

    The paragraph element will be 100 pixels long.

  25. What is the target event and what is the template statement in the code below?

    <button (click)=""onClick()"">Click me</button>

    (click) is the target event. onClick() is the template statement.

  26. What is @Input?

    A component input property decorator.

  27. What does the following code define?

    @Input() name = '"";

    Defines a name property that is an Input to the component.

  28. Explain the following code in detail?

    <app-product-detail [name]="selectedProduct"></app-product-detail>

    It binds the name property of app-product-detail to the selectedProduct property of the parent component.

  29. Explain the following code in detail?

    <app-product-detail name="Webcam"></app-product-detail> The string value Webcam is bound to the name property of app-product-detail.

  30. What is @Output?

    @Output is an angular property decorator for emitting events.

  31. What does the following code declare?

    @Output() bought = new EventEmitter();

    A property called bought that emits events.

  32. What does the following code do?

    this.bought.emit();

    It emits an event from the component.

  33. Explain the following code?

    <app-product-detail [name]="selectedProduct" (bought)="onBuy()"></app-product-detail>

    The name property of app-product-detail is bound to the selectedProduct of the parent component.
    The bought event of app-product-detail is bound to the onBuy() method of the parent component.

  34. What does the following code declare?

    @Output() bought = new EventEmitter<string>();

    A property called bought that emits string data.

  35. 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.

  36. What is the term in angular for #product?

    <app-product-detail #product [name]="selectedProduct" (bought)="onBuy()"></app-product-detail>

    template reference variable.

  37. What is View Encapsulation?

    View Encapsulation is how angular manages CSS scoping within a component.

  38. How can you change the view encapsulation of a component?

    By changing the encapsulation property of the @Component decorator.

  39. What are the three View Encapsulation enumeration value?

    • Emulated

    • Native

    • None

  40. What are the most basic angular lifecycle hooks?

    ngOnInit, ngOnDestroy, ngOnChanges, ngAfterViewInit

  41. What is the general advice towards using constructors in Angular?

    Should be relatively empty and devoid of logic.

  42. 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.

  43. When is a component destroyed?

    A component is destroyed when it is removed from the DOM.

  44. What should we do in ngOnDestroy method?

    Perform cleanups:

    • Reset timers and intervals

    • Unsubscribe from observable streams

  45. When does ngOnChanges get called?

    When the value of an input data binding has changed.

  46. Can you change the selector prefix 'app' that angular adds by default to a component?

    Yes in the angular.json file.

  47. Which flag of ng generate component should you use to create a standalone component?

    --standalone

  48. What is class property binding?

    Component property bound to [class.class-name].

  49. What is style property binding?

    Component property bound to [style.style-name].

  50. Why would you use the @Input artifact?

    To pass data from one component down to another component.

  51. Which class does the Output decorator use to emit events?

    The EventEmitter class.

  52. How would you emit an event from a component?

    Call the emit method - this.bought.emit(1);

  53. 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.

  54. 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.

  55. Which ViewEncapsulation option is the default option?

    Emulated

  56. What does the emulated ViewEncapsulation option provide?

    It simulates a Shadow DOM and encapsulates CSS rules.

  57. Which ViewEncapsulation option simulates a Shadow DOM and encapsulates CSS rules?

    Emulated

  58. What does the Native option of ViewEncapsulation do?

    It uses the native browser Shadow DOM.

  59. Which ViewEncapsulation option simulates a native browser Shadow DOM?

    Native

  60. What limitation does the Native option of ViewEncapsulation have?

    It works only on browsers that support Shadow DOM.

  61. Which lifecycle hook gets called when angular detects that the value of an input data binding has changed?

    ngOnChanges

  62. When does the ngOnChanges lifecycle hook get called?

    Whenever the change detection cycle runs.

  63. What is the first parameter of the ngOnChanges method?

    SimpleChanges

  64. 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;
        }
    }
  65. 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.

  66. Which lifecycle hook gets called when the html template of the component has been initialized?

    AfterViewInit

  67. Which lifecycle hook gets called when the html template of all child components have been initialized?

    ngAfterViewInit

  68. When would you use the @ViewChild?

    To get a reference object of a child component in the template.

  69. 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.

  70. What is the @ContentChild decorator?

    It helps an angular component have access to the elements inside the projected content i.e ng-content

  71. When should you use @ContentChild decorator?

    To get access to elements inside ng-content projected by the parent component.

  72. 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.

  73. 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.

  74. What does the following code do?

    @ContentChildren(CourseImageComponent)
    images: QueryList<CourseImageComponent>

    Selects all <course-image> component within ng-content.

  75. 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.

  76. What does SimpleChanges contain?

    It contains one key for each input property that changes.

  77. 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.

  78. How do you declare a standalone component?

    By setting the standalone property of the @Component decorator to true.

  79. Do standalone components need to be registered with a module?

    No

  80. 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, type bootstrapApplication passing in AppComponent

  81. Can you build angular without using modules?

    Yes by using standalone components.

  82. When would you use the ViewChild decorator?

    When you would like your component to have access to an element in its template.

  83. What does the following code do?

    @ViewChild(CourseCardComponent)
    card: CourseCardComponent

    Angular searches for a course-card component in the template and populates card with a reference to that component.

  84. 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.

  85. What does the following do?

    @ViewChild('cardRef')
    card: CourseCardComponent

    Selects a course-card component with a cardRef template reference variable.

  86. 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.

  87. What does the following code do?

    @ViewChild('cardRef1', {read: ElementRef})
    cardComponent1: ElementRef

    Angular queries the template for a card component with the template reference variable cardRef1 and returns the html element of that component.

  88. 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.

  89. When can you be sure that the @ViewChild objects are populated with the component objects and can be used safely?

    In the ngAfterViewInit() function.

  90. Is it advisable to do any data modification in the NgAfterViewInit lifecycle hook method?

    No you get an error.

  91. 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.

  92. What does the following do?

    @ViewChildren(CourseCardComponent)
    cards: QueryList<CourseCardComponent>;

    Creates a view children component for a list of course-card components.

  93. Which tag can you use for content projection?

    ng-content

  94. Explain what does the following do?

    <ng-content select="img"><ng-content>

    It selects all the img tags from the projected content.

  95. 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.

  96. 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.

  97. 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.

  98. Can you bootstrap an application using standalone components with no angular module?

    Yes

  99. 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

  1. When would you use a directive?

    When you would like to extend the behavior of the html tag itself.

  2. How many types of directives are there?

    There are 3 types of directive:-

    • Components

    • Structural directives

    • Attribute directives

  3. What is a Component directive?

    It is a directive with an associated template.

  4. Which type of directive has an associated template?

    The Component directive.

  5. What is a Structural Directive?

    A directive that adds or removes elements from the DOM.

  6. Which type of directive removes or adds elements to the DOM?

    The Structural Directive.

  7. Which type of directive can modify the appearance of or define a custom behaviour of a DOM element?

    An Attribute Directive.

  8. What are the 3 structural directives provided by angular?

    • ngIf

    • ngFor

    • ngSwitch

  9. What does the ngIf structural directive do?

    Adds or removes a dom element based on an expression.

  10. Which structural directive adds or removes a portion of the DOM tree based on an expression?

    ngIf

  11. What does the ngFor structural directive do?

    Iterates through a list of items and binds each item to a template

  12. Which structural directive iterates through a list of items and binds each item to a template?

    ngFor

  13. Which structural directive switches between templates and displays each one depending on the condition?

    ngSwitch

  14. What does the asterisk in an ngIf directive indicate?

    <div *ngIf=""name"">

    It indicates that it is a structural directive.

  15. Can the ngFor directive observe for changes in the underlying collection?

    Yes

  16. What does angular use to keep track of every item in the ngFor collection?

    An object identity

  17. What is an object identity?

    Angular uses it to keep track of every item in the ngFor collection.

  18. Can you change the object identity of an ngFor collection?

    Yes

  19. How can you change the object identity of an ngFor collection?

    By using the trackBy property.

  20. Why does angular keep tracks of object in an ngFor collection?

    Because any changes to the collection must be synced in the DOM.

  21. Is the ngSwitch directive prefixed by an asterisk?

    No

  22. What are the three sets of directives related to ngSwitch?

    • [ngSwitch]

    • *ngSwitchCase

    • *ngSwitchDefault

  23. What does *ngSwitchCase do?

    It adds or removes a template based on the value of [ngSwitch].

  24. What does *ngSwitchDefault do?

    Add a template if there is no match for *ngSwitchCase.

  25. 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.

  26. 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.

  27. 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.

  28. Which angular decorator allows you to set the properties of the host element from the directives class?

    @HostBinding()

  29. 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.

  30. 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.

  31. 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.

  32. 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 use properyName for internal purposes, whereas components that use the directive should use anotherProperty.

  33. Which angular cli command generates a directive?

    ng generate directive copyright

  34. How do you declare a directive?

    Using the @Directive decorator and defining its selector property.

  35. What is the ElementRef class?

    It gives us access to the host element of the directive.

  36. Which class allows us to manipulate the underlying HTML element attached to the directive?

    ElementRef

  37. Which property of the ElementRef class gives us access to the html element?

    nativeElement

  38. 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.

  39. What is the @HostBinding decorator?

    It binds a value to an attribute of the host element of the directive.

  40. You would like to assign a value to a property of the host element.

    Which decorator would you use?

    @HostBinding

  41. You would like to listen to events raised by the host element in a directive.

    Which decorator will you use?

    @HostListener

  42. What is $event?

    @HostListener('keypress', ['$event']) onKeyPress(event:KeyboardEvent)

    The event object of the triggered event.

  43. What does the ViewContainerRef artifact give access to?

    It gives us access to the angular view container that hosts a directive.

  44. 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.

  45. 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.

  46. If the view container creates more than one view, where is the second view added?

    The second view is added after the first view

  47. How can you create a new component and add it to the view of a ViewContainerRef?

    By using its createComponent method.

  48. How can you create a new ngTemplate and add it to the view of a ViewContainerRef?

    By using its createEmbeddedView method.

  49. What is TemplateRef?

    TemplateRef is a class and the way to reference the ng-template in the component or directive class.

  50. 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.

  51. What is ngTemplateOutlet?

    It is a structural directive that renders the template.

  52. Which event gets called when Angular initializes the view of the current component and its child components?

    ngAfterViewInit

  53. When does ngAfterViewInit get called?

    This is called after angular initializes the view of the current component and its child components.

  54. 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.

  55. Write the syntax for ngFor.

    *ngFor="let course of courses"

  56. Write the syntax for ngFor with an index parameter.

    *ngFor="let course of courses;index as i"

  57. Write the syntax for ngFor with an index and a first parameter

    *ngFor="let course of courses;index as i; first as isFirst"

  58. 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"

  59. 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"

  60. What happens to the element if the ngIf expression returns false?

    The element is removed from the page.

  61. What happens to the element if the ngIf expression returns true?

    The element is added on the page.

  62. What expressions are allowed in ngIf?

    Any variable, object or function that returns a truthy value.

  63. What does ngClass do?

    It adds a class to the element.

  64. What does the following do?

    [ngClass]="'beginner'"

    Adds the beginner class to the element.

  65. What does the following do?

    [ngClass]="['beginner', 'testClass']"

    Adds the beginner and testClass to the element.

  66. What does the following do?

    [ngClass]="{'beginner':true, 'course-card':true}"

    It reads the object and applies the class only if true.

  67. What does the following do?

    [ngClass]=cardClasses()

    It calls the function and the function must return a list of classes.

  68. When should you use ngClass?

    When we want to add a style depending on the content of the data.

  69. What does the following do?

    [style.text-decoration]=""'underline'""

    Adds a text-decoration style to the element.

  70. What does the following do?

    [ngStyle]="{'text-decoration': 'underline'}"

    Add the text-decoration style to the element.

  71. What is an <ng-container>?

    It is an angular element onto which you can apply a structural directive like ngIf or ngSwitch.

  72. 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

  1. What do pipes do?

    They transform the output of an expression in a template.

  2. What are the various types of built-in angular pipes?

    • uppercase/lowercase

    • percent

    • currency

    • slice

    • date

    • json

    • async

    • keyvalue

  3. When would you use the async pipe?

    When data that is handled asynchronously needs to be reflected in our view immediately.

  4. 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.

  5. Which angular command helps generate a new pipe?

    ng generate pipe pipe-name

  6. What is the only required property of the @Pipe decorator?

    The name of the pipe.

  7. "What does the following code do?

    import { Pipe, PipeTransform } from '@angular/core';

    Imports the Pipe and PipeTransform artifact from '@angular/core'.

  8. Which method must a pipe implement?

    It must always implement the transform method.

  9. Which method must an angular pipe implement?

    The transform method

  10. What are the two parameters that the PipeTransform method accepts?

    • The input value that needs to be transformed.

    • an optional arguments list.

  11. What is an impure pipe?

    An impure pipe is called for every change detection cycle no matter whether the value or parameter(s) change.

  12. 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.

  13. 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

  1. 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 use
    3 Configures the routes.
  2. In the bootstapApplication function, how can you configure routes?

    Pass in the routes to the provideRouter function.

  3. 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.

  4. Which function can you use to lazy load a standalone component?

    loadComponent

  5. 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.

  6. 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 and RouterModule.
    3 Adds routes array.
    4 Configures NgModule decorator.
    5 Exports the AppRoutingModule class
  7. 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.

  8. 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.

  9. In what order should you add routes?

    • Static Path

    • Empty Path - default route.

    • Wildcard Path - not found route.

  10. Which route should be last by default?

    The wildcard route.

  11. How can you add a wildcard route to your routes definition?

    { path: '**', component: <component-name> }

  12. In the providers array, in the provideRouter method, how will you enable input binding with routing?

    providers: [provideRouter(appRoutes, withComponentInputBinding()),]

  13. What are eager-loaded modules?

    These are loaded at application startup.

  14. How do you declare eager-loaded modules?

    In the imports array of another module.

  15. What are lazy-loaded modules?

    These are loaded on demand due to in-app navigation.

  16. How do you declare lazy-loaded modules?

    In the routing module.

  17. Which package does angular provide for routing?

    The @angular/router npm package.

  18. What is html5 pushstate?

    HTML5 pushState allows in-app navigation without causing a full reload of a page.

  19. Which html tag must be mandatorily set to enable pushState routing?

    The base href tag in the index.html file.

  20. 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.

  21. 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.

  22. 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.

  23. 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.

  24. 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.

  25. Should the path property of a route object contain a leading slash?

    The value of a path property should not contain a leading /.

  26. 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.

  27. How doe you define a wildcard route?

    The wildcard route has a path property with two asterisks which matches any URL.

  28. 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.

  29. Which angular component will you use as a placeholder for components that are activated with routing?

    router-outlet

  30. You would like to assign a route path to an anchor tag, which angular directive would you use?

    The routerLink directive.

  31. Should the route path be assigned to the routerLink directive contain a leading /?

    Yes it should contain a leading /.

  32. 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.

  33. 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.

  34. 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.

  35. 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.

  36. In which module must you always define the wildcard route?

    In the app-routing.module.ts file.

  37. 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.

  38. 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.

  39. 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.

  40. 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.

  41. 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.

  42. 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 of RouterModule.forRoot.

  43. 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.

  44. How can we retrieve information about the currently active route in a component?

    The ActivatedRoute service povides the necessary information.

  45. 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.

  46. 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.

  47. Why will you use a guard?

    When we want to prevent unauthorized access to a particular route.

  48. 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.

  49. 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.

  50. 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.

  51. 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.

  52. Why will you use a resolver?

    If you would like to prefetch some data before activating a route and intializing a component.

  53. 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.

  54. What is lazy loading?

    Lazy loading means that we don’t load all the modules of our application initially.

  55. 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.

  56. 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.

  57. 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.

  58. To control access to a lazy-loded module, which guard should you use?

    The canLoad guard.

  59. What is a RouterOutlet?

    It is an angular component that acts as a placeholder that Angular dynamically fills based on the current router state.

  60. What are named RouterOutlets?

    They are router outlet components that have the optional name property set. <router-outlet name='left'></router-outlet>

  61. How can you define a route object to use a named RouterOutlet?

    {path: <base-path>, component: <component>, outlet: <target_outlet_name>}

  62. How do you provide bootstrapApplication with routing?

    In the providers option, call the provideRouter function providing the appRoutes.

Change Detection

  1. When is change detection triggered?

    • button clicks

    • async requests

    • setTimeout

    • setInterval.

  2. Can we change the change detection strategy for a component?

    Yes by changing the changeDetection property of the @Component decorator.

  3. When does change detection cause a problem?

    When large amounts of data change rapidly, it can cause a performance problem.

  4. When does ChangeDetectionStrategy.OnPush get triggered?

    Only when the @Input property of the component changes.

  5. Why does Angular use Zone.js?

    To detect any changes to the application state.

  6. Which library helps angular detect changes to an application’s state?

    Zone.js

  7. What events does Zone.js listen for and capture in an angular application?

    setTimeouts, setIntervals, network requests, and event listeners.

  8. What does runOutsideAngular do?

    ngOnInit() {
        this.ngZone.runOutsideAngular(() => setInterval(pollForUpdates), 500);
    	}

    It excludes the passed in function for change detection.

  9. 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, and ngOnChanges lifecycle hooks of each component.

  10. Which lifecycle hooks does each change detection cycle execute?

    • ngDoCheck

    • ngAfterContentChecked

    • ngAfterViewChecked

    • ngOnChanges

  11. Why should you make sure that the ngDoCheck, ngAfterContentChecked, ngAfterViewChecked, and ngOnChanges lifecycle hooks execute quickly?

    Because these methods run after every change detection cycle.

  12. A component has OnPush change detection strategy, When does change detection run for this component?

    The component receives new inputs.

  13. Which command generates an application with routing enabled?

    ng new routing-app --routing --defaults

Services and Injector

  1. Which decorator helps you identify an Angular Service?

    The @Injectable decorator.

  2. What does @Injectable accept as its first parameter?

    An object with a single property named providedIn.

  3. What is the command to generate an Angular service?

    ng generate service serviceName

  4. By default, where is an angular service registered?

    By default, it is registered with a root injector.

  5. What is an Injector?

    An object that maintains a list of all dependencies that an Angular application needs.

  6. What does the Injector return?

    A singleton instance of the registered type.

  7. 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.

  8. What is a provider?

    A recipe containing guidelines on creating a specific service.

  9. What does the root injector do?

    The root injector creates singleton services.

  10. Where are the services created by the root injector available?

    Globally available throughout the application.

  11. Can we register services in the @NgModule decorator?

    Yes

  12. Where can we register services in @NgModule?

    The providers property of the`@NgModule` decorator.

  13. What is the difference between registering a service in the AppModule providers property and registering a service using the providedIn:root configuration?

    The main difference between them is that the providedIn syntax is tree shakable.

  14. 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.

  15. 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.

  16. 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.

  17. 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.

  18. Where will you register services in a component?

    In the providers property of the @Component decorator.

  19. Which components have access to services that are registered with the component injector?

    The component itself and the child components.

  20. 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.

  21. 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.

  22. 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]

  23. 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) {}

  24. 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) {}

  25. 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) {}

  26. 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) {}

  27. Give an example of a class provider syntax?

    providers: [ProductsService]

  28. 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.

  29. 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]
    }];
  30. What is an interface?

    Interfaces are syntactic sugar in TypeScript that are thrown away during compilation.

  31. 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.

  32. 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 object

      Second, 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 from appSettings object.

RxJs

  1. What is an Observable?

    It is a collection of items emitted over time.

  2. Why use an Observable?

    It is great for things that arrive over time like keypresses, events, an http response etc.

  3. What is an Observer?

    An observer watches an observable for notifications.

  4. What notifications does an observer watch for?

    next, error and complete.

  5. What is a first order mapping?

    It transforms each emitted value and emits the result.
    map(x ⇒ x * 2);

  6. What is a higher order mapping?

    It transforms each emitted value to an Observable.
    map(x ⇒ of(x*2)

  7. 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

  8. 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>>

  9. Give 3 examples of higher-order operators in rxjs.

    switchMap, mergeMap and concatMap

  10. 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.

  11. When would we need to define a Subject?

    If we need to define our own observables.

  12. How do you define a Subject whose stream will emit a number?

    private subject = new Subject<number>();
    subject$ = this.subject.asObservable();

  13. How do you define a BehaviourSubject whose stream will emit a number?

    private bhSubject = new BehaviorSubject<number>(1);
    bhSubject$ = this.bhSubject.asObservable();

  14. 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.

  15. How do you emit a value using a Subject?

    this.subject.next(categoryId)

  16. When would you use the fromEvent operator of rxjs?

    When you would like to create an observable from a DOM event of an html element.

  17. You would like to create an observable from a DOM event.

    Which rxjs operator would you use?

    The fromEvent operator.

  18. When would you use the of operator of rxjs?

    When you would like to create an observable from values such as numbers.

    const values = of(1, 2, 3);

  19. You would like to create an observable from values such as numbers.

    Which rxjs operator would you use?

    The of operator.

  20. When would you use the from operator of rxjs do?

    When you would like to convert an array or a promise to an observable:
    const values = from([1, 2, 3]);

  21. You would like to convert an array or a promise to an observable.

    Which rxjs operator would you use?

    The from operator.

  22. When would you use the pipe operator of rxjs?

    When you would like to chain multiple operators one after the other.

  23. You would like to chain multiple operators one after the other.

    Which rxjs operator would you use?

    The pipe operator.

  24. When would you use the tap operator of rxjs?

    When we would like to do something with the data emitted without modifying it.

  25. You would like to do something with the data emitted without modifying it.

    Which rxjs operator would you use?

    The tap operator.

  26. When would you use the map operator of rxjs?

    When you would like to perform an operation on each of the emitted value.

  27. You would like to perform an operation on each of the emitted value.

    Which rxjs operator would you use?

    The map operator.

  28. When would you use the filter operator of rxjs?

    When you would like to filter out emitted values based on a certain condition.

  29. You would like to filter out emitted values based on a certain condition.

    Which rxjs operator would you use?

    The filter operator.

  30. When would you use the forkJoin operator of rxjs?

    When you would like to wait for all of the inner observables to complete.

  31. You would like to wait for all of the inner observables to complete.

    Which operator would you use?

    The forkJoin operator.

  32. What are higher order observables?

    Observables that operate on other observables.

  33. What are observables that operate on other observables called?

    Higher-order observables.

  34. What do higher-order observables contain?

    They contain an inner observable that contains the actual values.

  35. When will you use the concatMap operator of rxjs?

    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.

  36. What are the scenarios where a concatMap operator of rxjs would be useful?

    When you have to perform insert, update and delete operation and the order is important.

  37. When will you use the mergeMap operator of rxjs?

    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.

  38. When will you use the switchMap operator of rxjs?

    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.

  39. What are the scenarios where a switchMap operator of rxjs would be useful?

    When you would like to perform an autocomplete but only on the latest value.

  40. 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

  1. What is an HTTP interceptor?

    An Angular service that intercepts HTTP requests and responses that pass through the HttpClient.

  2. 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.

  3. You would like to pass a custom Http header for every request.

    Which angular feature would you use?

    An HTTP interceptor.

  4. 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

  5. You would like to log every HTTP communication.

    Which angular feature can help with this?

    An HTTP interceptor

  6. In which module must an HTTP interceptor be provided?

    It must be provided in the same Angular module that imports HttpClientModule.

  7. 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.

  8. Write the command that creates an interceptor?

    ng generate interceptor auth

  9. Which interface must an angular interceptor implement?

    An HttpInterceptor interface.

  10. 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.

  11. Which interceptor is the last interceptor in the pipeline before the request is sent to the server?

    HttpBackend

Ngrx

  1. What is Redux?

    Redux is a global state of data that is outside your framework.

  2. What is Ngrx?

    It is a bunch of libraries that helps build reactive applications in Angular and implements redux in the Angular way.

  3. 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.

  4. What is an action in Ngrx?

    An action is a simple interface with a property called type that is a string.

  5. What is the purpose of the createAction function in Ngrx?

    It helps us create an action.

  6. What can you pass as parameters to the createAction function in Ngrx?

    • The global name of the action.

    • Additional properties that the action can accept as a parameter.

  7. 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.

  8. Why must we include a namespace while naming our Ngrx actions?

    Because all actions are stored in a global state.

  9. 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 a RegisterRequestInterface object as a parameter.

  10. What does the following code do?

    this.store.dispatch(register({request}));

    It dispatches a register action with a request parameter to the store.

  11. What does provideStore do in the following application?

    bootstrapApplication(AppComponent, {
      providers: [provideRouter(appRoutes), provideStore()],
    });

    It initializes the single store object for the application.

  12. Which Ngrx library provides redux dev tools?

    @ngrx/store-devtools

REST

  1. When must you use the patch method and when must you use the put method?

    The patch method should be used when we want to update only a subset of an object.
    The put method should be used when we want to update all of the object properties.

Reactive Forms

  1. Detail the steps that go into creating a reactive form in angular?

    • Create the form in html.

    • Import FormBuilder, ReactiveForms and Validators from @angular/forms.

    • Create a form group using this.fb.group or this.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.

  2. What is ReactiveFormsModule?

    Exports the required infrastructure and directives for reactive forms.

  3. What is a Validator?

    A validator is a function that processes a FormControl or collection of controls and returns.

  4. What does a Validator return?

    An error map in case of failure and null in case of success.

  5. 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 the configureTestingModule 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 ngOnInit. On subsequent runs, it executes ngOnChanges.

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.
Some services inject other services in their constructor, so using the real dependency in a test requires you to compensate for other dependencies.

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));
}
Using waitForAsync
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.
Using fakeAsync
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.
Testing the Input decorator
it('should display the title', () => {
  const titleDisplay: HTMLElement = fixture.nativeElement.querySelector('p');
  expect(titleDisplay.textContent).toEqual(component.testTitle);
});
Testing the Output decorator
it('should emit the liked event', () => {
  const button: HTMLButtonElement = fixture.nativeElement.querySelector('button');
  button.click();
  expect(component.isFavorite).toBeTrue();
});
Using debugElement
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.

Synchronous method testing
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.
Asynchronous method testing
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.
Testing services with dependencies
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