The Fundamentals

Every second article on Angular seems to throw forwardRef into the mix without a genuine need. This piece helps you separate the necessary from the superfluous, so your codebase stays lean and maintainable.

Let's begin with what the official Angular docs tell us:

Allows to refer to references which are not yet defined…
For instance, forwardRef is used when the token which we need to refer to for the purposes of DI is declared, but not yet defined. It is also used when the token which we use when creating a query is not yet defined.

That explanation centers on class references, with tokens pointing to classes as a typical case. Consider how we configure a dependency in Angular:

const dependency = {
    provide: SomeTokenClass,
    useClass: SomeProviderClass
};

You have the token supplied to provide and a recipe like useClass in that snippet. According to the docs, forwardRef can wrap the token in this fashion:

const dependency = {
    provide: forwardRef(()=>{ SomeTokenClass }),
    useClass: SomeProviderClass
};

But there’s another class reference sitting in the useClass part of the recipe. Can we apply our trick there too? The documentation stays quiet on that point, yet since useClass holds a reference to a class and we know forwardRef works on references, the answer is affirmative:

const dependency = {
    provide: forwardRef(()=>{ SomeTokenClass }),
    useClass: forwardRef(()=>{ SomeProviderClass })
};

The catch is that this only applies when your recipe actually involves a class reference, which holds for useClass or useExisting, like so:

const dependency = {
    provide: forwardRef(()=>{ SomeTokenClass }),
    useExisting: forwardRef(()=>{ SomeOtherClassToken })
};

Even when you’re injecting a class-based token through the Inject decorator, the same technique applies:

export class ADirective {
    constructor(@Inject(forwardRef(() => Token)) service) {}
    ...
}

The Real-World Scenario

The official docs give us this sample:

class Door {
    lock: Lock;

    // Door attempts to inject Lock, 
    // despite it not being defined yet.
    // forwardRef makes this possible.
    constructor(@Inject(forwardRef(() => Lock)) lock: Lock) { 
        this.lock = lock; 
    }
}

// Only at this point Lock is defined.
class Lock {}

Honestly, that example feels a bit forced. It illustrates the concept but doesn’t mirror typical application code. Swapping the order — declaring Lock before Door — would solve it just as well. A far more convincing case shows up within Angular’s own source code.

Consider forms: you have ngModel and formControl directives for your inputs. Each one sets up a provider so you can access the directive instance via the shared token NgModel. Want to grab that related form directive inside your custom directive? Easy:

@Directive({
    selector: '[mycustom]'
})
export class MyCustom {
    constructor(@Inject(NgControl) directive) {
...
<input type="text" ngModel mycustom>

To pull that off, both NgModel and formControl create a formControlBinding provider and declare it in their decorator descriptor. Here’s what the formControl directive does:

export const formControlBinding: any = {
  provide: NgControl,
  useExisting: FormControlDirective
};
@Directive({
  selector: '[formControl]',
  providers: [formControlBinding],
  ...
})

export class FormControlDirective { ... }

And for NgModel:

export const formControlBinding: any = {
  provide: NgControl,
  useExisting: NgModel
};
@Directive({
  selector: '[ngModel]',
  providers: [formControlBinding],
  ...
})
export class NgModel { ... }

Now here’s where it gets interesting: formControlBinding gets defined outside the decorator. By the time JavaScript evaluates that object, the NgModel class definition hasn’t been processed yet. Log the provider object and you’ll see:

Object {useExisting: undefined, token: function}

Indeed, useExisting is pointing at undefined, leaving Angular unable to resolve the other token. That’s precisely why forwardRef appears in the code:

export const formControlBinding: any = {
  provide: NgControl,
  useExisting: forwardRef(() => FormControlDirective)
};

export class FormControlDirective { ... }
...
export const formControlBinding: any = {
  provide: NgControl,
  useExisting: forwardRef(() => NgModel)
};

export class NgModel{ ... }

But what if we moved the provider inside the decorator without forwardRef, like this:

@Directive({
    selector: '[ngModel]',
    providers: [
        {
            provide: NgControl,
            useExisting: NgModel
        }
    ],
    ...
})
export class NgModel { ... }

Reading through, it looks like NgModel gets referenced before it’s defined. Yet remember: class decorators only kick in after the class is fully declared. So that setup works without forwardRef. The trade-off is that the provider becomes internal — no longer exported — which prevents reuse elsewhere in your app.

The Inner Mechanics

So how does forwardRef manage this feat? It all comes down to JavaScript closures. When a closure captures a variable, it grabs the variable reference, not its current value. A quick demo:

let a;
function enclose() {
    console.log(a);
}

enclose(); // undefined

a = 5;
enclose(); // 5

Notice that even though a was undefined when enclose got created, the closure held onto the reference. Only later, when a took on the value 5, did it log that number correctly.

Essentially, forwardRef is a function that holds a class reference within its closure. By the time Angular invokes it, the class is fully defined. The compilation pipeline calls resolveForwardRef to unwrap either the token or the provider type during execution.