@@ -1221,6 +1221,34 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1221
1221
return debugInfoEnabled ;
1222
1222
} ;
1223
1223
1224
+
1225
+ var TTL = 10 ;
1226
+ /**
1227
+ * @ngdoc method
1228
+ * @name $compileProvider#onChangesTtl
1229
+ * @description
1230
+ *
1231
+ * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
1232
+ * assuming that the model is unstable.
1233
+ *
1234
+ * The current default is 10 iterations.
1235
+ *
1236
+ * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
1237
+ * in several iterations of calls to these hooks. However if an application needs more than the default 10
1238
+ * iterations to stabilize then you should investigate what is causing the model to continuously change during
1239
+ * the `$onChanges` hook execution.
1240
+ *
1241
+ * Increasing the TTL could have performance implications, so you should not change it without proper justification.
1242
+ *
1243
+ * @param {number } limit The number of `$onChanges` hook iterations.
1244
+ */
1245
+ this . onChangesTtl = function ( value ) {
1246
+ if ( arguments . length ) {
1247
+ TTL = value ;
1248
+ }
1249
+ return TTL ;
1250
+ } ;
1251
+
1224
1252
this . $get = [
1225
1253
'$injector' , '$interpolate' , '$exceptionHandler' , '$templateRequest' , '$parse' ,
1226
1254
'$controller' , '$rootScope' , '$sce' , '$animate' , '$$sanitizeUri' ,
@@ -1230,12 +1258,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
1230
1258
var SIMPLE_ATTR_NAME = / ^ \w / ;
1231
1259
var specialAttrHolder = document . createElement ( 'div' ) ;
1232
1260
1261
+
1262
+
1263
+ var onChangesTtl ;
1233
1264
// The onChanges hooks should all be run together in a single digest
1234
1265
// When changes occur, the call to trigger their hooks will be added to this queue
1235
1266
var onChangesQueue ;
1236
1267
1237
1268
// This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
1238
1269
function flushOnChangesQueue ( ) {
1270
+ if ( ! ( onChangesTtl -- ) ) {
1271
+ throw $compileMinErr ( 'infchng' , '{0} $onChanges() iterations reached. Aborting!\n' , TTL ) ;
1272
+ }
1239
1273
// We must run this hook in an apply since the $$postDigest runs outside apply
1240
1274
$rootScope . $apply ( function ( ) {
1241
1275
for ( var i = 0 , ii = onChangesQueue . length ; i < ii ; ++ i ) {
@@ -3156,6 +3190,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
3156
3190
if ( isFunction ( destination . $onChanges ) && currentValue !== previousValue ) {
3157
3191
// If we have not already scheduled the top level onChangesQueue handler then do so now
3158
3192
if ( ! onChangesQueue ) {
3193
+ // Initialize the TTL tracker for tracking stack overflow
3194
+ if ( isUndefined ( onChangesTtl ) ) { onChangesTtl = TTL ; }
3159
3195
scope . $$postDigest ( flushOnChangesQueue ) ;
3160
3196
onChangesQueue = [ ] ;
3161
3197
}
0 commit comments