Angular – 兄弟组件之间共享状态的正确方式

huangapple go评论109阅读模式
英文:

Angular - Proper way to share reaction between sibling components

问题

标题并不十分解释性,但我找不到更好的方式来表达我的问题。

我正在使用Angular,并且遇到了一些问题。让我以一个示例来说明:

假设我有一个母组件MomComponent,其中包含多个KidComponent

每个KidComponent都可以由用户调整大小。如果发生这种情况,所有其他孩子也应该被调整大小。

一个孩子组件还应该能够独立存在,没有兄弟姐妹。

这是我的代码:

// mom.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'app-mom',
    templateUrl: './mom.component.html',
})
export class MomComponent {
    width: number;
}
<!-- mom.component.html -->

<app-kid [(width)]="width"></app-kid>

<app-kid [(width)]="width"></app-kid>

<app-kid [(width)]="width"></app-kid>
// kid.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'app-kid',
    templateUrl: './kid.component.html',
})
export class KidComponent {
    @Input() set width(w: number) {
        this._width = w;
        this.draw();
    }
    @Output() widthChange = new EventEmitter<number>();

    private _width = 100;

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        this._width = newWidth;
        this.draw();
    }

    draw() {
        // 绘制代码
    }
}

我的问题是,已调整大小的孩子会被绘制两次。首次是因为我在内部调用它,第二次是因为母亲的变量width将被更新。

我可以更改我的孩子组件如下,以便它只从外部更改中重绘:

// kid.component.ts
export class KidComponent {
    // [...]

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        // 我删除这两行:
        //
        // this._width = newWidth;
        // this.draw();
    }
  
    // [...]
}

但是,我还想能够单独使用我的KidComponent,而不与其他孩子共享宽度(只是<app-kid></app-kid>),在这种情况下,上述代码将不起作用。

我目前能想到的唯一解决方案是:

// kid.component.ts
export class KidComponent {
    // [...]

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        if (!this.widthChange.observers.length) {
            // 没有人监听这个事件,我自己重绘
            this._width = newWidth;
            this.draw();
        } else {
            // 有人监听我的调整大小,希望他们
            // 负责重绘我...
        }
    }

    // [...]
}

但我不太喜欢这种方法,widthChange发射器上有观察者并不意味着这个观察者将重新绘制KidComponent

所以,我的问题是:我是否忽略了更好的方法?

英文:

The title is not very explanatory, but I couldn't find any better way to phrase my problem.

I'm working with angular, and I've been facing a problem a few times now. Let me show it as an example :

Let's say I have a mother component MomComponent, which contains multiple KidComponent.

Each KidComponent can be resized by the user. If that happens, all of the other kids should be resized as well.

A Kid component should also be able to live without siblings.

Here's my code :

// mom.component.ts
import { Component } from &#39;@angular/core&#39;;

@Component({
    selector: &#39;app-mom&#39;,
    templateUrl: &#39;./mom.component.html&#39;,
})
export class MomComponent {
    width: number;
}
&lt;!-- mom.component.html --&gt;

&lt;app-kid [(width)]=&quot;width&quot;&gt;&lt;/app-kid&gt;

&lt;app-kid [(width)]=&quot;width&quot;&gt;&lt;/app-kid&gt;

&lt;app-kid [(width)]=&quot;width&quot;&gt;&lt;/app-kid&gt;
// kid.component.ts
import { Component } from &#39;@angular/core&#39;;

@Component({
    selector: &#39;app-kid&#39;,
    templateUrl: &#39;./kid.component.html&#39;,
})
export class KidComponent {
    @Input() set width(w: number) {
        this._width = w;
        this.draw();
    }
    @Output() widthChange = new EventEmitter&lt;number&gt;();

    private _width = 100;

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        this._width = newWidth;
        this.draw();
    }

    draw() {
        // Drawing code
    }
}

My problem here, is that the kid which has been resized will be drawn 2 times. One first time because I call it internally, and a second time because the mom's variable width will be updated.

I could change my kid component as follows, so that it's only redrawn from external change :

// kid.component.ts
export class KidComponent {
    // [...]

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        // I remove these 2 lines:
        //
        // this._width = newWidth;
        // this.draw();
    }
  
    // [...]
}

But then, I also want to be able to use my KidComponent on its own without sharing its width with other kids (just &lt;app-kid&gt;&lt;/app-kid&gt;), in which case, the above code wouldn't work.


The only solution that I can think of right now is the following :

// kid.component.ts
export class KidComponent {
    // [...]

    onUserResize(newWidth: number) {
        this.widthChange.emit(newWidth);

        if (!this.widthChange.observers.length) {
            // No-one is listening to the event, I redraw myself
            this._width = newWidth;
            this.draw();
        } else {
            // Someone is listening to my resize, let&#39;s hope they
            // take care of redrawing me...
        }
    }

    // [...]
}

But I don't really like this approach, the fact that there is an observer on the widthChage emitter doesn't mean that this observer will redraw the KidComponent.

So, my question is : Is there a better approach that I'm missing?

Thanks!

答案1

得分: 1

按照我的建议(简单建议):

// kid.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'app-kid',
    templateUrl: './kid.component.html',
})
export class KidComponent {
    @Input() set width(w: number) {
        if (this._width === w) return;
        this._width = w;
        this.draw();
    }

    @Output() widthChange = new EventEmitter<number>();

    private _width = 100;

    onUserResize(newWidth: number) {
        this.width = newWidth;
        this.widthChange.emit(newWidth);
    }

    draw() {
        // 绘制代码
    }
}

正如Brandon Tylor提到的,您还可以使用服务来在兄弟组件之间进行通信,但我不知道是否会变得复杂,因为您只需要一个属性。

英文:

As suggested in my comment (simple suggestion):

// kid.component.ts
import { Component } from &#39;@angular/core&#39;;

@Component({
    selector: &#39;app-kid&#39;,
    templateUrl: &#39;./kid.component.html&#39;,
})
export class KidComponent {
    @Input() set width(w: number) {
        if(this._width === w) return;
        this._width = w;
        this.draw();
    }

    @Output() widthChange = new EventEmitter&lt;number&gt;();

    private _width = 100;

    onUserResize(newWidth: number) {
        this.width = newWith;
        this.widthChange.emit(newWidth);
    }

    draw() {
        // Drawing code
    }
}

As Brandon Tylor mentioned you could also use services for communication between siblings but i don't know if it is not going to get complicated because you just need one property.

huangapple
  • 本文由 发表于 2023年3月8日 17:08:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75671150.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定