繁体   English   中英

角度异步调用

[英]Angular async calls

我的Angular应用程序中发生一个奇怪的问题,我认为Angular的异步性是原因,但我不确定如何解决。

.run我有以下内容:

app.run(function ($user, $soundcloud) {
    $user.set();
    $soundcloud.set();
});

然后是两项服务...

app.service('$user', ['$http',  function ($http) {    
    var currentUser;    
    this.set = function () {
        $http.get('/users/active/profile')
            .success(function (obj) {
                currentUser = obj;
            })
            .error(function () {
                console.log('ERR');
            })
    }    
    this.fetch = function () {
        return currentUser;
    }

}]);    
app.service('$soundcloud', ['$http', function ($http) {    
    var sc;    
    this.set = function () {
        $http.get('/users/active/services/soundcloud')
            .success(function (obj) {
                sc = obj;
            })
            .error(function () {
                console.log('Error fetching soundcloud feed')
            })
    }    
    this.fetch = function () {
        return sc;
    }    
}]);

我遇到的问题是,当我转到/ profile并重新加载页面时,从$ soundcloud.fetch()返回的数据不可用,除非我将$ soundcloud.fetch()包装在setTimeout()函数中,这显然是不是很理想,所以我想知道应该在哪里放置$soundcloud.set()以便在加载页面时立即使这些数据可用。

这是我目前的控制器:

app.controller('profileController', ['$scope', '$user', '$soundcloud', function ($scope, $user, $soundcloud) {

    $scope.activeUser = $user.fetch();
    $scope.activeSoundcloud = $soundcloud.fetch();

    console.log($user.fetch());
    setTimeout(function () {
        console.log($soundcloud.fetch());
    },100);
}]);

如果我将$soundcloud.fetch()置于setTimeout之外,那么结果将undefined 为了使两组数据(用户和soundcloud)立即可用,应该在哪里调用$ soundcloud.set / fetch?

朋克演示

解决此问题的一种方法是获取所有必需的数据,然后手动引导应用程序。

为此,请创建bootstrapper.js 您可以通过angular.injector(['ng']).invoke()来访问内置的角度服务,而无需引导模块,该方法接受具有可注入依赖性的函数。 urlMap变量只是用于设置及其每个相应URL的键值存储。 urlMap通过angular.forEach()循环此urlMap ,然后一个个地获取所有设置,并将每个设置及其每个键存储在settings变量中。 将每个$http promise请求存储在promises数组中,以使用$q.all()进行解析。 解决所有承诺后,调用bootstrap()函数,该函数会在app.settings模块中添加一个Settings值,然后手动引导该应用程序。

bootstrapper.js

angular.injector(['ng']).invoke(function($http, $q) {

  var urlMap = {
    $user: '/users/active/profile',
    $soundcloud: '/users/active/services/soundcloud'
  };

  var settings = {};

  var promises = [];

  var appConfig = angular.module('app.settings', []);

  angular.forEach(urlMap, function(url, key) {
    promises.push($http.get(url).success(function(data) {
      settings[key] = data;
    }));
  });

  $q.all(promises).then(function() {
    bootstrap(settings);
  }).catch(function() {
    bootstrap();
  });

  function bootstrap(settings) {
    appConfig.value('Settings', settings);

    angular.element(document).ready(function() {
      angular.bootstrap(document, ['app', 'app.settings']);
    });
  }

});

app.js

angular.module('app', [])

  .run(function(Settings) {
    if(Settings === null) {
      // go to login page or
      // a page that will display an
      // error if any of the requested
      // settings failed
    }
  })

  .controller('ProfileController', function($scope, Settings) {
    console.log(Settings);
  });

$ soundcloud.set()的结果将覆盖sc的值。

如果在返回$ soundcloud.set()之前尝试将$ soundcloud.fetch中的值绑定到作用域,则由于未定义var sc,因此它将未定义绑定到作用域。 当$ soundcloud.set()设置sc字段时,scope变量将永远不会得到通知,因为它仅获得未定义的值,而对sc一无所知。

最简单的解决方案是创建一个永远不会删除的包装对象。 这样的事情。

app.service('$soundcloud', ['$http', function ($http) {    
var sc = {};    // define an object instead of undefined
this.set = function () {
    $http.get('/users/active/services/soundcloud')
        .success(function (obj) {
            sc.soundcloud = obj; // write to a field on the object, 
                                 // do not override sc, since sc is already returned
        })
        .error(function () {
            console.log('Error fetching soundcloud feed')
        })
}    
this.fetch = function () {
    return sc;
}    
}]);

使用$ soundcloud.fetch()的结果时,需要引用soundcloud字段。 至少在调用$ soundcloud.set时,它将始终得到更新

您认为这是异步问题是对的。

  1. .run()的代码触发一个异步.get()
  2. 在结果返回并传递给.success()的回调运行之前,请运行fetch() 这样, fetch()返回一个未定义的sc
  3. 然后,您在console.log()看到,因此未定义。
  4. 使用您的.get()的数据解析承诺,然后将sc分配给结果。

@Andre Paap刚才概述的方法是一种很好的方法-本质上是通过创建一个空白引用对象并在回调上更新其属性之一(而不是完全覆盖该引用),您仍然可以将此方法分配给作用域,填充视图后(使用数据绑定)“神奇地”更新视图。 但是 ,如果立即console.log()结果,您仍然会看到相同的内容。

换句话说,如果您的服务中包含以下内容:

var currentUser = {};    
this.set = function () {
    return $http.get('/users/active/profile')
        .success(function (obj) {
            currentUser.name = obj.name;
        })
        .error(function () {
            console.log('ERR');
        })
}    

这在您的模板中:

<span ng-show="currentUser.name"> Hello {{currentUser.name}}! </span>

然后将其放在您的控制器中将导致上述范围在set()调用返回后正确显示:

$scope.currentUser =  $user.fetch()
// NOTE: the below would still log undefined:
console.log($scope.currentUser.name) //<- undefined.

如果您只想在返回.set()调用后才运行某些代码,则可以利用$http方法返回promise的事实,例如:

$user.set().then(function(){
  // This code runs *after* the call has returned.
  $scope.currentUser = $user.fetch()
  console.log($scope.currentUser.name) //<- "Ed"
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM