注入后,无法从控制器内访问ui-router状态的数组数据

时间:2015-11-19 23:21:54

标签: javascript arrays angularjs angular-ui-router

这看起来很简单,但我现在已经敲了几个小时了......

我有一个角度为1.2.29的模块,它使用ui-router来解析服务中的数据依赖关系并将其注入到命名的嵌套视图控制器中。该服务完全按照需要返回我的数据对象,我可以将返回的数据注入我的视图控制器并再次登录,没有任何问题。

我感到困惑的是,如果我在控制台中检查对象,我可以看到我试图在我从服务中注入的对象的一个​​嵌套属性上访问的数组(这是在上下文中控制器我试图在其中使用它。

但是,如果我尝试将该数据数组传递到同一控制器中的另一个方法(在这种情况下是绘制图形的d3方法) - 除了数组所在的空字符串外,我什么也得不到。

我可以访问我传递给方法的对象上的每个其他属性就好了,只要我不尝试直接访问数组,它在我的日志记录中对我来说是“可见的”,但是当我尝试和直接传递数组我得到一个空字符串。

我已经尝试了一切,我可以做一个深层复制或将数组推送到一个新对象并以这种方式访问​​它,尝试将该属性转换为一个数组(再次转换为一个新对象),以及一大堆其他随机黑客我遇到了所有无济于事。

我觉得我在这里错过了一些令人难以置信的微不足道的东西......

这三个日志在同一个对象中相继调用,您可以看到问题。

我正在尝试将data.datasources.tabledata.datum数组传递给另一个需要数组的方法,但我能得到的只是空字符串。

注意:'data'是在状态转换时从服务解析返回并注入视图控制器的对象。

> console.log(data);

// Expected object.
> Object {datasources: Object}
  datasources: Object
    tabledata: Object
      datum: Array[9383]
      source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv"
      __proto__: Object
    __proto__: Object
  __proto__: Object


> console.log(data.datasources);

// Expected object.
> Object {tabledata: Object}
   tabledata: Object
      datum: Array[9383]
      source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv"
      __proto__: Object
    __proto__: Object

> console.log(data.datasources.tabledata);

// String instead of array... WTH???
> Object {source: "./data/BSGAM_Heads_Wells_Drains_Zones_Master.csv", datum: ""}

> console.log(data.datasources.tabledata.datum);

// Returns NOTHING - not even undefined... just an empty log line @_@
> 

更新:这是加载数据的服务(它是我自己的)

(function() {
  'use strict';

  angular
    .module('mcsdss.providers')
    .factory('FormulationRetrieval', FormulationRetrieval);

  FormulationRetrieval.$inject = ['$http'];

  function FormulationRetrieval($http) {

    FormulationRetrieval.getFormulation = function (target) {
      var promise = $http
        .get(target)
        .then(function (response) {
          return FormulationRetrieval.configureFormulation(response.data);
        });
      return promise;
    };

    FormulationRetrieval.configureFormulation = function (f) {
      FormulationRetrieval.formulationContainer = f;
      FormulationRetrieval.loadFormulationSourceData(FormulationRetrieval.formulationContainer);
      FormulationRetrieval.loadFormulationGisData(FormulationRetrieval.formulationContainer);
      return FormulationRetrieval.formulationContainer;
    };

    FormulationRetrieval.loadFormulationSourceData = function (fc) {
      function parseFormulationDatasource(fd, destination) {
        Papa.parse(fd, {
          complete: function(results) {
            destination.datum = results.data;
          }
        });
      }

      function loadData(target) {
        var promise = $http
          .get(target.source)
          .then(function (response) {
            parseFormulationDatasource(response.data, target);
          });
        return promise;
      }

      var datasources = [fc.datagridConfig.datasources.tabledata, fc.graphConfig.datasources.graphContextData];
      angular.forEach(datasources, loadData);
    };

    FormulationRetrieval.loadFormulationGisData = function (fc) {
      function loadGeodata(target) {
        angular.forEach(target, function(value, key) {
          var promise = $http
            .get(value.source)
            .then(function (response) {
              value.datum = response.data;
            });
          return promise;
        });
      }

      var datasources = [fc.mapConfig.datasources.geojson];
      angular.forEach(datasources, loadGeodata);
    };

    FormulationRetrieval.getAnalysisConfig = function (fc) {
      var analysisConfig = fc.analysisConfig;
      return analysisConfig;
    };

    FormulationRetrieval.getMaufConfig = function (fc) {
      var maufConfig = fc.maufConfig;
      return maufConfig;
    };

    FormulationRetrieval.getGraphConfig = function (fc) {
      var graphConfig = fc.graphConfig;
      return graphConfig;
    };

    FormulationRetrieval.getTableConfig = function (fc) {
      var tableConfig = fc.datagridConfig;
      return tableConfig;
    };

    FormulationRetrieval.getMapConfig = function (fc) {
      var mapConfig = fc.mapConfig;
      return mapConfig;
    };

    return FormulationRetrieval;
  }
})();

以下是ui-router解决状态:

(function() {
  'use strict';

  angular
    .module('analyze')
    .config(DashboardRoutes);

  DashboardRoutes.$inject = ['$stateProvider'];

  function DashboardRoutes($stateProvider) {

    // Define states.
    var analyze_state = {
      abstract: true,
      url: '/analyze',
      templateUrl: 'modules/analyze/views/analyze.client.view.html',
      controller: 'AnalyzeViewController',
      controllerAs: 'analyze',
      data: {
        title: 'Analyze'
      },
      resolve: {
        analysisData: function(FormulationRetrieval) {
          return FormulationRetrieval.getFormulation('./data/formulations/bs.formulation.json');
        },
        analysisConfig: function(FormulationRetrieval, analysisData) {
          return FormulationRetrieval.getAnalysisConfig(analysisData);
        },
        maufConfig: function(FormulationRetrieval, analysisData) {
          return FormulationRetrieval.getMaufConfig(analysisData);
        },
        tableConfig: function(FormulationRetrieval, analysisData) {
          return FormulationRetrieval.getTableConfig(analysisData);
        },
        graphConfig: function(FormulationRetrieval, analysisData) {
          return FormulationRetrieval.getGraphConfig(analysisData);
        },
        mapConfig: function(FormulationRetrieval, analysisData) {
          return FormulationRetrieval.getMapConfig(analysisData);
        }
      }
    };

    var analyze_layout_state = {
      abstract: false,
      url: '',
      views: {
        'graph': {
          templateUrl: 'modules/analyze/views/analyze.graph.client.view.html',
          controller: 'GraphViewController'
        },
        'map': {
          templateUrl: 'modules/analyze/views/analyze.map.client.view.html',
          controller: 'MapViewController'
        },
        'filters': {
          templateUrl: 'modules/analyze/views/analyze.filters.client.view.html',
          controller: 'FiltersViewController',
          controllerAs: 'filters'
        },
        'datatable': {
          templateUrl: 'modules/analyze/views/analyze.datatable.client.view.html',
          controller: 'DatatableViewController',
          controllerAs: 'datatable'
        }
      }
    };

    // Populate provider.
    $stateProvider
      .state('analyze', analyze_state)
      .state('analyze.layout', analyze_layout_state);
  }
})();

这是接收注入数据的控制器:

(function() {
  'use strict';

  angular
    .module('analyze')
    .controller('AnalyzeViewController', AnalyzeViewController);

  AnalyzeViewController.$inject = ['$rootScope', '$scope', '$state', '$location', 'Authentication', 'httpq', 'analysisData', 'maufConfig', 'tableConfig', 'graphConfig', 'mapConfig'];

  function AnalyzeViewController($rootScope, $scope, $state, $location, Authentication, $httpq, analysisData, analysisConfig, maufConfig, tableConfig, graphConfig, mapConfig) {
    // This provides Authentication context.
    $scope.authentication = Authentication;
    $scope.currentRoute = 'Analyze';
    // console.log($scope.currentRoute);

    // ALL OF THESE ARE INJECTED AND APPEAR TO BE CORRECT IN CONSOLE.
    // console.log(analysisData);
    // console.log(analysisConfig);
    // console.log(maufConfig);
    // console.log(tableConfig);
    // console.log(graphConfig);
    // console.log(mapConfig);

// NOTE: At one point I was loading the data through promises inside the
//controller, but moved it into the state resolve for better SOC. 
//Strangely the $broadcast of the exact same value done here in the finally()
//block of the $httpq method works - using the new injected data object! 
//And yet, the same $broadcast on $stateChangeSuccess (which DOES send the correct
//data into the listening subscribers if I only send the entire object) sends only
//empty string if I specify the array.

    // Manual data loading.
    $scope.sourceFile_A = './data/BSGAM_Heads_Wells_Drains_Zones_Master.csv';

    $httpq.get($scope.sourceFile_A)
      .then(function(data) {
        // ...removed because not used.
      })
      .catch(function(data, status) {
        console.error('Load error', response.status, response.data);
      })
      .finally(function() {
        // Works here using the injected resolved promise, does not work in stateChangeSuccess... WTH??
        $scope.$broadcast('analysisDataLoaded', analysisData.datagridConfig.datasources.tabledata.datum);
      });

    $scope.$on('$stateChangeSuccess', function() {
      // EXACT SAME BROADCAST AS ABOVE FAILS HERE - EMPTY STRING.
      // $scope.$broadcast('analysisDataLoaded', analysisData.datagridConfig.datasources.tabledata.datum);
    });

    // extra code removed...

})();

最后是嵌套视图控制器(4个中的一个),我试图通过抽象父级的$ broadcast来使用数据。我也可以在没有广播的情况下直接访问所有注入的对象,但我试图尽可能地解耦,因为几个视图需要从广播同步更新。

(function() {
  'use strict';

  angular
    .module('analyze')
    .controller('DatatableViewController', DatatableViewController);

  DatatableViewController.$inject = ['$scope', 'Authentication', '$filter', 'ngTableParams', 'AnalysisDataFactory', 'tableConfig'];

  function DatatableViewController($scope, Authentication, $filter, ngTableParams, AnalysisDataFactory, tableConfig) {
    // This provides Authentication context.
    $scope.authentication = Authentication;

    // Expose public API.
    $scope.headerFilter = headerFilter;
    $scope.datasetOrder = datasetOrder;
    $scope.rowClicked = rowClicked;
    $scope.decorateSiblings = decorateSiblings;
    $scope.clearSiblings = clearSiblings;
    $scope.updateView = updateView;

    // Private members.
    $scope.headerdata = [];
    $scope.tabledata = [];
    $scope.suf01 = 0;
    $scope.suf02 = 0;
    $scope.suf03 = 0;
    $scope.muf = 0;

    $scope.$on('analysisDataLoaded', function(event, args) {
      console.log('analysisDataLoaded...', event, args);
      $scope.updateView(args);
       // THIS IS WHAT I WANT TO DO INSTEAD:
      // $scope.updateView(tableConfig);    // Not working yet.
    });

    function headerFilter(target) {
      return target.visible;
    }

    function datasetOrder(key) {    
      angular.forEach($scope.headers, function(target) {
        // console.log('key='+key);
        if (target.data == key) {
          if (target.visible) {
            return target.order;
          }
        }
      });
      return -1;
    }

    function rowClicked(target) {   
      for (var key in target) {
        if (target.hasOwnProperty(key)) {
          console.log(key + ' -> ' + target[key]);
        }
      }
    }

    function decorateSiblings(target) {
      // console.log('data row touched, sending emission.');
      $scope.$emit('currentDatatableTarget', target);
    }

    function clearSiblings(target) {
      // console.log('datarow cleared, sending all clear.');
      $scope.$emit('clearDatatableTarget', target);
    }

    function updateView(data) {    
      // ngTable    
      $scope.dataTable = new ngTableParams({
        page: 1,
        count: 10
      }, {
        total: $scope.tabledata.length,
        counts: [10, 25, 50, 100, 250],
        defaultSort: 'asc',
        getData: function($defer, params) {
          $scope.data = params.sorting() ? $filter('orderBy')($scope.tabledata, params.orderBy()) : $scope.tabledata;
          $scope.data = params.filter() ? $filter('filter')(data, params.filter()) : data;
          $scope.data = data.slice((params.page() - 1) * params.count(), params.page() * params.count());
          $defer.resolve($scope.data);
        }
      });    
    }
  }
})();

这里有一些简单的东西 - 可能是我忽略的JS问题......提前感谢任何输入!!

1 个答案:

答案 0 :(得分:0)

原来,这是因为在尝试绑定它们之前没有正确地推迟和解析promise。我仍然没有确切地确定我的代码中的时间错误在哪里,但我现在可以按照我的预期间歇性地加载数据,并且可以看出它显然是异步不一致的函数。 $ broadcast事件正常工作(当异步数据正确加载时)。