diff --git a/docs/content/error/ngRepeat/badident.ngdoc b/docs/content/error/ngRepeat/badident.ngdoc new file mode 100644 index 000000000000..9f095cf57be4 --- /dev/null +++ b/docs/content/error/ngRepeat/badident.ngdoc @@ -0,0 +1,39 @@ +@ngdoc error +@name ngRepeat:badident +@fullName Invalid identifier expression +@description + +Occurs when an invalid identifier is specified in an {@link ng.directive:ngRepeat ngRepeat} expression. + +The {@link ng.directive:ngRepeat ngRepeat} directive's `alias as` syntax is used to assign an alias for the processed collection in scope. + +If the expression is not a simple identifier (such that you could declare it with `var {name}`, or if the expression is a reserved name, +this error is thrown. + +Reserved names include: + + - `null` + - `this` + - `undefined` + - `$parent` + - `$even` + - `$odd` + - `$first` + - `$last` + - `$middle` + +Invalid expressions might look like this: + +```html +
  • {{item}}
  • +
  • {{item}}
  • +
  • {{item}}
  • +
  • {{item}}
  • +``` + +Valid expressions might look like this: + +```html +
  • {{item}}
  • +
  • {{item}}
  • +``` diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index 1d7d516fe8c9..95effc6d33fc 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -266,6 +266,12 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { var valueIdentifier = match[3] || match[1]; var keyIdentifier = match[2]; + if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) || + /^null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent$/.test(aliasAs))) { + throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.", + aliasAs); + } + var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn; var hashFnLocals = {$id: hashKey}; diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js index aeaeba3849a5..d2d81a6af87f 100644 --- a/test/ng/directive/ngRepeatSpec.js +++ b/test/ng/directive/ngRepeatSpec.js @@ -416,6 +416,52 @@ describe('ngRepeat', function() { expect(trim(element.text())).toEqual('No results found...'); }); + + + it('should throw if alias identifier is not a simple identifier', inject(function($exceptionHandler) { + scope.x = 'bl'; + scope.items = [ + { name : 'red' }, + { name : 'blue' }, + { name : 'green' }, + { name : 'black' }, + { name : 'orange' }, + { name : 'blonde' } + ]; + + forEach([ + 'null', + 'this', + 'undefined', + '$parent', + '$index', + '$first', + '$middle', + '$last', + '$even', + '$odd', + 'obj[key]', + 'obj["key"]', + 'obj[\'key\']', + 'obj.property', + 'foo=6' + ], function(expr) { + var expression = ('item in items | filter:x as ' + expr + ' track by $index').replace(/"/g, '"'); + element = $compile( + '
    ' + + '
    {{item}}
    ' + + '
    ')(scope); + + var expected = new RegExp('^\\[ngRepeat:badident\\] alias \'' + escape(expr) + '\' is invalid --- must be a valid JS identifier which is not a reserved name'); + expect($exceptionHandler.errors.shift()[0].message). + toMatch(expected); + dealoc(element); + }); + + function escape(text) { + return text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + } + })); });