Angular抽象类继承依赖注入热重载报错解决方案咨询
我来帮你解决这个Angular AOT热重载的问题。先梳理下你的场景:你定义了一个继承自Localization的抽象组件基类BaseComponent,子类SomeChildComponent继承它。初始执行ng serve --aot --preserve-symlinks一切正常,但文件更新触发热重载后,抛出了装饰器相关的错误。
你的代码片段
父抽象类代码:
import { Localization } from 'angular-l10n'; import { SomeModel} from '../../api/models/some-model'; import { SomeService } from '../../api/services/some.service'; export abstract class BaseComponent extends Localization implements OnInit { someModel: SomeModel[]; constructor( protected someService: SomeService ) { super(); } ngOnInit() { this.someService .getSomeModel() .pipe(take(1)) .subscribe(ref => (this.someModel= ref), err => this.onError(err)); } }
子类代码:
@Component({ selector: 'some-child-component', templateUrl: './some-child.component.html', styleUrls: ['./some-child.component.scss'], }) export class SomeChildComponent extends BaseComponent implements OnInit { constructor( protected someService: SomeService ) { super(someService) } ngOnInit() { super.ngOnInit(); } }
触发的错误:
Class BaseComponent in D:/some/path/components/base-component.ts extends from a Injectable in another compilation unit without duplicating the decorator Please add a Injectable or Pipe or Directive or Component or NgModule decorator to the class.
使用的版本信息:
Angular CLI: 1.7.4 Node: 6.11.4 OS: win32 x64 Angular: 5.2.10 typescript: 2.6.2 webpack: 3.11.0
解决方案:给抽象父类添加合适的装饰器
这个问题的核心是Angular的AOT增量编译(热重载时触发)对跨编译单元的继承装饰器检查更严格。你的BaseComponent继承了带有装饰器的Localization类,但自身没有添加任何Angular装饰器,导致热重载时编译校验失败。
根据BaseComponent的定位(组件基类),推荐添加@Directive()装饰器(因为组件本质是带有模板的指令,抽象组件基类用Directive装饰器最匹配):
修改后的父类代码:
import { Localization } from 'angular-l10n'; import { SomeModel} from '../../api/models/some-model'; import { SomeService } from '../../api/services/some.service'; import { Directive, OnInit } from '@angular/core'; // 导入Directive装饰器 @Directive() // 添加这个装饰器 export abstract class BaseComponent extends Localization implements OnInit { someModel: SomeModel[]; constructor( protected someService: SomeService ) { super(); } ngOnInit() { this.someService .getSomeModel() .pipe(take(1)) .subscribe(ref => (this.someModel= ref), err => this.onError(err)); } }
如果你的Localization是一个服务类(而非组件/指令类),也可以用@Injectable()装饰器替代@Directive(),但针对组件基类的场景,@Directive()是更规范的选择。
为什么初始启动正常,热重载出错?
初始AOT全量编译时,编译器可能没有严格校验跨文件继承的装饰器缺失问题,但热重载时的增量编译会做更细致的检查,从而触发了这个错误。添加装饰器后,无论是全量还是增量编译,编译器都能正确识别这个类的Angular身份,问题就解决了。
内容的提问来源于stack exchange,提问作者Deblaton Jean-Philippe

