Open Angular components dynamically in new browser tab without bootstrapping the whole app again

Saranya Thangaraj
4 min readJul 4, 2020

--

In this article, we will learn about using Angular Portal to dynamically render a component in a new browser tab and manage data interactions between the tab window and the main app.

Image by David Mark from Pixabay

Little Background..

Recently I worked on a requirement where an Angular component, which was previously rendered as a bootstrap modal, had to be rendered in a new browser tab instead. Any changes in the main application should update the content in the popout tab and vice-versa.

Seems simple enough, but not true in the world of single page applications! This is because if you create a route for the angular component and open it in a new window/tab it would end up bootstrapping the whole app again. Furthermore interactions and state management between two different angular applications is impossibly complex.

Angular Portal to the rescue!

After researching a bit on the topic, I stumbled upon Angular Portals. During my research I got bits of information from different sources but couldn't find one with a comprehensive solution. So I put together this article on Angular Portals.

Portals

Angular Portals package provides a flexible system for rendering dynamic content into an application.

A Portal is a piece of UI that can be dynamically rendered to an open slot on the page. The “piece of UI” can be either a Component or a TemplateRef and the "open slot" is a PortalOutlet.

For more information on portals checkout https://material.angular.io/cdk/portal/overview

First things first. The full source code for the example app can be found here https://github.com/tsaranya88/portal-simple.git. If you would like to follow along you can do so by cloning and editing the following repo https://github.com/tsaranya88/portal-starter.git.

In the AppComponent, we display the customer list. When we click on the link ‘Employer Details’, we want to open the EmployerComponent in a new browser tab and show the respective employer details. We also want to manage interaction between parent and child tab. We will see step by step how we can go about implementing this.

First we need to create a service called PopoutService. Add a function openPopoutModal in which we open a new browser window and store the reference. We then create a new DomPortalOutlet on the body of the new window document. This is the DOM element or location into which the content(Angular Component) will be projected.

popout.service.ts [Creating DomPortalOutlet]

Notice in the url, we have referred a static html file ‘assets/modal/popout.html’ instead of using ‘_blank’. popout.html is a blank html page added to assets folder onto which the Angular component will be projected.

If we use ‘_blank’ we could only open the component in a popup browser window we can’t open it as a browser tab. If we want to open as a new browser tab, we need to specify a valid location url.

We also give a unique target name to identify the child tab. If you open a new window with a different target name, it will open a new browser tab, otherwise it will reference the same child tab.

Since the new browser tab is a new window document it will not have any style definitions of its own so we copy all the styles from the parent app.

popout.service.ts [Copying Styles]

After the styles have loaded, we then attach the Angular component to the portal outlet by doing the following

  • Create a new PortalInjector with the data to be injected into component
  • Create a new ComponentPortal with the Angular component and the injector
  • Attach the ComponentPortal to the PortalOutlet

For injecting data into the angular component, we create a new InjectionToken called ‘POPOUT_MODAL_DATA’ that is passed to the PortalInjector as custom tokens.

popout.token.ts
popout.service.ts [Attach the ComponentPortal to the PortalOutlet]

Note: We need to add EmployerComponent to app.module entryComponents to be able to attach and render it dynamically.

We store three references in our global POPOUT_MODAL object.

The windowInstance to interact with our child windows, outlet instance to detach and attach components and componentInstance to access the component data and functions.

Now in our AppComponent, when we click on the employer name of a customer:

  • If the employer tab window is not already open, we will call popoutService.openPopoutModal with the data to be injected
  • If the employer tab window is already open and there is no change in data, we will just focus the tab window
  • If the employer tab window is already open and there is change in data, we will detach the existing component, create a new ComponentPortal with changed data and attach it to the portal outlet.
app.component.ts [Open Popout Modal]

In order to access the data in our EmployerComponent, we need to inject POPOUT_MODAL_DATA as a dependency.

Closing the child tabs on parent close

Finally, in the AppComponent, we add the code to close all the child windows when the main application is closed or refreshed.

app.component.ts [Close Popout Modals]

Dynamic Rendering

We can enhance this solution by adding another link ‘Personal Details’ that opens CustomerComponent. We can dynamically render the customer or employer component in the browser tab based on the link clicked.

Live demo with customer and employer components dynamically rendered on the new browser tab.

Viola, we have learnt how to open Angular components dynamically in a new browser tab without bootstrapping the whole app.

Hope you enjoyed this article and found it useful. Leave a comment if you have any thoughts or questions.

--

--