在现代的 Web 应用中,处理多窗口或多标签页的场景并不常见,但有时我们确实需要模拟桌面应用的多窗口管理功能。本文将介绍如何在 Angular 中设计一个多窗口页面应用,确保每个打开的窗口都是唯一的,并且能够保存其打开的状态。我们还会演示如何在主页面更新子窗口的数据,并且让子窗口聚焦。
需求背景
唯一窗口:每个打开的窗口都应该是唯一的,不能重复。
状态保存:每个窗口都需要能够保存其打开状态(例如,某些数据或交互状态)。
动态更新:能够在主页面点击按钮时更新已打开窗口的数据,并且更新后该窗口应该聚焦。
实现步骤
1. 创建 WindowManagerService
管理窗口
WindowManagerService
是管理多个窗口的核心服务,它负责打开新窗口、发送数据更新并确保窗口聚焦。
// window-manager.service.ts import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class WindowManagerService { private windows: { [name: string]: Window | null } = {}; // 打开窗口并发送初始数据 openWindow(path: string, name: string, params: { [key: string]: any }) { const paramString = new URLSearchParams(params).toString(); const fullUrl = `${window.location.origin}${path}?${paramString}`; // 检查是否已有同名窗口 if (this.windows[name] && !this.windows[name]?.closed) { this.windows[name]?.focus(); // 聚焦到现有窗口 } else { // 打开新的窗口 const newWindow = window.open(fullUrl, name, 'width=800,height=600'); this.windows[name] = newWindow; // 当窗口关闭时,从字典中移除 newWindow?.addEventListener('beforeunload', () => { delete this.windows[name]; }); } } // 发送更新数据到指定窗口并聚焦 updateWindowData(name: string, data: any) { const window = this.windows[name]; if (window && !window.closed) { window.postMessage(data, window.location.origin); // 向窗口发送数据 window.focus(); // 聚焦到该窗口 } } }
代码解释
openWindow
:负责打开一个新窗口或聚焦到已有窗口。updateWindowData
:向已打开的窗口发送数据更新,并聚焦该窗口。
2. 在 AppComponent
中调用更新方法
主页面组件将负责打开新的窗口和更新已打开窗口的数据。点击按钮后,我们将调用 WindowManagerService
来打开新窗口和更新数据。
// app.component.ts import { Component } from '@angular/core'; import { WindowManagerService } from './window-manager.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private windowManager: WindowManagerService) {} // 打开新窗口 openNewWindow(windowName: string) { const path = '/window'; // 路由路径 this.windowManager.openWindow(path, windowName, { windowId: windowName }); } // 更新窗口数据 updateWindowData(windowName: string) { const updatedData = { message: '更新的数据' }; // 这里是要发送的更新数据 this.windowManager.updateWindowData(windowName, updatedData); } }
代码解释
openNewWindow
:用于打开新的窗口。updateWindowData
:用于更新已打开窗口的数据。
3. 在 WindowComponent
中监听数据更新
每个窗口组件需要监听来自主页面的消息(使用 postMessage
)并根据收到的数据更新其内容。
// window.component.ts import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-window', templateUrl: './window.component.html', styleUrls: ['./window.component.css'] }) export class WindowComponent implements OnInit { windowId!: string; data: any = {}; ngOnInit() { // 监听父窗口发送的消息 window.addEventListener('message', (event: MessageEvent) => { if (event.origin === window.location.origin) { // 确保来源正确 this.data = event.data; // 接收数据并更新组件 } }); } }
代码解释
ngOnInit
:在组件初始化时,监听来自父窗口的消息,并根据数据更新视图。
4. window.component.html
展示数据
<div *ngIf="windowId"> <h2>窗口 {{ windowId }}</h2> <p>窗口中的数据: {{ data.message }}</p> </div>
5. 修改主页面的按钮来更新窗口
在主页面,您可以添加按钮来更新窗口中的数据。点击更新按钮时,窗口中的数据将被动态更新。
<div> <h1>多窗口管理示例</h1> <button (click)="openNewWindow('Window1')">打开窗口 1</button> <button (click)="openNewWindow('Window2')">打开窗口 2</button> <button (click)="openNewWindow('Window3')">打开窗口 3</button> <!-- 更新窗口数据 --> <button (click)="updateWindowData('Window1')">更新窗口 1 数据</button> <button (click)="updateWindowData('Window2')">更新窗口 2 数据</button> <button (click)="updateWindowData('Window3')">更新窗口 3 数据</button> </div> <router-outlet></router-outlet>
流程总结
打开窗口:点击 "打开窗口 1" 等按钮时,窗口会被打开,并显示唯一的
windowId
。更新数据:点击 "更新窗口 1 数据" 等按钮时,
updateWindowData
会向目标窗口发送更新数据,并聚焦该窗口。窗口监听更新:每个子窗口通过
postMessage
接收来自主页面的数据,并更新其视图。
小结
通过这种方式,我们能够在 Angular 中实现多窗口管理,并且每个窗口都能保存其状态。在更新数据时,还能确保子窗口聚焦,提升了用户体验。这种方法适合需要多个独立窗口管理的场景,例如在桌面应用风格的 Web 应用中。
作者:KirovReporting
链接:https://juejin.cn/post/7435643017149366326