AngularJs $scope doesn't update after a GET re

2020-02-26 07:58发布

问题:

I have been trying AngularJS for a experimental project and I came along with this problem. In my html I want to display a list of items

Index.html

<h1>Some list</h1>
<div ng-controller="datlist">
    <div ng-repeat="item in items">
        <div>Item description: {{item.description}}</div>
        <div>Item name: {{item.name}}</div>
    </div>
</div>

At first I was using a simple controller to get the information and update the view just using this:

controllers.js (original)

function datlist($scope,$http){
$http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}}).
    success(function(data, status, headers, config) {
        $scope.items=data.itemsToReturn;
        console.log(data);
}).
error(function(data, status, headers, config) {
    console.log("fail");
});

}

This was working pretty well and I could get the list of items. Whilst, by changing my structure to use a factory to make the same request and bind it to $scope.items it doesn't work. I tried a lot of variations of $watch but I couldn't get it to update $scope.items. I found something about $apply but I really can't understand how to use it.

controllers.js (new one)

var datModule = angular.module('datModule',[]);
datModule.controller('datlist', function ($scope, datfactory){
    $scope.items = datfactory.getlist();
    $scope.$watch($scope.items, $scope.items = datfactory.getlist());
});
datModule.factory('datfactory', function ($http){
    var factory = {};
    factory.getlist = function(){
        $http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}}).
        success(function(data, status, headers, config) {
            console.log(data.itemsToReturn); //I get the correct items, all seems ok here
            return data.itemsToReturn;
        }).
        error(function(data, status, headers, config) {
            console.log("fail");
        });

    }
    return factory;
});

Any ideas about this will be great. PS: I found a lot of posts talking about this issue but none of them helped me to get a full solution.

Thanks

回答1:

Using a watch for that is kinda ugly.

try this:

datModule.factory('datfactory', function ($http, $q){

    this.getlist = function(){            
        return $http.get('http://localhost:61686/getdatlist?format=json',{'Access-Control-Allow-Origin': 'localhost:*'})
            .then(function(response) {
              console.log(response); //I get the correct items, all seems ok here
              return response.data.itemsToReturn;
            });            
    }
    return this;
});

datModule.controller('datlist', function ($scope, datfactory){
    datfactory.getlist()
      .then(function(arrItems){
         $scope.items = arrItems;
       });
});

This is how you use promises for async matter.

UPDATE (15.01.2015): Now even sleeker!



回答2:

The issue is nothing to do with the scope digest cycle. You are trying to return from inside a callback directly, which is not asynchronously possible.

I recommend you either use a promise, or return the http promise directly.

var factory = {};
factory.getlist = function(){
    return $http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}});

}
return factory;

To return the promise directly, and handle the success/fail at factory.getlist().success()

Alternatively, use your own promise if you want to wrap additional logic around the request.

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

datModule.controller('datlist', function ($scope, datfactory){
    $scope.items = [];
    datfactory.getlist().then(function(data) { $scope.items = data });
});

datModule.factory('datfactory', function ($http, $q){
    var factory = {};
    factory.getlist = function(){
        var defer = $q.defer();
        $http({method: 'GET', url: 'http://localhost:61686/getdatlist?format=json', headers: {'Access-Control-Allow-Origin': 'localhost:*'}}).
        success(function(data) {
            // alter data if needed
            defer.resolve(data.itemsToReturn);
        }).
        error(function(data, status, headers, config) {
            defer.reject();
        });
        return defer.promise;
    }
    return factory;
});


回答3:

try to initialize $scope.items = []; at controller, before call $http

I hope it helps you.



回答4:

Well it looks perfect but you can use $apply like this.

datModule.controller('datlist', function ($scope, datfactory){
    $scope.$apply(function() {
        $scope.items = datfactory.getlist();
    });
});


回答5:

I think another elegant solution to this problem could be - if you are using one of the routing libraries, in my case it is the UI-Router, but could be also ngRoute, is making your controller dependent on the response of the promise, eg. adding a resolve property to the adequate state/route which doesn't let the controller load until the promise is solved and the data is ready, so in your config:

.state('datpage', {
      url: '/datpage',
      controller: 'DatpageController',
      resolve:{
        datData: function (datfactory) {
            return datDataService.getData("datDataParam");
        }]
      },
      templateUrl: 'views/datpage.html'
    })

And inject the datData dependency in your controller, where you can apply it directly to the $scope:

.controller('DatpageController', function ($scope,datData) {
$scope.datPageData = datData; ...