使用$ http时加载AngularJS

时间:2015-03-12 10:45:56

标签: angularjs

当我从API中提取数据时,我想显示一个加载gif。 在某些情况下,我连接到同一视图上的多个资源,并且我不想使用$ http拦截器,所以我创建了这样的视图:

.controller('ViewOrderController', ['$routeParams', 'OrderService', 'AccountService', function (params, orderService, accountService) {
    var self = this;

    self.orderNumber = params.orderNumber;
    self.loadingAccounts = true;
    self.loadingOrders = true;
    self.order = {};
    self.account = {};

    orderService.get(self.orderNumber, false).then(function (response) {
        self.order = response.data;
        self.loadingOrders = false;

        accountService.get(self.order.accountNumber).then(function (response) {
            self.account = response.data;
            self.loadingOrders = false;
        });
    });
}])

但很快就会变得混乱,因为在某些情况下我会连接到4或5个不同的资源。 我的第一反应是将每个调用分成它自己的控制器,所以看起来有点像这样:

.controller('OrderController', ['$routeParams', 'OrderService', 'AccountService', function (params, orderService, accountService) {
    var self = this;

    self.loading = true;
    self.orderNumber = params.orderNumber;
    self.order = {};

    orderService.get(self.orderNumber, false).then(function (response) {
        self.order = response.data;
        accountService.accountNumber = self.order.accountNumber; // Bind the account number to the service for use later
        self.loading = false;
    });
}])

.controller('AccountController', ['$scope', 'AccountService', function (scope, service) {
    var self = this;

    self.loading = true;
    self.account = {};

    scope.$watch(function () {
        return service.accountNumber;
    }, function (accountNumber) {
        if (accountNumber) {
            service.get(accountNumber).then(function (response) {
                self.account = response.data;
                self.loading = false;
            });
        }
    });
}])

然后我开始遇到重复问题:

.controller('RecentOrdersController', ['OrderService', function (service) {
    var self = this; // store our controller in a variable

    self.loading = true; // This is what the ajax loading gif looks at to see if it should be displayed
    self.orders = []; // Our orders array

    service.recent(30).then(function (response) { // Get the 30 most recent orders
        for (var i = 0; i < response.data.length; i++) {
            var order = response.data[i]; // Store the order in a variable (for use later)
            var desciption = service.getDescription(order); // Get our description

            order.description = desciption; // Set our order description
            self.orders.push(order); // Push our order to our array
        }

        self.loading = false; // Set our loading flag to false (hide the ajax loading gif);
    });
}])

.controller('SyncFailureOrdersController', ['OrderService', function (service) {
    var self = this; // store our controller in a variable

    self.loading = true; // This is what the ajax loading gif looks at to see if it should be displayed
    self.orders = []; // Our orders array

    service.syncFailures(30).then(function (response) { // Get the 30 most sync failures
        for (var i = 0; i < response.data.length; i++) {
            var order = response.data[i]; // Store the order in a variable (for use later)
            var desciption = service.getDescription(order); // Get our description

            order.description = desciption; // Set our order description
            self.orders.push(order); // Push our order to our array
        }

        self.loading = false; // Set our loading flag to false (hide the ajax loading gif);
    });
}])

我的上一篇文章,有人说要将我的共享代码放入服务中,这就是我想要做的。 我想创建一个服务,告诉控制器何时加载或者每次调用完成时都会告诉控制器。我不知道如何处理这个问题。另外,我看过(经过几个小时的搜索)人们使用$ resource;这有什么需要吗?它能让工作更容易吗?

非常感谢任何帮助。

更新1

感谢@tommes我已经更新了他的小提琴,以准确显示我的目标:

https://jsfiddle.net/2mhq9crf/3/

希望有所帮助

1 个答案:

答案 0 :(得分:1)

在angularjs中组合异步操作的一般方法是承诺 $ q

你应该把你的api电话放在服务中(我的个人服务偏好是.factory(...)

见这个例子:

https://jsfiddle.net/2mhq9crf/

JS

var myApp = angular.module('myApp',[]);

myApp.factory('myService', function($timeout, $q) {
    // Stub of $http.get(...)
    return function () {
        var d = $q.defer();
        $timeout(function () {
            d.resolve("val!");
        }, 1000);
        return d.promise;
    };
});

myApp.factory('myOtherService', function($timeout, $q) {
    // Stub of $http.get(...)
    return function () {
        var d = $q.defer();
        $timeout(function () {
            d.resolve("val!");
        }, 500);
        return d.promise;
    };
});


myApp.controller("MyCtrl", function ($scope, myService, myOtherService) {
    $scope.loading = [];
    $scope.vals = [];

    // Add loading-specific promise-handlers
    var loadThisPromise = function (p) {
        // Add the promise to our list of loading promises
        $scope.loading.push(p);

        p.finally(function () {
            // .finally is called even if the request fails

            // Remove the promise from our list of loading
            // promises, using object identity
            $scope.loading.splice($scope.loading.indexOf(p), 1);
        });

        // Return promise for further promise handling
        return p;
    };

    // Produce a function that always logs the given text
    var logThis = function (str) {
        return function (val) {
            console.log(str);

            // Remember to forward value for promise chaining
            return val;
        };
    };

    // Promise handler for pushing a value to the scope
    var pushVal = function (val) {
        $scope.vals.push(val);
        return val;
    };

    // Add loading-specific promise-handlers and 
    // work with the promise from there
    loadThisPromise(myService())
        .then(pushVal)
        .then(logThis("myService loaded!"));
    loadThisPromise(myOtherService())
        .then(pushVal)
        .then(logThis("myOtherService loaded!"));
    $scope.name = 'Superhero';
});

HTML

<body ng-app="MyApp">
    <div ng-controller="MyCtrl">
        <h2>Hello, {{name}}!</h2>
        <tt style="color: red;" ng-show="loading.length > 0">LOADING...</tt>
        <pre ng-bind="vals | json"></pre>
    </div>
</body>

更新1

如果同一页面上有多个列表,包含单独的列表和加载状态,我会重用这样的控制器函数:

https://jsfiddle.net/2mhq9crf/4/

app.controller("AccountCtrl", ["OrderService", function (OrderService) {
    ControllerFun.call(this, OrderService);
}]);

如果您希望在父控制器中具有组合加载状态,则应通过服务共享组合加载状态。

希望它有所帮助: - )

更新2

这是一个数据服务管理加载状态的解决方案,以及通过就地更新数据来响应数据: https://jsfiddle.net/2mhq9crf/8/

这或多或少是$resource的工作方式。