You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Angular工厂解析器仅能正常工作一次?

问题分析与解决方案

你遇到的核心问题是Angular依赖注入的模块级提供者单例特性导致的,咱们一步步拆解并解决:

问题根源

你在DialogLoadDocumentModule的providers里定义了DocumentCustomBlock的工厂提供者,但Angular中模块级别的提供者默认是单例的——第一次创建DocumentCustomBlock实例后,后续无论打开多少次对话框,Angular都会复用这个已有的实例,不会再执行你的工厂函数。这就是为什么console.log(customBlockService.type)只打印一次,且新设置的customBlockService.block无法生效的原因。

另外你尝试添加{ provide: CustomBlockService, useClass: CustomBlockService }没解决问题,大概率是因为CustomBlockService已经在父模块/根模块中被提供过了,Angular会优先使用更高层级的单例实例,不会加载模块内的新实例。


解决方案1:将提供者移到对话框组件级别(推荐)

把DocumentCustomBlock的提供者从模块移到对话框组件的元数据中,这样每次打开对话框(组件实例化)时,都会重新执行工厂函数,创建新的DocumentCustomBlock实例,自然能拿到最新的CustomBlockService数据。

修改步骤:

  1. 从DialogLoadDocumentModule的providers中移除DocumentCustomBlock的配置。
  2. 在对话框组件的@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 09:37:51