如何在不破坏封装的情况下调用子组件方法?

时间:2018-03-06 08:21:34

标签: angular typescript

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

@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打破了对孩子的封装。而且我无法使用“私人”关键字来保护其他子字段,例如“日期”和“时间”,因为它们在模板中使用。所以问题是角度是否具有通过某些声明的公共接口(如使用输入)调用子方法或者保护其他子组件字段仍然可以在模板中使用但不能在外部访问的机制?

1 个答案:

答案 0 :(得分: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原则的存在是为了指导和帮助开发人员,而不是跳过箍。