Angularjs:在服务中加载数据,然后将其加载到控制器

Angularjs: load data in service then load it to controllers

本文关键字:加载 然后 控制器 数据 服务 Angularjs      更新时间:2023-09-26

我已经在网上检查了几个解决方案,但我不太明白如何阻止控制器加载。所以,我创建了一个 plunkr 来突出我想做的事情。

基本上:我想加载服务中的所有数据,然后将该数据从该服务传递到每个控制器。首次加载应用时,由于它是异步的,因此首先加载控制器。

我本可以在每个控制器中使用工厂,但我想将数据保存在服务的"allProducts"属性中。我认为不需要在每次加载视图时调用工厂函数。

在示例中,我也尝试了 $q 服务,但在我看来,它具有与工厂中的 http 相同的行为,并且仍然需要在每次视图加载时调用 http 请求......

那么,有人可以帮助我完成此示例并实现一个优雅的解决方案吗?

app.factory('productsFactory', ['$http', '$q',
    function($http, $q) {
        var cachedData; //here we hold the data after the first api call  
        function getData(callback) {
            if (cachedData) {
                callback(cachedData);
            } else {
                $http.get('http://api.bestbuy.com/v1/products(longDescription=iPhone*|sku=7619002)?show=sku,name&pageSize=15&page=5&apiKey=bqs7a4gwmnuj9tq6bmyysndv&format=json')
                    .success(function(data) {
                        cachedData = data; //caching the data in a local variable
                        callback(data);
                    });
            }
        }
        return {
            getProds: getData
        }
    }
])
app.service('appService', ['productsFactory', '$q',
    function(productsFactory, $q) {
        var _this = this;
        productsFactory.getProds(function(data) {
            _this.allProducts = data; //wait for data to load before loading the controllers
        })
    }
])
app.controller('productsCtrl', ['$scope', 'appService',
    function($scope, appService) {
        $scope.myProducts = appService.allProducts;
    }
]);

在这里:http://plnkr.co/edit/ZvtYwXHSasC3fCAZKkDF?p=preview

我实际上并没有测试它,但看起来你需要创建并返回一个承诺,以便在数据可用时返回数据。

    app.factory('productsFactory', ['$http', '$q',
    function($http, $q) {
        var cachedData; //here we hold the data after the first api call  
        function getData(callback) {
var d = $q.defer();
            if (cachedData) {
d.resolve(callback(cachedData));
            } else {
                $http.get('http://api.bestbuy.com/v1/products(longDescription=iPhone*|sku=7619002)?show=sku,name&pageSize=15&page=5&apiKey=bqs7a4gwmnuj9tq6bmyysndv&format=json')
                    .success(function(data) {
                        cachedData = data; //caching the data in a local variable
                        d.resolve(callback(cachedData));
                    });
            }
return d.promise;
        }
        return {
            getProds: getData
        }
    }
])
app.service('appService', ['productsFactory', '$q',
    function(productsFactory, $q) {
        var _this = this;
        productsFactory.getProds(function(data) {
            _this.allProducts = data; //wait for data to load before loading the controllers
        })
    }
])
app.controller('productsCtrl', ['$scope', 'appService',
    function($scope, appService) {
        $scope.myProducts = appService.allProducts;
    }
]);

检查这个: http://plnkr.co/edit/ey0na3l2lyT0tUdbDyrf?p=preview

变化:
- 一个主应用程序控制器,用于包装您的所有应用程序。
在此控制器中,如果启动尚未完成其所有作业,我们将防止路由更改。启动完成后,我们将位置更改为主/默认路由。
- 在我们的run块中,我们将bootStatus VaR 设置为 false 并等待产品获取。

此外,我已将服务的结果存储到$rootScope以便您可以在所有控制器中使用该数据,而无需一遍又一遍地注入服务。

最后!!

我设法做到了我想要的。

请注意,这不一定是最佳实践,但这是一个困扰我并想知道它是如何完成的问题。

这样做的方法是创建一个全局变量,在其中我使用一个主要的解析函数来等待工厂执行 http get,然后将其传递给服务。然后,我在需要该数据的每个状态上使用 resolve 并引用该函数。

更新:意识到每次状态为changins时我都会调用相同的工厂函数,所以我决定使用一个变量 - appService中的一个属性,当http get被调用一次时,它变为true:appService.retrieved并稍微更改了主要的解析函数。

var mainResolve = ['$q', 'appService', 'productsFactory', function($q, appService, productsFactory) {
var defer = $q.defer();
if(appService.retrieved) {
    defer.resolve();
} else {
  productsFactory.getProds(function(data) {
      appService.allProducts = data;
      defer.resolve();
  })
  appService.retrieved = true;
}
return defer.promise;
}]

而在州

.state('home', {
  url: "/home",
  templateUrl: "home.html",
  controller: 'homeCtrl',
  resolve: {
    waitingFor: mainResolve
  }
})

您可以在此处找到带有工作解决方案的 plnkr:http://plnkr.co/edit/ZvtYwXHSasC3fCAZKkDF?p=preview

同样,工厂可以再重构一些,并消除一些代码,如缓存数据。这样,我们只去一次工厂功能。