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>
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:Alternatively, you can write a helper for binding it to a scope:
And then do: