X Tutup
Skip to content

Commit 9db13be

Browse files
committed
feat: change template micro-syntax to new syntax
Old syntax: - ng-repeat: #item in items; - ng-repeat: #item; in: items; - <template let-ng-repeat=“item” [in]=items> New syntax: - ng-repeat: var item in items; - ng-repeat: var item; in items - <template ng-repeat var-item [in]=items> Notice that the var is now a standalone binding rather then an argument to ng-repeat. This will make the var bindings consistent with the rest of the system. Closes angular#482
1 parent b1e76c5 commit 9db13be

File tree

14 files changed

+88
-30
lines changed

14 files changed

+88
-30
lines changed

modules/change_detection/src/parser/ast.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,12 @@ export class ASTWithSource extends AST {
425425

426426
export class TemplateBinding {
427427
key:string;
428+
keyIsVar:boolean;
428429
name:string;
429430
expression:ASTWithSource;
430-
constructor(key:string, name:string, expression:ASTWithSource) {
431+
constructor(key:string, keyIsVar:boolean, name:string, expression:ASTWithSource) {
431432
this.key = key;
433+
this.keyIsVar = keyIsVar;
432434
// only either name or expression will be filled.
433435
this.name = name;
434436
this.expression = expression;

modules/change_detection/src/parser/lexer.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ export class Token {
6262
return (this.type == TOKEN_TYPE_KEYWORD);
6363
}
6464

65+
isKeywordVar():boolean {
66+
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "var");
67+
}
68+
6569
isKeywordNull():boolean {
6670
return (this.type == TOKEN_TYPE_KEYWORD && this._strValue == "null");
6771
}
@@ -469,6 +473,7 @@ var OPERATORS = SetWrapper.createFromList([
469473

470474

471475
var KEYWORDS = SetWrapper.createFromList([
476+
'var',
472477
'null',
473478
'undefined',
474479
'true',

modules/change_detection/src/parser/parser.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,19 @@ class _ParseAST {
123123
}
124124
}
125125

126+
optionalKeywordVar():boolean {
127+
if (this.peekKeywordVar()) {
128+
this.advance();
129+
return true;
130+
} else {
131+
return false;
132+
}
133+
}
134+
135+
peekKeywordVar():boolean {
136+
return this.next.isKeywordVar() || this.next.isOperator('#');
137+
}
138+
126139
expectCharacter(code:int) {
127140
if (this.optionalCharacter(code)) return;
128141
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
@@ -469,21 +482,26 @@ class _ParseAST {
469482
parseTemplateBindings() {
470483
var bindings = [];
471484
while (this.index < this.tokens.length) {
485+
var keyIsVar:boolean = this.optionalKeywordVar();
472486
var key = this.expectTemplateBindingKey();
473487
this.optionalCharacter($COLON);
474488
var name = null;
475489
var expression = null;
476490
if (this.next !== EOF) {
477-
if (this.optionalOperator("#")) {
478-
name = this.expectIdentifierOrKeyword();
479-
} else {
491+
if (keyIsVar) {
492+
if (this.optionalOperator("=")) {
493+
name = this.expectTemplateBindingKey();
494+
} else {
495+
name = '\$implicit';
496+
}
497+
} else if (!this.peekKeywordVar()) {
480498
var start = this.inputIndex;
481499
var ast = this.parseExpression();
482500
var source = this.input.substring(start, this.inputIndex);
483501
expression = new ASTWithSource(ast, source, this.location);
484502
}
485503
}
486-
ListWrapper.push(bindings, new TemplateBinding(key, name, expression));
504+
ListWrapper.push(bindings, new TemplateBinding(key, keyIsVar, name, expression));
487505
if (!this.optionalCharacter($SEMICOLON)) {
488506
this.optionalCharacter($COMMA);
489507
};

modules/change_detection/test/parser/parser_spec.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,16 @@ export function main() {
408408
return ListWrapper.map(templateBindings, (binding) => binding.key );
409409
}
410410

411+
function keyValues(templateBindings) {
412+
return ListWrapper.map(templateBindings, (binding) => {
413+
if (binding.keyIsVar) {
414+
return '#' + binding.key + (isBlank(binding.name) ? '' : '=' + binding.name);
415+
} else {
416+
return binding.key + (isBlank(binding.expression) ? '' : `=${binding.expression}`)
417+
}
418+
});
419+
}
420+
411421
function names(templateBindings) {
412422
return ListWrapper.map(templateBindings, (binding) => binding.name );
413423
}
@@ -466,9 +476,7 @@ export function main() {
466476

467477
it('should detect names as value', () => {
468478
var bindings = parseTemplateBindings("a:#b");
469-
expect(names(bindings)).toEqual(['b']);
470-
expect(exprSources(bindings)).toEqual([null]);
471-
expect(exprAsts(bindings)).toEqual([null]);
479+
expect(keyValues(bindings)).toEqual(['a', '#b']);
472480
});
473481

474482
it('should allow space and colon as separators', () => {
@@ -497,6 +505,26 @@ export function main() {
497505
var bindings = parseTemplateBindings("a 1,b 2", 'location');
498506
expect(bindings[0].expression.location).toEqual('location');
499507
});
508+
509+
it('should support var/# notation', () => {
510+
var bindings = parseTemplateBindings("var i");
511+
expect(keyValues(bindings)).toEqual(['#i']);
512+
513+
bindings = parseTemplateBindings("#i");
514+
expect(keyValues(bindings)).toEqual(['#i']);
515+
516+
bindings = parseTemplateBindings("var i-a = k-a");
517+
expect(keyValues(bindings)).toEqual(['#i-a=k-a']);
518+
519+
bindings = parseTemplateBindings("keyword var item; var i = k");
520+
expect(keyValues(bindings)).toEqual(['keyword', '#item=\$implicit', '#i=k']);
521+
522+
bindings = parseTemplateBindings("keyword: #item; #i = k");
523+
expect(keyValues(bindings)).toEqual(['keyword', '#item=\$implicit', '#i=k']);
524+
525+
bindings = parseTemplateBindings("directive: var item in expr; var a = b", 'location');
526+
expect(keyValues(bindings)).toEqual(['directive', '#item=\$implicit', 'in=expr in location', '#a=b']);
527+
});
500528
});
501529

502530
describe('parseInterpolation', () => {

modules/core/src/compiler/pipeline/compile_element.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export class CompileElement {
2323
textNodeBindings:Map;
2424
propertyBindings:Map;
2525
eventBindings:Map;
26+
27+
/// Store directive name to template name mapping.
28+
/// Directive name is what the directive exports the variable as
29+
/// Template name is how it is reffered to it in template
2630
variableBindings:Map;
2731
decoratorDirectives:List<DirectiveMetadata>;
2832
templateDirective:DirectiveMetadata;
@@ -102,11 +106,11 @@ export class CompileElement {
102106
MapWrapper.set(this.propertyBindings, property, expression);
103107
}
104108

105-
addVariableBinding(contextName:string, templateName:string) {
109+
addVariableBinding(directiveName:string, templateName:string) {
106110
if (isBlank(this.variableBindings)) {
107111
this.variableBindings = MapWrapper.create();
108112
}
109-
MapWrapper.set(this.variableBindings, contextName, templateName);
113+
MapWrapper.set(this.variableBindings, templateName, directiveName);
110114
}
111115

112116
addEventBinding(eventName:string, expression:AST) {

modules/core/src/compiler/pipeline/element_binder_builder.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ export class ElementBinderBuilder extends CompileStep {
9797
MapWrapper.get(compileElement.propertyBindings, elProp) :
9898
null;
9999
if (isBlank(expression)) {
100-
throw new BaseException('No element binding found for property '+elProp
101-
+' which is required by directive '+stringify(directive.type));
100+
throw new BaseException("No element binding found for property '" + elProp
101+
+ "' which is required by directive '" + stringify(directive.type) + "'");
102102
}
103103
var len = dirProp.length;
104104
var dirBindingName = dirProp;

modules/core/src/compiler/pipeline/property_binding_parser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {CompileElement} from './compile_element';
99
import {CompileControl} from './compile_control';
1010

1111
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
12-
var BIND_NAME_REGEXP = RegExpWrapper.create('^(?:(?:(bind)|(let)|(on))-(.+))|\\[([^\\]]+)\\]|\\(([^\\)]+)\\)');
12+
var BIND_NAME_REGEXP = RegExpWrapper.create('^(?:(?:(bind)|(var)|(on))-(.+))|\\[([^\\]]+)\\]|\\(([^\\)]+)\\)');
1313

1414
/**
1515
* Parses the property bindings on a single element.
@@ -40,7 +40,7 @@ export class PropertyBindingParser extends CompileStep {
4040
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
4141
// only be present on <template> elements any more!
4242
if (!(current.element instanceof TemplateElement)) {
43-
throw new BaseException('let-* is only allowed on <template> elements!');
43+
throw new BaseException('var-* is only allowed on <template> elements!');
4444
}
4545
current.addVariableBinding(bindParts[4], attrValue);
4646
} else if (isPresent(bindParts[3])) {

modules/core/src/compiler/pipeline/view_splitter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class ViewSplitter extends CompileStep {
8080
var bindings = this._parser.parseTemplateBindings(templateBindings, this._compilationUnit);
8181
for (var i=0; i<bindings.length; i++) {
8282
var binding = bindings[i];
83-
if (isPresent(binding.name)) {
83+
if (binding.keyIsVar) {
8484
compileElement.addVariableBinding(binding.key, binding.name);
8585
} else if (isPresent(binding.expression)) {
8686
compileElement.addPropertyBinding(binding.key, binding.expression);

modules/core/test/compiler/integration_spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export function main() {
9797
});
9898

9999
it('should support template directives via `<template>` elements.', (done) => {
100-
compiler.compile(MyComp, el('<div><template let-some-tmpl="greeting"><copy-me>{{greeting}}</copy-me></template></div>')).then((pv) => {
100+
compiler.compile(MyComp, el('<div><template some-tmplate var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template></div>')).then((pv) => {
101101
createView(pv);
102102

103103
cd.detectChanges();
@@ -112,7 +112,7 @@ export function main() {
112112
});
113113

114114
it('should support template directives via `template` attribute.', (done) => {
115-
compiler.compile(MyComp, el('<div><copy-me template="some-tmpl #greeting">{{greeting}}</copy-me></div>')).then((pv) => {
115+
compiler.compile(MyComp, el('<div><copy-me template="some-tmplate: var greeting=some-tmpl">{{greeting}}</copy-me></div>')).then((pv) => {
116116
createView(pv);
117117

118118
cd.detectChanges();
@@ -170,7 +170,7 @@ class ChildComp {
170170
}
171171

172172
@Template({
173-
selector: '[some-tmpl]'
173+
selector: '[some-tmplate]'
174174
})
175175
class SomeTemplate {
176176
constructor(viewPort: ViewPort) {

modules/core/test/compiler/pipeline/element_binder_builder_spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export function main() {
249249
var pipeline = createPipeline({propertyBindings: MapWrapper.create(), directives: [SomeDecoratorDirectiveWithBinding]});
250250
expect( () => {
251251
pipeline.process(el('<div viewroot prop-binding directives>'));
252-
}).toThrowError('No element binding found for property boundprop1 which is required by directive SomeDecoratorDirectiveWithBinding');
252+
}).toThrowError("No element binding found for property 'boundprop1' which is required by directive 'SomeDecoratorDirectiveWithBinding'");
253253
});
254254

255255
});

0 commit comments

Comments
 (0)
X Tutup