首页 > 解决方案 > 如何从多个模块中删除所有相关记录

问题描述

我的角度应用程序中有多个模块,每个模块都将一些相关数据存储到我的主数据中。现在,我想删除我的主数据,我也想删除所有相关数据。

所以我想有一个service1带有删除方法的服务()和一个可观察的,让其他服务知道这些数据将被删除,其他依赖服务(service2, service3, ...)将订阅并删除它们的相关数据

@Injectable({
  providedIn: "root"
})
export class Service1Service {
  deleteData$: Subject<string> = new Subject<string>();
  constructor() {
    console.log("Service1Service.constructor");
  }

  delete(id: string) {
    console.log("service1.delete");
    this.deleteData$.next(id);
  }
}
@Injectable({
  providedIn: "root"
})
export class Service2Service {
  constructor(private service1: Service1Service) {
    console.log("Service2Service.constructor");
    this.service1.deleteData$.subscribe(id => this.delete(id));
  }

  delete(id: string) {
    console.log("service2.delete");
  }
}

export class MyComponent {
  constructor(
    private service1: Service1Service,
    ) {}

  delete() {
    this.service1.delete("5");
  }
}

所以这种方式,Service2将依赖于Service1,如果将来开发一个新的模块来存储一些其他相关数据到母数据,它将是打开/关闭的。

问题是如果我不注入Service2已经加载的组件,角度不会创建它们,所以它们不会订阅服务来删除数据。

我尝试使用providedIn: "root"或直接将它们添加到 app.moduleproviders没有区别。

注意:我不希望Service1依赖于其他服务,即我不希望 service1 调用其他服务删除方法

闪电战

标签: angularangular-servicessolid-principles

解决方案


Angular 不会创建服务,直到出于优化原因第一次需要它。此外,如果您的服务未在代码中的任何地方使用,则 Tree-shaking 会将其从生产代码中删除。有关更多详细信息,请查看有关Tree-shakable providers的文档。

我认为对于这种情况,更好的解决方案是为所有服务定义一个通用接口并使用多个提供程序。这意味着您为同一个接口提供多种服务。然后,您可以一次将所有服务注入组件并循环通过它们并调用 delete 全部或添加另一个中间服务来执行此操作,如果您不希望组件中的逻辑。然后,当您添加新服务时,您只需将其作为另一个提供商提供,一切都会自动运行。这样,服务是完全独立的,组件或父服务只与抽象接口对话。

这是此类解决方案的示例代码。

export abstract class ServiceBase {
  public abstract delete(id: string): void;
}

@Injectable()
export class Service1Service extends ServiceBase {
  constructor() {
    super();
    console.log("Service1Service.constructor");
  }

  delete(id: string) {
    console.log("service1.delete");
  }
}

@Injectable()
export class Service2Service extends ServiceBase {
  constructor() {
    super()
    console.log("Service2Service.constructor");
  }

  delete(id: string) {
    console.log("service2.delete");
  }
}

export class AppComponent {
  constructor(
    @Inject(ServiceBase)private services: ServiceBase[],
    ) {}

  delete() {
    this.services.forEach((service) => service.delete());
  }
}

然后在应用程序模块中将服务注册为多提供者。

@NgModule({
  imports:      [ BrowserModule, FormsModule ],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ],
  providers: [
    { provide: ServiceBase, useClass: Service1Service, multi: true },
    { provide: ServiceBase, useClass: Service2Service, multi: true }
  ]
})
export class AppModule { }

这是实现此解决方案的 StackBlitz 分支的链接。

还有一个解决方法可以让您的解决方案不那么干净,所以我不建议使用它。为此,您需要强制创建子服务。我发现的一种方法是提供一个自定义应用程序初始化程序,该初始化程序依赖于Service2Service将强制创建的程序。这是建议此解决方案的帖子的链接。

这是您的案例的示例:

@NgModule({
  imports:      [ BrowserModule, FormsModule ],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ],
  providers: [{
    provide: APP_INITIALIZER,
    useFactory: () => () => {},
    deps: [Service2Service],
    multi: true
  }]
})
export class AppModule { }

实现此解决方法的 StackBlitz 分支的链接。


推荐阅读