我有基于供应商组件的自定义日期和时间选择器组件。如果简化它可以看起来如此:
@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打破了对孩子的封装。而且我无法使用“私人”关键字来保护其他子字段,例如“日期”和“时间”,因为它们在模板中使用。所以问题是角度是否拥有通过某些声明的公共接口(如使用输入)调用子方法或者保护其他子组件字段仍然可以在模板中使用但不能在外部访问的机制?
父组件不会破坏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原则的存在是为了指导和帮助开发人员,而不是跳过箍。