注册一个对象从另一个对象的 属性 变化

Registering property changes of an object from another object

在我的项目中,我有一个 widgetBar 可以包含一个或多个 widget

单击 widgetBar 中的小部件图标时,会打开一个面板以显示所按小部件的内容(类似于带有所有下拉菜单的导航栏)。

每个 widget 都是一个 class,它有一个 togglePanel() 方法,该方法绑定到小部件图标和 openes/closes 面板上的 onlcick 事件。此方法还将小部件的 isActive 属性 设置为 true(当面板打开时)或 false(当面板关闭时)。

class Widget {
    isActive: boolean = false;
    element: HTMLElement;
    id: string;

    constructor(id: string) {
        this.id = id;
        this.element = document.crateElement('DIV');
        // ... this.element and other things represent the widget DOM object
        this.element.onclick = this.togglePanel.bind(this);
    }

    togglePanel() {
        if (this.panel.classList.contains('active')) {
            this.openPanel();
            this.isActive = true;
        } else {
            this.closePanel();
            this.isActive = false;
        }
    }
}

WidgetBar 构造函数将 widget 的列表作为参数,成为它的 this.widgets 属性。它还有一个 getWidgets() 方法可以访问此 属性.

class WidgetBar {
    private widgets: Widget[];

    constructor(widgets: Widget[]) {
        this.widgets = widgets;
    }

    getWidgets() {
        return this.widgets;
    }
}

(我的项目的)规则是,每当一个小部件打开其面板时,所有其他面板都应关闭。

如果其中一个 widget 已更改其 isActive 属性,我如何从 WidgetBar 自动检查,以便我可以确保不超过一个一次打开面板?

我读到了 Proxy,但我不确定这是否可以帮助我解决这个问题。

编辑

Observables呢?

Widget class 的 isActive 属性 成为 Observable 并从 WidgetBar 订阅以监听任何更新,如 ?

中所述

您可以在打开时获得小部件 emit an event。小部件栏的工作是执行“规则”。当它获得一个新的打开事件时,您可以让它在所有其他子部件上调用关闭(通过 ViewChild/ViewChildren)。

或者,您可以将逻辑移出组件并拥有一个负责所有逻辑的服务。该服务可以通过 rxjs 订阅将小部件及其属性(例如活动)公开到小部件栏。

这对我来说有点棘手,但最后我用 rxjs Subjects 解决了我的问题。

我所做的是创建一个新的 属性 this.widgetActivated$ = new Subject();,每当打开小部件面板时,我都会使用 {id: currentWidget.id, isActive: currentWidget.isActive} 之类的对象填充调用 .next()。然后我订阅它并在其他活动且具有不同 ID 的小部件上调用 closePanel()

这是更新后的代码:

interface widgetStatus {
    id: string;
    isActive: boolean;
}

class WidgetBar {
    private widgets: Widget[];
    private widgetActivated$: Subject<widgetStatus>; // IMPORTANT CHANGE

    constructor(widgets: Widget[]) {
        this.widgetActivated$ = new Subject(); // IMPORTANT CHANGE
        this.widgets = widgets;
    }

    getWidgets() {
        return this.widgets;
    }

    private widgetHandler(widget: Widget) {
        fromEvent(widget.widgetBox, 'click').subscribe(
            () => this.toggleWidgetPanel(widget)
        );
        this.widgetActivated$.subscribe(status => {
            const widgetsToClose = this.getWidgets().filter(widget => 
                widget.isActive === true && widget.id !== status.id);
            if (widgetsToClose) {
                widgetsToClose.forEach(widgetToClose => this.closePanel(widgetToClose));
            }
        })
    }

    private toggleWidgetPanel(widget: Widget) {
        if (!widget.getPanel().classList.contains("active")) {
            this.openPanel(widget);
        } else {
            this.closePanel(widget);
        }
    }

    private openPanel(widget: Widget) {
        widget.getPanel().classList.add("active");
        widget.isActive = true;
        this.widgetActivated$.next(this.getWidgetStatus(widget)); // IMPORTANT CHANGE
    }

    private closePanel(widget: Widget) {
        widget.getPanel().classList.remove("active");
    }

    private getWidgetStatus(widget: Widget): widgetStatus {
        return {id: widget.id, isActive: widget.isActive};
    }

}

我还将 this.element.onclick = this.togglePanel.bind(this); 转换为 fromEvent(widget.widgetBox, 'click').subscribe(() => this.toggleWidgetPanel(widget));,因为我觉得它更具可读性,并且不会带来很多关于 thisArg 上下文的问题(参见 ).