在浏览器的子窗口中使用Angular组件

17

在Angular应用程序中,有没有办法创建浏览器的子窗口并在其中显示预定义的Angular组件。

澄清:我不是在寻找模态对话框解决方案。


你是在说将一个组件加载到iframe中吗? - jazza1000
这个不起作用吗?:<a target="_blank" routerLink="/SomeValidRoute"> - dmcgrandle
@dmcgrandle 我已经在下面建议了这个答案。 - Nicolas Roehm
@Shrikey,你解决了这个问题吗?具体来说,是在浏览器的子窗口中使用Angular组件(仅限组件而不是整个应用程序实例)?我想要复制谷歌聊天从Gmail收件箱页面弹出的方式。 - ttugates
我已经发布了一个 Angular 库,npm 包,可以满足您的需求 - “将 Angular 应用程序的任何部分弹出到新的未停靠浏览器子窗口中”请查看 - https://www.npmjs.com/package/angular-popout-window - shemesh
显示剩余4条评论
6个回答

12

我之前也遇到过这样的问题,如果有人仍在尝试在不运行新应用程序的情况下动态加载不同窗口上的组件,则可以按照以下方式进行操作:

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

太好了。有时候,这些最简单的老派解决方案是我最喜欢的。完全忘记了如何访问新窗口/选项卡的正文。 - Max
这在Chrome/Edge/FF中可以工作,但在IE11中不行。会抛出一个ERROR HierarchyRequestError。 - user2268642
我失去了所有的布局和样式。 - ed4becky
1
如何处理组件上的CSS。我使用了这种方法,对于组件来说完美地工作着。但是它无法正确地渲染CSS。有人可以提供如何渲染CSS的建议吗? - Mounica
2
这可能是很久以前的事了,但是你们有没有尝试过像这样解决样式问题:document.querySelectorAll('link, style').forEach((htmlElement) => { if (this.windowReference) { ​this.windowReference.document.head.appendChild(htmlElement.cloneNode(true)); } });这样你就可以将你的样式附加到窗口中并且它们应该会被加载。 - RowdyArg
显示剩余2条评论

8

这就是我为自己找到的,完全符合你所需。

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

可以在新的子窗口中显示自定义的Angular组件,任何Angular服务也将注入到在子窗口中显示的任何组件中。
它定义了一个新的WindowComponent,其中包含一个门户主机。该门户主机附加到子窗口的主体上。这意味着附加到门户主机的任何门户都将呈现到子窗口的主体上。通过门户主机传递componentFactoryResolverapplicationRefinjector,可能是为了初始化您的自定义Angular组件并在子窗口中注入所需的服务。
const host = new DomPortalHost(
    this.externalWindow.document.body,
    this.componentFactoryResolver,
    this.applicationRef,
    this.injector
);

WindowComponent的模板中,它使用*cdkPortal定义了一个门户。在门户内部,它使用ng-content投射了窗口组件的内容,该内容由窗口内容的父级定义。请注意保留HTML标签。
<ng-container *cdkPortal>
  <ng-content></ng-content>
</ng-container>

每当创建 WindowComponent 时,它将打开一个子窗口,并将其门户附加到门户主机。当销毁 WindowComponent 时,它将关闭子窗口。
  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()
  }

这意味着在应用程序组件中,您可以使用*ngIf指令在窗口组件上切换弹出窗口,并将任何要显示的内容嵌套在模板中。
<window *ngIf="showPortal">
  <h2>Hello world from another window!!</h2>
  <button (click)="this.showPortal = false">Close me!</button>
</window>

使用@angular/cdk软件包,您还可以通过使用ComponentPortal而不是使用@ViewChild装饰器检索CdkPortal来以编程方式创建子窗口。通过检索对门户主机的引用,您甚至可以以编程方式使用不同的组件替换子窗口的内容。将门户连接到门户主机时,您还可以获取实例化组件的ComponentRef,从代码中与其交互。

3
这个方案看起来非常干净和好,但我遇到了以下问题:
  1. 在新打开的窗口中没有应用任何 CSS 样式。甚至连整个应用程序的样式也没有。
  2. 如果我在新的浏览器弹出窗口中打开模块化弹出窗口,那么模块化弹出窗口会在原始的浏览器窗口中打开。我使用 MatDialog 来进行模块化弹出。
非常感谢您能提供任何帮助。
- LetzFlow
3
我尝试了你的解决方案,但是当我关闭弹出窗口并再次点击“打开我”时,它没有显示出来。 - Hien Nguyen
1
不幸的是,这在微软Edge浏览器中无法运行。 - Nabil.A
1
这是一个非常好的解决方案,但在ie11中不起作用。 - user2268642
你如何处理样式? - Robert

4
如果您想使用window.open打开子窗口来展示Angular组件,那么该窗口应该加载一个使用Angular的website,这意味着您可以为该页面创建一个专门的子路由,并将您自己的应用程序打开到该特定路由。如果您选择此解决方案,请注意您的应用程序大小,如果您使用异步路由,则此解决方案将有效,因此当您在新窗口中打开应用程序时,您将下载所使用的angular + 其他核心库以及该特定路由的js文件
另一种选择是在服务器上创建另一个仅包含该内容的Angular应用程序,并且您在同一服务器上提供了两个不同URL的应用程序,在这种情况下,您需要创建一个新的Angular应用程序,其中只包含特定的内容。

我无法创建另一个Angular应用程序。我想要的是在新窗口中将现有的Angular“组件”显示为“弹出窗口”,以便它可以在具有所有功能的单独窗口中。 - Shrikey
嗯,如果我理解正确的话,您想要一个链接,该链接可以在新的浏览器窗口中打开同一应用程序,但是在您的应用程序中,您已经打开了一个对话框,并且该对话框包含您的组件? - Nicu
请告诉我答案是否有帮助,这样我就可以更新主要答案。 - Nicu
我在问题中提到,我不想采用模态方法 :) - Shrikey
@Nicu - 我假设您的最后一个建议会在子窗口中创建一个新的应用程序实例,需要SPA启动。不仅是组件。 - ttugates
显示剩余3条评论

2
您可以使用带有 target="_blank" 属性的链接来实现此功能。

example.component.html

<a [routerLink]="['/route-to-your-component']" target="_blank">
  Open the component in a new tab
</a>

1

您可以尝试为不同的窗口命名。这将使用指定的名称打开每个新窗口。

window.open('url', 'window 1', '');
window.open('url', 'window 2', '');

1
创建延迟加载组件(作为url),并将您的url放入其中。
window.open('url', 'window name', 'settings')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接