From 5993070c6c600fe2bc02039dd668ee9986e2576d Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Mon, 25 Apr 2016 23:43:38 +0300 Subject: [PATCH 1/2] feat($compile): support omitting required controller name if same as the local name Basically, making `require: {someDir: '?^someDir'}` equivalent to `require: {someDir: '?^'}`. --- src/ng/compile.js | 19 ++++++++++-- test/ng/compileSpec.js | 70 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/ng/compile.js b/src/ng/compile.js index fee065820bc4..e5b80a582840 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -324,8 +324,9 @@ * If the `require` property is an object and `bindToController` is truthy, then the required controllers are * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers * have been constructed but before `$onInit` is called. + * If the name of the required controller is the same as the local name (the key), the name can be + * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`. * See the {@link $compileProvider#component} helper for an example of how this can be used. - * * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is * raised (unless no link function is specified and the required controllers are not being bound to the directive * controller, in which case error checking is skipped). The name can be prefixed with: @@ -954,6 +955,20 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } } + function getDirectiveRequire(directive) { + var require = directive.require || (directive.controller && directive.name); + + if (!isArray(require) && isObject(require)) { + forEach(require, function(value, key) { + var match = value.match(REQUIRE_PREFIX_REGEXP); + var name = value.substring(match[0].length); + if (!name) require[key] = match[0] + key; + }); + } + + return require; + } + /** * @ngdoc method * @name $compileProvider#directive @@ -990,7 +1005,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { directive.priority = directive.priority || 0; directive.index = index; directive.name = directive.name || name; - directive.require = directive.require || (directive.controller && directive.name); + directive.require = getDirectiveRequire(directive); directive.restrict = directive.restrict || 'EA'; directive.$$moduleName = directiveFactory.$$moduleName; directives.push(directive); diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 2b64f8ae8170..6702283bee77 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -6268,6 +6268,51 @@ describe('$compile', function() { }); }); + it('should use the key if the name of a required controller is omitted', function() { + var parentController, siblingController; + + function ParentController() { this.name = 'Parent'; } + function SiblingController() { this.name = 'Sibling'; } + function MeController() { this.name = 'Me'; } + MeController.prototype.$onInit = function() { + parentController = this.parent; + siblingController = this.sibling; + }; + spyOn(MeController.prototype, '$onInit').and.callThrough(); + + angular.module('my', []) + .directive('me', function() { + return { + restrict: 'E', + scope: {}, + require: { parent: '^', sibling: '' }, + bindToController: true, + controller: MeController, + controllerAs: '$ctrl' + }; + }) + .directive('parent', function() { + return { + restrict: 'E', + scope: {}, + controller: ParentController + }; + }) + .directive('sibling', function() { + return { + controller: SiblingController + }; + }); + + module('my'); + inject(function($compile, $rootScope, meDirective) { + element = $compile('')($rootScope); + expect(MeController.prototype.$onInit).toHaveBeenCalled(); + expect(parentController).toEqual(jasmine.any(ParentController)); + expect(siblingController).toEqual(jasmine.any(SiblingController)); + }); + }); + it('should not bind required controllers if bindToController is falsy', function() { var parentController, siblingController; @@ -6797,6 +6842,31 @@ describe('$compile', function() { }); }); + it('should support omitting the name of the required controller if it is the same as the key', + function() { + module(function() { + directive('myC1', valueFn({ + controller: function() { this.name = 'c1'; } + })); + directive('myC2', valueFn({ + controller: function() { this.name = 'c2'; } + })); + directive('dep', function(log) { + return { + require: { myC1: '^', myC2: '^' }, + link: function(scope, element, attrs, controllers) { + log('dep:' + controllers.myC1.name + '-' + controllers.myC2.name); + } + }; + }); + }); + inject(function(log, $compile, $rootScope) { + element = $compile('
')($rootScope); + expect(log).toEqual('dep:c1-c2'); + }); + } + ); + it('should instantiate the controller just once when template/templateUrl', function() { var syncCtrlSpy = jasmine.createSpy('sync controller'), asyncCtrlSpy = jasmine.createSpy('async controller'); From a4e83c190f532386f00fba2d9a08be4b5a484a04 Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Tue, 26 Apr 2016 19:34:37 +0300 Subject: [PATCH 2/2] fixup 1 --- test/ng/compileSpec.js | 98 +++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 6702283bee77..b3582d897dd2 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -6269,47 +6269,85 @@ describe('$compile', function() { }); it('should use the key if the name of a required controller is omitted', function() { - var parentController, siblingController; - function ParentController() { this.name = 'Parent'; } + function ParentOptController() { this.name = 'ParentOpt'; } + function ParentOrSiblingController() { this.name = 'ParentOrSibling'; } + function ParentOrSiblingOptController() { this.name = 'ParentOrSiblingOpt'; } function SiblingController() { this.name = 'Sibling'; } - function MeController() { this.name = 'Me'; } - MeController.prototype.$onInit = function() { - parentController = this.parent; - siblingController = this.sibling; - }; - spyOn(MeController.prototype, '$onInit').and.callThrough(); + function SiblingOptController() { this.name = 'SiblingOpt'; } angular.module('my', []) - .directive('me', function() { - return { - restrict: 'E', - scope: {}, - require: { parent: '^', sibling: '' }, - bindToController: true, - controller: MeController, - controllerAs: '$ctrl' - }; + .component('me', { + require: { + parent: '^^', + parentOpt: '?^^', + parentOrSibling1: '^', + parentOrSiblingOpt1: '?^', + parentOrSibling2: '^', + parentOrSiblingOpt2: '?^', + sibling: '', + siblingOpt: '?' + } }) .directive('parent', function() { - return { - restrict: 'E', - scope: {}, - controller: ParentController - }; + return {controller: ParentController}; + }) + .directive('parentOpt', function() { + return {controller: ParentOptController}; + }) + .directive('parentOrSibling1', function() { + return {controller: ParentOrSiblingController}; + }) + .directive('parentOrSiblingOpt1', function() { + return {controller: ParentOrSiblingOptController}; + }) + .directive('parentOrSibling2', function() { + return {controller: ParentOrSiblingController}; + }) + .directive('parentOrSiblingOpt2', function() { + return {controller: ParentOrSiblingOptController}; }) .directive('sibling', function() { - return { - controller: SiblingController - }; + return {controller: SiblingController}; + }) + .directive('siblingOpt', function() { + return {controller: SiblingOptController}; }); module('my'); - inject(function($compile, $rootScope, meDirective) { - element = $compile('')($rootScope); - expect(MeController.prototype.$onInit).toHaveBeenCalled(); - expect(parentController).toEqual(jasmine.any(ParentController)); - expect(siblingController).toEqual(jasmine.any(SiblingController)); + inject(function($compile, $rootScope) { + var template = + '
' + + // With optional + '' + + '' + + '' + + // Without optional + '' + + '' + + '' + + '
'; + element = $compile(template)($rootScope); + + var ctrl1 = element.find('me').eq(0).controller('me'); + expect(ctrl1.parent).toEqual(jasmine.any(ParentController)); + expect(ctrl1.parentOpt).toEqual(jasmine.any(ParentOptController)); + expect(ctrl1.parentOrSibling1).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl1.parentOrSiblingOpt1).toEqual(jasmine.any(ParentOrSiblingOptController)); + expect(ctrl1.parentOrSibling2).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl1.parentOrSiblingOpt2).toEqual(jasmine.any(ParentOrSiblingOptController)); + expect(ctrl1.sibling).toEqual(jasmine.any(SiblingController)); + expect(ctrl1.siblingOpt).toEqual(jasmine.any(SiblingOptController)); + + var ctrl2 = element.find('me').eq(1).controller('me'); + expect(ctrl2.parent).toEqual(jasmine.any(ParentController)); + expect(ctrl2.parentOpt).toBe(null); + expect(ctrl2.parentOrSibling1).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl2.parentOrSiblingOpt1).toBe(null); + expect(ctrl2.parentOrSibling2).toEqual(jasmine.any(ParentOrSiblingController)); + expect(ctrl2.parentOrSiblingOpt2).toBe(null); + expect(ctrl2.sibling).toEqual(jasmine.any(SiblingController)); + expect(ctrl2.siblingOpt).toBe(null); }); });