为何Angular工厂解析器仅能正常工作一次?
你遇到的核心问题是Angular依赖注入的模块级提供者单例特性导致的,咱们一步步拆解并解决:
问题根源
你在DialogLoadDocumentModule的providers里定义了DocumentCustomBlock的工厂提供者,但Angular中模块级别的提供者默认是单例的——第一次创建DocumentCustomBlock实例后,后续无论打开多少次对话框,Angular都会复用这个已有的实例,不会再执行你的工厂函数。这就是为什么console.log(customBlockService.type)只打印一次,且新设置的customBlockService.block无法生效的原因。
另外你尝试添加{ provide: CustomBlockService, useClass: CustomBlockService }没解决问题,大概率是因为CustomBlockService已经在父模块/根模块中被提供过了,Angular会优先使用更高层级的单例实例,不会加载模块内的新实例。
解决方案1:将提供者移到对话框组件级别(推荐)
把DocumentCustomBlock的提供者从模块移到对话框组件的元数据中,这样每次打开对话框(组件实例化)时,都会重新执行工厂函数,创建新的DocumentCustomBlock实例,自然能拿到最新的CustomBlockService数据。
修改步骤:
- 从
DialogLoadDocumentModule的providers中移除DocumentCustomBlock的配置。 - 在对话框组件的
@Component装饰器中添加providers:
@Component({ selector: 'app-dialog-load-document', templateUrl: './dialog-load-document.component.html', styleUrls: ['./dialog-load-document.component.css'], providers: [ CustomBlockService, // 这里提供确保每次组件实例化都生成新的服务实例 { provide: DocumentCustomBlock, useFactory: (httpClient: HttpClient, customBlockService: CustomBlockService) => { console.log(customBlockService.type); switch (customBlockService.type) { case 'doc_num_date_block': return new DocNumDateBlock( httpClient, customBlockService, new DocNumDateBlockFormGroup(createFormControls(customBlockService.value)), ); case 'find_unom_by_address_block': return new FindUnomAddressBlock( httpClient, customBlockService, new FindUnomAddressBlockFormGroup(createFormControls(customBlockService.value)), ); default: throw 'Error type custom block!'; } }, deps: [HttpClient, CustomBlockService], } ] }) export class DialogLoadDocumentComponent { // 组件逻辑... }
这样每次打开对话框,都会创建全新的CustomBlockService和DocumentCustomBlock实例,工厂函数会重新执行,拿到最新的配置数据。
解决方案2:用可观察对象传递状态(保留单例服务场景)
如果需要CustomBlockService保持单例(比如多组件共享状态),可以修改服务用BehaviorSubject传递block的变化,然后在各个DocumentCustomBlock实现类中订阅这个变化,实时更新内部状态。
第一步:修改CustomBlockService
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CustomBlockService { private readonly blockSubject = new BehaviorSubject<any>(null); public block$ = this.blockSubject.asObservable(); setDocument(block: any): void { this.blockSubject.next(block); } get block(): any { return this.blockSubject.value; } get type(): string { return this.block?.type; } get value(): any { return this.block?.value; } }
第二步:修改Block实现类(以DocNumDateBlock为例)
export class DocNumDateBlock { constructor( private httpClient: HttpClient, private customBlockService: CustomBlockService, private formGroup: DocNumDateBlockFormGroup ) { // 订阅block变化,实时更新表单 this.customBlockService.block$.subscribe(block => { if (block?.type === 'doc_num_date_block') { this.formGroup.patchValue(createFormControls(block.value)); } }); } }
这种方式下,即便DocumentCustomBlock是单例,也能通过订阅实时获取最新的block数据,无需重新创建实例。
内容的提问来源于stack exchange,提问作者user13419533

