17

From an Angular application is there any way that I can create a Browser's child window and show some predefined angular component in it.

Clarification : I am not looking for a Modal dialog solution.

Shrikey
  • 858
  • 3
  • 11
  • 34

6 Answers6

10

I landed on this post before, so in case someone still trying to render a component dynamically loaded on a different window without running a new app, this is how I did it:

export class ChildLoaderComponent implements OnInit, AfterViewInit {
  constructor(private r: ComponentFactoryResolver, private 
                           viewContainerRef: ViewContainerRef) { }
  public windowReference: any;
  ngAfterViewInit() {
    setTimeout(() => {
      //create the component dynamically
      const factory = this.r.resolveComponentFactory(AChildComponent);
      const comp: ComponentRef<AChildComponent> =
        this.viewContainerRef.createComponent(factory);
      //in case you also need to inject an input to the child, 
      //like the windows reference 
      comp.instance.someInputField = this.windowReference.document.body;
      //add you freshly baked component on the windows
      this.windowReference.document.body.appendChild(comp.location.nativeElement);
    });
  }

  ngOnInit() {
    //create the windows and save the reference     
    this.windowReference = window.open('', '_blank', 'toolbar=0, width=800, height=400');
  }
}
Anup Sharma
  • 2,053
  • 20
  • 30
RowdyArg
  • 101
  • 1
  • 3
  • Excellent. Sometimes, the easiest old school solutions like these are my favorite. Totally forgot about accessing the body of the new window/tab. – Max Mar 23 '20 at 03:48
  • This is exactly what I needed! I was looking for a way to open an Angular component in a new window, without bootstrapping the whole app again. We are using this to show a page with manuals and we don't want to open the entire app 2 times. – Emmy Jul 01 '20 at 12:29
  • This works in chrome/edge/ff but not in ie11. Throws an ERROR HierarchyRequestError –  Sep 18 '20 at 07:24
  • using this approach the window will open but without the proper css – Kardon63 Oct 08 '20 at 12:49
  • I lost all my layout and styling – ed4becky Dec 04 '21 at 13:31
  • how to handle the css on the component. I used this approach its working perfectly fine for component. but its not rendering css properly. can anyone give suggestion on how to render the css. – Mounica Dec 21 '21 at 22:57
  • 2
    this was a long time ago, but have you guys tried something like this to solve styles: `document.querySelectorAll('link, style').forEach((htmlElement) => { if (this.windowReference) { ​this.windowReference.document.head.appendChild(htmlElement.cloneNode(true)); } });` so you can append your styles into the windows and they should load. – RowdyArg Jan 14 '22 at 10:55
8

This is what i have found for myself, and does exactly what you need.

https://stackblitz.com/edit/angular-open-window

It is possible to show your custom angular components in the new child window, and any angular services will also be injected in any component that is shown in the child window.

It defines a new WindowComponent, which contains a portal host. This portal host is attached to the body of the child window. This means that any portals attached to the portal host will be rendered to the body of the child window. To the portal host is passes the componentFactoryResolver, the applicationRef and the injector, presumably so that it can initialize your custom angular components and inject the required services inside the child window.

const host = new DomPortalHost(
    this.externalWindow.document.body,
    this.componentFactoryResolver,
    this.applicationRef,
    this.injector
);

In the template of the WindowComponent, it defines a portal using *cdkPortal. Inside the portal, it projects the window component's content, as defined by the parent of the window content, using ng-content.

<ng-container *cdkPortal>
  <ng-content></ng-content>
</ng-container>

Whenever the WindowComponent is created, it will open a child window, and attach its portal to the portal host. When the WindowComponent is destroyed, it will close the child window.

  ngOnInit(){
    // STEP 4: create an external window
    this.externalWindow = window.open('', '', 'width=600,height=400,left=200,top=200');

    // STEP 5: create a PortalHost with the body of the new window document    
    const host = ...

    // STEP 6: Attach the portal
    host.attach(this.portal);
  }

  ngOnDestroy(){
    // STEP 7: close the window when this component destroyed
    this.externalWindow.close()
  }

This means that in the application component you can simply toggle the popup using the *ngIf directive on your window component, and nest any content you want to show in the template.

<window *ngIf="showPortal">
  <h2>Hello world from another window!!</h2>
  <button (click)="this.showPortal = false">Close me!</button>
</window>

Using the @angular/cdk package you could also modify this example to create a child window programmatically, by using a ComponentPortal instead of retrieving a CdkPortal with the @ViewChild decorator. By retrieving a reference to the portal host, you can even programmatically replace content of the child window with a different component. When attaching a portal to a portal host, you can also get a ComponentRef for your instantiated component, allowing you to interact with it from code.

Bubblewrap
  • 7,266
  • 1
  • 35
  • 33
  • 3
    This seems like a very clean and good solution, but I run into the following problems: 1. none of the CSS styling is applied in the new window. Not even the app wide style stuff. 2. If I open a modular popup inside of the new Browser-popup the modular popup is opened in the original browser window. I use MatDialog for the modular popups. Any help with this would be greatly appreciated. – LetzFlow Jul 10 '19 at 08:12
  • 3
    I tried your solution, but when I close popup window and click open me again, it is not shown? – Hien Nguyen Aug 17 '19 at 07:34
  • 1
    unfortunately this doesn't work in Microsoft Edge browser. – Nabil.A Mar 04 '20 at 01:06
  • 1
    This is a very nice solution but doesn't work in ie11 –  Sep 18 '20 at 07:25
  • how you handling styles ? – Robert Oct 08 '21 at 13:59
4

In case you are using window.open for the child window in order to show an angular component that window should load a website which uses angular this means you eighter create in your own app a child route special for that page and you open your own app to that specific route. If you chose this solution be aware of the size of your app this solution works fine if you use async routing so when you open the app on a new window you will download angular + other core libs which you are using and + js for that specific route.

Another option is that in your server you create another angular app with only that content and you have 2 apps on the same server served at different url's in that case you need to create a new angular app which contains only that specific content

Sean Pianka
  • 2,157
  • 2
  • 27
  • 43
Nicu
  • 3,476
  • 4
  • 23
  • 43
  • I cannot create another angular app. What i want is to show the existing angular "component" in a new window as a "popup" so that it can be in a separate window with all the functionality. – Shrikey Dec 10 '18 at 01:11
  • Hmm so if I understand correctly you want to have a link which opens a new browser window with the same app but in your app you have a dialog opened and that dialog contains your component ? – Nicu Dec 10 '18 at 06:12
  • Uhmm.. I have a link for **every** component in my app. If i click on it, that particular component should be opened in a child window. Is it still unclear :( – Shrikey Dec 10 '18 at 08:46
  • In case you want that depending on what packages you use you might have the solution for it, so `angular-material` has a [`dialog`](https://material.angular.io/components/dialog/overview) component which let's you open a component in a dialog, But that component is not bound to routing if you need to be able to access the dialog using the url you could use `angular` named outlets and have a separate route for the dialog then you use the router to open the dialog here is an [example](https://netbasal.com/give-your-modals-url-address-with-auxiliary-routes-in-angular-eb76497c0bca) – Nicu Dec 12 '18 at 11:28
  • Let me know if the answer helps so I can update the main answer – Nicu Dec 12 '18 at 11:32
  • :) I mentioned in the question that i am not looking for the modal approach :) – Shrikey Dec 12 '18 at 12:44
  • Than why don't you add a separate route for your component and just use `window.open(`myAppUrl/urlToMyComponent`)` ? from what I find on the internet that is a browser child [window](https://stackoverflow.com/questions/2813561/open-child-browser-window-from-child-window) eighter I don't understand the term at all – Nicu Dec 12 '18 at 15:40
  • [@Nicu](https://stackoverflow.com/users/3497559/nicu) - I presume because your last suggestion would create a new app instance in the child window requiring SPA to boot up.. No just a component. – ttugates Dec 29 '18 at 00:06
2

You can do that using a link with the target="_blank" attribute

example.component.html

<a [routerLink]="['/route-to-your-component']" target="_blank">
  Open the component in a new tab
</a>
Nicolas Roehm
  • 688
  • 1
  • 8
  • 15
1

You can try giving names to your different windows. This will open each new window with the specified name.

window.open('url', 'window 1', '');
window.open('url', 'window 2', '');
Dale K
  • 25,246
  • 15
  • 42
  • 71
pratRockss
  • 455
  • 8
  • 25
1

Create lazy load component (as url) and put your url in

window.open('url', 'window name', 'settings')
Akhi Akl
  • 3,795
  • 1
  • 15
  • 26