Today I Learned

TIL, 2022-01-13, `trackBy`, `InjectionToken`

Angular 2 — Improve Performance with trackBy


  • If we need at some point to change the data in the collection, we’ll have a problem because Angular can’t keep track of items in the collection and has no knowledge of which items have been removed or added.
  • Angular needs to remove all the DOM elements and create them again.
  • trackBy takes the index and current item as arguments and needs to return the unique identifier for the item.
  selector: 'my-app',
  template: `
      <li *ngFor="let item of collection;trackBy: trackByFn"></li>
    <button (click)="getItems()">Refresh items</button>
export class App {

  constructor() {
    this.collection = [{id: 1}, {id: 2}, {id: 3}];

  getItems() {
    this.collection = this.getItemsFromServer();

  getItemsFromServer() {
    return [{id: 1}, {id: 2}, {id: 3}, {id: 4}];

  trackByFn(index, item) {
    return index; // or
  • Selenium tests get stale element exceptions because the full component is re-rendered instead of just the changed parts.
  • If you have a menu within one of the inner components (card), and it was open, when the component gets re-rendered, the menu will close because that was internal state of the card component that was re-rendered.
  • Haven’t tried it by myself, but maybe there will be a problem with trackBy when the ID of an object of your list didn’t change but one of its properties actually did. In that case I would guess that the modified property will not be reflected in the DOM.
  • Default “inject” - combination of logical nullish assignment + @Optional. constructor(@Optional() @Inject('env') private env: string) { this.env ??= 'development'; }

The Hidden Power of InjectionToken Factory Functions in Angular


  • Injecting something that is based on a different injector/default injector:
const ENVIRONMENT = new InjectionToken<string>('environment token', {
  factory(): string {
    // @ts-ignore - This is how to use string injector
    const providedEnv = inject('env', InjectFlags.Optional) as string;

    return providedEnv || 'development';

  providedIn: 'root',
export class CartItemTransformService {
  constructor(@Inject(ENVIRONMENT) private env: string) {
import { inject, InjectionToken, InjectFlags } from '@angular/core';

const MY_PROVIDER = new InjectionToken('', {
  factory: () => {
    const optional = inject(SomeProvider, InjectFlags.Optional);

    return optional ?? fallback;
  • Benefits of InjectionToken factory function are:
    • Provider is tree-shakeable, since we don’t need to inject it in our app module as we’d do with the useFactory provider.
    • Using inject() to request a provider is faster and more type-safe than providing an additional array of dependencies.
    • The provider has a single responsibility, and our components are injected only with the data they need. -> As opposed to Service?
    • It makes testing more straightforward.
  • Question: Unit testing of InjectionToken?

Angular: Dependency Injection vs. Static methods


  • Recording all providers in the root module makes a sync component effect in which if a component changes a property within the service, all the other components can access the same value of the property.
  • In this situation, all components under a specific sub-module will have the same service and all value properties. However, this is completely unlinked to the service injected in the other sub-module so as to lose the sync effect across the application, keeping it only in the submodule branch.
  • We should analyze case by case but as a main rule, it’s recommended that if a method can grow in complexity and dependencies can be necessary, it’s better you start your code by dependency injection.

This project is maintained by daryllxd