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

Commit 905a4ff

Browse files
committed
feat($compile): added new method strictComponentBindingsEnabled
1 parent e6d5fe7 commit 905a4ff

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@ngdoc error
2+
@name $compile:missingattr
3+
@fullName Missing required attribute
4+
@description
5+
6+
This error may occur only when `$compileProvider.strictComponentBindingsEnabled` is set to `true`.
7+
Then all attributes mentioned in `bindings` without `?` must be set. If one or more aren't set,
8+
the first one will throw an error.

src/ng/compile.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,32 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
14031403
return debugInfoEnabled;
14041404
};
14051405

1406+
/**
1407+
* @ngdoc method
1408+
* @name $compileProvider#strictComponentBindingsEnabled
1409+
*
1410+
* @param {boolean=} enabled update the strictComponentBindingsEnabled state if provided, otherwise just return the
1411+
* current strictComponentBindingsEnabled state
1412+
* @returns {*} current value if used as getter or itself (chaining) if used as setter
1413+
*
1414+
* @kind function
1415+
*
1416+
* @description
1417+
* Call this method to enable/disable strict component bindings check. If enabled, the compiler will enforce that
1418+
* for all bindings of a component that are not set as optional with `?`, an attribute needs to be provided
1419+
* on the component's HTML tag.
1420+
*
1421+
* The default value is false.
1422+
*/
1423+
var strictComponentBindingsEnabled = false;
1424+
this.strictComponentBindingsEnabled = function(enabled) {
1425+
if (isDefined(enabled)) {
1426+
strictComponentBindingsEnabled = enabled;
1427+
return this;
1428+
}
1429+
return strictComponentBindingsEnabled;
1430+
};
1431+
14061432
var TTL = 10;
14071433
/**
14081434
* @ngdoc method
@@ -3413,12 +3439,20 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
34133439
}
34143440
}
34153441

3442+
function strictBindingsCheck(attrName, directiveName) {
3443+
if (strictComponentBindingsEnabled) {
3444+
throw $compileMinErr('missingattr',
3445+
'Attribute \'{0}\' of \'{1}\' is non-optional and must be set!',
3446+
attrName, directiveName);
3447+
}
3448+
};
34163449

34173450
// Set up $watches for isolate scope and controller bindings.
34183451
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
34193452
var removeWatchCollection = [];
34203453
var initialChanges = {};
34213454
var changes;
3455+
34223456
forEach(bindings, function initializeBinding(definition, scopeName) {
34233457
var attrName = definition.attrName,
34243458
optional = definition.optional,
@@ -3430,7 +3464,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
34303464

34313465
case '@':
34323466
if (!optional && !hasOwnProperty.call(attrs, attrName)) {
3467+
strictBindingsCheck(attrName, directive.name);
34333468
destination[scopeName] = attrs[attrName] = undefined;
3469+
34343470
}
34353471
removeWatch = attrs.$observe(attrName, function(value) {
34363472
if (isString(value) || isBoolean(value)) {
@@ -3457,6 +3493,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
34573493
case '=':
34583494
if (!hasOwnProperty.call(attrs, attrName)) {
34593495
if (optional) break;
3496+
strictBindingsCheck(attrName, directive.name);
34603497
attrs[attrName] = undefined;
34613498
}
34623499
if (optional && !attrs[attrName]) break;
@@ -3501,6 +3538,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
35013538
case '<':
35023539
if (!hasOwnProperty.call(attrs, attrName)) {
35033540
if (optional) break;
3541+
strictBindingsCheck(attrName, directive.name);
35043542
attrs[attrName] = undefined;
35053543
}
35063544
if (optional && !attrs[attrName]) break;
@@ -3526,6 +3564,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
35263564
break;
35273565

35283566
case '&':
3567+
if (!optional && !hasOwnProperty.call(attrs, attrName)) {
3568+
strictBindingsCheck(attrName, directive.name);
3569+
}
35293570
// Don't assign Object.prototype method to scope
35303571
parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
35313572

test/ng/compileSpec.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ describe('$compile', function() {
169169
inject();
170170
});
171171

172+
it('should allow strictComponentBindingsEnabled to be configured', function() {
173+
module(function($compileProvider) {
174+
expect($compileProvider.strictComponentBindingsEnabled()).toBe(false); // the default
175+
$compileProvider.strictComponentBindingsEnabled(true);
176+
expect($compileProvider.strictComponentBindingsEnabled()).toBe(true);
177+
});
178+
inject();
179+
});
180+
172181
it('should allow onChangesTtl to be configured', function() {
173182
module(function($compileProvider) {
174183
expect($compileProvider.onChangesTtl()).toBe(10); // the default
@@ -2854,6 +2863,21 @@ describe('$compile', function() {
28542863
})
28552864
);
28562865

2866+
it('should throw an error for undefined non-optional "=" bindings when strictComponentBindingsEnabled is true', function() {
2867+
module(function($compileProvider) {
2868+
$compileProvider.strictComponentBindingsEnabled(true);
2869+
});
2870+
inject(
2871+
function($rootScope, $compile) {
2872+
var func = function() {
2873+
element = $compile(
2874+
'<div prototype-method-name-as-scope-var-a></div>'
2875+
)($rootScope);
2876+
};
2877+
expect(func).toThrowMinErr('$compile', 'missingattr', 'Attribute \'valueOf\' of \'prototypeMethodNameAsScopeVarA\' is non-optional and must be set!');
2878+
});
2879+
});
2880+
28572881
it('should handle "@" bindings with same method names in Object.prototype correctly when not present', inject(
28582882
function($rootScope, $compile) {
28592883
var func = function() {
@@ -2891,6 +2915,21 @@ describe('$compile', function() {
28912915
})
28922916
);
28932917

2918+
it('should throw an error for undefined non-optional "@" bindings when strictComponentBindingsEnabled is true', function() {
2919+
module(function($compileProvider) {
2920+
$compileProvider.strictComponentBindingsEnabled(true);
2921+
});
2922+
inject(
2923+
function($rootScope, $compile) {
2924+
var func = function() {
2925+
element = $compile(
2926+
'<div prototype-method-name-as-scope-var-b></div>'
2927+
)($rootScope);
2928+
};
2929+
expect(func).toThrowMinErr('$compile', 'missingattr', 'Attribute \'valueOf\' of \'prototypeMethodNameAsScopeVarB\' is non-optional and must be set!');
2930+
});
2931+
});
2932+
28942933
it('should handle "&" bindings with same method names in Object.prototype correctly when not present', inject(
28952934
function($rootScope, $compile) {
28962935
var func = function() {
@@ -2923,6 +2962,36 @@ describe('$compile', function() {
29232962
})
29242963
);
29252964

2965+
it('should throw an error for undefined non-optional "&" bindings when strictComponentBindingsEnabled is true', function() {
2966+
module(function($compileProvider) {
2967+
$compileProvider.strictComponentBindingsEnabled(true);
2968+
});
2969+
inject(
2970+
function($rootScope, $compile) {
2971+
var func = function() {
2972+
element = $compile(
2973+
'<div prototype-method-name-as-scope-var-c></div>'
2974+
)($rootScope);
2975+
};
2976+
expect(func).toThrowMinErr('$compile', 'missingattr', 'Attribute \'valueOf\' of \'prototypeMethodNameAsScopeVarC\' is non-optional and must be set!');
2977+
});
2978+
});
2979+
2980+
it('should throw an error for undefined non-optional "&" bindings when strictComponentBindingsEnabled is true', function() {
2981+
module(function($compileProvider) {
2982+
$compileProvider.strictComponentBindingsEnabled(true);
2983+
});
2984+
inject(
2985+
function($rootScope, $compile) {
2986+
var func = function() {
2987+
element = $compile(
2988+
'<div watch-as-scope-var></div>'
2989+
)($rootScope);
2990+
};
2991+
expect(func).toThrowMinErr('$compile', 'missingattr', 'Attribute \'watch\' of \'watchAsScopeVar\' is non-optional and must be set!');
2992+
});
2993+
});
2994+
29262995
it('should not throw exception when using "watch" as binding in Firefox', inject(
29272996
function($rootScope, $compile) {
29282997
$rootScope.watch = 'watch';

0 commit comments

Comments
 (0)