在 Angular 中实现多窗口页面应用
在现代的 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 应用中。