具有摘要/应用的AngularJS的异步性质

时间:2015-04-20 20:40:06

标签: javascript php angularjs asynchronous

我经常遇到问题,我在父控制器中设置了范围变量,子控制器调用此范围变量。但是,它在函数能够设置范围元素之前调用它,导致它返回undefined。例如:

家长控制器:

module.controller('parent', '$scope', '$http', function ($scope, $http) {
$scope.init = function(profileID, profileViewStatus) {
    //Initiiaze user properities
    $http.get(requestUserInformationGetURL + profileID)
        .success(function(profile) {
            $scope.profile = profile;
            $scope.userID = profile.user_id;
            $scope.username = profile.username; 
            console.log($scope.userID);
        })
        .error(function() {
            exit();
        });
}

儿童控制器:

module.controller('child', function($scope, $http, fetchInfo) {

console.log($scope.userID);

//Fetch the HTTP POST data for the user profile
var promise = $http({
    method: "post",
    url: fetchInfo,
    data: {
        user_id: $scope.userID //From the parent controller
    },
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
promise.then(function(successResponse) {
    //Populate the scope, log the data
    console.log(successResponse);
    $scope.data = successResponse.data;
}, function(error) {
    alert(error);
});

HTML:

<div ng-controller="parent" init="init('<?php $user_id;?>')">
     <div ng-controller="child">
     </div>
 </div>

经常发生的情况是,userID将在子控制器中报告为未定义,但之后,它将按照父控制器中的定义报告回来。显然,在父控制器中的init函数完成之前,正在调用使用$ scope.userID的子控制器。在init函数完成之前,如何强制AngularJS在子控制器中等待?我尝试过类似的事情:

if (!$scope.userID) {
   $scope.$digest();
}

但它没有用,我不认为它是正确的语法。我想,我不完全理解AngularJS的Asycn性质,这种情况多次发生。你如何控制DOM加载元素来解决这个问题呢?

4 个答案:

答案 0 :(得分:1)

在这种情况下,正确的方法是使用专用服务来处理异步操作,请求,数据缓存等。但由于您还没有服务层,我将使用控制器范围提出简单的基于Promise的解决方案承诺对象。

检查修改后的代码:

module.controller('parent', ['$scope', '$http', function ($scope, $http) {
    $scope.init = function (profileID, profileViewStatus) {
        $scope.profilePromise = $http.get(requestUserInformationGetURL + profileID).success(function (profile) {
            $scope.profile = profile;
            $scope.userID = profile.user_id;
            $scope.username = profile.username;
        })
        .error(exit);
    }
}]);

module.controller('child', function($scope, $http, fetchInfo) {

    // Fetch the HTTP POST data for the user profile
    $scope.profilePromise.then(function() {
        return $http({
            method: "post",
            url: fetchInfo,
            data: { user_id: $scope.userID },
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
        });
    })
    .then(function(successResponse) {
        console.log(successResponse);
        $scope.data = successResponse.data;
    }, function(error) {
        alert(error);
    });
});

如您所见,仍然调用父控制器init方法,但现在它立即设置范围属性profilePromise,可以在子控制器中访问。

子控制器使用父控制器then对象的profilePromise方法,该方法保证在配置文件可用后,$http使用$scope.userID请求将会触发。

答案 1 :(得分:1)

通常,您将使用UI路由器解析路由,以确保在构建任一控制器之前完成工作。子状态自动可以访问其父级的结果。

 //Router configuration
.state('app.inspections.list', {
    url: '',
    templateUrl: 'Template/parent',
    controller: "Parent as vm",
    resolve: {
        profile: ['$http', function ($http) {
            return $http.get(requestUserInformationGetURL + profileID)
                .success(function(profile) {                    
                    console.log(profile.userID);
                    return profile;
                })
                .error(function() {
                    exit();
                });
        }]
    }
}).state('parent.child', {
    url: 'child',
    templateUrl: 'Template/child',
    controller: "Child as vm"
})

   //parent controller
    module.controller('parent', '$scope', 'profile', function ($scope, profile){
        $scope.profile = profile;
        $scope.userID = profile.user_id;
        $scope.username = profile.username; 
    }

//child controller
module.controller('child', 'profile', function($scope, $http, fetchInfo, profile){

console.log(profile.userID);

//Fetch the HTTP POST data for the user profile
var promise = $http({
    method: "post",
    url: fetchInfo,
    data: {
        user_id: profile.userID //From the parent controller
    },
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
promise.then(function(successResponse) {
    //Populate the scope, log the data
    console.log(successResponse);
    $scope.data = successResponse.data;
}, function(error) {
    alert(error);
});

答案 2 :(得分:1)

你可以使用promise($ q service):尝试使用这段代码:

父控制器:

$scope.init = function(profileID, profileViewStatus) {
    var deferred = $q.defer();
    $http.get(requestUserInformationGetURL + profileID)
        .success(function(profile) {
            $scope.profile = profile;
            $scope.userID = profile.user_id;
            $scope.username = profile.username; 
            deferred.resolve($scope.userID);
            console.log($scope.userID);
        })
        .error(function() {
            deferred.reject('error');
            exit();
        });
       return deferred.promise;
}

不要在父控制器中调用init方法。

子控制器中的

$scope.init().then(function(userID){
    var promise = $http({
    method: "post",
    url: fetchInfo,
    data: {
        user_id: userID //From the parent controller
    },
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
   });
   promise.then(function(successResponse) {
    //Populate the scope, log the data
    console.log(successResponse);
    $scope.data = successResponse.data;
   }, function(error) {
    alert(error);
  });
})
.catch(function(error){
 console.log('error');
})

答案 3 :(得分:0)

您的问题可能是异步调用$.get,这是默认行为。您的init方法实际上可能按照您预期的顺序进行调用,但发生的是:

  • init被称为
  • $.get被调用,但服务器的响应是非瞬时的
  • 名为init的儿童
  • GET数据从服务器反弹
  • $.get(..).success(function(data){...});被调用来处理数据

我建议其他人使用承诺推迟执行。