javascript - Angular : circular dependency of specific case

标签 javascript angularjs dependency-injection circular-dependency

前段时间,我开始重构我的主项目代码,根据指南将业务逻辑从 Controller 解耦到服务。一切顺利,直到我遇到循环依赖(CD)的问题。我阅读了一些关于这个问题的资源:

Question 1 on Stack Overflow

Question 2 on Stack Overflow

Miško Hevery blog

不幸的是,我现在还不清楚,我该如何解决我的项目的 CD 问题。因此,我准备了一个小演示,它代表了我项目的核心功能:

Link to Plunker

Link to GitHub

组件的简短描述:

  • gridCtrl 不包含任何业务逻辑,仅触发数据加载,当数据准备好时,显示网格。
  • gridMainService 这是主要服务,包含对象gridOptions。该对象是grid的核心对象,包含一些api,由框架初始化,行数据,列标题等。通过这项服务,我计划控制所有相关的网格内容。 loadGridOptions 函数由 gridCtrl 触发,等待相应服务加载行数据和列定义。然后,它使用此数据 gridOptions 对象进行初始化。
  • gridConfigService 这是一个简单的服务,用于加载列定义。这里没问题。
  • gridDataService 此服务用于通过函数 loadRowData 加载行数据。该服务的另一个特点:它模拟来自服务器的实时更新($interval 函数)。 问题 1 在这里!
  • gridSettingsService 此服务用于将一些设置应用于网格(例如,应如何对列进行排序)。 问题 2 在这里!

问题 1: 我在 gridMainServicegridDataService 之间存在循环依赖。首先gridMainService使用gridDataService加载行数据,函数:

self.loadRowData = function () {
    // implementation here
}

但是,gridDataService 从服务器接收更新并且必须将一些行插入到网格中。所以,它必须使用 gridMainService:

$interval(function () {

    var rowToAdd = {
      make: "VW " + index,
      model: "Golf " + index,
      price: 10000 * index
     };

     var newItems = [rowToAdd];

     // here I need to get access to the gridMainService
     var gridMainService = $injector.get('gridMainService');
     gridMainService.gridOptions.api.addItems(newItems);

     index++;

 }, 500, 10);

所以,在这里我遇到了第一张 CD。

问题 2: 我在 gridMainServicegridSettingsService 之间存在循环依赖。首先,gridMainService 触发,当网格初始加载时,然后通过以下函数设置默认设置:

self.onGridReady = function () {
    $log.info("Grid is ready, apply default settings");
    gridSettingsService.applyDefaults();
};

但是,要进行一些更改,gridSettingsService 需要访问 gridMainService 及其对象 gridOptions 才能应用设置:

 self.applyDefaults = function() {
     var sort = [
       {colId: 'make', sort: 'asc'}
     ];

     var gridMainService = $injector.get('gridMainService');
     gridMainService.gridOptions.api.setSortModel(sort);
 };

问题: 如何正确解决这些 CD 盒?因为,Miško Hevery 的博客很短而且很好,但我没有设法将他的策略应用到我的案例中。

目前,我不太喜欢手动注入(inject)的方法,因为我将不得不经常使用它,而且代码看起来有点模糊。

请注意: 我只准备了我的大项目的演示。您可能会建议将所有代码放入 gridDataService 并准备就绪。但是,我在这个服务中已经有 500 LOC,如果我合并所有服务,那将是 ~1300 LOC 的噩梦。

最佳答案

在这类问题中有很多解决方案,这取决于您的思维方式。我更愿意认为每个服务(或类)都有一些目标并且需要一些其他服务来完成它的目标,但它的目标是一个明确的小目标。让我们通过这个 View 查看您的代码。

问题 1:

GridData:这里存放的是网格数据。 MainService 来这里获取所需的数据,因此我们将其注入(inject) mainService 并像您一样使用 loadRowData 函数获取 rowData 数据,但是在 $interval 中,您将 mainService 注入(inject)到 gridData 中,但 gridData 不需要 mainService 来结束它的目标(从服务器获取项目)。

我使用观察者设计模式(使用 $rootScope)解决了这个问题。这意味着当数据到达并且 mainService 来获取它们时我会收到通知。

网格数据.service.js:

angular.module("gridApp").service("gridDataService",
         ["$injector", "$interval", "$timeout", "$rootScope",
 function ($injector, $interval, $timeout, $rootScope) {
   […]
   $interval(function () {
   [..]
   self.newItems = [rowToAdd];

    // delete this code
    // var gridMainService = $injector.get('gridMainService'); 
    // gridMainService.gridOptions.api.addItems(newItems);

    // notify the mainService that new data has come!
    $rootScope.$broadcast('newGridItemAvailable');

grid-main.service.js:

 angular.module("gridApp").service("gridMainService",
          ["$log", "$q", "gridConfigService", "gridDataService", '$rootScope',
  function ($log, $q, gridConfigService, gridDataService, $rootScope) {
     [..]
     // new GridData data  arrive go to GridData to get it!
     $rootScope.$on('newGridItemAvailable', function(){
        self.gridOptions.api.addItems(gridDataService.getNewItems());
     })
     [..]

当使用真实服务器时,最常见的解决方案是使用 promises(而非观察者模式),例如 loadRowData。

问题 2:

gridSettingsService:这个服务改变了mainService的设置所以它需要mainService但是mainService不关心gridSettings,当有人想要改变或者学习mainService内部状态(数据,表单)必须与主服务接口(interface)。

因此,我从 gridMainService 中删除了 grid Settings 注入(inject),只提供了一个接口(interface),用于在 Grid 就绪时放置回调函数。

grid-main.service.js:

angular.module("gridApp").service("gridMainService",
          ["$log", "$q", "gridConfigService", "gridDataService", '$rootScope',
 function ($log, $q, gridConfigService, gridDataService, $rootScope) {
   […]
   // You want a function to run  onGridReady, put it here!
   self.loadGridOptions = function (onGridReady) {
          [..]
          self.gridOptions = {
              columnDefs: gridConfigService.columnDefs,
              rowData: gridDataService.rowData,
              enableSorting: true,
              onGridReady: onGridReady // put callback here
            };
          return self.gridOptions;
        });
[..]// I delete the onGridReady function , no place for outsiders
    // If you want to change my state do it with the my interface

Ag-grid-controller.js:

    gridMainService.loadGridOptions(gridSettingsService.applyDefaults).then(function () {
      vm.gridOptions = gridMainService.gridOptions;
      vm.showGrid = true;
});

这里是完整代码:https://plnkr.co/edit/VRVANCXiyY8FjSfKzPna?p=preview

关于javascript - Angular : circular dependency of specific case,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41509684/

相关文章:

javascript - 通过 es6 脚本创建 Web 表单

javascript - 如何创建嵌套 For 循环以在 Nightmare javascript 代码中输入数据

javascript - cytoscape.js-qtip 扩展 : qtip. $domEle.qitp 不是函数

angularjs - 如何更改 angularjs 中每行的按钮文本

Angular 7 库 - 检测到循环依赖(指令、服务、模块)

javascript - 将 localStorage 包装在 Angular2 服务中?

javascript - ng-repeat 表中的 AngularJS 自定义过滤器

javascript - 单页网站 ~ 关于索引和社交媒体共享的最佳实践和方法

javascript - 将父函数与 & from 指令一起使用以实现自定义行为,但需要回调?

c# - 在 Unity 中使用抽象工厂作为注入(inject)工厂?