如何在AngularJS中异步填充$ scope变量?

时间:2015-05-12 15:13:28

标签: javascript angularjs asynchronous angularjs-scope angularjs-service

我有以下服务:

app.service('Library', ['$http', function($http) {

    this.fonts = [];
    this.families = [];

    // ... some common CRUD functions here ...

    // Returns the font list
    this.getFonts = function() {
        if(_.isEmpty(this.fonts)) this.updateFonts();
        return this.fonts;
    };

    // Returns the family list
    this.getFamilies = function() {
        if(_.isEmpty(this.families)) this.updateFamilies();
        return this.families;
    };

    // Update the font list
    this.updateFonts = function() {
        var self = this;
        $http.get(BACKEND_URL+'/fonts').success(function(data) {
            self.fonts = data;
            console.log('Library:: fonts updated', self.fonts);
        });
    };

    // Update the family
    this.updateFamilies = function() {
        var self = this;
        $http.get(BACKEND_URL+'/families').success(function(data) {
            var sorted = _.sortBy(data, function(item) { return item });
            self.families = sorted;
            console.log('Library:: families updated', self.families);
        });
    };
}]);

以下主控制器代码:

app.controller('MainController', ['$scope', '$state', 'Cart', 'Library', function($scope, $state, Cart, Library) {
    console.log('-> MainController');

    // Serve the right font list depending on the page
    $scope.fonts = $state.is('home.cart') ? Cart.getFonts() : Library.getFonts();
    $scope.families = Library.getFamilies();

}]);

问题是,当视图请求$scope.fonts的内容时,它仍然是空的。

加载结束后如何更新$scope.fonts$scope.families

我可以使用$scope.$watch,但我确信有更简洁的方法可以做到这一点......

7 个答案:

答案 0 :(得分:2)

这就是promises的真正含义。您的服务应返回要解决的承诺。您还可以简化服务:

app.service('Library', ['$http', '$q', function($http, $q) {
    var self = this;

    self.families = [];

    // Returns the family list
    self.getFamilies = function() {
        var deferred = $q.defer();

        if(_.isEmpty(self.families)) {
            $http.get(BACKEND_URL+'/families').success(function(data) {
                var sorted = _.sortBy(data, function(item) { return item });
                self.families = sorted;
                deferred.resolve(self.families);
                console.log('Library:: families updated', self.families);
            });
        } else {
            deferred.resolve(self.families);
        }

        return deferred.promise;
    };
}]);

然后在您的控制器中,使用promises then方法:

app.controller('MainController', ['$scope', '$state', 'Cart', 'Library', function($scope, $state, Cart, Library) {
    console.log('-> MainController');

    // Serve the right font list depending on the page
    $scope.fonts = $state.is('home.cart') ? Cart.getFonts() : Library.getFonts();
    Library.getFamilies().then(function(result) {
        $scope.families = result;
    });
}]);

由于$ http,这是未经测试的,但这是使用$ timeout的演示:

JSFiddle

答案 1 :(得分:1)

考虑传递一个回调函数。

服务:

this.getFonts = function(callback) {
    if(_.isEmpty(this.fonts)) this.updateFonts(callback);
    return this.fonts;
};

this.updateFonts = function(callback) {
    var self = this;
    $http.get(BACKEND_URL+'/fonts').success(function(data) {
        self.fonts = data;
        console.log('Library:: fonts updated', self.fonts);
        callback(data);
    });
};

控制器:

Library.getFonts(function (data) { $scope.fonts = data; });

这可以稍微整理一下,因为回调消除了对某些代码的需要,但它将作为一个例子。

答案 2 :(得分:1)

感谢所有答案!我最终使用了回调和承诺的混合,如下所示:

app.service('Library', function($http) {

    // Returns the font list
    this.getFonts = function(callback) {
        if(_.isEmpty(self.fonts)) return self.updateFonts(callback);
        else return callback(self.fonts);
    };

    // Update the font list
    this.updateFonts = function(callback) {
        return $http.get(BACKEND_URL+'/fonts').success(function(data) {
            self.fonts = data;
            callback(data);
        });
    };
});

而且,在控制器中:

app.controller('MainController', function(Library) {
    Library.getFonts(function(fonts) { $scope.fonts = fonts });
});

我尝试了你的所有建议,但这是我用其余代码处理的最佳建议。

答案 3 :(得分:0)

this.getFonts函数(以及其他函数)中,从this调用数据,该数据指向函数而不是所需的控制器范围。请尝试以下方法:

var self = this;

self.fonts = [];
self.families = [];

// ... some common CRUD functions here ...

// Returns the font list
self.getFonts = function() {
    if(_.isEmpty(self.fonts)) self.updateFonts();
    return self.fonts; // <-- self.fonts will point to the fonts you want
};

答案 4 :(得分:0)

我会尝试在

中包装你正在调用的getScope和getFonts体
$scope.$apply(function(){ ...body here... });

答案 5 :(得分:0)

确保在任何功能之外声明var self = this; self.data = []; this.updateFonts = function() { self.fonts = $http.get(BACKEND_URL+'/fonts').success(function(data) { return data.data }); return self.fonts };

将调用分配给要存储数据的值,然后将其返回。

beforeEach(module('myServiceApp'));

beforeEach(inject(function(_CommonService_) {
  CommoService = _CommonService_;
}));

答案 6 :(得分:0)

因为您正在使用ui-router(我看到了$ state)。您可以在州内使用解决方案并返回承诺。

Doc:https://github.com/angular-ui/ui-router/wiki

例如:

$stateProvider.state('myState', {
      resolve:{
         // Example using function with returned promise.
         // This is the typical use case of resolve.
         // You need to inject any services that you are
         // using, e.g. $http in this example
         promiseObj:  function($http){
            // $http returns a promise for the url data
            return $http({method: 'GET', url: '/someUrl'});
         }
      },   
       controller: function($scope,promiseObj){
          // You can be sure that promiseObj is ready to use!
          $scope.items = promiseObj.data;
      }
}

在您的情况下,您需要将this.getFonts和getFamilies转换为承诺

this.getFonts = function(){
  return $http.get(BACKEND_URL+'/fonts').success(function(data) {
            self.fonts = data;
            console.log('Library:: fonts updated', self.fonts);
        });
}

有很多方法可以做到这一点,但在我看来,解决方法是最好的。