Use TypeScript enum values in Angular HTML templates

You can use your enum values in your HTML templates. This comes in handy for cases like ngSwitch where it’s more readable to use the enum value than it’s underlying value.

There are many ways to do this but I like to use this technique which requires defining the enum in your component class:

  // Expose enum to HTML template.
  myEnum: typeof MyEnum = MyEnum;

And then using it in your component template:

*ngSwitchCase="myEnum.Done"

Using a type alias to create a new name for a preexisting type like Document

I often need to work with auto-generated types that have been generated by Swagger based on an API definition.

These types sometimes clash with the names of preexisting types. For example the Document object, which normally refers to: Any web page loaded in the browser and serves as an entry point into the web page’s content, but in my case is also the name of an interface generated by Swagger.

Since you don’t need to import the Document type to use it in TypeScript / Angular projects, every time I try to use the generated Document class it’s referring to the DOM’s Document by default.

This can be fixed by adding an import statement in each file where you need to use Document. To avoid dealing with this project-wide we can use type aliases which let us give a type a new name:

type MyAppDocument = Document;

Since I can’t change the generated Document file itself, it would get overwritten the next time it’s generated, I create a type-alias.ts file like this:

import { Document } from './document';

export type MyAppDocument = Document;

Which then allows me to use the new MyAppDocument type everywhere.

How to read from .json files from TypeScript / Angular

Locate, or create, your typings.d.ts file and add the following lines:

declare module "*.json" {
  const value: any;
  export default value;
}

Otherwise you will get an error saying the compiler can’t find module filename.json.

Then you can include a .json file in your project. For example this file:

{
  "version": "6.01"
}

And you can read from such a file like this:

import * as versionData from '../version.json';

// ...

if (versionData && (versionData).version) {
      this.version = (versionData).version;
}

Controlling service injection per configuration in Angular

In the previous post I mentioned we could use a factory method and the useFactory attribute of the Injectable decorator to read which service to inject from some sort of configuration file.

To do this we will configure our factories per build configuration. Controlling dependency injection per configuration allows us to to things like injecting different services for different clients/versions or injecting different services for dev and prod.

We will start by adding a services.factories.ts file to our environments folder (if you created your app with Angular Cli, otherwise just add it wherever you please) and adding the following service factory in it:

import { FirstService } from 'src/services/first.service';

export let mainServiceFactory = () => {
  return new FirstService();
};

// include factory methods for the other services that need customization

We will need to add one factory method per service we want to customize.
Then we add a similar file called services.factories.alt.ts that uses our alternate services:

import { SecondService } from 'src/services/second.service';

export let mainServiceFactory = () => {
  return new SecondService();
};

In our services themselves we specify the useFactory attribute as a decorator:

import { Injectable } from '@angular/core';
import { mainServiceFactory } from 'src/environments/service.factories';

@Injectable({
  providedIn: 'root',
  useFactory: mainServiceFactory
})
export class FirstService {

  constructor() { }

  output() {
    return 'first';
  }
}

Then in the angular.json file we will add some new build configurations or modify the existing one to customize the services per configuration.

Since the angular.json file can get quite lengthy, I’ll only include the important bits.

// ... under architect / build, after options
"configurations": {
            "altServices": {
              "fileReplacements": [
                {
                  "replace": "src/environments/service.factories.ts",
                  "with": "src/environments/service.factories.alt.ts"
                }
              ]
            },
            "production": {
// ... normaly found after build
"serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "dependency-injection:build"
          },
          "configurations": {
            "altServices": {
              "browserTarget": "dependency-injection:build:altServices"
            },
            "production": {
              "browserTarget": "dependency-injection:build:production"
            }
          }
        },
// ... if you're using e2e tests
"e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "dependency-injection:serve"
          },
          "configurations": {
            "altServices": {
              "devServerTarget": "dependency-injection:serve:altServices"
            },
            "production": {
              "devServerTarget": "dependency-injection:serve:production"
            }
          }
        },

By using ng serve or ng build, the regular services will be used. To switch to the other services we need to use the -c switch like such:

ng serve -c altServices
ng build -c altServices

Note that the unused services are tree shaken, meaning they won’t appear in the final bundle. To check this for yourself do a ng build and inspect the main.js file in your dist folder.

One downside of this approach is that it creates a circular reference between the file containing the factory method and the service itself. To fix this you can add a third class, a service registry which contains the services and has no dependency. The service factories will register themselves with this registry and then the services will only depend on the registry and not the factories themselves. See this SO question.

Instead of using factories, we could also use providers instead. This would allow us to only create one provider and set it up in one place, in our @NgModule providers attribute rather than in all services we want to customize. The downside to this is that it prevents tree shaking of any sort.

Let’s show how we would do this.

We would create a service.providers.ts and service.providers.alt.ts file instead of the service.factories.ts. Here’s an example of such a file:

export let servicesProvider = [
  { provide: FirstService, useClass: FirstService },
  { provide: GraphicsService, useClass: GraphicsService },
];

Remove the useFactory attribute in our service and then configure our AppModule to use this provider:

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