Skip to content

Commit 65bcd6d

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 angular#4518
1 parent 6639ca9 commit 65bcd6d

File tree

2 files changed

+58
-8
lines changed

2 files changed

+58
-8
lines changed

src/ng/compile.js

+21-8
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
567567
Suffix = 'Directive',
568568
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
569569
CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/,
570-
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset');
570+
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
571+
REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
571572

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

15661567
function getControllers(directiveName, require, $element, elementControllers) {
15671568
var value, retrievalMethod = 'data', optional = false;
1569+
var $searchElement = $element;
1570+
var match;
15681571
if (isString(require)) {
1569-
while((value = require.charAt(0)) == '^' || value == '?') {
1570-
require = require.substr(1);
1571-
if (value == '^') {
1572-
retrievalMethod = 'inheritedData';
1573-
}
1574-
optional = optional || value == '?';
1572+
match = require.match(REQUIRE_PREFIX_REGEXP);
1573+
require = require.substring(match[0].length);
1574+
1575+
if (match[3]) {
1576+
if (match[1]) match[3] = null;
1577+
else match[1] = match[3];
1578+
}
1579+
if (match[1] === '^') {
1580+
retrievalMethod = 'inheritedData';
1581+
} else if (match[1] === '^^') {
1582+
retrievalMethod = 'inheritedData';
1583+
$searchElement = $element.parent();
15751584
}
1585+
if (match[2] === '?') {
1586+
optional = true;
1587+
}
1588+
15761589
value = null;
15771590

15781591
if (elementControllers && retrievalMethod === 'data') {
15791592
if (value = elementControllers[require]) {
15801593
value = value.instance;
15811594
}
15821595
}
1583-
value = value || $element[retrievalMethod]('$' + require + 'Controller');
1596+
value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
15841597

15851598
if (!value && !optional) {
15861599
throw $compileMinErr('ctreq',

test/ng/compileSpec.js

+37
Original file line numberDiff line numberDiff line change
@@ -3635,6 +3635,43 @@ describe('$compile', function() {
36353635
});
36363636

36373637

3638+
it('should get required parent controller', function() {
3639+
module(function() {
3640+
directive('nested', function(log) {
3641+
return {
3642+
require: '^^?nested',
3643+
controller: function($scope) {},
3644+
link: function(scope, element, attrs, controller) {
3645+
log(!!controller);
3646+
}
3647+
};
3648+
});
3649+
});
3650+
inject(function(log, $compile, $rootScope) {
3651+
element = $compile('<div nested><div nested></div></div>')($rootScope);
3652+
expect(log).toEqual('true; false');
3653+
});
3654+
});
3655+
3656+
3657+
it('should throw if required parent is not found', function() {
3658+
module(function() {
3659+
directive('nested', function() {
3660+
return {
3661+
require: '^^nested',
3662+
controller: function($scope) {},
3663+
link: function(scope, element, attrs, controller) {}
3664+
};
3665+
});
3666+
});
3667+
inject(function($compile, $rootScope) {
3668+
expect(function() {
3669+
element = $compile('<div nested></div>')($rootScope);
3670+
}).toThrowMinErr('$compile', 'ctreq', "Controller 'nested', required by directive 'nested', can't be found!");
3671+
});
3672+
});
3673+
3674+
36383675
it('should get required controller via linkingFn (template)', function() {
36393676
module(function() {
36403677
directive('dirA', function() {

0 commit comments

Comments
 (0)