loading...

Angular: Dynamically configure an injector for dynamic views

anduser96 profile image Andrei Gatej ・2 min read

Imagine you are in a situation the requires you to work with complex views(host views, embedded views).

As if it wasn't enough...you might also want to change these views' parent injector at runtime.

Let's see an example:

app.module.ts

@NgModule({
 /* ... */
 providers: [
  {
   provide: FooToken,
   useValue: { message: 'default foo value' }
  },
 ],
})
export class AppModule { }

app.component.ts

@Component({
  selector: 'my-app',
  template: `
    <ng-container #vcr></ng-container>
  `,
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  @ViewChild('vcr', { static: true, read: ViewContainerRef })
  vcr: ViewContainerRef;

  ngAfterViewInit () {
    const compFactory = this.cfr.resolveComponentFactory(FooComp);

    this.vcr.createComponent(compFactory);
  }
}

foo.component.ts

@Component({
  selector: 'foo',
  template: `Foo!!`
})
export class FooComp {
  constructor (@Inject(FooToken) private fooToken) {
    console.log('[FOO]', this.fooToken)
  }
}

As you can see, this.fooToken will be the value defined in AppModule's injector: { message: 'default foo value' }.

However, now you want to change this behavior, maybe you want to alter the FooToken's value depending on a certain action.
Imagine you have multiple tabs and you would like to dynamically display some content, depending on which tab is active. Now, in this case, each tab will have its own component that will make use of the value provided by FooToken and this value can result from other condition.

Let's see how we can achieve this:

app.component.ts

/* ... */
ngAfterViewInit() {
    const compFactory = this.cfr.resolveComponentFactory(FooComp);

    const inj = Injector.create({
        providers: [
            {
                provide: FooToken,
                useValue: { message: this.getMessageOfCurrentTab() }
            }
        ]
    })

    this.vcr.createComponent(compFactory, 0, inj);
}

We're using Injector.create() to create a custom injector for the tab components.

You can read more about ViewContainerRef's API here.

Now, this.fooToken's value will depend on the returned value from this.getMessageOfCurrentTab().

Wrapping Up

Of course, this example is rather trivial, but I think knowing such things can come in handy in certain situations.

A similar example can be found in my dependency-injection-playground.

Thanks for reading!

Discussion

markdown guide