繁体   English   中英

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

[英]Async nature of AngularJS with digest/apply

我经常遇到一个问题,我在父控制器中设置了一个范围变量,子控制器调用了这个范围变量。 但是,它在函数能够设置范围元素之前调用它,导致它返回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加载元素来解决这个问题呢?

在这种情况下,正确的方法是使用专用服务来处理异步操作,请求,数据缓存等。但由于您还没有服务层,我将使用控制器范围承诺对象提出简单的基于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 ,可在子控制器中访问。

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

通常,您将使用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);
});

你可以使用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');
})

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

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

我建议其他人使用promises来推迟执行。

暂无
暂无

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

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