DEV Community

Andrei Gatej
Andrei Gatej

Posted on

Angular: Dynamically configure an injector for dynamic views

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 { }
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

foo.component.ts

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

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);
}
Enter fullscreen mode Exit fullscreen mode

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 (1)

Collapse
jsstampede profile image
Janko Stevanovic

nicely done.. this is similar as angular material does for dialog box components..