英文:
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 '@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 } 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() {
        // 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 <app-kid></app-kid>), 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'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 '@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 = 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论