Angular HTTP 返回 $$state 对象

17

我有以下工厂定义:

angular.module("account").factory("users",["$http",
    function(a){
      return {
         getUser: function(){
            return a.get("/user/me").then(function(r){
                return r.data;
            });
        }
    };
  }
]);

我的控制器:

angular.module("test.controllers",["account"])
.controller("TestCtrl",["$scope","users",
    function(a,u){
        a.user = u.getUser();
        console.log(a.user);
}]);

这是console.log的输出:

 d {$$state: Object, then: function, catch: function, finally: function} $$state: Object status: 1 value: Object user: Object__v: 0 _id: "54c1fg29f36e117332000005" temp: "1ce3793175e0b2548fb9918385c2de09"  __proto__: Object __proto__: Object __proto__: Object __proto__: Object

上述代码返回的是状态对象而不是用户对象。但从日志中可以看到,状态对象中包含了用户对象。我该如何获取用户对象?还是说我完全弄错了?

我知道另一种方法是返回 $http.get 并在控制器内调用 then() 方法。但是我将频繁使用用户对象,如果在控制器中调用 then() 方法,几乎和在工厂中使用 $http.get 相同。


你在 console.log 中看到的对象是从调用 .getUser 中的 .get 返回的 promise。调用 .then 创建了一个新的 promise,当你从那里返回一些东西时,它会修改任何后续 then 的已解决数据。 - shaunhusain
请查看我在另一篇帖子中的答案:https://dev59.com/pI7da4cB1Zd3GeqP7gbv#38007172 - Barani r
3个回答

26

JavaScript I/O通常是异步的,包括这种情况。您的getUser调用返回一个$q promise。为了解开它,您必须链接到它,然后将其解开:

angular.module("test.controllers",["account"])
.controller("TestCtrl",["$scope","users",
    function(a,u){
        u.getUser().then(function(user){
            a.user = user;
            console.log(a.user);
        });
}]);

如果您正在使用路由,您可能还想查看路线中的resolve选项。此外,请参见这个相关问题


但如果我在控制器中使用then(),我可以摆脱工厂并只需进行$http.get('/user/me').then(..)调用。 - wdphd
4
@wdphd,你可以这样理解,将这些调用放在工厂中而不是控制器中的原因是为了让代码更加整洁,而不是因为有什么神奇的地方。任何你可以在工厂中编写的内容也同样可以在控制器中编写,只是这样做不能很好地实现关注点分离。 - Benjamin Gruenbaum

2
angular.module("account").factory("users",["$http",
    function(a){
      var factObj = {
         user: null,
         getUser: function(){
            return a.get("/user/me").then(function(r){
                factObj.user = r.data;
            });
        }
      factObj.getUser();

      return factObj;
    };
  }
]);

angular.module("test.controllers",["account"])
.controller("TestCtrl",["$scope","users",
    function(a,u){
        a.userService = u;
}]);

在你的观点中
<div ng-controller="TestCtrl as test">{{test.userService.user}}</div>

根据评论进行编辑,确实这种模式无法与控制器或视图一起使用,但是这种模式效果很好,并且消除了每个可能重复使用数据的控制器中都需要使用.then的需要。

通常将模型存储在处理获取数据的工厂或服务中,可以更轻松地将数据传递到需要它的每个视图中。缺点在于当引用此工厂时,您将获取用户,而不是从控制器显式触发getUser函数。如果要避免这种情况,可以在第一次请求getUser函数时存储承诺,并在已设置承诺的情况下为所有后续调用返回该承诺,这样可以多次调用getUser而不重复API请求。


这仍然会失败 - OP的问题在于get的异步性,而不是数据存储的位置。 - Benjamin Gruenbaum
Benjamin,我在许多工作应用程序中使用这种模式,只要您通过控制器中的工厂引用数据,它就可以正常工作。更详细的示例在此处http://plnkr.co/edit/cqyeXB5WgKZynQZdvEYP?p=preview - shaunhusain
你只需要阅读代码就能知道这不会起作用。a.get 返回一个符合 Promises/A+ 标准的 Promise - 它必须这样做才能将执行推迟到下一个循环中。你将调用 getUser,它将返回一个“无事发生”的 promise,然后调用 .user,它将返回 null。如果你遇到任何其他行为,请在 问题跟踪器 上报告错误。我所描述的行为已经被明确定义了。 - Benjamin Gruenbaum
应该可以工作 http://plnkr.co/edit/pVDWKb5HEtsTnnYo7ANH?p=preview 你对 $http 的调用仍然是异步的,所以如果你立即从工厂中尝试 console.log 值,它仍然会是未定义的,直到响应返回,但由于大多数时候你都在视图中绑定数据,无论如何观察者都会在 $http 调用的 $apply 运行时更新它。我还删除了注释,因为我只是在构建过程中使用 ngAnnotate 来完成这个操作。 - shaunhusain
@shaunhusain:这实际上在运行!是否有一种方法可以通过上述方法传递参数给getUser? - wdphd
显示剩余2条评论

0

我这里有一个类似的情况.. 希望能帮到某些人...

我的工厂..

 angular.module('programList.services',[])
    .factory('PROGRAMS', function($http) {
     return {
       getprogramList: function() {
       return $http.get('/api/programs/list').then(function(result) {
       return result.data[0];
       });
      }
     };
    });

这是我的控制器...

 angular.module('programList.services'])
 .controller('tviProgramsController',
    function($scope,$state,$stateParams,PROGRAMS){
        //this is the call to access the list as answered by Benjamin Above using then on success 
        PROGRAMS.getprogramList().then(function(list){
                  $scope.programlist = list;
              });
  });

;

这是在服务器端路由器上的内容...

  var express     = require('express'),
     programscontroller = require(path + 'programservercontroller'),
     router         = new express.Router();
     router.get('/api/programs/list', programscontroller.programlist);

这是用于 MySQL 调用的模块

var con   = require('../../database/dbcon'),
mysql = require('mysql');

module.exports.programlist = function(req, res){
 var data = req.params.data;
 var sql = "CALL `sp_programlist`";
  con.query(sql, function(err, result) {
      if(err){
          throw err;
      } else {
          res.send(JSON.stringify(result));
      }
     });
};

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