【问题标题】:How to call child component method without breaking encapsulation?如何在不破坏封装的情况下调用子组件方法?
【发布时间】:2018-03-06 08:21:33
【问题描述】:

我有基于供应商组件的自定义日期和时间选择器组件。 如果简化它可以看起来像这样:

@Component({
  selector: 'app-date-time-picker',
  template: `
    <vendor-date-picker [(ngModel)]="date"></vendor-date-picker>
    <vendor-time-picker [(ngModel)]="time"></vendor-time-picker>
  `
})
export class DateTimePickerComponent {
  @Output() timestampChanged = new EventEmitter<number>();

  date: string;
  time: string;

  reset() {
    this.date = "";
    this.time = "";
  }
}

我有一个使用 DateTimePicker 的父组件,有时需要重置它的状态:

@Component({
  selector: 'app-parent',
  template: `
    <app-date-time-picker #dateTimePicker (timestampChanged)="updateDateTime($event)"></app-date-time-picker>
  `
})
export class Parent {
  @ViewChild('dateTimePicker') private _dateTimePicker: DateTimePickerComponent;

  someEvent() {
    this._dateTimePicker.reset();
  }
}

我从供应商那里创建如此丑陋的组件的原因(和相关代码)超出了问题的范围。对我来说重要的是使用 ViewChild 打破了对孩子的封装。而且我无法使用“日期”和“时间”等“私有”关键字保护其他子字段,因为它们在模板中使用。所以问题是角度拥有机制是通过某些声明的公共接口(如使用输入)调用子方法还是保护其他子组件字段在模板中仍然可用但不能从外部访问?

【问题讨论】:

  • 我会选择ViewChild。我不知道你为什么认为它破坏了封装
  • ViewChild 非常适合您的情况。可以使用它,私有字段不会受到影响,如果您有公共字段,则可以从其他类访问它们
  • OOP / SOLID 背景。这就是为什么我认为这根本不行。
  • 这不是解释。我也没有看到破绽。
  • @HenkHolterman 我认为 OP 意味着可能存在 TS 目前无法阻止的潜在封装破损。

标签: angular typescript


【解决方案1】:

父组件不会破坏 DateTimePickerComponent 封装 - 只要 reset 方法被认为是公共的。组件只是类实例,父组件可以访问其公共成员。

考虑到父组件和子组件都是第一方组件,通常没有实际封装问题,因为开发人员通常知道组件的预期用途,但这对于更大的团队来说可能是个问题。

据我了解,这里的问题在于,由于 Angular AOT 编译的工作方式,它在编译模板中提供了类型安全性,但要求组件模板中使用的所有组件成员为 public - 而组件模板和类开发人员可以将其视为单个实体,而这些成员实际上是私有的。

一种选择是使用匈牙利表示法并在所有事实上私有的组件成员前加上下划线(这提供了certain benefits,尽管Angular opinionated style guide 声明了什么)——包括在组件模板中使用的那些。缺点是模板变得不太可读,因为有大量的下划线属性。另一个缺点是,这种表示法无助于区分不应在模板中使用的成员(例如大多数注入服务)。

另一种选择是为预期具有公共成员的组件提供相应的公共接口:

interface IDateTimePickerComponent {
  reset();
}

export class DateTimePickerComponent implements IDateTimePickerComponent {
  @Output() timestampChanged = new EventEmitter<number>();

  date: string;
  time: string;

  reset() {
    this.date = "";
    this.time = "";
  }
}

使用如下:

@ViewChild('dateTimePicker') private _dateTimePicker: IDateTimePickerComponent;

当然,还有其他component interaction options,但不应仅仅因为封装问题而首选它们。 OOP 原则的存在是为了指导和帮助开发人员,而不是为了跳槽。

【讨论】:

  • 你很清楚我在乎什么。我真的不喜欢这样一个事实,即字段不能对模板公开,而同时对外部使用是私有的。是的,使用界面确实可以避免这个问题。
猜你喜欢
  • 2018-05-09
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-15
  • 1970-01-01
  • 2019-02-28
  • 2023-03-25
相关资源
最近更新 更多