【问题标题】:Access HTTP GET JSON property in angular controller在角度控制器中访问 HTTP GET JSON 属性
【发布时间】:2015-10-21 06:46:35
【问题描述】:

我正在使用 angular.js 中的工厂和 $http.get 方法来获取和处理 JSON 数据。 JSON 数据似乎已成功解析为工厂,但我无法访问此 JSON 数据的属性。

这是我的js代码:

var app = angular.module("app", []);      

app.factory('mainInfo', function($http) { 

    var obj = {content:null};
    //the php will return json data below
    $http.get('http://localhost/test.php').success(function(response){ 
        obj.content = response.records;

    });    

    return obj;    
});


app.controller('Ctrl', function($scope, mainInfo){
    $scope.foo = mainInfo.content;
}) ;

现在如果我尝试在Ctrl 控制器中访问 foo,网页将不显示任何数据:
<div ng-controller="Ctrl">Controller: {{foo}}</div>
但是,如果我在Ctrl 中更改为$scope.foo = mainInfo,则网页将正确显示JSON 数据。

我可以知道在Ctrl 控制器中访问mainInfo.content 属性的正确方法是什么吗?

之所以需要访问 JSON 属性是因为我需要对数据进行预处理。我打算在图表中使用这些数据,如下面的控制器。目前这个控制器也不起作用,因为我在访问 JSON 属性时遇到了与Ctrl 控制器中相同的问题。

app.controller("LineCtrl", function ($scope, mainInfo) {
    var timePoints = [];
    var percentagePoints = [];
    var i = 0;
    for( i = 0; i < mainInfo.content.length; i ++) {
        timePoints.push(mainInfo.content[i].Time);
        percentagePoints.push(mainInfo.content[i].Percentage);
    }

    $scope.labels = timePoints;

    $scope.data = percentagePoints;

    $scope.onClick = function (points, evt) {
        console.log(points, evt);
    };
});

json数据:

{
"records": [
    {
        "Id": "1",
        "Time": "2015-07-25 08:00:00",
        "Percentage": "60"
    },
    {
        "Id": "2",
        "Time": "2015-07-25 09:00:00",
        "Percentage": "70"
    },
    {
        "Id": "3",
        "Time": "2015-07-25 10:00:00",
        "Percentage": "80"
    }
    ]
}

关于工厂-控制器通信,我只是参考另一个帖子的解决方案:reference

【问题讨论】:

    标签: javascript json angularjs


    【解决方案1】:

    $http.get 返回一个承诺——您的问题是您立即返回“obj”,并且您的控制器尝试在 $http.get 承诺解决之前访问数据。

    像这样使用 $http (不需要使用 $q.defer() ,如另一条评论所示):

    var app = angular.module("app", []);      
    
    app.factory('mainInfo', function($http) { 
        var getRecordsPromise = $http.get('http://localhost/test.php').then(function(response){ 
            //whatever is returned here will be accessible in the .then that is chained
            return response.data.records;
        });    
    
        return {
            getRecords: getRecordsPromise
        };    
    });
    
    
    app.controller('Ctrl', function($scope, mainInfo){
        mainInfo.getRecords.then(function (records) {
            $scope.foo = records;
        });
    }) ;
    

    【讨论】:

    • 其实这根本不是问题。 OP 正试图在他们的工厂内维护数据。你会注意到他们说如果使用$scope.foo = mainInfo 效果很好。问题是由于 JavaScript 引用引起的
    • $scope.foo = mainInfo 将作用域对象设置为工厂返回的相同对象——当你这样做时,一旦 mainInfo.content(或$scope.foo.content,相同的参考)已设置。但是,如果您不链接到 $http.get 承诺,这不会改变控制器在承诺解决之前设置值。如果 Factory 返回原始值而不是对象,如果不使用 Promise,您将面临同样的问题
    • 如果您检查 OP 引用的帖子,它提供了相同的两个选项——返回一个其数据将在稍后设置的对象(如在 OP 中),或返回 $http 承诺本身。此外,问题特别是“在控制器中访问这些数据的正确方法是什么?”唯一的答案是使用promise——让工厂返回一个对象,该对象的数据将在以后设置,这对于模板目的来说是很好的,但要在控制器中对你应该链接到$http.get promise的数据执行代码
    • 你说得对,在这里不恰当地链接成功是正确的,尽管它是可链接的。它返回原始承诺而不是新承诺,因此链接的“.then”将获得原始响应,而不是 .success 返回的值——我忽略了这一点,并进行了适当的编辑。
    • 请注意,工厂内部的数据已经持久化,并且可以通过 mainInfo.getRecords 访问,这将是将来被其他模块访问时的已解决承诺
    【解决方案2】:

    试试这个

    var obj = {content: []};
    obj.$promise = $http.get('http://localhost/test.php').then(function(response) {
        angular.copy(response.data.records, obj.content);
        return response.data;
    });
    return obj;
    

    您之前的方法不起作用的原因是因为您重新分配了objcontent 属性,从而破坏了之前的所有引用。

    这也是使用$scope.foo = mainInfo 有效的原因,因为对obj 的引用(通过mainInfo)被保留了。

    使用angular.copy 在填充数据的同时维护先前的引用。


    这应该注意模板中的引用,这些引用会在 $http 承诺解决时更新(由于 $http 触发 digest 循环)。要访问控制器中的数据,请使用 $promise 属性

    $scope.foo = mainInfo.content;
    mainInfo.$promise.then(function(data) {
        // access data.records here
    });
    

    如果您与多个消费者(例如控制器)共享您的工厂,那么维护工厂内部的数据是一个好主意。他们可能都引用了mainInfo.contentmainInfo.$promise,并且都将访问同一个数据集。

    【讨论】:

      【解决方案3】:

      试试这个:

      var app = angular.module("app", []);      
      
      app.factory('mainInfo', function($http) { 
          return {
              getData: function() {
                  var deferred = $q.defer();
      
      
                  //the php will return json data below
                  $http.get('http://localhost/test.php').success(function(response){ 
                      deferred.resolve(response.records);});
      
      
      
                  return deferred.promise;
              }
          }
      });
      
      
      app.controller('Ctrl', function($scope, mainInfo){
          mainInfo.getData().then(function(result) {
              $scope.foo = result.content;
          });
      }) ;
      

      【讨论】:

      • 啊,explicit promise construction anti-pattern 又一次抬起了丑陋的脑袋
      • 我同意在这种情况下,我应该将 $http 承诺链接起来......我的错误。但是,我倾向于发现这种模式在许多情况下很有用,在这些情况下,我更喜欢对服务中的数据进行错误处理或处理,而不是使用服务的每个单独的控制器。在我看来,它作为反模式的原因似乎并没有超过它在这些情况下提供的 DRY 能力。
      • @GPicazo 这就是承诺链如此有效的原因。你可以在你的服务中处理错误、转换、进一步的请求等,最后只返回一个承诺。当你已经有一个承诺时,你不必使用延迟对象
      • @GPicazo 您可以在链接的 .then 中返回以处理数据/处理错误。例如,在您的帖子中,只需将 deferred.resolve(response.records); 替换为 return response.records;
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 2016-11-15
      相关资源
      最近更新 更多