Skip to content

Commit b265f38

Browse files
author
Caitlin Potter
committed
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 08f376f commit b265f38

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

docs/content/guide/directive.ngdoc

+5-3
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ Here's an example directive declared with a Directive Definition Object:
271271
restrict: 'A',
272272
scope: false,
273273
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
274-
require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
274+
require: 'siblingDirectiveName', // or // ['^parentOrSiblingDirectiveName', '?optionalDirectiveName', '?^optionalParentOrSibling'], // or // ['^^parentDirectiveName', '?^^optionalParentDirectiveName'],
275275
compile: function compile(tElement, tAttrs, transclude) {
276276
return {
277277
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
@@ -398,8 +398,10 @@ compiler}. The attributes are:
398398

399399
* (no prefix) - Locate the required controller on the current element.
400400
* `?` - Attempt to locate the required controller, or return `null` if not found.
401-
* `^` - Locate the required controller by searching the element's parents.
402-
* `?^` - Attempt to locate the required controller by searching the element's parents, or return `null` if not found.
401+
* `^` - Locate the required controller by searching the element and its parents until found.
402+
* `^^` - Locate the required controller by searching the element's parents until found.
403+
* `?^` - Attempt to locate the required controller by searching the element and its parents, or return `null` if not found.
404+
* `?^^` - Attempt to locate the required controller by searching the element's parents, or return `null` if not found.
403405

404406
* `controllerAs` - Creates a controller alias in the directive scope so it
405407
can be referenced in the directive template. The directive must define a scope for this

src/ng/compile.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -950,17 +950,20 @@ function $CompileProvider($provide) {
950950

951951

952952
function getControllers(require, $element) {
953-
var value, retrievalMethod = 'data', optional = false;
953+
var value, retrievalMethod = 'data', optional = false, $searchElement = $element;
954954
if (isString(require)) {
955955
while((value = require.charAt(0)) == '^' || value == '?') {
956956
require = require.substr(1);
957957
if (value == '^') {
958+
if (retrievalMethod === 'inheritedData') {
959+
$searchElement = $element.parent();
960+
}
958961
retrievalMethod = 'inheritedData';
959962
}
960963
optional = optional || value == '?';
961964
}
962965

963-
value = $element[retrievalMethod]('$' + require + 'Controller');
966+
value = $searchElement[retrievalMethod]('$' + require + 'Controller');
964967

965968
if ($element[0].nodeType == 8 && $element[0].$$controller) { // Transclusion comment node
966969
value = value || $element[0].$$controller;

test/ng/compileSpec.js

+37
Original file line numberDiff line numberDiff line change
@@ -2282,6 +2282,43 @@ describe('$compile', function() {
22822282
});
22832283

22842284

2285+
it('should get required parent controller', function() {
2286+
module(function() {
2287+
directive('nested', function(log) {
2288+
return {
2289+
require: '^^?nested',
2290+
controller: function($scope) {},
2291+
link: function(scope, element, attrs, controller) {
2292+
log(!!controller);
2293+
}
2294+
};
2295+
});
2296+
});
2297+
inject(function(log, $compile, $rootScope) {
2298+
element = $compile('<div nested><div nested></div></div>')($rootScope);
2299+
// Nested div should be post-linked first.
2300+
expect(log).toEqual('true; false')
2301+
});
2302+
});
2303+
2304+
2305+
it('should throw if required parent is not found', function() {
2306+
module(function() {
2307+
directive('nested', function() {
2308+
return {
2309+
require: '^^nested',
2310+
controller: function($scope) {},
2311+
link: function(scope, element, attrs, controller) {}
2312+
};
2313+
});
2314+
});
2315+
inject(function($compile, $rootScope) {
2316+
var fn = angular.bind(this, $compile('<div nested></div>'), $rootScope);
2317+
expect(fn).toThrowMinErr('$compile', 'ctreq', "Controller 'nested', required by directive 'nested', can't be found!");
2318+
});
2319+
})
2320+
2321+
22852322
it('should support controllerAs', function() {
22862323
module(function() {
22872324
directive('main', function() {

0 commit comments

Comments
 (0)