can't simply use ES6 promise to update data in ng-repeat?

六眼飞鱼酱① 提交于 2019-12-08 10:36:10

问题


I'm trying to build a drill-down list with angular and es6 promise. Without using promise my code works as demoed in the snippet below. Every time you click the parent, it expands the children (just foo and bar in the sample for simplicity).

angular.module('demo', [])
  .controller('DemoController', ['$scope', 'dataService', function($scope, dataSvc) {
    $scope.entities = dataSvc.loadInitialData();
  }])
  .directive('drillDown', ['$compile', 'dataService', function($compile, dataSvc) {
    return {
      restrict: 'A',
      scope: {
        entities: '='
      },
      controller: function($scope) {
        $scope.load = function() {
          this.entity.subEntities = dataSvc.load();
        };
      },
      compile: function(element) {
        var contents = element.contents().remove();
        var compiled = null;

        return function(scope, element) {
          if (!compiled) {
            compiled = $compile(contents);
          }

          compiled(scope, function(clone) {
            element.append(clone);
          });
        };
      },
      template:
        '<li ng-repeat="entity in entities">' +
          '<a href="#" ng-click="load()"><span ng-bind="entity.name"></span></a>' +
          '<ul drill-down entities="entity.subEntities"></ul>' +
        '</li>'
    };
  }])
  .service('dataService', function() {
    this.loadInitialData = function() {
      return [
        {
          name: 'foo',
          subEntities: []
        },
        {
          name: 'bar',
          subEntities: []
        }
      ];
    };
    this.load = function() {
      return this.loadInitialData();
    };
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js"></script>
<div ng-app="demo" ng-controller="DemoController">
  <ul drill-down entities="entities"></ul>
</div>

However when I change it to using promise, something goes wrong: now you'd have to double click the element to expand it and the scopes are also messed up.

The difference is essentially just in the load function in the service and the directive controller. So far I haven't really looked into angular's $q api but why can't I just use promise? Is there some magic there in $q?

angular.module('demo', [])
  .controller('DemoController', ['$scope', 'dataService', function($scope, dataSvc) {
    $scope.entities = dataSvc.loadInitialData();
  }])
  .directive('drillDown', ['$compile', 'dataService', function($compile, dataSvc) {
    return {
      restrict: 'A',
      scope: {
        entities: '='
      },
      controller: function($scope) {
        $scope.load = function() {
          var s = this;
          dataSvc.load().then(function(res) {
            s.entity.subEntities = res;
          });
        };
      },
      compile: function(element) {
        var contents = element.contents().remove();
        var compiled = null;

        return function(scope, element) {
          if (!compiled) {
            compiled = $compile(contents);
          }

          compiled(scope, function(clone) {
            element.append(clone);
          });
        };
      },
      template:
        '<li ng-repeat="entity in entities">' +
          '<a href="#" ng-click="load()"><span ng-bind="entity.name"></span></a>' +
          '<ul drill-down entities="entity.subEntities"></ul>' +
        '</li>'
    };
  }])
  .service('dataService', function() {
    this.loadInitialData = function() {
      return [
          {
            name: 'foo',
            subEntities: []
          },
          {
            name: 'bar',
            subEntities: []
          }
        ];
    };
    this.load = function() {
      return new Promise(function(resolve, reject) {
        resolve([
          {
            name: 'foo',
            subEntities: []
          },
          {
            name: 'bar',
            subEntities: []
          }
        ]);
      });
    };
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.min.js"></script>
<div ng-app="demo" ng-controller="DemoController">
  <ul drill-down entities="entities"></ul>
</div>

回答1:


This would require ES6 promises to either expose a hook for setting the scheduler (like bluebird promises) or to expose "post-then" hooks - neither of which it does publicly.

You'd have to coerce the ES6 promise to a $q one by doing:

$q.when(dataSvc.load()).then(...

Alternatively, you can write a helper for binding it to a scope:

var withDigest = function(fn){
    fn().then(function(){
        $rootScope.evalAsync(angular.noop); // schedule digest if not scheduled
    });
};

And then do:

withDigest(function(){
    return dataSvc.load().then(...
});



回答2:


Maybe because you are outside the angular world in the callback function

dataSvc.load().then(function(res) {
  s.entity.subEntities = res;
});

This is not the best solution but if you call $scope.$apply() it should works :

dataSvc.load().then(function(res) {
  s.entity.subEntities = res;
  $scope.$apply()
});

jsfiddle with $scope.$apply() :)

http://jsfiddle.net/Fieldset/xdxprzgw/

The best solution is to use $q.

From the angularjs doc :

'$q is integrated with the $rootScope.Scope Scope model observation mechanism in angular, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI.''



来源:https://stackoverflow.com/questions/30835399/cant-simply-use-es6-promise-to-update-data-in-ng-repeat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!