Angular懒加载模块中的服务提供商

4

我目前遇到了一个懒加载模块和服务的问题,这些服务是在这个懒加载模块中提供的(providedIn)。

我的文件夹结构如下:

app
-> featureModule1 (懒加载)
-> featureModule2 (懒加载)
-->services
--->service1.ts

我希望只能在featureModule2中使用service1.ts。因此,我包含了@Injectable。

// service.ts    
@Injectable({
   providedIn: featureModule2
})
export class service1 { /* some logic */ }

当我尝试路由到这个惰性加载的模块时,我会收到一个控制台错误:
// console    
ERROR Error: Uncaught (in promise): ReferenceError: Cannot access 'featureModule2' before initialization
    ReferenceError: Cannot access 'featureModule2' before initialization

我的当前的图片懒加载路由如下:

// app-routing.module.ts    
const routes: Routes = [
      {path: '', redirectTo: '', pathMatch: 'full'},
      { path: 'feature1', loadChildren: () => import('./feature1/feature1.module').then(m => m.featureModule1) },
      { path: 'feature2', loadChildren: () => import('./feature2/feature2.module').then(m => m.featureModule2) }
    ];

我尝试在模块中提供它:

// featureModule1     
@NgModule({
 declarations: [
  featureComponent
 ],
 imports: [
   ...
 ],
 providers: [
  service1
 ]
})

那行不通。

我试图在组件 (featureComponent) 中直接导入 service1.ts

// featureComponent
import { service1 } from '../featureModule2/services/service1.service';

@Component({
  ...
})
export class featureComponent {
  constructor(private service1: service1) { }

  ngOnInit(): void {
    this.service1.init();
  }
}

这种方法导致了相同的错误信息。

目前唯一解决此问题的方法是创建一个“包装器”模块,该模块引入所有其他带有 @Injectable 服务的模块。

是否有不同的解决方法?每个模块中的服务不应该被注入到 rootany 中,因为它们只应在每个 featureModule 中可用。


在模块配置中使用“providers”将百分之百有效。你为什么说它无效? - Antoniossss
只要我在路由器中使用延迟加载,它就能正常工作。 - MrDeibl
它能正常工作吗?你可以移除 @Injectable 然后再试一次。 - Antoniossss
不,它不能在惰性加载模块中使用。当我删除@Injectable时,我可以在应用程序中任何地方注入服务。这不是我尝试实现的目标。 - MrDeibl
这是一个较旧版本的Angular,但应该没问题 https://stackblitz.com/edit/angular-s5xxnb-j9ch1z?file=src%2Fapp%2Fcustomers%2Fcustomers.module.ts - Antoniossss
在我的例子中,你只能将它注入到声明它的模块中,不能在其他地方这样做。对于客户有效,但对于订单则无效。 - Antoniossss
2个回答

1
你只需要在providers部分声明它,它就会工作。我使用了useFactory提供程序变体,以清楚地显示它被用于执行注入,而不是仅仅在原地创建一个新的LazyService。我也将使用providers:[LazyService](我相信这是一个“类提供程序”,等同于useClass:LazyService)。
在惰性模块中。
import {
  CustomerListComponent,
  LazyService
} from './customer-list/customer-list.component';

@NgModule({
  imports: [CommonModule, CustomersRoutingModule],
  declarations: [CustomerListComponent],
  providers: [
    {
      provide: LazyService,
      useFactory: () => new LazyService('proofThatItIsFromPRoviders')
    }
  ]
})
export class CustomersModule {}

在同一模块中的惰性组件中(“printsLazy service works proofThatItIsFromPRoviders”)

export class LazyService {
  constructor(private test: string) {}
  public say() {
    console.log('Lazy service works', this.test);
  }
}

@Component({
  selector: 'app-customer-list',
  templateUrl: './customer-list.component.html',
  styleUrls: ['./customer-list.component.css']
})
export class CustomerListComponent implements OnInit {
  constructor(private lazy: LazyService) {}

  ngOnInit() {
    this.lazy.say();
  }
}

不同模块中的注入失败。
@Component({
  selector: 'app-order-list',
  templateUrl: './order-list.component.html',
  styleUrls: ['./order-list.component.css']
})
export class OrderListComponent implements OnInit {

  constructor(private lazy:LazyService) { }

  ngOnInit() {
    this.lazy.say();
  }

}

这里有一个工作示例:https://stackblitz.com/edit/angular-s5xxnb-j9ch1z?file=src%2Fapp%2Forders%2Forder-list%2Forder-list.component.ts


你说得完全正确,但如果我在订单模块中集成的订单组件中也提供了lazyService,则可以注入此服务。https://stackblitz.com/edit/angular-s5xxnb-53w6bp?file=src%2Fapp%2Forders%2Forders.module.ts - MrDeibl
@MrDeibl 两个实例没问题,这意味着您不想共享状态。那么为什么要使用服务呢?如果意外地维护状态,您最终会遇到难以找到的错误。 - Ritesh Waghela
因为有许多子模块使用相同的服务和状态。例如: service1 被 fModule1.1 fModule1.2 fModule1.3 使用, 但不应该被 fModule2.1fModule2.2 使用。 - MrDeibl
@MrDeibl,你需要像Java中的protected/default访问范围一样的东西,但是没有现成的。注入定义只是运行时提示,而你需要编译时保护。这里可能有一个有趣的想法 https://dev59.com/IVQK5IYBdhLWcg3wDLkw#52582023 - Antoniossss
@Antoniossss 这是个好提示,非常感谢!我会尝试的。 - MrDeibl
显示剩余5条评论

1
当我们配置一个像这样的服务时:

   @Injectable({
       providedIn: featureModule2
    })
    export class TestService {
    }

这意味着,除非应用程序导入featureModule2,否则TestService将不可用。
由于featureModule2是惰性加载的模块,您肯定不希望在应用程序模块导入中指定它,因此您需要使用:
     @Injectable({
          providedIn: 'any'
      })
      export class TestService {
      }

或者

      @Injectable({
          providedIn: 'root'
      })
      export class TestService {
      }

以上两者的区别在于,当我们使用 'root' 时,该服务将在整个应用程序中存在单个实例。对于按需加载的模块,使用 'any' 将创建一个新的服务实例,并且对于急切加载的模块,它将作为单例运行。

好的,你的回答证明了我之前的测试。所以使用一个模块进行延迟加载,并在该模块的子模块中使用import来限制服务,这是正确的方法吗? featureModule1(lazy) -> import: [featureModule1.1]; service1.ts providesIn: featureModule1.1 - MrDeibl
@MrDeibl:除非有一种情况,我们希望该服务仅在使用者导入特定的NgModule时才可用,否则我们应始终在根注入器中提供您的服务。 - Ritesh Waghela
非常好,谢谢你。在这种情况下,我想要实现的是有些服务不应该被注入到其他模块中。 - MrDeibl
1
所有这样的服务都应该使用provideIn提供给特定的模块,然后需要从该模块获取服务的任何模块都必须导入此模块。 - Ritesh Waghela

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接