Skip to content

Commit

Permalink
feat($compile): optionally get controllers from ancestors only
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Caitlin Potter authored and caitp committed Sep 5, 2014
1 parent e322cd9 commit b93b2c4
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 8 deletions.
24 changes: 16 additions & 8 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/,
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset');
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
REQUIRE_PREFIX_REGEXP = /(\?)|(\^\^?)/g,
REQUIRE_PREFIX_PRE_REGEXP = /^((\?)|(\^\^?))+/;

// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
// The assumption is that future DOM event attribute names will begin with
Expand Down Expand Up @@ -1564,14 +1566,20 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {


function getControllers(directiveName, require, $element, elementControllers) {
var value, retrievalMethod = 'data', optional = false;
var value, retrievalMethod = 'data', optional = false, search = $element, prematch, match;
if (isString(require)) {
while((value = require.charAt(0)) == '^' || value == '?') {
require = require.substr(1);
if (value == '^') {
retrievalMethod = 'inheritedData';
if ((prematch = require.match(REQUIRE_PREFIX_PRE_REGEXP)) &&
(match = prematch[0].match(REQUIRE_PREFIX_REGEXP))) {
for (var i=0, ii=match.length; i<ii; ++i) {
value = match[i];
switch (value) {
case '^': retrievalMethod = 'inheritedData'; break;
case '^^': retrievalMethod = 'inheritedData'; search = $element.parent(); break;
case '?': optional = true; break;
}
}
optional = optional || value == '?';

require = require.substring(prematch[0].length);
}
value = null;

Expand All @@ -1580,7 +1588,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
value = value.instance;
}
}
value = value || $element[retrievalMethod]('$' + require + 'Controller');
value = value || search[retrievalMethod]('$' + require + 'Controller');

if (!value && !optional) {
throw $compileMinErr('ctreq',
Expand Down
37 changes: 37 additions & 0 deletions test/ng/compileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3635,6 +3635,43 @@ describe('$compile', function() {
});


it('should get required parent controller', function() {
module(function() {
directive('nested', function(log) {
return {
require: '^^?nested',
controller: function($scope) {},
link: function(scope, element, attrs, controller) {
log(!!controller);
}
};
});
});
inject(function(log, $compile, $rootScope) {
element = $compile('<div nested><div nested></div></div>')($rootScope);
expect(log).toEqual('true; false');
});
});


it('should throw if required parent is not found', function() {
module(function() {
directive('nested', function() {
return {
require: '^^nested',
controller: function($scope) {},
link: function(scope, element, attrs, controller) {}
};
});
});
inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div nested></div>')($rootScope);
}).toThrowMinErr('$compile', 'ctreq', "Controller 'nested', required by directive 'nested', can't be found!");
});
});


it('should get required controller via linkingFn (template)', function() {
module(function() {
directive('dirA', function() {
Expand Down

0 comments on commit b93b2c4

Please sign in to comment.