【问题标题】:How can I avoid bi-direction data flow between angular components when they interact with each other?当它们相互交互时,如何避免角度组件之间的双向数据流?
【发布时间】:2020-02-28 14:47:17
【问题描述】:

假设我有一个非常简单的编辑器组件。没什么复杂的,只是一个输入字段,也可以从外部源加载一些数据:

@Component({
    selector: 'my-editor',
    template: `<input [disabled]="loading" [(ngModel)]="text">`
})
class EditorComponent {
    loading: boolean = false;
    text: string;

    load(item: Observable<string>) {
        this.loading = true;
        item.subscribe(value => {
            this.text = value;
            this.loading = false;
        });
    }
}

除了从外部源加载一些数据时输入被禁用,因为加载后任何数据都会被丢弃,我们不想让用户措手不及。

然后在某些情况下,我们有一些模板可供用户选择。也没有什么复杂的,看起来像这样:

@Component({
    selector: 'my-template-manager',
    template: `<button [disabled]="disabled">Dummy</button>`
})
class TemplateManagerComponent implements OnInit {
    @Input()
    disabled: boolean = false;
    @Output()
    load = new EventEmitter<Observable<string>>();

    constructor(private cd: ChangeDetectorRef) {}

    ngOnInit(): void {
        this.load.emit(of('initial value').pipe(delay(1000)));
    }
}

也是非常简单的组件,它并没有做太多事情。当它启动时,它将从某个 HTTP 服务加载默认模板(此处由 delay 模拟),否则,如果用户点击按钮,则会加载不同的模板(为简单起见,此处未实现)。

现在我想同时使用这两个组件,这也很简单:

@Component({
    template: `
        <my-template-manager [disabled]="editor.loading" (load)="editor.load($event)"></my-template-manager>
        <my-editor #editor></my-editor>
    `
})
class ParentComponent {
}

这只是这些组件的一些粘合剂。所以我有小型轻量级和可测试的组件,它们的 API 很好地定义了它们,可以像这样粘合在一起。但是,现在我遇到了麻烦:当编辑器加载某些内容时,我还希望禁用模板管理器。但愚蠢的我:我刚刚在这两个组件之间创建了双向数据流:在onInit-Method 中,模板管理器发出一个事件,该事件以禁用状态的形式返回到模板管理器,并且角度非常好通知我们,在ExpressionChangedAfterItHasBeenCheckedError 的帮助下,这确实是一种不好的做法。 (要更深入地了解正在发生的事情,请参阅:https://stackoverflow.com/a/44691880)。另外,我不能只离开[disabled]-binding,因为loading-state 可能会因不同的情况而被触发...

有一些变通方法,例如setTimeoutPromise.resolve().then()new EventEmitter(true)。但是,例如Maxim Koretskyi says:

我不建议使用它们,而是重新设计您的应用程序

我倾向于同意。至少异步包装事件只是“让它工作”感觉很尴尬。单独查看这两个组件(编辑器和模板管理器)看起来也不错,不是吗?毕竟,将代码分离到多个组件中的目的还包括它们可以单独开发、审查和测试,并且通过添加这样的包装,我承认事实并非如此。所以我在设计交互的方式上可能犯了一个大错误。

如何根据角度设计原则设计此处所示的交互?

【问题讨论】:

标签: angular angular-components


【解决方案1】:

在您的初始示例中,您可以通过此步骤避免ExpressionChangedAfterItHasBeenCheckedError

  • 在您的组件中使用changeDetection: ChangeDetectionStrategy.OnPush
  • loading 定义为loading$: BehaviorSubject&lt;boolean&gt; = new BehaviorSubject(false);
  • 在您的 HTML 中使用 loading$ | async
  • this.loading = true; 替换为this.loading$.next(true);

为什么? OnPush 避免在没有真正更改 @Input、@Output 或带有 async 的 html 中的 observable 的情况下重新加载组件。 我们将loading 更改为可观察对象,以便它可以渲染组件。

(我尝试了@Andrew Allen 的示例并且它有效)

【讨论】:

    猜你喜欢
    • 2020-08-19
    • 2020-10-21
    • 2018-05-23
    • 2016-02-08
    • 2022-01-13
    • 1970-01-01
    • 2012-07-22
    • 2018-04-25
    • 1970-01-01
    相关资源
    最近更新 更多