-
Notifications
You must be signed in to change notification settings - Fork 27.5k
fix for #11343 - bindToController for multiple directives #11345
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1970,26 +1970,25 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { | |
} | ||
if (elementControllers) { | ||
// Initialize bindToController bindings for new/isolate scopes | ||
var scopeDirective = newIsolateScopeDirective || newScopeDirective; | ||
var bindings; | ||
var controllerForBindings; | ||
if (scopeDirective && elementControllers[scopeDirective.name]) { | ||
bindings = scopeDirective.$$bindings.bindToController; | ||
controller = elementControllers[scopeDirective.name]; | ||
|
||
if (controller && controller.identifier && bindings) { | ||
controllerForBindings = controller; | ||
thisLinkFn.$$destroyBindings = | ||
initializeDirectiveBindings(scope, attrs, controller.instance, | ||
bindings, scopeDirective); | ||
} | ||
} | ||
for (i in elementControllers) { | ||
var scopeDirective = newIsolateScopeDirective || controllerDirectives[i]; | ||
var bindings; | ||
var controllerForBindings; | ||
if (scopeDirective && elementControllers[scopeDirective.name]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think is probably not quite right any more, since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see now that the |
||
bindings = scopeDirective.$$bindings.bindToController; | ||
controller = elementControllers[scopeDirective.name]; | ||
if (controller && controller.identifier && bindings) { | ||
controllerForBindings = controller; | ||
thisLinkFn.$$destroyBindings = | ||
initializeDirectiveBindings(scope, attrs, controller.instance, | ||
bindings, scopeDirective); | ||
} | ||
} | ||
controller = elementControllers[i]; | ||
var controllerResult = controller(); | ||
if (controllerResult !== controller.instance) { | ||
controller.instance = controllerResult; | ||
$element.data('$' + directive.name + 'Controller', controllerResult); | ||
$element.data('$' + controllerDirectives[i].name + 'Controller', controllerResult); | ||
if (controller === controllerForBindings) { | ||
// Remove and re-install bindToController bindings | ||
thisLinkFn.$$destroyBindings(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4033,6 +4033,68 @@ describe('$compile', function() { | |
}); | ||
|
||
|
||
it('should bind to multiple directives controllers via object notation (new scope)', function() { | ||
var controllerCalled = false; | ||
var secondControllerCalled = false; | ||
module(function($compileProvider, $controllerProvider) { | ||
$controllerProvider.register('myCtrl', function() { | ||
expect(this.data).toEqualData({ | ||
'foo': 'bar', | ||
'baz': 'biz' | ||
}); | ||
expect(this.str).toBe('Hello, world!'); | ||
expect(this.fn()).toBe('called!'); | ||
controllerCalled = true; | ||
}); | ||
$controllerProvider.register('secondCtrl', function() { | ||
expect(this.data).toEqualData({ | ||
'foo2': 'bar2', | ||
'baz2': 'biz2' | ||
}); | ||
expect(this.str).toBe('Hello, second world!'); | ||
expect(this.fn()).toBe('second called!'); | ||
secondControllerCalled = true; | ||
}); | ||
$compileProvider.directive('fooDir', valueFn({ | ||
bindToController: { | ||
'data': '=dirData', | ||
'str': '@dirStr', | ||
'fn': '&dirFn' | ||
}, | ||
scope: true, | ||
controller: 'myCtrl as myCtrl' | ||
})); | ||
$compileProvider.directive('barDir', valueFn({ | ||
bindToController: { | ||
'data': '=barData', | ||
'str': '@barStr', | ||
'fn': '&barFn' | ||
}, | ||
scope: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. technically, you're not supposed to have multiple "new scope" directives on the same element, so this doesn't really work in practice (if it does work, it's a bug!). I'm a bit uncomfortable with this because, given the above, it could become unclear what's happening if you have directives which implicitly depend on there being a new scope directive on the same element to work correctly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @caitp This is not what the issue is about. The "scope: true" functionality creating the common scope that's being shared among the directives is already in the Angular. Those directives can interact with one another by reading and writing to the scope directly without the need of bindToController. Whether this is good or not is probably up for another discussion. This issue is about bindToController binding the data for only single directive leaving all others in the undefined state. That's obviously not correct. It should either bind for all, or bindToController should be disabled for "scope: true". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Believe it or not, I understand what this issue is about --- The problem is the "scope: true" behaviour being shared by multiple directives on the same element. This was never meant to be supported, there is a WONTFIX'd bug about this, because it's explicitly not supported, it's just that we fail to throw the error sometimes (depending on types of scopes used, and directive priorities). This is never something that should be depended on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do support multiple directives asking for new normal scope (i.e. not isolate) as can be seen in the docs (https://docs.angularjs.org/api/ng/service/$compile#-scope-):
So this is a valid bug and the fix looks pretty good to me too. I will give it a review and land this week. |
||
controller: 'secondCtrl as secondCtrl' | ||
})); | ||
}); | ||
inject(function($compile, $rootScope) { | ||
$rootScope.fn = valueFn('called!'); | ||
$rootScope.whom = 'world'; | ||
$rootScope.remoteData = { | ||
'foo': 'bar', | ||
'baz': 'biz' | ||
}; | ||
$rootScope.fn2 = valueFn('second called!'); | ||
$rootScope.whom2 = 'second world'; | ||
$rootScope.remoteData2 = { | ||
'foo2': 'bar2', | ||
'baz2': 'biz2' | ||
}; | ||
element = $compile('<div foo-dir dir-data="remoteData" dir-str="Hello, {{whom}}!" dir-fn="fn()" bar-dir bar-data="remoteData2" bar-str="Hello, {{whom2}}!" bar-fn="fn2()" ></div>')($rootScope); | ||
$rootScope.$digest(); | ||
expect(controllerCalled).toBe(true); | ||
expect(secondControllerCalled).toBe(true); | ||
}); | ||
}); | ||
|
||
|
||
it('should put controller in scope when controller identifier present but not using controllerAs', function() { | ||
var controllerCalled = false; | ||
var myCtrl; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have you changed this from checking
newScopeDirective
to checkingcontrollerDirectives[i]
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh hold on, you are actually calculating the controller directive here - probably should use a different name for the var.