fix($compile): update data() when controller returns custom value

When controller functions return an explicit value that value should
be what is passed to the linking functions, and to any child/sibling
controllers that `require` it. It should also be bound to the data
store on the dom element.

Closes #11147
Closes #11326
This commit is contained in:
James Talmage
2015-03-13 22:20:35 -04:00
committed by Caitlin Potter
parent db866f1f86
commit 9900610eea
2 changed files with 133 additions and 7 deletions
+9 -7
View File
@@ -1978,13 +1978,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
for (i in elementControllers) {
controller = elementControllers[i];
var controllerResult = controller();
if (controllerResult !== controller.instance &&
controller === controllerForBindings) {
// Remove and re-install bindToController bindings
thisLinkFn.$$destroyBindings();
thisLinkFn.$$destroyBindings =
initializeDirectiveBindings(scope, attrs, controllerResult,
bindings, scopeDirective);
if (controllerResult !== controller.instance) {
controller.instance = controllerResult;
$element.data('$' + directive.name + 'Controller', controllerResult);
if (controller === controllerForBindings) {
// Remove and re-install bindToController bindings
thisLinkFn.$$destroyBindings();
thisLinkFn.$$destroyBindings =
initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
}
}
}
}
+124
View File
@@ -4197,6 +4197,130 @@ describe('$compile', function() {
});
it('should respect explicit return value from controller', function() {
module(function() {
directive('logControllerProp', function(log) {
return {
controller: function($scope) {
this.foo = 'baz'; // value should not be used.
return {foo: 'bar'};
},
link: function(scope, element, attrs, controller) {
log(controller.foo);
}
};
});
});
inject(function(log, $compile, $rootScope) {
element = $compile('<log-controller-prop></log-controller-prop>')($rootScope);
expect(log).toEqual('bar');
expect(element.data('$logControllerPropController').foo).toEqual('bar');
});
});
it('should get explicit return value of required parent controller', function() {
module(function() {
directive('nested', function(log) {
return {
require: '^^?nested',
controller: function() {
return {foo: 'bar'};
},
link: function(scope, element, attrs, controller) {
log(!!controller && controller.foo);
}
};
});
});
inject(function(log, $compile, $rootScope) {
element = $compile('<div nested><div nested></div></div>')($rootScope);
expect(log).toEqual('bar; false');
});
});
it('should respect explicit controller return value when using controllerAs', function() {
module(function() {
directive('main', function() {
return {
templateUrl: 'main.html',
scope: {},
controller: function() {
this.name = 'lucas';
return {name: 'george'};
},
controllerAs: 'mainCtrl'
};
});
});
inject(function($templateCache, $compile, $rootScope) {
$templateCache.put('main.html', '<span>template:{{mainCtrl.name}}</span>');
element = $compile('<main/>')($rootScope);
$rootScope.$apply();
expect(element.text()).toBe('template:george');
});
});
it('transcluded children should receive explicit return value of parent controller', function() {
var expectedController;
module(function() {
directive('nester', valueFn({
transclude: 'content',
controller: function($transclude) {
this.foo = 'baz';
expectedController = {transclude:$transclude, foo: 'bar'};
return expectedController;
},
link: function(scope, el, attr, ctrl) {
ctrl.transclude(cloneAttach);
function cloneAttach(clone) {
el.append(clone);
}
}
}));
directive('nested', function(log) {
return {
require: '^^nester',
link: function(scope, element, attrs, controller) {
expect(controller).toBe(expectedController);
log('done');
}
};
});
});
inject(function(log, $compile) {
element = $compile('<div nester><div nested></div></div>')($rootScope);
$rootScope.$apply();
expect(log).toEqual('done');
});
});
it('explicit controller return values are ignored if they are primitives', function() {
module(function() {
directive('logControllerProp', function(log) {
return {
controller: function($scope) {
this.foo = 'baz'; // value *will* be used.
return 'bar';
},
link: function(scope, element, attrs, controller) {
log(controller.foo);
}
};
});
});
inject(function(log, $compile, $rootScope) {
element = $compile('<log-controller-prop></log-controller-prop>')($rootScope);
expect(log).toEqual('baz');
expect(element.data('$logControllerPropController').foo).toEqual('baz');
});
});
it('should get required parent controller', function() {
module(function() {
directive('nested', function(log) {