16
votes

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.

6
i am not getting you what is child window stand for?R. Viral
@R.Viral .. Browser's child window.Shrikey
Do you mean with window.open ?Nicu
Are you talking about loading a component into an iframe?jazza1000

6 Answers

8
votes

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');
  }
}
6
votes

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.

4
votes

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

2
votes

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>
1
votes

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

window.open('url', 'window name', 'settings')
1
votes

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', '');