How to provide data to ComponentPortal using Dependency injection

Some time ago I published a video where in details showed how to use the Angular CDK Portal module and in which use-cases it might be useful. Almost immediately in the comments I got a question: “How to provide data to the ComponentPortal factory?”. Fair enough! This is indeed a good question and in this article I am going to give you an answer.

By the way, here is the video I mentioned and I would encourage you to watch it first if you are not familiar with the Portal module yet and also because I will be using the project from this video as a basis.

I hope you enjoyed the video and now let’s get back to our problem – providing data to ComponentPortal!

Clone project

Because I will be using an already existing project we need to clone it from GitHub, so in your Terminal run these commands:

git clone https://github.com/DMezhenskyi/angular-cdk-portal-example.git
cd angular-cdk-portal-example/
git checkout provide-data-to-component-portal
git checkout -b my-solution 7ae90c8cf2f2addcd8316d37431f7dee97cea3e
code .

Here we do the next: 1) Clone the project from GitHub. 2) Navigate to the project folder. 3) Switch to the “provide-data-to-component-portal” branch. 4) Create a new branch “my-solution” from a specific commit which contains the initial application state. 5) Open the project with VS Code.

Once you are in VS Code open the Terminal, install node dependencies and start the app locally. Here is commands:

npm install
npm run start

Cool! Now we are ready to go 💪

Let’s get started with solution

First, I would suggest you to have a look which params ComponentPortal takes, just go to users-page.component.ts file and hover over ComponentPortal and you should see something like this:

Params which takes ComponentPortal class

As you can see we do not have anything like “inputs”, however, as the 3rd parameter we have an Injector… It actually gives us a hint that we could use Dependency Injection to solve our problem. How exactly are we going to do it? Actually I am going to create a separate injector. Let’s try it out…

@Component({
  selector: 'app-users-page',
  templateUrl: './users-page.component.html',
  styleUrls: ['./users-page.component.scss'],
})
export class UsersPageComponent implements OnInit, OnDestroy {
  //...
  constructor(private portalBridge: PortalBridgeService) {}

  ngOnInit(): void {
    const portalInjector = Injector.create(); // <-- Created Injector
    this.portalContent = new ComponentPortal<ActionsButtonsComponent>(
      ActionsButtonsComponent
    );
    this.portalBridge.setPortal(compPortal); 
  }
  //...
}

Not a big deal as you can see but our Injector is a bit useless because it doesn’t provide any value, so let’s try to modify it by adding some Dependency Provider. First, I would suggest you create an injection token for it. Just create a separate file anywhere in your project and paste there next code:

import { InjectionToken } from '@angular/core';

export const DATA_TOKEN = new InjectionToken<string>('portal-data');

Awesome! Now we can get back to users-page.component.ts file and modify our Injector by providing a DATA_TOKEN and provide just some string as a value of this token

const portalInjector = Injector.create({
  providers: [{ provide: DATA_TOKEN, useValue: 'Hello from Portal' }],
});

And then we are going to provide this configured Injector to ComponentPortal as 3rd param. Hint: pay attention that we are not interested in viewContainerRef which goes as the 2nd argument, so we place null instead.

this.portalContent = new ComponentPortal<ActionsButtonsComponent>(
  ActionsButtonsComponent,
  null, // it is viewContainerRef which is optional
  portalInjector
);

Great! Now the only thing we have to do is to go to ActionsButtonsComponent and inject DATA_TOKEN as any other token and read data from it.

export class ActionsButtonsComponent implements OnInit {
  // This is how we inject tokens
  constructor(@Inject(DATA_TOKEN) private data: string) {

  ngOnInit(): void {
    console.log(this.data);
  }
}

Now we can navigate to http://localhost:4200/users and here we go 🎉🎉🎉

Note: if you get an error that “async pipe was not found” then just restart “npm run start”.
Link to the source code of the final version please find here.

Of course you are not restricted by only string values. You can use any object, class or service. Also when you create your custom injector you can provide a parent injector as a second argument if it is necessary for your use-case. If you are curious why providing a Parent Injector might be important – please check my video about Dependency Injection and how it works.

I hope this article helped you! Do not forget also to subscribe to my YouTube channel and checkout my Video Courses made with love for you! 🙂 Thank you for your attention!

About the author

Dmytro Mezhenskyi

I am a frontend developer with focus on Angular. Also YouTuber and instructor on Udemy and SkillShare.

Add Comment

Tags

Dmytro Mezhenskyi

I am a frontend developer with focus on Angular. Also YouTuber and instructor on Udemy and SkillShare.

Get in touch