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

Commit 3e1b3de

Browse files
committed
feat($parse): provide a mechanism to access the locals object
Extends the built-in identifiers definitions by adding `$local`. This is a non-assignable reference to the locals object. Closes: #13247
1 parent 7a668cd commit 3e1b3de

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

docs/content/guide/expression.ngdoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ This restriction is intentional. It prevents accidental access to the global sta
113113
Instead use services like `$window` and `$location` in functions called from expressions. Such services
114114
provide mockable access to globals.
115115

116+
It is possible to access the context object using the identifier `this` and the locals object using the
117+
identifier `$local`.
118+
116119
<example module="expressionExample">
117120
<file name="index.html">
118121
<div class="example2" ng-controller="ExampleController">

src/ng/parse.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ AST.ArrayExpression = 'ArrayExpression';
325325
AST.Property = 'Property';
326326
AST.ObjectExpression = 'ObjectExpression';
327327
AST.ThisExpression = 'ThisExpression';
328+
AST.LocalExpression = 'LocalExpression';
328329

329330
// Internal use only
330331
AST.NGValueParameter = 'NGValueParameter';
@@ -625,7 +626,8 @@ AST.prototype = {
625626
'false': { type: AST.Literal, value: false },
626627
'null': { type: AST.Literal, value: null },
627628
'undefined': {type: AST.Literal, value: undefined },
628-
'this': {type: AST.ThisExpression }
629+
'this': {type: AST.ThisExpression },
630+
'$local': {type: AST.LocalExpression }
629631
}
630632
};
631633

@@ -745,6 +747,10 @@ function findConstantAndWatchExpressions(ast, $filter) {
745747
ast.constant = false;
746748
ast.toWatch = [];
747749
break;
750+
case AST.LocalExpression:
751+
ast.constant = false;
752+
ast.toWatch = [];
753+
break;
748754
}
749755
}
750756

@@ -1114,6 +1120,10 @@ ASTCompiler.prototype = {
11141120
this.assign(intoId, 's');
11151121
recursionFn('s');
11161122
break;
1123+
case AST.LocalExpression:
1124+
this.assign(intoId, 'l');
1125+
recursionFn('l');
1126+
break;
11171127
case AST.NGValueParameter:
11181128
this.assign(intoId, 'v');
11191129
recursionFn('v');
@@ -1441,6 +1451,10 @@ ASTInterpreter.prototype = {
14411451
return function(scope) {
14421452
return context ? {value: scope} : scope;
14431453
};
1454+
case AST.LocalExpression:
1455+
return function(scope, locals) {
1456+
return context ? {value: locals} : locals;
1457+
};
14441458
case AST.NGValueParameter:
14451459
return function(scope, locals, assign, inputs) {
14461460
return context ? {value: assign} : assign;

test/ng/parseSpec.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,23 @@ describe('parser', function() {
550550
});
551551

552552

553-
it('should not confuse `this`, `undefined`, `true`, `false`, `null` when used as identfiers', function() {
554-
forEach(['this', 'undefined', 'true', 'false', 'null'], function(identifier) {
553+
it('should understand the `$local` expression', function() {
554+
expect(createAst('$local')).toEqual(
555+
{
556+
type: 'Program',
557+
body: [
558+
{
559+
type: 'ExpressionStatement',
560+
expression: { type: 'LocalExpression' }
561+
}
562+
]
563+
}
564+
);
565+
});
566+
567+
568+
it('should not confuse `this`, `$local`, `undefined`, `true`, `false`, `null` when used as identfiers', function() {
569+
forEach(['this', '$local', 'undefined', 'true', 'false', 'null'], function(identifier) {
555570
expect(createAst('foo.' + identifier)).toEqual(
556571
{
557572
type: 'Program',
@@ -3589,6 +3604,28 @@ describe('parser', function() {
35893604
$rootScope.null = {a: 42};
35903605
expect($rootScope.$eval('this.null.a')).toBe(42);
35913606
}));
3607+
3608+
it('should allow accessing $local', inject(function($rootScope) {
3609+
$rootScope.foo = 'foo';
3610+
$rootScope.bar = 'bar';
3611+
$rootScope.$local = 'foo';
3612+
var local = {foo: 42};
3613+
expect($rootScope.$eval('$local')).toBeUndefined();
3614+
expect($rootScope.$eval('$local.foo')).toBeUndefined();
3615+
expect(function() {
3616+
$rootScope.$eval('$local = {}');
3617+
}).toThrow();
3618+
expect(function() {
3619+
$rootScope.$eval('$local.bar = 23');
3620+
}).toThrow();
3621+
expect($rootScope.$eval('$local', local)).toBe(local);
3622+
expect($rootScope.$eval('$local.foo', local)).toBe(42);
3623+
expect(function() {
3624+
$rootScope.$eval('$local = {}', local);
3625+
}).toThrow();
3626+
expect($rootScope.$eval('$local.bar = 23', local)).toEqual(23);
3627+
expect(local.bar).toBe(23);
3628+
}));
35923629
});
35933630
});
35943631
});

0 commit comments

Comments
 (0)