X Tutup
Skip to content

Commit 2fea0c2

Browse files
committed
feat(compiler): allow to create ChangeDetectors from parsed templates
Part of #3605 Closes #3950
1 parent 5c9613e commit 2fea0c2

File tree

5 files changed

+530
-39
lines changed

5 files changed

+530
-39
lines changed

modules/angular2/src/compiler/api.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,41 @@ export class ChangeDetectionMetadata {
2020
events: string[];
2121
hostListeners: StringMap<string, string>;
2222
hostProperties: StringMap<string, string>;
23-
constructor({changeDetection, properties, events, hostListeners, hostProperties}: {
23+
callAfterContentInit: boolean;
24+
callAfterContentChecked: boolean;
25+
callAfterViewInit: boolean;
26+
callAfterViewChecked: boolean;
27+
callOnChanges: boolean;
28+
callDoCheck: boolean;
29+
callOnInit: boolean;
30+
constructor({changeDetection, properties, events, hostListeners, hostProperties,
31+
callAfterContentInit, callAfterContentChecked, callAfterViewInit,
32+
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit}: {
2433
changeDetection?: ChangeDetectionStrategy,
2534
properties?: string[],
2635
events?: string[],
2736
hostListeners?: StringMap<string, string>,
28-
hostProperties?: StringMap<string, string>
37+
hostProperties?: StringMap<string, string>,
38+
callAfterContentInit?: boolean,
39+
callAfterContentChecked?: boolean,
40+
callAfterViewInit?: boolean,
41+
callAfterViewChecked?: boolean,
42+
callOnChanges?: boolean,
43+
callDoCheck?: boolean,
44+
callOnInit?: boolean
2945
}) {
3046
this.changeDetection = changeDetection;
3147
this.properties = properties;
3248
this.events = events;
3349
this.hostListeners = hostListeners;
3450
this.hostProperties = hostProperties;
51+
this.callAfterContentInit = callAfterContentInit;
52+
this.callAfterContentChecked = callAfterContentChecked;
53+
this.callAfterViewInit = callAfterViewInit;
54+
this.callAfterViewChecked = callAfterViewChecked;
55+
this.callOnChanges = callOnChanges;
56+
this.callDoCheck = callDoCheck;
57+
this.callOnInit = callOnInit;
3558
}
3659
}
3760

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
import {ListWrapper} from 'angular2/src/core/facade/collection';
2+
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
3+
import {reflector} from 'angular2/src/core/reflection/reflection';
4+
5+
import {
6+
ChangeDetection,
7+
DirectiveIndex,
8+
BindingRecord,
9+
DirectiveRecord,
10+
ProtoChangeDetector,
11+
ChangeDetectionStrategy,
12+
ChangeDetectorDefinition,
13+
ChangeDetectorGenConfig,
14+
ASTWithSource
15+
} from 'angular2/src/core/change_detection/change_detection';
16+
17+
import {DirectiveMetadata, TypeMetadata} from './api';
18+
import {
19+
TemplateAst,
20+
ElementAst,
21+
BoundTextAst,
22+
PropertyBindingType,
23+
DirectiveAst,
24+
TemplateAstVisitor,
25+
templateVisitAll,
26+
NgContentAst,
27+
EmbeddedTemplateAst,
28+
VariableAst,
29+
BoundElementPropertyAst,
30+
BoundEventAst,
31+
BoundDirectivePropertyAst,
32+
AttrAst,
33+
TextAst
34+
} from './template_ast';
35+
36+
export function createChangeDetectorDefinitions(
37+
componentType: TypeMetadata, componentStrategy: ChangeDetectionStrategy,
38+
genConfig: ChangeDetectorGenConfig, parsedTemplate: TemplateAst[]): ChangeDetectorDefinition[] {
39+
var visitor = new ProtoViewVisitor(componentStrategy);
40+
templateVisitAll(visitor, parsedTemplate);
41+
return createChangeDefinitions(visitor.allProtoViews, componentType, genConfig);
42+
}
43+
44+
class ProtoViewVisitor implements TemplateAstVisitor {
45+
viewCount: number = 0;
46+
protoViewStack: ProtoViewVisitorData[] = [];
47+
allProtoViews: ProtoViewVisitorData[] = [];
48+
49+
constructor(componentStrategy: ChangeDetectionStrategy) {
50+
this._beginProtoView(new ProtoViewVisitorData(null, componentStrategy, this.viewCount++));
51+
}
52+
53+
private _beginProtoView(data: ProtoViewVisitorData) {
54+
this.protoViewStack.push(data);
55+
this.allProtoViews.push(data);
56+
}
57+
58+
get currentProtoView(): ProtoViewVisitorData {
59+
return this.protoViewStack[this.protoViewStack.length - 1];
60+
}
61+
62+
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
63+
this.currentProtoView.boundElementCount++;
64+
templateVisitAll(this, ast.directives);
65+
66+
this.viewCount++;
67+
this._beginProtoView(new ProtoViewVisitorData(
68+
this.currentProtoView, ChangeDetectionStrategy.Default, this.viewCount - 1));
69+
// Attention: variables present on an embedded template count towards
70+
// the embedded template and not the template anchor!
71+
templateVisitAll(this, ast.vars);
72+
templateVisitAll(this, ast.children);
73+
this.protoViewStack.pop();
74+
return null;
75+
}
76+
77+
visitElement(ast: ElementAst, context: any): any {
78+
if (ast.isBound()) {
79+
this.currentProtoView.boundElementCount++;
80+
}
81+
templateVisitAll(this, ast.properties, null);
82+
templateVisitAll(this, ast.events);
83+
templateVisitAll(this, ast.vars);
84+
for (var i = 0; i < ast.directives.length; i++) {
85+
ast.directives[i].visit(this, i);
86+
}
87+
templateVisitAll(this, ast.children);
88+
return null;
89+
}
90+
91+
visitNgContent(ast: NgContentAst, context: any): any { return null; }
92+
93+
visitVariable(ast: VariableAst, context: any): any {
94+
this.currentProtoView.variableNames.push(ast.name);
95+
return null;
96+
}
97+
98+
visitEvent(ast: BoundEventAst, directiveRecord: DirectiveRecord): any {
99+
var bindingRecord =
100+
isPresent(directiveRecord) ?
101+
BindingRecord.createForHostEvent(ast.handler, ast.name, directiveRecord) :
102+
BindingRecord.createForEvent(ast.handler, ast.name,
103+
this.currentProtoView.boundElementCount - 1);
104+
this.currentProtoView.eventRecords.push(bindingRecord);
105+
return null;
106+
}
107+
108+
visitElementProperty(ast: BoundElementPropertyAst, directiveRecord: DirectiveRecord): any {
109+
var boundElementIndex = this.currentProtoView.boundElementCount - 1;
110+
var dirIndex = isPresent(directiveRecord) ? directiveRecord.directiveIndex : null;
111+
var bindingRecord;
112+
if (ast.type === PropertyBindingType.Property) {
113+
bindingRecord =
114+
isPresent(dirIndex) ?
115+
BindingRecord.createForHostProperty(dirIndex, ast.value, ast.name) :
116+
BindingRecord.createForElementProperty(ast.value, boundElementIndex, ast.name);
117+
} else if (ast.type === PropertyBindingType.Attribute) {
118+
bindingRecord =
119+
isPresent(dirIndex) ?
120+
BindingRecord.createForHostAttribute(dirIndex, ast.value, ast.name) :
121+
BindingRecord.createForElementAttribute(ast.value, boundElementIndex, ast.name);
122+
} else if (ast.type === PropertyBindingType.Class) {
123+
bindingRecord =
124+
isPresent(dirIndex) ?
125+
BindingRecord.createForHostClass(dirIndex, ast.value, ast.name) :
126+
BindingRecord.createForElementClass(ast.value, boundElementIndex, ast.name);
127+
} else if (ast.type === PropertyBindingType.Style) {
128+
bindingRecord =
129+
isPresent(dirIndex) ?
130+
BindingRecord.createForHostStyle(dirIndex, ast.value, ast.name, ast.unit) :
131+
BindingRecord.createForElementStyle(ast.value, boundElementIndex, ast.name, ast.unit);
132+
}
133+
this.currentProtoView.bindingRecords.push(bindingRecord);
134+
return null;
135+
}
136+
visitAttr(ast: AttrAst, context: any): any { return null; }
137+
visitBoundText(ast: BoundTextAst, context: any): any {
138+
var boundTextIndex = this.currentProtoView.boundTextCount++;
139+
this.currentProtoView.bindingRecords.push(
140+
BindingRecord.createForTextNode(ast.value, boundTextIndex));
141+
return null;
142+
}
143+
visitText(ast: TextAst, context: any): any { return null; }
144+
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
145+
var directiveIndex =
146+
new DirectiveIndex(this.currentProtoView.boundElementCount - 1, directiveIndexAsNumber);
147+
var directiveMetadata = ast.directive;
148+
var changeDetectionMeta = directiveMetadata.changeDetection;
149+
var directiveRecord = new DirectiveRecord({
150+
directiveIndex: directiveIndex,
151+
callAfterContentInit: changeDetectionMeta.callAfterContentInit,
152+
callAfterContentChecked: changeDetectionMeta.callAfterContentChecked,
153+
callAfterViewInit: changeDetectionMeta.callAfterViewInit,
154+
callAfterViewChecked: changeDetectionMeta.callAfterViewChecked,
155+
callOnChanges: changeDetectionMeta.callOnChanges,
156+
callDoCheck: changeDetectionMeta.callDoCheck,
157+
callOnInit: changeDetectionMeta.callOnInit,
158+
changeDetection: changeDetectionMeta.changeDetection
159+
});
160+
this.currentProtoView.directiveRecords.push(directiveRecord);
161+
162+
templateVisitAll(this, ast.properties, directiveRecord);
163+
var bindingRecords = this.currentProtoView.bindingRecords;
164+
if (directiveRecord.callOnChanges) {
165+
bindingRecords.push(BindingRecord.createDirectiveOnChanges(directiveRecord));
166+
}
167+
if (directiveRecord.callOnInit) {
168+
bindingRecords.push(BindingRecord.createDirectiveOnInit(directiveRecord));
169+
}
170+
if (directiveRecord.callDoCheck) {
171+
bindingRecords.push(BindingRecord.createDirectiveDoCheck(directiveRecord));
172+
}
173+
templateVisitAll(this, ast.hostProperties, directiveRecord);
174+
templateVisitAll(this, ast.hostEvents, directiveRecord);
175+
return null;
176+
}
177+
visitDirectiveProperty(ast: BoundDirectivePropertyAst, directiveRecord: DirectiveRecord): any {
178+
// TODO: these setters should eventually be created by change detection, to make
179+
// it monomorphic!
180+
var setter = reflector.setter(ast.directiveName);
181+
this.currentProtoView.bindingRecords.push(
182+
BindingRecord.createForDirective(ast.value, ast.directiveName, setter, directiveRecord));
183+
return null;
184+
}
185+
}
186+
187+
class ProtoViewVisitorData {
188+
boundTextCount: number = 0;
189+
boundElementCount: number = 0;
190+
variableNames: string[] = [];
191+
bindingRecords: BindingRecord[] = [];
192+
eventRecords: BindingRecord[] = [];
193+
directiveRecords: DirectiveRecord[] = [];
194+
constructor(public parent: ProtoViewVisitorData, public strategy: ChangeDetectionStrategy,
195+
public viewIndex: number) {}
196+
}
197+
198+
function createChangeDefinitions(pvDatas: ProtoViewVisitorData[], componentType: TypeMetadata,
199+
genConfig: ChangeDetectorGenConfig): ChangeDetectorDefinition[] {
200+
var pvVariableNames = _collectNestedProtoViewsVariableNames(pvDatas);
201+
return pvDatas.map(pvData => {
202+
var viewType = pvData.viewIndex === 0 ? 'component' : 'embedded';
203+
var id = _protoViewId(componentType, pvData.viewIndex, viewType);
204+
return new ChangeDetectorDefinition(id, pvData.strategy, pvVariableNames[pvData.viewIndex],
205+
pvData.bindingRecords, pvData.eventRecords,
206+
pvData.directiveRecords, genConfig);
207+
208+
});
209+
}
210+
211+
function _collectNestedProtoViewsVariableNames(pvs: ProtoViewVisitorData[]): string[][] {
212+
var nestedPvVariableNames: string[][] = ListWrapper.createFixedSize(pvs.length);
213+
pvs.forEach((pv) => {
214+
var parentVariableNames: string[] =
215+
isPresent(pv.parent) ? nestedPvVariableNames[pv.parent.viewIndex] : [];
216+
nestedPvVariableNames[pv.viewIndex] = parentVariableNames.concat(pv.variableNames);
217+
});
218+
return nestedPvVariableNames;
219+
}
220+
221+
222+
function _protoViewId(hostComponentType: TypeMetadata, pvIndex: number, viewType: string): string {
223+
return `${hostComponentType.typeName}_${viewType}_${pvIndex}`;
224+
}

0 commit comments

Comments
 (0)
X Tutup