Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 07e3abc

Browse files
Caitlin Pottercaitp
Caitlin Potter
authored andcommitted
feat($compile): optionally get controllers from ancestors only
Implement option to strengthen require '^' operator, by adding another '^'. When a second '^' is used, the controller will only search parent nodes for the matching controller, and will throw or return null if not found, depending on whether or not the requirement is optional. Closes #4518 Closes #4540 Closes #8240 Closes #8511
1 parent b9df121 commit 07e3abc

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

src/ng/compile.js

+24-8
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,11 @@
212212
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
213213
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
214214
* * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
215+
* * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
215216
* * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
216217
* `null` to the `link` fn if not found.
218+
* * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
219+
* `null` to the `link` fn if not found.
217220
*
218221
*
219222
* #### `controllerAs`
@@ -567,7 +570,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
567570
Suffix = 'Directive',
568571
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
569572
CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/,
570-
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset');
573+
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
574+
REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
571575

572576
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
573577
// The assumption is that future DOM event attribute names will begin with
@@ -1589,22 +1593,34 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
15891593

15901594
function getControllers(directiveName, require, $element, elementControllers) {
15911595
var value, retrievalMethod = 'data', optional = false;
1596+
var $searchElement = $element;
1597+
var match;
15921598
if (isString(require)) {
1593-
while((value = require.charAt(0)) == '^' || value == '?') {
1594-
require = require.substr(1);
1595-
if (value == '^') {
1596-
retrievalMethod = 'inheritedData';
1597-
}
1598-
optional = optional || value == '?';
1599+
match = require.match(REQUIRE_PREFIX_REGEXP);
1600+
require = require.substring(match[0].length);
1601+
1602+
if (match[3]) {
1603+
if (match[1]) match[3] = null;
1604+
else match[1] = match[3];
15991605
}
1606+
if (match[1] === '^') {
1607+
retrievalMethod = 'inheritedData';
1608+
} else if (match[1] === '^^') {
1609+
retrievalMethod = 'inheritedData';
1610+
$searchElement = $element.parent();
1611+
}
1612+
if (match[2] === '?') {
1613+
optional = true;
1614+
}
1615+
16001616
value = null;
16011617

16021618
if (elementControllers && retrievalMethod === 'data') {
16031619
if (value = elementControllers[require]) {
16041620
value = value.instance;
16051621
}
16061622
}
1607-
value = value || $element[retrievalMethod]('$' + require + 'Controller');
1623+
value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
16081624

16091625
if (!value && !optional) {
16101626
throw $compileMinErr('ctreq',

test/ng/compileSpec.js

+37
Original file line numberDiff line numberDiff line change
@@ -3672,6 +3672,43 @@ describe('$compile', function() {
36723672
});
36733673

36743674

3675+
it('should get required parent controller', function() {
3676+
module(function() {
3677+
directive('nested', function(log) {
3678+
return {
3679+
require: '^^?nested',
3680+
controller: function($scope) {},
3681+
link: function(scope, element, attrs, controller) {
3682+
log(!!controller);
3683+
}
3684+
};
3685+
});
3686+
});
3687+
inject(function(log, $compile, $rootScope) {
3688+
element = $compile('<div nested><div nested></div></div>')($rootScope);
3689+
expect(log).toEqual('true; false');
3690+
});
3691+
});
3692+
3693+
3694+
it('should throw if required parent is not found', function() {
3695+
module(function() {
3696+
directive('nested', function() {
3697+
return {
3698+
require: '^^nested',
3699+
controller: function($scope) {},
3700+
link: function(scope, element, attrs, controller) {}
3701+
};
3702+
});
3703+
});
3704+
inject(function($compile, $rootScope) {
3705+
expect(function() {
3706+
element = $compile('<div nested></div>')($rootScope);
3707+
}).toThrowMinErr('$compile', 'ctreq', "Controller 'nested', required by directive 'nested', can't be found!");
3708+
});
3709+
});
3710+
3711+
36753712
it('should get required controller via linkingFn (template)', function() {
36763713
module(function() {
36773714
directive('dirA', function() {

0 commit comments

Comments
 (0)