如何为Angular组件及其内容子项提供同一服务实例?
让注入的HeaderCell获取Table组件的MyService实例的方案
你当前的问题核心是:通过ng-content投影的组件(HeaderCell)的依赖注入上下文属于声明它的父组件,而非投影目标组件(Table),所以默认无法直接获取Table组件内提供的MyService实例。不需要必须用@Input()结合ContentChildren传递,以下是几种可行方案:
方案1:通过祖先组件注入Table实例间接获取服务
在HeaderCell中注入祖先链中的Table组件实例,再从Table中拿到对应的MyService。这种方式耦合性较低,且符合Angular的注入逻辑:
// Table组件:将MyService设为public或提供公共访问方法 @Component({ providers: [MyService], selector: 'app-table', template: '<ng-content></ng-content>' }) export class Table { constructor(public myService: MyService) {} } // HeaderCell组件:注入Table实例并获取服务 @Component({ selector: 'app-header-cell', template: '...' }) export class HeaderCell { private myService: MyService; // @SkipSelf() 跳过自身注入器,向上查找Table实例;@Optional() 避免未投影到Table时报错 constructor(@Optional() @SkipSelf() private table: Table) { if (table) { this.myService = table.myService; } } }
方案2:使用自定义注入标记关联Table的服务实例
通过创建注入令牌,让Table组件把自己的MyService实例绑定到该令牌上,HeaderCell再通过令牌注入目标服务:
// 定义全局注入令牌 export const TABLE_MY_SERVICE = new InjectionToken<MyService>('TABLE_MY_SERVICE'); // Table组件:将自身的MyService绑定到令牌 @Component({ providers: [ MyService, { provide: TABLE_MY_SERVICE, useExisting: MyService } ], selector: 'app-table', template: '<ng-content></ng-content>' }) export class Table {} // HeaderCell组件:通过令牌注入服务 @Component({ selector: 'app-header-cell', template: '...' }) export class HeaderCell { constructor( // 可选注入,避免未在Table中使用时报错 @Optional() @Inject(TABLE_MY_SERVICE) private myService: MyService ) {} }
方案3:利用Portal组件切换注入上下文
使用Angular CDK的Portal功能,让HeaderCell在Table组件的注入器上下文中渲染,从而直接获取MyService:
// Table组件:使用PortalOutlet接收内容 @Component({ providers: [MyService], selector: 'app-table', template: '<ng-template [cdkPortalOutlet]="contentPortal"></ng-template>' }) export class Table { @Input() contentPortal: TemplateRef<any>; } // 父组件模板:将HeaderCell包装为Portal传入Table <app-table [contentPortal]="headerCellTemplate"></app-table> <ng-template #headerCellTemplate> <app-header-cell></app-header-cell> </ng-template> // HeaderCell组件:直接注入MyService即可 @Component({ selector: 'app-header-cell', template: '...' }) export class HeaderCell { constructor(private myService: MyService) {} }
备选方案:@Input()结合ContentChildren传递(你想避免的方式)
虽然你不想用,但还是可以作为参考:Table通过ContentChildren获取所有投影的HeaderCell,再通过@Input()把MyService实例传递给它们:
// Table组件 @Component({ providers: [MyService], selector: 'app-table', template: '<ng-content></ng-content>' }) export class Table implements AfterContentInit { @ContentChildren(HeaderCell) headerCells: QueryList<HeaderCell>; constructor(private myService: MyService) {} ngAfterContentInit() { this.headerCells.forEach(cell => cell.myService = this.myService); } } // HeaderCell组件 @Component({ selector: 'app-header-cell', template: '...' }) export class HeaderCell { @Input() myService: MyService; }
内容的提问来源于stack exchange,提问作者nsquires
相关产品推荐
相关产品推荐

