diff --git a/modules/angular2/src/common/directives/ng_class.ts b/modules/angular2/src/common/directives/ng_class.ts
index 2f77b0767f..cae1ca01e6 100644
--- a/modules/angular2/src/common/directives/ng_class.ts
+++ b/modules/angular2/src/common/directives/ng_class.ts
@@ -169,10 +169,10 @@ export class NgClass implements DoCheck, OnDestroy {
if (className.indexOf(' ') > -1) {
var classes = className.split(/\s+/g);
for (var i = 0, len = classes.length; i < len; i++) {
- this._renderer.setElementClass(this._ngEl, classes[i], enabled);
+ this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
}
} else {
- this._renderer.setElementClass(this._ngEl, className, enabled);
+ this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
}
}
}
diff --git a/modules/angular2/src/common/directives/ng_for.ts b/modules/angular2/src/common/directives/ng_for.ts
index 7529f14f08..ec768c4981 100644
--- a/modules/angular2/src/common/directives/ng_for.ts
+++ b/modules/angular2/src/common/directives/ng_for.ts
@@ -6,7 +6,7 @@ import {
IterableDiffers,
ViewContainerRef,
TemplateRef,
- ViewRef
+ EmbeddedViewRef
} from 'angular2/core';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
@@ -110,7 +110,8 @@ export class NgFor implements DoCheck {
}
for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
- this._viewContainer.get(i).setLocal('last', i === ilen - 1);
+ var viewRef = this._viewContainer.get(i);
+ viewRef.setLocal('last', i === ilen - 1);
}
}
@@ -153,7 +154,7 @@ export class NgFor implements DoCheck {
}
class RecordViewTuple {
- view: ViewRef;
+ view: EmbeddedViewRef;
record: any;
constructor(record, view) {
this.record = record;
diff --git a/modules/angular2/src/common/directives/ng_style.ts b/modules/angular2/src/common/directives/ng_style.ts
index 90b5d74483..99d658a0f9 100644
--- a/modules/angular2/src/common/directives/ng_style.ts
+++ b/modules/angular2/src/common/directives/ng_style.ts
@@ -92,6 +92,6 @@ export class NgStyle implements DoCheck {
}
private _setStyle(name: string, val: string): void {
- this._renderer.setElementStyle(this._ngEl, name, val);
+ this._renderer.setElementStyle(this._ngEl.nativeElement, name, val);
}
}
diff --git a/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts b/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts
index 3e52b078b3..0943268392 100644
--- a/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts
+++ b/modules/angular2/src/common/forms/directives/checkbox_value_accessor.ts
@@ -27,7 +27,7 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor {
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: any): void {
- this._renderer.setElementProperty(this._elementRef, 'checked', value);
+ this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value);
}
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
diff --git a/modules/angular2/src/common/forms/directives/default_value_accessor.ts b/modules/angular2/src/common/forms/directives/default_value_accessor.ts
index 67c88b3fac..1cc88993a5 100644
--- a/modules/angular2/src/common/forms/directives/default_value_accessor.ts
+++ b/modules/angular2/src/common/forms/directives/default_value_accessor.ts
@@ -31,7 +31,7 @@ export class DefaultValueAccessor implements ControlValueAccessor {
writeValue(value: any): void {
var normalizedValue = isBlank(value) ? '' : value;
- this._renderer.setElementProperty(this._elementRef, 'value', normalizedValue);
+ this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
}
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
diff --git a/modules/angular2/src/common/forms/directives/number_value_accessor.ts b/modules/angular2/src/common/forms/directives/number_value_accessor.ts
index 41b04f0481..1122c60625 100644
--- a/modules/angular2/src/common/forms/directives/number_value_accessor.ts
+++ b/modules/angular2/src/common/forms/directives/number_value_accessor.ts
@@ -31,7 +31,7 @@ export class NumberValueAccessor implements ControlValueAccessor {
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
writeValue(value: number): void {
- this._renderer.setElementProperty(this._elementRef, 'value', value);
+ this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
}
registerOnChange(fn: (_: number) => void): void {
diff --git a/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts b/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts
index 2fe527af13..1cb88f6db8 100644
--- a/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts
+++ b/modules/angular2/src/common/forms/directives/select_control_value_accessor.ts
@@ -51,7 +51,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
writeValue(value: any): void {
this.value = value;
- this._renderer.setElementProperty(this._elementRef, 'value', value);
+ this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
}
registerOnChange(fn: () => any): void { this.onChange = fn; }
diff --git a/modules/angular2/src/common/forms/directives/shared.ts b/modules/angular2/src/common/forms/directives/shared.ts
index a0a5bb53ec..e9a232b069 100644
--- a/modules/angular2/src/common/forms/directives/shared.ts
+++ b/modules/angular2/src/common/forms/directives/shared.ts
@@ -80,7 +80,6 @@ export function selectValueAccessor(dir: NgControl,
var defaultAccessor;
var builtinAccessor;
var customAccessor;
-
valueAccessors.forEach(v => {
if (v instanceof DefaultValueAccessor) {
defaultAccessor = v;
diff --git a/modules/angular2/src/compiler/change_definition_factory.ts b/modules/angular2/src/compiler/change_definition_factory.ts
index ce7c940aa9..de12f8cbcd 100644
--- a/modules/angular2/src/compiler/change_definition_factory.ts
+++ b/modules/angular2/src/compiler/change_definition_factory.ts
@@ -1,4 +1,4 @@
-import {ListWrapper} from 'angular2/src/facade/collection';
+import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/core/reflection/reflection';
@@ -43,7 +43,7 @@ export function createChangeDetectorDefinitions(
class ProtoViewVisitor implements TemplateAstVisitor {
viewIndex: number;
- boundTextCount: number = 0;
+ nodeCount: number = 0;
boundElementCount: number = 0;
variableNames: string[] = [];
bindingRecords: BindingRecord[] = [];
@@ -57,6 +57,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
+ this.nodeCount++;
this.boundElementCount++;
templateVisitAll(this, ast.outputs);
for (var i = 0; i < ast.directives.length; i++) {
@@ -73,6 +74,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
}
visitElement(ast: ElementAst, context: any): any {
+ this.nodeCount++;
if (ast.isBound()) {
this.boundElementCount++;
}
@@ -132,14 +134,20 @@ class ProtoViewVisitor implements TemplateAstVisitor {
}
visitAttr(ast: AttrAst, context: any): any { return null; }
visitBoundText(ast: BoundTextAst, context: any): any {
- var boundTextIndex = this.boundTextCount++;
- this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, boundTextIndex));
+ var nodeIndex = this.nodeCount++;
+ this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, nodeIndex));
+ return null;
+ }
+ visitText(ast: TextAst, context: any): any {
+ this.nodeCount++;
return null;
}
- visitText(ast: TextAst, context: any): any { return null; }
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
var directiveMetadata = ast.directive;
+ var outputsArray = [];
+ StringMapWrapper.forEach(ast.directive.outputs, (eventName, dirProperty) => outputsArray.push(
+ [dirProperty, eventName]));
var directiveRecord = new DirectiveRecord({
directiveIndex: directiveIndex,
callAfterContentInit:
@@ -153,7 +161,9 @@ class ProtoViewVisitor implements TemplateAstVisitor {
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
- changeDetection: directiveMetadata.changeDetection
+ callOnDestroy: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1,
+ changeDetection: directiveMetadata.changeDetection,
+ outputs: outputsArray
});
this.directiveRecords.push(directiveRecord);
diff --git a/modules/angular2/src/compiler/change_detector_compiler.ts b/modules/angular2/src/compiler/change_detector_compiler.ts
index 1f42bd823a..db3b727b4a 100644
--- a/modules/angular2/src/compiler/change_detector_compiler.ts
+++ b/modules/angular2/src/compiler/change_detector_compiler.ts
@@ -3,6 +3,9 @@ import {SourceExpressions, moduleRef} from './source_module';
import {
ChangeDetectorJITGenerator
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
+import {AbstractChangeDetector} from 'angular2/src/core/change_detection/abstract_change_detector';
+import {ChangeDetectionUtil} from 'angular2/src/core/change_detection/change_detection_util';
+import {ChangeDetectorState} from 'angular2/src/core/change_detection/constants';
import {createChangeDetectorDefinitions} from './change_definition_factory';
import {IS_DART, isJsObject, CONST_EXPR} from 'angular2/src/facade/lang';
@@ -23,6 +26,12 @@ const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
const UTIL = "ChangeDetectionUtil";
const CHANGE_DETECTOR_STATE = "ChangeDetectorState";
+export const CHANGE_DETECTION_JIT_IMPORTS = CONST_EXPR({
+ 'AbstractChangeDetector': AbstractChangeDetector,
+ 'ChangeDetectionUtil': ChangeDetectionUtil,
+ 'ChangeDetectorState': ChangeDetectorState
+});
+
var ABSTRACT_CHANGE_DETECTOR_MODULE = moduleRef(
`package:angular2/src/core/change_detection/abstract_change_detector${MODULE_SUFFIX}`);
var UTIL_MODULE =
@@ -45,14 +54,8 @@ export class ChangeDetectionCompiler {
}
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
- if (IS_DART || !this._genConfig.useJit) {
- var proto = new DynamicProtoChangeDetector(definition);
- return (dispatcher) => proto.instantiate(dispatcher);
- } else {
- return new ChangeDetectorJITGenerator(definition, UTIL, ABSTRACT_CHANGE_DETECTOR,
- CHANGE_DETECTOR_STATE)
- .generate();
- }
+ var proto = new DynamicProtoChangeDetector(definition);
+ return () => proto.instantiate();
}
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
@@ -81,7 +84,7 @@ export class ChangeDetectionCompiler {
definition, `${UTIL_MODULE}${UTIL}`,
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`,
`${CONSTANTS_MODULE}${CHANGE_DETECTOR_STATE}`);
- factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
+ factories.push(`function() { return new ${codegen.typeName}(); }`);
sourcePart = codegen.generateSource();
}
index++;
diff --git a/modules/angular2/src/compiler/command_compiler.ts b/modules/angular2/src/compiler/command_compiler.ts
deleted file mode 100644
index 5819b4da1e..0000000000
--- a/modules/angular2/src/compiler/command_compiler.ts
+++ /dev/null
@@ -1,375 +0,0 @@
-import {isPresent, isBlank, Type, isString, StringWrapper, IS_DART} from 'angular2/src/facade/lang';
-import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
-import {
- TemplateCmd,
- TextCmd,
- NgContentCmd,
- BeginElementCmd,
- EndElementCmd,
- BeginComponentCmd,
- EndComponentCmd,
- EmbeddedTemplateCmd,
- CompiledComponentTemplate
-} from 'angular2/src/core/linker/template_commands';
-import {
- TemplateAst,
- TemplateAstVisitor,
- NgContentAst,
- EmbeddedTemplateAst,
- ElementAst,
- VariableAst,
- BoundEventAst,
- BoundElementPropertyAst,
- AttrAst,
- BoundTextAst,
- TextAst,
- DirectiveAst,
- BoundDirectivePropertyAst,
- templateVisitAll
-} from './template_ast';
-import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
-import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
-
-import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
-import {
- escapeSingleQuoteString,
- codeGenConstConstructorCall,
- codeGenValueFn,
- MODULE_SUFFIX
-} from './util';
-import {Injectable} from 'angular2/src/core/di';
-
-export var TEMPLATE_COMMANDS_MODULE_REF =
- moduleRef(`package:angular2/src/core/linker/template_commands${MODULE_SUFFIX}`);
-
-const IMPLICIT_TEMPLATE_VAR = '\$implicit';
-const CLASS_ATTR = 'class';
-const STYLE_ATTR = 'style';
-
-@Injectable()
-export class CommandCompiler {
- compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
- changeDetectorFactories: Function[],
- componentTemplateFactory: Function): TemplateCmd[] {
- var visitor = new CommandBuilderVisitor(
- new RuntimeCommandFactory(component, componentTemplateFactory, changeDetectorFactories), 0);
- templateVisitAll(visitor, template);
- return visitor.result;
- }
-
- compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
- changeDetectorFactoryExpressions: string[],
- componentTemplateFactory: Function): SourceExpression {
- var visitor =
- new CommandBuilderVisitor(new CodegenCommandFactory(component, componentTemplateFactory,
- changeDetectorFactoryExpressions),
- 0);
- templateVisitAll(visitor, template);
- return new SourceExpression([], codeGenArray(visitor.result));
- }
-}
-
-interface CommandFactory {
- createText(value: string, isBound: boolean, ngContentIndex: number): R;
- createNgContent(index: number, ngContentIndex: number): R;
- createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- isBound: boolean, ngContentIndex: number): R;
- createEndElement(): R;
- createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- encapsulation: ViewEncapsulation, ngContentIndex: number): R;
- createEndComponent(): R;
- createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- isMerged: boolean, ngContentIndex: number, children: R[]): R;
-}
-
-class RuntimeCommandFactory implements CommandFactory {
- constructor(private component: CompileDirectiveMetadata,
- private componentTemplateFactory: Function,
- private changeDetectorFactories: Function[]) {}
- private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] {
- return directives.map(directive => directive.type.runtime);
- }
-
- createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
- return new TextCmd(value, isBound, ngContentIndex);
- }
- createNgContent(index: number, ngContentIndex: number): TemplateCmd {
- return new NgContentCmd(index, ngContentIndex);
- }
- createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- isBound: boolean, ngContentIndex: number): TemplateCmd {
- return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
- this._mapDirectives(directives), isBound, ngContentIndex);
- }
- createEndElement(): TemplateCmd { return new EndElementCmd(); }
- createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- encapsulation: ViewEncapsulation, ngContentIndex: number): TemplateCmd {
- var nestedTemplateAccessor = this.componentTemplateFactory(directives[0]);
- return new BeginComponentCmd(name, attrNameAndValues, eventTargetAndNames,
- variableNameAndValues, this._mapDirectives(directives),
- encapsulation, ngContentIndex, nestedTemplateAccessor);
- }
- createEndComponent(): TemplateCmd { return new EndComponentCmd(); }
- createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- isMerged: boolean, ngContentIndex: number,
- children: TemplateCmd[]): TemplateCmd {
- return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues,
- this._mapDirectives(directives), isMerged, ngContentIndex,
- this.changeDetectorFactories[embeddedTemplateIndex], children);
- }
-}
-
-class CodegenCommandFactory implements CommandFactory {
- constructor(private component: CompileDirectiveMetadata,
- private componentTemplateFactory: Function,
- private changeDetectorFactoryExpressions: string[]) {}
-
- createText(value: string, isBound: boolean, ngContentIndex: number): Expression {
- return new Expression(
- `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'TextCmd')}(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`);
- }
- createNgContent(index: number, ngContentIndex: number): Expression {
- return new Expression(
- `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'NgContentCmd')}(${index}, ${ngContentIndex})`);
- }
- createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- isBound: boolean, ngContentIndex: number): Expression {
- var attrsExpression = codeGenArray(attrNameAndValues);
- return new Expression(
- `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginElementCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
- `${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`);
- }
- createEndElement(): Expression {
- return new Expression(
- `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndElementCmd')}()`);
- }
- createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- encapsulation: ViewEncapsulation, ngContentIndex: number): Expression {
- var attrsExpression = codeGenArray(attrNameAndValues);
- return new Expression(
- `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginComponentCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
- `${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${codeGenViewEncapsulation(encapsulation)}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`);
- }
- createEndComponent(): Expression {
- return new Expression(
- `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndComponentCmd')}()`);
- }
- createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
- variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
- isMerged: boolean, ngContentIndex: number,
- children: Expression[]): Expression {
- return new Expression(
- `${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EmbeddedTemplateCmd')}(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` +
- `${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, ${codeGenArray(children)})`);
- }
-}
-
-function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
- context: any): any {
- templateVisitAll(visitor, asts, context);
- return context;
-}
-
-class CommandBuilderVisitor implements TemplateAstVisitor {
- result: R[] = [];
- transitiveNgContentCount: number = 0;
- constructor(public commandFactory: CommandFactory, public embeddedTemplateIndex: number) {}
-
- private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
- attrAsts: TemplateAst[]): string[] {
- var attrs = keyValueArrayToMap(visitAndReturnContext(this, attrAsts, []));
- directives.forEach(directiveMeta => {
- StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
- var prevValue = attrs[name];
- attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
- });
- });
- return mapToKeyValueArray(attrs);
- }
-
- visitNgContent(ast: NgContentAst, context: any): any {
- this.transitiveNgContentCount++;
- this.result.push(this.commandFactory.createNgContent(ast.index, ast.ngContentIndex));
- return null;
- }
- visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
- this.embeddedTemplateIndex++;
- var childVisitor = new CommandBuilderVisitor(this.commandFactory, this.embeddedTemplateIndex);
- templateVisitAll(childVisitor, ast.children);
- var isMerged = childVisitor.transitiveNgContentCount > 0;
- var variableNameAndValues = [];
- ast.vars.forEach((varAst) => {
- variableNameAndValues.push(varAst.name);
- variableNameAndValues.push(varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR);
- });
- var directives = [];
- ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
- directiveAst.visit(this, new DirectiveContext(index, [], [], directives));
- });
- this.result.push(this.commandFactory.createEmbeddedTemplate(
- this.embeddedTemplateIndex, this._readAttrNameAndValues(directives, ast.attrs),
- variableNameAndValues, directives, isMerged, ast.ngContentIndex, childVisitor.result));
- this.transitiveNgContentCount += childVisitor.transitiveNgContentCount;
- this.embeddedTemplateIndex = childVisitor.embeddedTemplateIndex;
- return null;
- }
- visitElement(ast: ElementAst, context: any): any {
- var component = ast.getComponent();
- var eventTargetAndNames = visitAndReturnContext(this, ast.outputs, []);
- var variableNameAndValues = [];
- if (isBlank(component)) {
- ast.exportAsVars.forEach((varAst) => {
- variableNameAndValues.push(varAst.name);
- variableNameAndValues.push(null);
- });
- }
- var directives = [];
- ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
- directiveAst.visit(this, new DirectiveContext(index, eventTargetAndNames,
- variableNameAndValues, directives));
- });
- eventTargetAndNames = removeKeyValueArrayDuplicates(eventTargetAndNames);
-
- var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
- if (isPresent(component)) {
- this.result.push(this.commandFactory.createBeginComponent(
- ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
- component.template.encapsulation, ast.ngContentIndex));
- templateVisitAll(this, ast.children);
- this.result.push(this.commandFactory.createEndComponent());
- } else {
- this.result.push(this.commandFactory.createBeginElement(
- ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
- ast.isBound(), ast.ngContentIndex));
- templateVisitAll(this, ast.children);
- this.result.push(this.commandFactory.createEndElement());
- }
- return null;
- }
- visitVariable(ast: VariableAst, ctx: any): any { return null; }
- visitAttr(ast: AttrAst, attrNameAndValues: string[]): any {
- attrNameAndValues.push(ast.name);
- attrNameAndValues.push(ast.value);
- return null;
- }
- visitBoundText(ast: BoundTextAst, context: any): any {
- this.result.push(this.commandFactory.createText(null, true, ast.ngContentIndex));
- return null;
- }
- visitText(ast: TextAst, context: any): any {
- this.result.push(this.commandFactory.createText(ast.value, false, ast.ngContentIndex));
- return null;
- }
- visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any {
- ctx.targetDirectives.push(ast.directive);
- templateVisitAll(this, ast.hostEvents, ctx.eventTargetAndNames);
- ast.exportAsVars.forEach(varAst => {
- ctx.targetVariableNameAndValues.push(varAst.name);
- ctx.targetVariableNameAndValues.push(ctx.index);
- });
- return null;
- }
- visitEvent(ast: BoundEventAst, eventTargetAndNames: string[]): any {
- eventTargetAndNames.push(ast.target);
- eventTargetAndNames.push(ast.name);
- return null;
- }
- visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
- visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
-}
-
-function removeKeyValueArrayDuplicates(keyValueArray: string[]): string[] {
- var knownPairs = new Set();
- var resultKeyValueArray = [];
- for (var i = 0; i < keyValueArray.length; i += 2) {
- var key = keyValueArray[i];
- var value = keyValueArray[i + 1];
- var pairId = `${key}:${value}`;
- if (!SetWrapper.has(knownPairs, pairId)) {
- resultKeyValueArray.push(key);
- resultKeyValueArray.push(value);
- knownPairs.add(pairId);
- }
- }
- return resultKeyValueArray;
-}
-
-function keyValueArrayToMap(keyValueArr: string[]): {[key: string]: string} {
- var data: {[key: string]: string} = {};
- for (var i = 0; i < keyValueArr.length; i += 2) {
- data[keyValueArr[i]] = keyValueArr[i + 1];
- }
- return data;
-}
-
-function mapToKeyValueArray(data: {[key: string]: string}): string[] {
- var entryArray = [];
- StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
- // We need to sort to get a defined output order
- // for tests and for caching generated artifacts...
- ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
- var keyValueArray = [];
- entryArray.forEach((entry) => {
- keyValueArray.push(entry[0]);
- keyValueArray.push(entry[1]);
- });
- return keyValueArray;
-}
-
-function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
- if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
- return `${attrValue1} ${attrValue2}`;
- } else {
- return attrValue2;
- }
-}
-
-class DirectiveContext {
- constructor(public index: number, public eventTargetAndNames: string[],
- public targetVariableNameAndValues: any[],
- public targetDirectives: CompileDirectiveMetadata[]) {}
-}
-
-class Expression {
- constructor(public value: string) {}
-}
-
-function escapeValue(value: any): string {
- if (value instanceof Expression) {
- return value.value;
- } else if (isString(value)) {
- return escapeSingleQuoteString(value);
- } else if (isBlank(value)) {
- return 'null';
- } else {
- return `${value}`;
- }
-}
-
-function codeGenArray(data: any[]): string {
- var base = `[${data.map(escapeValue).join(',')}]`;
- return IS_DART ? `const ${base}` : base;
-}
-
-function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
- var expressions = directives.map(
- directiveType => `${moduleRef(directiveType.type.moduleUrl)}${directiveType.type.name}`);
- var base = `[${expressions.join(',')}]`;
- return IS_DART ? `const ${base}` : base;
-}
-
-function codeGenViewEncapsulation(value: ViewEncapsulation): string {
- if (IS_DART) {
- return `${TEMPLATE_COMMANDS_MODULE_REF}${value}`;
- } else {
- return `${value}`;
- }
-}
diff --git a/modules/angular2/src/compiler/compiler.ts b/modules/angular2/src/compiler/compiler.ts
index c35bc9104c..23426ce6c1 100644
--- a/modules/angular2/src/compiler/compiler.ts
+++ b/modules/angular2/src/compiler/compiler.ts
@@ -17,7 +17,8 @@ import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
-import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
+import {ViewCompiler} from 'angular2/src/compiler/view_compiler';
+import {ProtoViewCompiler} from 'angular2/src/compiler/proto_view_compiler';
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
import {Compiler} from 'angular2/src/core/linker/compiler';
@@ -44,7 +45,8 @@ export const COMPILER_PROVIDERS: Array = CONST_EXPR([
RuntimeMetadataResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
- CommandCompiler,
+ ProtoViewCompiler,
+ ViewCompiler,
ChangeDetectionCompiler,
new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}),
TemplateCompiler,
diff --git a/modules/angular2/src/compiler/directive_metadata.ts b/modules/angular2/src/compiler/directive_metadata.ts
index f82f6b031a..2fe7ef0d38 100644
--- a/modules/angular2/src/compiler/directive_metadata.ts
+++ b/modules/angular2/src/compiler/directive_metadata.ts
@@ -7,6 +7,7 @@ import {
RegExpWrapper,
StringWrapper
} from 'angular2/src/facade/lang';
+import {unimplemented} from 'angular2/src/facade/exceptions';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {
ChangeDetectionStrategy,
@@ -21,6 +22,16 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i
// group 2: "event" from "(event)"
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
+export abstract class CompileMetadataWithType {
+ static fromJson(data: {[key: string]: any}): CompileMetadataWithType {
+ return _COMPILE_METADATA_FROM_JSON[data['class']](data);
+ }
+
+ abstract toJson(): {[key: string]: any};
+
+ get type(): CompileTypeMetadata { return unimplemented(); }
+}
+
/**
* Metadata regarding compilation of a type.
*/
@@ -107,7 +118,7 @@ export class CompileTemplateMetadata {
/**
* Metadata regarding compilation of a directive.
*/
-export class CompileDirectiveMetadata {
+export class CompileDirectiveMetadata implements CompileMetadataWithType {
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
outputs, host, lifecycleHooks, template}: {
type?: CompileTypeMetadata,
@@ -241,6 +252,7 @@ export class CompileDirectiveMetadata {
toJson(): {[key: string]: any} {
return {
+ 'class': 'Directive',
'isComponent': this.isComponent,
'dynamicLoadable': this.dynamicLoadable,
'selector': this.selector,
@@ -284,3 +296,38 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
selector: '*'
});
}
+
+
+export class CompilePipeMetadata implements CompileMetadataWithType {
+ type: CompileTypeMetadata;
+ name: string;
+ pure: boolean;
+ constructor({type, name,
+ pure}: {type?: CompileTypeMetadata, name?: string, pure?: boolean} = {}) {
+ this.type = type;
+ this.name = name;
+ this.pure = normalizeBool(pure);
+ }
+
+ static fromJson(data: {[key: string]: any}): CompilePipeMetadata {
+ return new CompilePipeMetadata({
+ type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'],
+ name: data['name'],
+ pure: data['pure']
+ });
+ }
+
+ toJson(): {[key: string]: any} {
+ return {
+ 'class': 'Pipe',
+ 'type': isPresent(this.type) ? this.type.toJson() : null,
+ 'name': this.name,
+ 'pure': this.pure
+ };
+ }
+}
+
+var _COMPILE_METADATA_FROM_JSON = {
+ 'Directive': CompileDirectiveMetadata.fromJson,
+ 'Pipe': CompilePipeMetadata.fromJson
+};
diff --git a/modules/angular2/src/compiler/proto_view_compiler.ts b/modules/angular2/src/compiler/proto_view_compiler.ts
new file mode 100644
index 0000000000..9b211391bc
--- /dev/null
+++ b/modules/angular2/src/compiler/proto_view_compiler.ts
@@ -0,0 +1,397 @@
+import {
+ isPresent,
+ isBlank,
+ Type,
+ isString,
+ StringWrapper,
+ IS_DART,
+ CONST_EXPR
+} from 'angular2/src/facade/lang';
+import {
+ SetWrapper,
+ StringMapWrapper,
+ ListWrapper,
+ MapWrapper
+} from 'angular2/src/facade/collection';
+import {
+ TemplateAst,
+ TemplateAstVisitor,
+ NgContentAst,
+ EmbeddedTemplateAst,
+ ElementAst,
+ VariableAst,
+ BoundEventAst,
+ BoundElementPropertyAst,
+ AttrAst,
+ BoundTextAst,
+ TextAst,
+ DirectiveAst,
+ BoundDirectivePropertyAst,
+ templateVisitAll
+} from './template_ast';
+import {
+ CompileTypeMetadata,
+ CompileDirectiveMetadata,
+ CompilePipeMetadata
+} from './directive_metadata';
+import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
+import {AppProtoView, AppView} from 'angular2/src/core/linker/view';
+import {ViewType} from 'angular2/src/core/linker/view_type';
+import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
+import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
+import {
+ escapeSingleQuoteString,
+ codeGenConstConstructorCall,
+ codeGenValueFn,
+ codeGenFnHeader,
+ MODULE_SUFFIX,
+ codeGenStringMap,
+ Expression,
+ Statement
+} from './util';
+import {Injectable} from 'angular2/src/core/di';
+
+export const PROTO_VIEW_JIT_IMPORTS = CONST_EXPR(
+ {'AppProtoView': AppProtoView, 'AppProtoElement': AppProtoElement, 'ViewType': ViewType});
+
+// TODO: have a single file that reexports everything needed for
+// codegen explicitly
+// - helps understanding what codegen works against
+// - less imports in codegen code
+export var APP_VIEW_MODULE_REF = moduleRef('package:angular2/src/core/linker/view' + MODULE_SUFFIX);
+export var VIEW_TYPE_MODULE_REF =
+ moduleRef('package:angular2/src/core/linker/view_type' + MODULE_SUFFIX);
+export var APP_EL_MODULE_REF =
+ moduleRef('package:angular2/src/core/linker/element' + MODULE_SUFFIX);
+export var METADATA_MODULE_REF =
+ moduleRef('package:angular2/src/core/metadata/view' + MODULE_SUFFIX);
+
+const IMPLICIT_TEMPLATE_VAR = '\$implicit';
+const CLASS_ATTR = 'class';
+const STYLE_ATTR = 'style';
+
+@Injectable()
+export class ProtoViewCompiler {
+ constructor() {}
+
+ compileProtoViewRuntime(metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
+ template: TemplateAst[], pipes: CompilePipeMetadata[]):
+ CompileProtoViews {
+ var protoViewFactory = new RuntimeProtoViewFactory(metadataCache, component, pipes);
+ var allProtoViews = [];
+ protoViewFactory.createCompileProtoView(template, [], [], allProtoViews);
+ return new CompileProtoViews([], allProtoViews);
+ }
+
+ compileProtoViewCodeGen(resolvedMetadataCacheExpr: Expression,
+ component: CompileDirectiveMetadata, template: TemplateAst[],
+ pipes: CompilePipeMetadata[]):
+ CompileProtoViews {
+ var protoViewFactory = new CodeGenProtoViewFactory(resolvedMetadataCacheExpr, component, pipes);
+ var allProtoViews = [];
+ var allStatements = [];
+ protoViewFactory.createCompileProtoView(template, [], allStatements, allProtoViews);
+ return new CompileProtoViews(
+ allStatements.map(stmt => stmt.statement), allProtoViews);
+ }
+}
+
+export class CompileProtoViews {
+ constructor(public declarations: STATEMENT[],
+ public protoViews: CompileProtoView[]) {}
+}
+
+
+export class CompileProtoView {
+ constructor(public embeddedTemplateIndex: number,
+ public protoElements: CompileProtoElement[],
+ public protoView: APP_PROTO_VIEW) {}
+}
+
+export class CompileProtoElement {
+ constructor(public boundElementIndex, public attrNameAndValues: string[][],
+ public variableNameAndValues: string[][], public renderEvents: BoundEventAst[],
+ public directives: CompileDirectiveMetadata[], public embeddedTemplateIndex: number,
+ public appProtoEl: APP_PROTO_EL) {}
+}
+
+function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
+ context: any): any {
+ templateVisitAll(visitor, asts, context);
+ return context;
+}
+
+abstract class ProtoViewFactory {
+ constructor(public component: CompileDirectiveMetadata) {}
+
+ abstract createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
+ templateVariableBindings: string[][],
+ targetStatements: STATEMENT[]): APP_PROTO_VIEW;
+
+ abstract createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
+ variableNameAndValues: string[][],
+ directives: CompileDirectiveMetadata[],
+ targetStatements: STATEMENT[]): APP_PROTO_EL;
+
+ createCompileProtoView(template: TemplateAst[], templateVariableBindings: string[][],
+ targetStatements: STATEMENT[],
+ targetProtoViews: CompileProtoView[]):
+ CompileProtoView {
+ var embeddedTemplateIndex = targetProtoViews.length;
+ // Note: targetProtoViews needs to be in depth first order.
+ // So we "reserve" a space here that we fill after the recursion is done
+ targetProtoViews.push(null);
+ var builder = new ProtoViewBuilderVisitor(
+ this, targetStatements, targetProtoViews);
+ templateVisitAll(builder, template);
+ var viewType = getViewType(this.component, embeddedTemplateIndex);
+ var appProtoView = this.createAppProtoView(embeddedTemplateIndex, viewType,
+ templateVariableBindings, targetStatements);
+ var cpv = new CompileProtoView(
+ embeddedTemplateIndex, builder.protoElements, appProtoView);
+ targetProtoViews[embeddedTemplateIndex] = cpv;
+ return cpv;
+ }
+}
+
+class CodeGenProtoViewFactory extends ProtoViewFactory {
+ private _nextVarId: number = 0;
+
+ constructor(public resolvedMetadataCacheExpr: Expression, component: CompileDirectiveMetadata,
+ public pipes: CompilePipeMetadata[]) {
+ super(component);
+ }
+
+ private _nextProtoViewVar(embeddedTemplateIndex: number): string {
+ return `appProtoView${this._nextVarId++}_${this.component.type.name}${embeddedTemplateIndex}`;
+ }
+
+ createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
+ templateVariableBindings: string[][],
+ targetStatements: Statement[]): Expression {
+ var protoViewVarName = this._nextProtoViewVar(embeddedTemplateIndex);
+ var viewTypeExpr = codeGenViewType(viewType);
+ var pipesExpr = embeddedTemplateIndex === 0 ?
+ codeGenTypesArray(this.pipes.map(pipeMeta => pipeMeta.type)) :
+ null;
+ var statement =
+ `var ${protoViewVarName} = ${APP_VIEW_MODULE_REF}AppProtoView.create(${this.resolvedMetadataCacheExpr.expression}, ${viewTypeExpr}, ${pipesExpr}, ${codeGenStringMap(templateVariableBindings)});`;
+ targetStatements.push(new Statement(statement));
+ return new Expression(protoViewVarName);
+ }
+
+ createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
+ variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
+ targetStatements: Statement[]): Expression {
+ var varName = `appProtoEl${this._nextVarId++}_${this.component.type.name}`;
+ var value = `${APP_EL_MODULE_REF}AppProtoElement.create(
+ ${this.resolvedMetadataCacheExpr.expression},
+ ${boundElementIndex},
+ ${codeGenStringMap(attrNameAndValues)},
+ ${codeGenDirectivesArray(directives)},
+ ${codeGenStringMap(variableNameAndValues)}
+ )`;
+ var statement = `var ${varName} = ${value};`;
+ targetStatements.push(new Statement(statement));
+ return new Expression(varName);
+ }
+}
+
+class RuntimeProtoViewFactory extends ProtoViewFactory {
+ constructor(public metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
+ public pipes: CompilePipeMetadata[]) {
+ super(component);
+ }
+
+ createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
+ templateVariableBindings: string[][], targetStatements: any[]): AppProtoView {
+ var pipes =
+ embeddedTemplateIndex === 0 ? this.pipes.map(pipeMeta => pipeMeta.type.runtime) : [];
+ var templateVars = keyValueArrayToStringMap(templateVariableBindings);
+ return AppProtoView.create(this.metadataCache, viewType, pipes, templateVars);
+ }
+
+ createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
+ variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
+ targetStatements: any[]): AppProtoElement {
+ var attrs = keyValueArrayToStringMap(attrNameAndValues);
+ return AppProtoElement.create(this.metadataCache, boundElementIndex, attrs,
+ directives.map(dirMeta => dirMeta.type.runtime),
+ keyValueArrayToStringMap(variableNameAndValues));
+ }
+}
+
+class ProtoViewBuilderVisitor implements
+ TemplateAstVisitor {
+ protoElements: CompileProtoElement[] = [];
+ boundElementCount: number = 0;
+
+ constructor(public factory: ProtoViewFactory,
+ public allStatements: STATEMENT[],
+ public allProtoViews: CompileProtoView[]) {}
+
+ private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
+ attrAsts: TemplateAst[]): string[][] {
+ var attrs = visitAndReturnContext(this, attrAsts, {});
+ directives.forEach(directiveMeta => {
+ StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
+ var prevValue = attrs[name];
+ attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
+ });
+ });
+ return mapToKeyValueArray(attrs);
+ }
+
+ visitBoundText(ast: BoundTextAst, context: any): any { return null; }
+ visitText(ast: TextAst, context: any): any { return null; }
+
+ visitNgContent(ast: NgContentAst, context: any): any { return null; }
+
+ visitElement(ast: ElementAst, context: any): any {
+ var boundElementIndex = null;
+ if (ast.isBound()) {
+ boundElementIndex = this.boundElementCount++;
+ }
+ var component = ast.getComponent();
+
+ var variableNameAndValues: string[][] = [];
+ if (isBlank(component)) {
+ ast.exportAsVars.forEach((varAst) => { variableNameAndValues.push([varAst.name, null]); });
+ }
+ var directives = [];
+ var renderEvents: Map =
+ visitAndReturnContext(this, ast.outputs, new Map());
+ ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
+ directiveAst.visit(this, new DirectiveContext(index, boundElementIndex, renderEvents,
+ variableNameAndValues, directives));
+ });
+ var renderEventArray = [];
+ renderEvents.forEach((eventAst, _) => renderEventArray.push(eventAst));
+
+ var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
+ this._addProtoElement(ast.isBound(), boundElementIndex, attrNameAndValues,
+ variableNameAndValues, renderEventArray, directives, null);
+ templateVisitAll(this, ast.children);
+ return null;
+ }
+
+ visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
+ var boundElementIndex = this.boundElementCount++;
+ var directives: CompileDirectiveMetadata[] = [];
+ ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
+ directiveAst.visit(
+ this, new DirectiveContext(index, boundElementIndex, new Map(), [],
+ directives));
+ });
+
+ var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
+ var templateVariableBindings = ast.vars.map(
+ varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
+ var nestedProtoView = this.factory.createCompileProtoView(
+ ast.children, templateVariableBindings, this.allStatements, this.allProtoViews);
+ this._addProtoElement(true, boundElementIndex, attrNameAndValues, [], [], directives,
+ nestedProtoView.embeddedTemplateIndex);
+ return null;
+ }
+
+ private _addProtoElement(isBound: boolean, boundElementIndex, attrNameAndValues: string[][],
+ variableNameAndValues: string[][], renderEvents: BoundEventAst[],
+ directives: CompileDirectiveMetadata[], embeddedTemplateIndex: number) {
+ var appProtoEl = null;
+ if (isBound) {
+ appProtoEl =
+ this.factory.createAppProtoElement(boundElementIndex, attrNameAndValues,
+ variableNameAndValues, directives, this.allStatements);
+ }
+ var compileProtoEl = new CompileProtoElement(
+ boundElementIndex, attrNameAndValues, variableNameAndValues, renderEvents, directives,
+ embeddedTemplateIndex, appProtoEl);
+ this.protoElements.push(compileProtoEl);
+ }
+
+ visitVariable(ast: VariableAst, ctx: any): any { return null; }
+ visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any {
+ attrNameAndValues[ast.name] = ast.value;
+ return null;
+ }
+ visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any {
+ ctx.targetDirectives.push(ast.directive);
+ templateVisitAll(this, ast.hostEvents, ctx.hostEventTargetAndNames);
+ ast.exportAsVars.forEach(
+ varAst => { ctx.targetVariableNameAndValues.push([varAst.name, ctx.index]); });
+ return null;
+ }
+ visitEvent(ast: BoundEventAst, eventTargetAndNames: Map): any {
+ eventTargetAndNames.set(ast.fullName, ast);
+ return null;
+ }
+ visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
+ visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
+}
+
+function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
+ var entryArray = [];
+ StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
+ // We need to sort to get a defined output order
+ // for tests and for caching generated artifacts...
+ ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
+ var keyValueArray = [];
+ entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
+ return keyValueArray;
+}
+
+function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
+ if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
+ return `${attrValue1} ${attrValue2}`;
+ } else {
+ return attrValue2;
+ }
+}
+
+class DirectiveContext {
+ constructor(public index: number, public boundElementIndex: number,
+ public hostEventTargetAndNames: Map,
+ public targetVariableNameAndValues: any[][],
+ public targetDirectives: CompileDirectiveMetadata[]) {}
+}
+
+function keyValueArrayToStringMap(keyValueArray: any[][]): {[key: string]: any} {
+ var stringMap: {[key: string]: string} = {};
+ for (var i = 0; i < keyValueArray.length; i++) {
+ var entry = keyValueArray[i];
+ stringMap[entry[0]] = entry[1];
+ }
+ return stringMap;
+}
+
+function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
+ var expressions = directives.map(directiveType => typeRef(directiveType.type));
+ return `[${expressions.join(',')}]`;
+}
+
+function codeGenTypesArray(types: CompileTypeMetadata[]): string {
+ var expressions = types.map(typeRef);
+ return `[${expressions.join(',')}]`;
+}
+
+function codeGenViewType(value: ViewType): string {
+ if (IS_DART) {
+ return `${VIEW_TYPE_MODULE_REF}${value}`;
+ } else {
+ return `${value}`;
+ }
+}
+
+function typeRef(type: CompileTypeMetadata): string {
+ return `${moduleRef(type.moduleUrl)}${type.name}`;
+}
+
+function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
+ if (embeddedTemplateIndex > 0) {
+ return ViewType.EMBEDDED;
+ } else if (component.type.isHost) {
+ return ViewType.HOST;
+ } else {
+ return ViewType.COMPONENT;
+ }
+}
diff --git a/modules/angular2/src/compiler/runtime_compiler.ts b/modules/angular2/src/compiler/runtime_compiler.ts
index 54ae61b31d..07ac2990a3 100644
--- a/modules/angular2/src/compiler/runtime_compiler.ts
+++ b/modules/angular2/src/compiler/runtime_compiler.ts
@@ -1,23 +1,23 @@
-import {Compiler, Compiler_, internalCreateProtoView} from 'angular2/src/core/linker/compiler';
-import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
-import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
+import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler';
+import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
import {TemplateCompiler} from './template_compiler';
import {Injectable} from 'angular2/src/core/di';
import {Type} from 'angular2/src/facade/lang';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
-export abstract class RuntimeCompiler extends Compiler {}
+export abstract class RuntimeCompiler extends Compiler {
+ abstract compileInHost(componentType: Type): Promise;
+ abstract clearCache();
+}
@Injectable()
export class RuntimeCompiler_ extends Compiler_ implements RuntimeCompiler {
- constructor(_protoViewFactory: ProtoViewFactory, private _templateCompiler: TemplateCompiler) {
- super(_protoViewFactory);
- }
+ constructor(private _templateCompiler: TemplateCompiler) { super(); }
- compileInHost(componentType: Type): Promise {
+ compileInHost(componentType: Type): Promise {
return this._templateCompiler.compileHostComponentRuntime(componentType)
- .then(compiledHostTemplate => internalCreateProtoView(this, compiledHostTemplate));
+ .then(hostViewFactory => new HostViewFactoryRef_(hostViewFactory));
}
clearCache() {
diff --git a/modules/angular2/src/compiler/runtime_metadata.ts b/modules/angular2/src/compiler/runtime_metadata.ts
index 1fceb0e859..51b3494b4e 100644
--- a/modules/angular2/src/compiler/runtime_metadata.ts
+++ b/modules/angular2/src/compiler/runtime_metadata.ts
@@ -11,25 +11,29 @@ import {BaseException} from 'angular2/src/facade/exceptions';
import * as cpl from './directive_metadata';
import * as md from 'angular2/src/core/metadata/directives';
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
+import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver';
import {ViewResolver} from 'angular2/src/core/linker/view_resolver';
import {ViewMetadata} from 'angular2/src/core/metadata/view';
import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector';
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
-import {PLATFORM_DIRECTIVES} from 'angular2/src/core/platform_directives_and_pipes';
+import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
import {MODULE_SUFFIX} from './util';
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
@Injectable()
export class RuntimeMetadataResolver {
- private _cache = new Map();
+ private _directiveCache = new Map();
+ private _pipeCache = new Map();
- constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
- @Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[]) {}
+ constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
+ private _viewResolver: ViewResolver,
+ @Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[],
+ @Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Type[]) {}
- getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
- var meta = this._cache.get(directiveType);
+ getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
+ var meta = this._directiveCache.get(directiveType);
if (isBlank(meta)) {
var dirMeta = this._directiveResolver.resolve(directiveType);
var moduleUrl = null;
@@ -63,7 +67,23 @@ export class RuntimeMetadataResolver {
host: dirMeta.host,
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType))
});
- this._cache.set(directiveType, meta);
+ this._directiveCache.set(directiveType, meta);
+ }
+ return meta;
+ }
+
+ getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata {
+ var meta = this._pipeCache.get(pipeType);
+ if (isBlank(meta)) {
+ var pipeMeta = this._pipeResolver.resolve(pipeType);
+ var moduleUrl = reflector.importUri(pipeType);
+ meta = new cpl.CompilePipeMetadata({
+ type: new cpl.CompileTypeMetadata(
+ {name: stringify(pipeType), moduleUrl: moduleUrl, runtime: pipeType}),
+ name: pipeMeta.name,
+ pure: pipeMeta.pure
+ });
+ this._pipeCache.set(pipeType, meta);
}
return meta;
}
@@ -72,13 +92,25 @@ export class RuntimeMetadataResolver {
var view = this._viewResolver.resolve(component);
var directives = flattenDirectives(view, this._platformDirectives);
for (var i = 0; i < directives.length; i++) {
- if (!isValidDirective(directives[i])) {
+ if (!isValidType(directives[i])) {
throw new BaseException(
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
}
}
- return directives.map(type => this.getMetadata(type));
+ return directives.map(type => this.getDirectiveMetadata(type));
+ }
+
+ getViewPipesMetadata(component: Type): cpl.CompilePipeMetadata[] {
+ var view = this._viewResolver.resolve(component);
+ var pipes = flattenPipes(view, this._platformPipes);
+ for (var i = 0; i < pipes.length; i++) {
+ if (!isValidType(pipes[i])) {
+ throw new BaseException(
+ `Unexpected piped value '${stringify(pipes[i])}' on the View of component '${stringify(component)}'`);
+ }
+ }
+ return pipes.map(type => this.getPipeMetadata(type));
}
}
@@ -93,6 +125,17 @@ function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[
return directives;
}
+function flattenPipes(view: ViewMetadata, platformPipes: any[]): Type[] {
+ let pipes = [];
+ if (isPresent(platformPipes)) {
+ flattenArray(platformPipes, pipes);
+ }
+ if (isPresent(view.pipes)) {
+ flattenArray(view.pipes, pipes);
+ }
+ return pipes;
+}
+
function flattenArray(tree: any[], out: Array): void {
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
@@ -104,7 +147,7 @@ function flattenArray(tree: any[], out: Array): void {
}
}
-function isValidDirective(value: Type): boolean {
+function isValidType(value: Type): boolean {
return isPresent(value) && (value instanceof Type);
}
diff --git a/modules/angular2/src/compiler/source_module.ts b/modules/angular2/src/compiler/source_module.ts
index 49dc23a9bf..c100d36c29 100644
--- a/modules/angular2/src/compiler/source_module.ts
+++ b/modules/angular2/src/compiler/source_module.ts
@@ -10,6 +10,10 @@ export function moduleRef(moduleUrl): string {
* Represents generated source code with module references. Internal to the Angular compiler.
*/
export class SourceModule {
+ static getSourceWithoutImports(sourceWithModuleRefs: string): string {
+ return StringWrapper.replaceAllMapped(sourceWithModuleRefs, MODULE_REGEXP, (match) => '');
+ }
+
constructor(public moduleUrl: string, public sourceWithModuleRefs: string) {}
getSourceWithImports(): SourceWithImports {
diff --git a/modules/angular2/src/compiler/style_compiler.ts b/modules/angular2/src/compiler/style_compiler.ts
index a4ee6fd0c9..8cc5437214 100644
--- a/modules/angular2/src/compiler/style_compiler.ts
+++ b/modules/angular2/src/compiler/style_compiler.ts
@@ -14,7 +14,10 @@ import {
MODULE_SUFFIX
} from './util';
import {Injectable} from 'angular2/src/core/di';
-import {COMPONENT_VARIABLE, HOST_ATTR, CONTENT_ATTR} from 'angular2/src/core/render/view_factory';
+
+const COMPONENT_VARIABLE = '%COMP%';
+const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
+const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
@Injectable()
export class StyleCompiler {
diff --git a/modules/angular2/src/compiler/template_compiler.ts b/modules/angular2/src/compiler/template_compiler.ts
index 06b6300000..54a5fccef8 100644
--- a/modules/angular2/src/compiler/template_compiler.ts
+++ b/modules/angular2/src/compiler/template_compiler.ts
@@ -1,37 +1,74 @@
-import {IS_DART, Type, Json, isBlank, stringify} from 'angular2/src/facade/lang';
-import {BaseException} from 'angular2/src/facade/exceptions';
-import {ListWrapper, SetWrapper, MapWrapper} from 'angular2/src/facade/collection';
-import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {
- CompiledComponentTemplate,
- TemplateCmd,
- CompiledHostTemplate,
- BeginComponentCmd
-} from 'angular2/src/core/linker/template_commands';
+ IS_DART,
+ Type,
+ Json,
+ isBlank,
+ isPresent,
+ stringify,
+ evalExpression
+} from 'angular2/src/facade/lang';
+import {BaseException} from 'angular2/src/facade/exceptions';
+import {
+ ListWrapper,
+ SetWrapper,
+ MapWrapper,
+ StringMapWrapper
+} from 'angular2/src/facade/collection';
+import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {
createHostComponentMeta,
CompileDirectiveMetadata,
CompileTypeMetadata,
- CompileTemplateMetadata
+ CompileTemplateMetadata,
+ CompilePipeMetadata,
+ CompileMetadataWithType
} from './directive_metadata';
-import {TemplateAst} from './template_ast';
+import {
+ TemplateAst,
+ TemplateAstVisitor,
+ NgContentAst,
+ EmbeddedTemplateAst,
+ ElementAst,
+ VariableAst,
+ BoundEventAst,
+ BoundElementPropertyAst,
+ AttrAst,
+ BoundTextAst,
+ TextAst,
+ DirectiveAst,
+ BoundDirectivePropertyAst,
+ templateVisitAll
+} from './template_ast';
import {Injectable} from 'angular2/src/core/di';
-import {SourceModule, moduleRef} from './source_module';
-import {ChangeDetectionCompiler} from './change_detector_compiler';
+import {SourceModule, moduleRef, SourceExpression} from './source_module';
+import {ChangeDetectionCompiler, CHANGE_DETECTION_JIT_IMPORTS} from './change_detector_compiler';
import {StyleCompiler} from './style_compiler';
-import {CommandCompiler} from './command_compiler';
-import {TemplateParser} from './template_parser';
+import {ViewCompiler, VIEW_JIT_IMPORTS} from './view_compiler';
+import {
+ ProtoViewCompiler,
+ APP_VIEW_MODULE_REF,
+ CompileProtoView,
+ PROTO_VIEW_JIT_IMPORTS
+} from './proto_view_compiler';
+import {TemplateParser, PipeCollector} from './template_parser';
import {TemplateNormalizer} from './template_normalizer';
import {RuntimeMetadataResolver} from './runtime_metadata';
+import {HostViewFactory} from 'angular2/src/core/linker/view';
+import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
+import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
-import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
import {
codeGenExportVariable,
escapeSingleQuoteString,
codeGenValueFn,
- MODULE_SUFFIX
+ MODULE_SUFFIX,
+ addAll,
+ Expression
} from './util';
+export var METADATA_CACHE_MODULE_REF =
+ moduleRef('package:angular2/src/core/linker/resolved_metadata_cache' + MODULE_SUFFIX);
+
/**
* An internal module of the Angular compiler that begins with component types,
* extracts templates, and eventually produces a compiled version of the component
@@ -40,15 +77,16 @@ import {
@Injectable()
export class TemplateCompiler {
private _hostCacheKeys = new Map();
- private _compiledTemplateCache = new Map();
- private _compiledTemplateDone = new Map>();
- private _nextTemplateId: number = 0;
+ private _compiledTemplateCache = new Map();
+ private _compiledTemplateDone = new Map>();
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
private _templateNormalizer: TemplateNormalizer,
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
- private _commandCompiler: CommandCompiler,
- private _cdCompiler: ChangeDetectionCompiler) {}
+ private _cdCompiler: ChangeDetectionCompiler,
+ private _protoViewCompiler: ProtoViewCompiler, private _viewCompiler: ViewCompiler,
+ private _resolvedMetadataCache: ResolvedMetadataCache,
+ private _genConfig: ChangeDetectorGenConfig) {}
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
Promise {
@@ -75,99 +113,29 @@ export class TemplateCompiler {
}));
}
- compileHostComponentRuntime(type: Type): Promise {
+ compileHostComponentRuntime(type: Type): Promise {
+ var compMeta: CompileDirectiveMetadata =
+ this._runtimeMetadataResolver.getDirectiveMetadata(type);
var hostCacheKey = this._hostCacheKeys.get(type);
if (isBlank(hostCacheKey)) {
hostCacheKey = new Object();
this._hostCacheKeys.set(type, hostCacheKey);
- var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
assertComponent(compMeta);
var hostMeta: CompileDirectiveMetadata =
createHostComponentMeta(compMeta.type, compMeta.selector);
- this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
+ this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], [], new Set());
}
return this._compiledTemplateDone.get(hostCacheKey)
- .then(compiledTemplate => new CompiledHostTemplate(compiledTemplate));
+ .then((compiledTemplate: CompiledTemplate) =>
+ new HostViewFactory(compMeta.selector, compiledTemplate.viewFactory));
}
clearCache() {
- this._hostCacheKeys.clear();
this._styleCompiler.clearCache();
this._compiledTemplateCache.clear();
this._compiledTemplateDone.clear();
- }
-
- private _compileComponentRuntime(
- cacheKey: any, compMeta: CompileDirectiveMetadata, viewDirectives: CompileDirectiveMetadata[],
- compilingComponentCacheKeys: Set): CompiledComponentTemplate {
- let uniqViewDirectives = removeDuplicates(viewDirectives);
- var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
- var done = this._compiledTemplateDone.get(cacheKey);
- if (isBlank(compiledTemplate)) {
- var styles = [];
- var changeDetectorFactory;
- var commands = [];
- var templateId = `${stringify(compMeta.type.runtime)}Template${this._nextTemplateId++}`;
- compiledTemplate = new CompiledComponentTemplate(
- templateId, (dispatcher) => changeDetectorFactory(dispatcher), commands, styles);
- this._compiledTemplateCache.set(cacheKey, compiledTemplate);
- compilingComponentCacheKeys.add(cacheKey);
- done = PromiseWrapper
- .all([this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
- uniqViewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
- .then((stylesAndNormalizedViewDirMetas: any[]) => {
- var childPromises = [];
- var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
- var parsedTemplate = this._templateParser.parse(
- compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
-
- var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
- compMeta.type, compMeta.changeDetection, parsedTemplate);
- changeDetectorFactory = changeDetectorFactories[0];
- var tmpStyles: string[] = stylesAndNormalizedViewDirMetas[0];
- tmpStyles.forEach(style => styles.push(style));
- var tmpCommands: TemplateCmd[] = this._compileCommandsRuntime(
- compMeta, parsedTemplate, changeDetectorFactories,
- compilingComponentCacheKeys, childPromises);
- tmpCommands.forEach(cmd => commands.push(cmd));
- return PromiseWrapper.all(childPromises);
- })
- .then((_) => {
- SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
- return compiledTemplate;
- });
- this._compiledTemplateDone.set(cacheKey, done);
- }
- return compiledTemplate;
- }
-
- private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
- changeDetectorFactories: Function[],
- compilingComponentCacheKeys: Set,
- childPromises: Promise[]): TemplateCmd[] {
- var cmds: TemplateCmd[] = this._commandCompiler.compileComponentRuntime(
- compMeta, parsedTemplate, changeDetectorFactories,
- (childComponentDir: CompileDirectiveMetadata) => {
- var childCacheKey = childComponentDir.type.runtime;
- var childViewDirectives: CompileDirectiveMetadata[] =
- this._runtimeMetadataResolver.getViewDirectivesMetadata(
- childComponentDir.type.runtime);
- var childIsRecursive = SetWrapper.has(compilingComponentCacheKeys, childCacheKey);
- var childTemplate = this._compileComponentRuntime(
- childCacheKey, childComponentDir, childViewDirectives, compilingComponentCacheKeys);
- if (!childIsRecursive) {
- // Only wait for a child if it is not a cycle
- childPromises.push(this._compiledTemplateDone.get(childCacheKey));
- }
- return () => childTemplate;
- });
- cmds.forEach(cmd => {
- if (cmd instanceof BeginComponentCmd) {
- cmd.templateGetter();
- }
- });
- return cmds;
+ this._hostCacheKeys.clear();
}
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
@@ -175,38 +143,22 @@ export class TemplateCompiler {
throw new BaseException('No components given');
}
var declarations = [];
- var templateArguments = [];
- var componentMetas: CompileDirectiveMetadata[] = [];
components.forEach(componentWithDirs => {
var compMeta = componentWithDirs.component;
assertComponent(compMeta);
- componentMetas.push(compMeta);
-
- this._processTemplateCodeGen(compMeta, componentWithDirs.directives, declarations,
- templateArguments);
+ this._compileComponentCodeGen(compMeta, componentWithDirs.directives, componentWithDirs.pipes,
+ declarations);
if (compMeta.dynamicLoadable) {
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
- componentMetas.push(hostMeta);
- this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
+ var viewFactoryExpression =
+ this._compileComponentCodeGen(hostMeta, [compMeta], [], declarations);
+ var constructionKeyword = IS_DART ? 'const' : 'new';
+ var compiledTemplateExpr =
+ `${constructionKeyword} ${APP_VIEW_MODULE_REF}HostViewFactory('${compMeta.selector}',${viewFactoryExpression})`;
+ var varName = codeGenHostViewFactoryName(compMeta.type);
+ declarations.push(`${codeGenExportVariable(varName)}${compiledTemplateExpr};`);
}
});
- ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
- index: number) => {
- var templateId = `${compMeta.type.moduleUrl}|${compMeta.type.name}`;
- var constructionKeyword = IS_DART ? 'const' : 'new';
- var compiledTemplateExpr =
- `${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledComponentTemplate('${templateId}',${(templateArguments[index]).join(',')})`;
- var variableValueExpr;
- if (compMeta.type.isHost) {
- variableValueExpr =
- `${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${compiledTemplateExpr})`;
- } else {
- variableValueExpr = compiledTemplateExpr;
- }
- var varName = templateVariableName(compMeta.type);
- declarations.push(`${codeGenExportVariable(varName)}${variableValueExpr};`);
- declarations.push(`${codeGenValueFn([], varName, templateGetterName(compMeta.type))};`);
- });
var moduleUrl = components[0].component.type.moduleUrl;
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
}
@@ -215,31 +167,149 @@ export class TemplateCompiler {
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
}
- private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
- directives: CompileDirectiveMetadata[],
- targetDeclarations: string[], targetTemplateArguments: any[][]) {
- let uniqueDirectives = removeDuplicates(directives);
+
+
+ private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata,
+ viewDirectives: CompileDirectiveMetadata[],
+ pipes: CompilePipeMetadata[],
+ compilingComponentCacheKeys: Set): CompiledTemplate {
+ let uniqViewDirectives = removeDuplicates(viewDirectives);
+ let uniqViewPipes = removeDuplicates(pipes);
+ var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
+ var done = this._compiledTemplateDone.get(cacheKey);
+ if (isBlank(compiledTemplate)) {
+ compiledTemplate = new CompiledTemplate();
+ this._compiledTemplateCache.set(cacheKey, compiledTemplate);
+ compilingComponentCacheKeys.add(cacheKey);
+ done = PromiseWrapper
+ .all([this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
+ uniqViewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
+ .then((stylesAndNormalizedViewDirMetas: any[]) => {
+ var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
+ var styles = stylesAndNormalizedViewDirMetas[0];
+ var parsedTemplate = this._templateParser.parse(
+ compMeta.template.template, normalizedViewDirMetas, uniqViewPipes,
+ compMeta.type.name);
+
+ var childPromises = [];
+ var usedDirectives = DirectiveCollector.findUsedDirectives(parsedTemplate);
+ usedDirectives.components.forEach(
+ component => this._compileNestedComponentRuntime(
+ component, compilingComponentCacheKeys, childPromises));
+ return PromiseWrapper.all(childPromises)
+ .then((_) => {
+ var filteredPipes = filterPipes(parsedTemplate, uniqViewPipes);
+ compiledTemplate.init(this._createViewFactoryRuntime(
+ compMeta, parsedTemplate, usedDirectives.directives, styles,
+ filteredPipes));
+ SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
+ return compiledTemplate;
+ });
+ });
+ this._compiledTemplateDone.set(cacheKey, done);
+ }
+ return compiledTemplate;
+ }
+
+ private _compileNestedComponentRuntime(childComponentDir: CompileDirectiveMetadata,
+ compilingComponentCacheKeys: Set,
+ childPromises: Promise[]) {
+ var childCacheKey = childComponentDir.type.runtime;
+ var childViewDirectives: CompileDirectiveMetadata[] =
+ this._runtimeMetadataResolver.getViewDirectivesMetadata(childComponentDir.type.runtime);
+ var childViewPipes: CompilePipeMetadata[] =
+ this._runtimeMetadataResolver.getViewPipesMetadata(childComponentDir.type.runtime);
+ var childIsRecursive = SetWrapper.has(compilingComponentCacheKeys, childCacheKey);
+ this._compileComponentRuntime(childCacheKey, childComponentDir, childViewDirectives,
+ childViewPipes, compilingComponentCacheKeys);
+ if (!childIsRecursive) {
+ // Only wait for a child if it is not a cycle
+ childPromises.push(this._compiledTemplateDone.get(childCacheKey));
+ }
+ }
+
+ private _createViewFactoryRuntime(compMeta: CompileDirectiveMetadata,
+ parsedTemplate: TemplateAst[],
+ directives: CompileDirectiveMetadata[], styles: string[],
+ pipes: CompilePipeMetadata[]): Function {
+ if (IS_DART || !this._genConfig.useJit) {
+ var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
+ compMeta.type, compMeta.changeDetection, parsedTemplate);
+ var protoViews = this._protoViewCompiler.compileProtoViewRuntime(
+ this._resolvedMetadataCache, compMeta, parsedTemplate, pipes);
+ return this._viewCompiler.compileComponentRuntime(
+ compMeta, parsedTemplate, styles, protoViews.protoViews, changeDetectorFactories,
+ (compMeta) => this._getNestedComponentViewFactory(compMeta));
+ } else {
+ var declarations = [];
+ var viewFactoryExpr = this._createViewFactoryCodeGen('resolvedMetadataCache', compMeta,
+ new SourceExpression([], 'styles'),
+ parsedTemplate, pipes, declarations);
+ var vars: {[key: string]: any} =
+ {'exports': {}, 'styles': styles, 'resolvedMetadataCache': this._resolvedMetadataCache};
+ directives.forEach(dirMeta => {
+ vars[dirMeta.type.name] = dirMeta.type.runtime;
+ if (dirMeta.isComponent && dirMeta.type.runtime !== compMeta.type.runtime) {
+ vars[`viewFactory_${dirMeta.type.name}0`] = this._getNestedComponentViewFactory(dirMeta);
+ }
+ });
+ pipes.forEach(pipeMeta => vars[pipeMeta.type.name] = pipeMeta.type.runtime);
+ var declarationsWithoutImports =
+ SourceModule.getSourceWithoutImports(declarations.join('\n'));
+ return evalExpression(
+ `viewFactory_${compMeta.type.name}`, viewFactoryExpr, declarationsWithoutImports,
+ mergeStringMaps(
+ [vars, CHANGE_DETECTION_JIT_IMPORTS, PROTO_VIEW_JIT_IMPORTS, VIEW_JIT_IMPORTS]));
+ }
+ }
+
+ private _getNestedComponentViewFactory(compMeta: CompileDirectiveMetadata): Function {
+ return this._compiledTemplateCache.get(compMeta.type.runtime).viewFactory;
+ }
+
+ private _compileComponentCodeGen(compMeta: CompileDirectiveMetadata,
+ directives: CompileDirectiveMetadata[],
+ pipes: CompilePipeMetadata[],
+ targetDeclarations: string[]): string {
+ let uniqueDirectives = removeDuplicates(directives);
+ let uniqPipes = removeDuplicates(pipes);
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
var parsedTemplate = this._templateParser.parse(compMeta.template.template, uniqueDirectives,
- compMeta.type.name);
+ uniqPipes, compMeta.type.name);
+ var filteredPipes = filterPipes(parsedTemplate, uniqPipes);
+ return this._createViewFactoryCodeGen(
+ `${METADATA_CACHE_MODULE_REF}CODEGEN_RESOLVED_METADATA_CACHE`, compMeta, styleExpr,
+ parsedTemplate, filteredPipes, targetDeclarations);
+ }
+
+ private _createViewFactoryCodeGen(resolvedMetadataCacheExpr: string,
+ compMeta: CompileDirectiveMetadata, styleExpr: SourceExpression,
+ parsedTemplate: TemplateAst[], pipes: CompilePipeMetadata[],
+ targetDeclarations: string[]): string {
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
compMeta.type, compMeta.changeDetection, parsedTemplate);
- var commandsExpr = this._commandCompiler.compileComponentCodeGen(
- compMeta, parsedTemplate, changeDetectorsExprs.expressions,
- codeGenComponentTemplateFactory);
+ var protoViewExprs = this._protoViewCompiler.compileProtoViewCodeGen(
+ new Expression(resolvedMetadataCacheExpr), compMeta, parsedTemplate, pipes);
+ var viewFactoryExpr = this._viewCompiler.compileComponentCodeGen(
+ compMeta, parsedTemplate, styleExpr, protoViewExprs.protoViews, changeDetectorsExprs,
+ codeGenComponentViewFactoryName);
- addAll(styleExpr.declarations, targetDeclarations);
addAll(changeDetectorsExprs.declarations, targetDeclarations);
- addAll(commandsExpr.declarations, targetDeclarations);
+ addAll(protoViewExprs.declarations, targetDeclarations);
+ addAll(viewFactoryExpr.declarations, targetDeclarations);
- targetTemplateArguments.push(
- [changeDetectorsExprs.expressions[0], commandsExpr.expression, styleExpr.expression]);
+ return viewFactoryExpr.expression;
}
}
export class NormalizedComponentWithViewDirectives {
constructor(public component: CompileDirectiveMetadata,
- public directives: CompileDirectiveMetadata[]) {}
+ public directives: CompileDirectiveMetadata[], public pipes: CompilePipeMetadata[]) {}
+}
+
+class CompiledTemplate {
+ viewFactory: Function = null;
+ init(viewFactory: Function) { this.viewFactory = viewFactory; }
}
function assertComponent(meta: CompileDirectiveMetadata) {
@@ -248,30 +318,28 @@ function assertComponent(meta: CompileDirectiveMetadata) {
}
}
-function templateVariableName(type: CompileTypeMetadata): string {
- return `${type.name}Template`;
-}
-
-function templateGetterName(type: CompileTypeMetadata): string {
- return `${templateVariableName(type)}Getter`;
-}
-
function templateModuleUrl(moduleUrl: string): string {
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
}
-function addAll(source: any[], target: any[]) {
- for (var i = 0; i < source.length; i++) {
- target.push(source[i]);
- }
+
+function codeGenHostViewFactoryName(type: CompileTypeMetadata): string {
+ return `hostViewFactory_${type.name}`;
}
-function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
- return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateGetterName(nestedCompType.type)}`;
+function codeGenComponentViewFactoryName(nestedCompType: CompileDirectiveMetadata): string {
+ return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}viewFactory_${nestedCompType.type.name}0`;
}
-function removeDuplicates(items: CompileDirectiveMetadata[]): CompileDirectiveMetadata[] {
+function mergeStringMaps(maps: Array<{[key: string]: any}>): {[key: string]: any} {
+ var result = {};
+ maps.forEach(
+ (map) => { StringMapWrapper.forEach(map, (value, key) => { result[key] = value; }); });
+ return result;
+}
+
+function removeDuplicates(items: CompileMetadataWithType[]): CompileMetadataWithType[] {
let res = [];
items.forEach(item => {
let hasMatch =
@@ -284,3 +352,100 @@ function removeDuplicates(items: CompileDirectiveMetadata[]): CompileDirectiveMe
});
return res;
}
+
+class DirectiveCollector implements TemplateAstVisitor {
+ static findUsedDirectives(parsedTemplate: TemplateAst[]): DirectiveCollector {
+ var collector = new DirectiveCollector();
+ templateVisitAll(collector, parsedTemplate);
+ return collector;
+ }
+
+ directives: CompileDirectiveMetadata[] = [];
+ components: CompileDirectiveMetadata[] = [];
+
+ visitBoundText(ast: BoundTextAst, context: any): any { return null; }
+ visitText(ast: TextAst, context: any): any { return null; }
+
+ visitNgContent(ast: NgContentAst, context: any): any { return null; }
+
+ visitElement(ast: ElementAst, context: any): any {
+ templateVisitAll(this, ast.directives);
+ templateVisitAll(this, ast.children);
+ return null;
+ }
+
+ visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
+ templateVisitAll(this, ast.directives);
+ templateVisitAll(this, ast.children);
+ return null;
+ }
+ visitVariable(ast: VariableAst, ctx: any): any { return null; }
+ visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any { return null; }
+ visitDirective(ast: DirectiveAst, ctx: any): any {
+ if (ast.directive.isComponent) {
+ this.components.push(ast.directive);
+ }
+ this.directives.push(ast.directive);
+ return null;
+ }
+ visitEvent(ast: BoundEventAst, eventTargetAndNames: Map): any {
+ return null;
+ }
+ visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
+ visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
+}
+
+
+function filterPipes(template: TemplateAst[],
+ allPipes: CompilePipeMetadata[]): CompilePipeMetadata[] {
+ var visitor = new PipeVisitor();
+ templateVisitAll(visitor, template);
+ return allPipes.filter((pipeMeta) => SetWrapper.has(visitor.collector.pipes, pipeMeta.name));
+}
+
+class PipeVisitor implements TemplateAstVisitor {
+ collector: PipeCollector = new PipeCollector();
+
+ visitBoundText(ast: BoundTextAst, context: any): any {
+ ast.value.visit(this.collector);
+ return null;
+ }
+ visitText(ast: TextAst, context: any): any { return null; }
+
+ visitNgContent(ast: NgContentAst, context: any): any { return null; }
+
+ visitElement(ast: ElementAst, context: any): any {
+ templateVisitAll(this, ast.inputs);
+ templateVisitAll(this, ast.outputs);
+ templateVisitAll(this, ast.directives);
+ templateVisitAll(this, ast.children);
+ return null;
+ }
+
+ visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
+ templateVisitAll(this, ast.outputs);
+ templateVisitAll(this, ast.directives);
+ templateVisitAll(this, ast.children);
+ return null;
+ }
+ visitVariable(ast: VariableAst, ctx: any): any { return null; }
+ visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any { return null; }
+ visitDirective(ast: DirectiveAst, ctx: any): any {
+ templateVisitAll(this, ast.inputs);
+ templateVisitAll(this, ast.hostEvents);
+ templateVisitAll(this, ast.hostProperties);
+ return null;
+ }
+ visitEvent(ast: BoundEventAst, eventTargetAndNames: Map): any {
+ ast.handler.visit(this.collector);
+ return null;
+ }
+ visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
+ ast.value.visit(this.collector);
+ return null;
+ }
+ visitElementProperty(ast: BoundElementPropertyAst, context: any): any {
+ ast.value.visit(this.collector);
+ return null;
+ }
+}
diff --git a/modules/angular2/src/compiler/template_parser.ts b/modules/angular2/src/compiler/template_parser.ts
index c93307378b..deaae63693 100644
--- a/modules/angular2/src/compiler/template_parser.ts
+++ b/modules/angular2/src/compiler/template_parser.ts
@@ -5,10 +5,11 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
-import {CompileDirectiveMetadata} from './directive_metadata';
+import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata';
import {HtmlParser} from './html_parser';
import {splitNsName} from './html_tags';
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
+import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
import {
@@ -88,9 +89,10 @@ export class TemplateParser {
private _htmlParser: HtmlParser,
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
- parse(template: string, directives: CompileDirectiveMetadata[],
+ parse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
templateUrl: string): TemplateAst[] {
- var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
+ var parseVisitor =
+ new TemplateParseVisitor(directives, pipes, this._exprParser, this._schemaRegistry);
var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors);
@@ -111,9 +113,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
errors: TemplateParseError[] = [];
directivesIndex = new Map();
ngContentCount: number = 0;
+ pipesByName: Map;
- constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser,
- private _schemaRegistry: ElementSchemaRegistry) {
+ constructor(directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
+ private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {
this.selectorMatcher = new SelectorMatcher();
ListWrapper.forEachWithIndex(directives,
(directive: CompileDirectiveMetadata, index: number) => {
@@ -121,6 +124,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
this.selectorMatcher.addSelectables(selector, directive);
this.directivesIndex.set(directive, index);
});
+ this.pipesByName = new Map();
+ pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
}
private _reportError(message: string, sourceSpan: ParseSourceSpan) {
@@ -130,7 +135,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
var sourceInfo = sourceSpan.start.toString();
try {
- return this._exprParser.parseInterpolation(value, sourceInfo);
+ var ast = this._exprParser.parseInterpolation(value, sourceInfo);
+ this._checkPipes(ast, sourceSpan);
+ return ast;
} catch (e) {
this._reportError(`${e}`, sourceSpan);
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
@@ -140,7 +147,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
var sourceInfo = sourceSpan.start.toString();
try {
- return this._exprParser.parseAction(value, sourceInfo);
+ var ast = this._exprParser.parseAction(value, sourceInfo);
+ this._checkPipes(ast, sourceSpan);
+ return ast;
} catch (e) {
this._reportError(`${e}`, sourceSpan);
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
@@ -150,7 +159,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
var sourceInfo = sourceSpan.start.toString();
try {
- return this._exprParser.parseBinding(value, sourceInfo);
+ var ast = this._exprParser.parseBinding(value, sourceInfo);
+ this._checkPipes(ast, sourceSpan);
+ return ast;
} catch (e) {
this._reportError(`${e}`, sourceSpan);
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
@@ -160,13 +171,31 @@ class TemplateParseVisitor implements HtmlAstVisitor {
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
var sourceInfo = sourceSpan.start.toString();
try {
- return this._exprParser.parseTemplateBindings(value, sourceInfo);
+ var bindings = this._exprParser.parseTemplateBindings(value, sourceInfo);
+ bindings.forEach((binding) => {
+ if (isPresent(binding.expression)) {
+ this._checkPipes(binding.expression, sourceSpan);
+ }
+ });
+ return bindings;
} catch (e) {
this._reportError(`${e}`, sourceSpan);
return [];
}
}
+ private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
+ if (isPresent(ast)) {
+ var collector = new PipeCollector();
+ ast.visit(collector);
+ collector.pipes.forEach((pipeName) => {
+ if (!this.pipesByName.has(pipeName)) {
+ this._reportError(`The pipe '${pipeName}' could not be found`, sourceSpan);
+ }
+ });
+ }
+ }
+
visitText(ast: HtmlTextAst, component: Component): any {
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
@@ -714,3 +743,14 @@ function createElementCssSelector(elementName: string, matchableAttrs: string[][
var EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
+
+
+export class PipeCollector extends RecursiveAstVisitor {
+ pipes: Set = new Set();
+ visitPipe(ast: BindingPipe): any {
+ this.pipes.add(ast.name);
+ ast.exp.visit(this);
+ this.visitAll(ast.args);
+ return null;
+ }
+}
diff --git a/modules/angular2/src/compiler/util.ts b/modules/angular2/src/compiler/util.ts
index ca0c6e8005..f3ebdfd1a7 100644
--- a/modules/angular2/src/compiler/util.ts
+++ b/modules/angular2/src/compiler/util.ts
@@ -1,4 +1,11 @@
-import {IS_DART, StringWrapper, isBlank} from 'angular2/src/facade/lang';
+import {
+ IS_DART,
+ StringWrapper,
+ isBlank,
+ isPresent,
+ isString,
+ isArray
+} from 'angular2/src/facade/lang';
var CAMEL_CASE_REGEXP = /([A-Z])/g;
var DASH_CASE_REGEXP = /-([a-z])/g;
@@ -7,6 +14,8 @@ var DOUBLE_QUOTE_ESCAPE_STRING_RE = /"|\\|\n|\r|\$/g;
export var MODULE_SUFFIX = IS_DART ? '.dart' : '.js';
+export var CONST_VAR = IS_DART ? 'const' : 'var';
+
export function camelCaseToDashCase(input: string): string {
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
(m) => { return '-' + m[1].toLowerCase(); });
@@ -63,12 +72,19 @@ export function codeGenConstConstructorCall(name: string): string {
export function codeGenValueFn(params: string[], value: string, fnName: string = ''): string {
if (IS_DART) {
- return `${fnName}(${params.join(',')}) => ${value}`;
+ return `${codeGenFnHeader(params, fnName)} => ${value}`;
} else {
- return `function ${fnName}(${params.join(',')}) { return ${value}; }`;
+ return `${codeGenFnHeader(params, fnName)} { return ${value}; }`;
}
}
+export function codeGenFnHeader(params: string[], fnName: string = ''): string {
+ if (IS_DART) {
+ return `${fnName}(${params.join(',')})`;
+ } else {
+ return `function ${fnName}(${params.join(',')})`;
+ }
+}
export function codeGenToString(expr: string): string {
if (IS_DART) {
return `'\${${expr}}'`;
@@ -86,3 +102,77 @@ export function splitAtColon(input: string, defaultValues: string[]): string[] {
return defaultValues;
}
}
+
+
+export class Statement {
+ constructor(public statement: string) {}
+}
+
+export class Expression {
+ constructor(public expression: string, public isArray = false) {}
+}
+
+export function escapeValue(value: any): string {
+ if (value instanceof Expression) {
+ return value.expression;
+ } else if (isString(value)) {
+ return escapeSingleQuoteString(value);
+ } else if (isBlank(value)) {
+ return 'null';
+ } else {
+ return `${value}`;
+ }
+}
+
+export function codeGenArray(data: any[]): string {
+ return `[${data.map(escapeValue).join(',')}]`;
+}
+
+export function codeGenFlatArray(values: any[]): string {
+ var result = '([';
+ var isFirstArrayEntry = true;
+ var concatFn = IS_DART ? '.addAll' : 'concat';
+ for (var i = 0; i < values.length; i++) {
+ var value = values[i];
+ if (value instanceof Expression && (value).isArray) {
+ result += `]).${concatFn}(${value.expression}).${concatFn}([`;
+ isFirstArrayEntry = true;
+ } else {
+ if (!isFirstArrayEntry) {
+ result += ',';
+ }
+ isFirstArrayEntry = false;
+ result += escapeValue(value);
+ }
+ }
+ result += '])';
+ return result;
+}
+
+export function codeGenStringMap(keyValueArray: any[][]): string {
+ return `{${keyValueArray.map(codeGenKeyValue).join(',')}}`;
+}
+
+function codeGenKeyValue(keyValue: any[]): string {
+ return `${escapeValue(keyValue[0])}:${escapeValue(keyValue[1])}`;
+}
+
+export function addAll(source: any[], target: any[]) {
+ for (var i = 0; i < source.length; i++) {
+ target.push(source[i]);
+ }
+}
+
+export function flattenArray(source: any[], target: any[]): any[] {
+ if (isPresent(source)) {
+ for (var i = 0; i < source.length; i++) {
+ var item = source[i];
+ if (isArray(item)) {
+ flattenArray(item, target);
+ } else {
+ target.push(item);
+ }
+ }
+ }
+ return target;
+}
diff --git a/modules/angular2/src/compiler/view_compiler.ts b/modules/angular2/src/compiler/view_compiler.ts
new file mode 100644
index 0000000000..2f6ec19697
--- /dev/null
+++ b/modules/angular2/src/compiler/view_compiler.ts
@@ -0,0 +1,600 @@
+import {
+ isPresent,
+ isBlank,
+ Type,
+ isString,
+ StringWrapper,
+ IS_DART,
+ CONST_EXPR
+} from 'angular2/src/facade/lang';
+import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
+import {
+ TemplateAst,
+ TemplateAstVisitor,
+ NgContentAst,
+ EmbeddedTemplateAst,
+ ElementAst,
+ VariableAst,
+ BoundEventAst,
+ BoundElementPropertyAst,
+ AttrAst,
+ BoundTextAst,
+ TextAst,
+ DirectiveAst,
+ BoundDirectivePropertyAst,
+ templateVisitAll
+} from './template_ast';
+import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
+import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
+import {
+ AppProtoView,
+ AppView,
+ flattenNestedViewRenderNodes,
+ checkSlotCount
+} from 'angular2/src/core/linker/view';
+import {ViewType} from 'angular2/src/core/linker/view_type';
+import {AppViewManager_} from 'angular2/src/core/linker/view_manager';
+import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
+import {Renderer, ParentRenderer} from 'angular2/src/core/render/api';
+import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
+import {
+ escapeSingleQuoteString,
+ codeGenConstConstructorCall,
+ codeGenValueFn,
+ codeGenFnHeader,
+ MODULE_SUFFIX,
+ Statement,
+ escapeValue,
+ codeGenArray,
+ codeGenFlatArray,
+ Expression,
+ flattenArray,
+ CONST_VAR
+} from './util';
+import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
+
+import {
+ APP_VIEW_MODULE_REF,
+ APP_EL_MODULE_REF,
+ METADATA_MODULE_REF,
+ CompileProtoView,
+ CompileProtoElement
+} from './proto_view_compiler';
+
+export const VIEW_JIT_IMPORTS = CONST_EXPR({
+ 'AppView': AppView,
+ 'AppElement': AppElement,
+ 'flattenNestedViewRenderNodes': flattenNestedViewRenderNodes,
+ 'checkSlotCount': checkSlotCount
+});
+
+
+@Injectable()
+export class ViewCompiler {
+ constructor() {}
+
+ compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
+ styles: Array,
+ protoViews: CompileProtoView[],
+ changeDetectorFactories: Function[],
+ componentViewFactory: Function): Function {
+ var viewFactory = new RuntimeViewFactory(component, styles, protoViews, changeDetectorFactories,
+ componentViewFactory);
+ return viewFactory.createViewFactory(template, 0, []);
+ }
+
+ compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
+ styles: SourceExpression,
+ protoViews: CompileProtoView[],
+ changeDetectorFactoryExpressions: SourceExpressions,
+ componentViewFactory: Function): SourceExpression {
+ var viewFactory = new CodeGenViewFactory(
+ component, styles, protoViews, changeDetectorFactoryExpressions, componentViewFactory);
+ var targetStatements: Statement[] = [];
+ var viewFactoryExpression = viewFactory.createViewFactory(template, 0, targetStatements);
+ return new SourceExpression(targetStatements.map(stmt => stmt.statement),
+ viewFactoryExpression.expression);
+ }
+}
+
+interface ViewFactory {
+ createText(renderer: EXPRESSION, parent: EXPRESSION, text: string,
+ targetStatements: STATEMENT[]): EXPRESSION;
+
+ createElement(renderer: EXPRESSION, parent: EXPRESSION, name: string, rootSelector: EXPRESSION,
+ targetStatements: STATEMENT[]): EXPRESSION;
+
+ createTemplateAnchor(renderer: EXPRESSION, parent: EXPRESSION,
+ targetStatements: STATEMENT[]): EXPRESSION;
+
+ createGlobalEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
+ eventAst: BoundEventAst, targetStatements: STATEMENT[]): EXPRESSION;
+
+ createElementEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
+ renderNode: EXPRESSION, eventAst: BoundEventAst,
+ targetStatements: STATEMENT[]);
+
+ setElementAttribute(renderer: EXPRESSION, renderNode: EXPRESSION, attrName: string,
+ attrValue: string, targetStatements: STATEMENT[]);
+
+ createAppElement(appProtoEl: EXPRESSION, view: EXPRESSION, renderNode: EXPRESSION,
+ parentAppEl: EXPRESSION, embeddedViewFactory: EXPRESSION,
+ targetStatements: STATEMENT[]): EXPRESSION;
+
+ createAndSetComponentView(renderer: EXPRESSION, viewManager: EXPRESSION, view: EXPRESSION,
+ appEl: EXPRESSION, component: CompileDirectiveMetadata,
+ contentNodesByNgContentIndex: EXPRESSION[][],
+ targetStatements: STATEMENT[]);
+
+ getProjectedNodes(projectableNodes: EXPRESSION, ngContentIndex: number): EXPRESSION;
+
+ appendProjectedNodes(renderer: EXPRESSION, parent: EXPRESSION, nodes: EXPRESSION,
+ targetStatements: STATEMENT[]);
+
+ createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
+ targetStatements: STATEMENT[]): EXPRESSION;
+}
+
+class CodeGenViewFactory implements ViewFactory {
+ private _nextVarId: number = 0;
+ constructor(public component: CompileDirectiveMetadata, public styles: SourceExpression,
+ public protoViews: CompileProtoView[],
+ public changeDetectorExpressions: SourceExpressions,
+ public componentViewFactory: Function) {}
+
+ private _nextVar(prefix: string): string {
+ return `${prefix}${this._nextVarId++}_${this.component.type.name}`;
+ }
+
+ private _nextRenderVar(): string { return this._nextVar('render'); }
+
+ private _nextAppVar(): string { return this._nextVar('app'); }
+
+ private _nextDisposableVar(): string {
+ return `disposable${this._nextVarId++}_${this.component.type.name}`;
+ }
+
+ createText(renderer: Expression, parent: Expression, text: string,
+ targetStatements: Statement[]): Expression {
+ var varName = this._nextRenderVar();
+ var statement =
+ `var ${varName} = ${renderer.expression}.createText(${isPresent(parent) ? parent.expression : null}, ${escapeSingleQuoteString(text)});`;
+ targetStatements.push(new Statement(statement));
+ return new Expression(varName);
+ }
+
+ createElement(renderer: Expression, parentRenderNode: Expression, name: string,
+ rootSelector: Expression, targetStatements: Statement[]): Expression {
+ var varName = this._nextRenderVar();
+ var valueExpr;
+ if (isPresent(rootSelector)) {
+ valueExpr = `${rootSelector.expression} == null ?
+ ${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)}) :
+ ${renderer.expression}.selectRootElement(${rootSelector.expression});`;
+ } else {
+ valueExpr =
+ `${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)})`;
+ }
+ var statement = `var ${varName} = ${valueExpr};`;
+ targetStatements.push(new Statement(statement));
+ return new Expression(varName);
+ }
+
+ createTemplateAnchor(renderer: Expression, parentRenderNode: Expression,
+ targetStatements: Statement[]): Expression {
+ var varName = this._nextRenderVar();
+ var valueExpr =
+ `${renderer.expression}.createTemplateAnchor(${isPresent(parentRenderNode) ? parentRenderNode.expression : null});`;
+ targetStatements.push(new Statement(`var ${varName} = ${valueExpr}`));
+ return new Expression(varName);
+ }
+
+ createGlobalEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
+ eventAst: BoundEventAst, targetStatements: Statement[]): Expression {
+ var disposableVar = this._nextDisposableVar();
+ var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
+ targetStatements.push(new Statement(
+ `var ${disposableVar} = ${renderer.expression}.listenGlobal(${escapeValue(eventAst.target)}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
+ return new Expression(disposableVar);
+ }
+
+ createElementEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
+ renderNode: Expression, eventAst: BoundEventAst,
+ targetStatements: Statement[]) {
+ var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
+ targetStatements.push(new Statement(
+ `${renderer.expression}.listen(${renderNode.expression}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
+ }
+
+ setElementAttribute(renderer: Expression, renderNode: Expression, attrName: string,
+ attrValue: string, targetStatements: Statement[]) {
+ targetStatements.push(new Statement(
+ `${renderer.expression}.setElementAttribute(${renderNode.expression}, ${escapeSingleQuoteString(attrName)}, ${escapeSingleQuoteString(attrValue)});`));
+ }
+
+ createAppElement(appProtoEl: Expression, appView: Expression, renderNode: Expression,
+ parentAppEl: Expression, embeddedViewFactory: Expression,
+ targetStatements: Statement[]): Expression {
+ var appVar = this._nextAppVar();
+ var varValue =
+ `new ${APP_EL_MODULE_REF}AppElement(${appProtoEl.expression}, ${appView.expression},
+ ${isPresent(parentAppEl) ? parentAppEl.expression : null}, ${renderNode.expression}, ${isPresent(embeddedViewFactory) ? embeddedViewFactory.expression : null})`;
+ targetStatements.push(new Statement(`var ${appVar} = ${varValue};`));
+ return new Expression(appVar);
+ }
+
+ createAndSetComponentView(renderer: Expression, viewManager: Expression, view: Expression,
+ appEl: Expression, component: CompileDirectiveMetadata,
+ contentNodesByNgContentIndex: Expression[][],
+ targetStatements: Statement[]) {
+ var codeGenContentNodes;
+ if (this.component.type.isHost) {
+ codeGenContentNodes = `${view.expression}.projectableNodes`;
+ } else {
+ codeGenContentNodes =
+ `[${contentNodesByNgContentIndex.map( nodes => codeGenFlatArray(nodes) ).join(',')}]`;
+ }
+ targetStatements.push(new Statement(
+ `${this.componentViewFactory(component)}(${renderer.expression}, ${viewManager.expression}, ${appEl.expression}, ${codeGenContentNodes}, null, null, null);`));
+ }
+
+ getProjectedNodes(projectableNodes: Expression, ngContentIndex: number): Expression {
+ return new Expression(`${projectableNodes.expression}[${ngContentIndex}]`, true);
+ }
+
+ appendProjectedNodes(renderer: Expression, parent: Expression, nodes: Expression,
+ targetStatements: Statement[]) {
+ targetStatements.push(new Statement(
+ `${renderer.expression}.projectNodes(${parent.expression}, ${APP_VIEW_MODULE_REF}flattenNestedViewRenderNodes(${nodes.expression}));`));
+ }
+
+ createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
+ targetStatements: Statement[]): Expression {
+ var compileProtoView = this.protoViews[embeddedTemplateIndex];
+ var isHostView = this.component.type.isHost;
+ var isComponentView = embeddedTemplateIndex === 0 && !isHostView;
+ var visitor = new ViewBuilderVisitor(
+ new Expression('renderer'), new Expression('viewManager'),
+ new Expression('projectableNodes'), isHostView ? new Expression('rootSelector') : null,
+ new Expression('view'), compileProtoView, targetStatements, this);
+
+ templateVisitAll(
+ visitor, asts,
+ new ParentElement(isComponentView ? new Expression('parentRenderNode') : null, null, null));
+
+ var appProtoView = compileProtoView.protoView.expression;
+ var viewFactoryName = codeGenViewFactoryName(this.component, embeddedTemplateIndex);
+ var changeDetectorFactory = this.changeDetectorExpressions.expressions[embeddedTemplateIndex];
+ var factoryArgs = [
+ 'parentRenderer',
+ 'viewManager',
+ 'containerEl',
+ 'projectableNodes',
+ 'rootSelector',
+ 'dynamicallyCreatedProviders',
+ 'rootInjector'
+ ];
+ var initRendererStmts = [];
+ var rendererExpr = `parentRenderer`;
+ if (embeddedTemplateIndex === 0) {
+ var renderCompTypeVar = this._nextVar('renderType');
+ targetStatements.push(new Statement(`var ${renderCompTypeVar} = null;`));
+ var stylesVar = this._nextVar('styles');
+ targetStatements.push(
+ new Statement(`${CONST_VAR} ${stylesVar} = ${this.styles.expression};`));
+ var encapsulation = this.component.template.encapsulation;
+ initRendererStmts.push(`if (${renderCompTypeVar} == null) {
+ ${renderCompTypeVar} = viewManager.createRenderComponentType(${codeGenViewEncapsulation(encapsulation)}, ${stylesVar});
+ }`);
+ rendererExpr = `parentRenderer.renderComponent(${renderCompTypeVar})`;
+ }
+ var statement = `
+${codeGenFnHeader(factoryArgs, viewFactoryName)}{
+ ${initRendererStmts.join('\n')}
+ var renderer = ${rendererExpr};
+ var view = new ${APP_VIEW_MODULE_REF}AppView(
+ ${appProtoView}, renderer, viewManager,
+ projectableNodes,
+ containerEl,
+ dynamicallyCreatedProviders, rootInjector,
+ ${changeDetectorFactory}()
+ );
+ ${APP_VIEW_MODULE_REF}checkSlotCount(${escapeValue(this.component.type.name)}, ${this.component.template.ngContentSelectors.length}, projectableNodes);
+ ${isComponentView ? 'var parentRenderNode = renderer.createViewRoot(view.containerAppElement.nativeElement);' : ''}
+ ${visitor.renderStmts.map(stmt => stmt.statement).join('\n')}
+ ${visitor.appStmts.map(stmt => stmt.statement).join('\n')}
+
+ view.init(${codeGenFlatArray(visitor.rootNodesOrAppElements)}, ${codeGenArray(visitor.renderNodes)}, ${codeGenArray(visitor.appDisposables)},
+ ${codeGenArray(visitor.appElements)});
+ return view;
+}`;
+ targetStatements.push(new Statement(statement));
+ return new Expression(viewFactoryName);
+ }
+}
+
+class RuntimeViewFactory implements ViewFactory {
+ constructor(public component: CompileDirectiveMetadata, public styles: Array,
+ public protoViews: CompileProtoView[],
+ public changeDetectorFactories: Function[], public componentViewFactory: Function) {}
+
+ createText(renderer: Renderer, parent: any, text: string, targetStatements: any[]): any {
+ return renderer.createText(parent, text);
+ }
+
+ createElement(renderer: Renderer, parent: any, name: string, rootSelector: string,
+ targetStatements: any[]): any {
+ var el;
+ if (isPresent(rootSelector)) {
+ el = renderer.selectRootElement(rootSelector);
+ } else {
+ el = renderer.createElement(parent, name);
+ }
+ return el;
+ }
+
+ createTemplateAnchor(renderer: Renderer, parent: any, targetStatements: any[]): any {
+ return renderer.createTemplateAnchor(parent);
+ }
+
+ createGlobalEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
+ eventAst: BoundEventAst, targetStatements: any[]): any {
+ return renderer.listenGlobal(
+ eventAst.target, eventAst.name,
+ (event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex));
+ }
+
+ createElementEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
+ renderNode: any, eventAst: BoundEventAst, targetStatements: any[]) {
+ renderer.listen(renderNode, eventAst.name, (event) => appView.triggerEventHandlers(
+ eventAst.fullName, event, boundElementIndex));
+ }
+
+ setElementAttribute(renderer: Renderer, renderNode: any, attrName: string, attrValue: string,
+ targetStatements: any[]) {
+ renderer.setElementAttribute(renderNode, attrName, attrValue);
+ }
+
+ createAppElement(appProtoEl: AppProtoElement, appView: AppView, renderNode: any,
+ parentAppEl: AppElement, embeddedViewFactory: Function,
+ targetStatements: any[]): any {
+ return new AppElement(appProtoEl, appView, parentAppEl, renderNode, embeddedViewFactory);
+ }
+
+ createAndSetComponentView(renderer: Renderer, viewManager: AppViewManager_, appView: AppView,
+ appEl: AppElement, component: CompileDirectiveMetadata,
+ contentNodesByNgContentIndex: Array>,
+ targetStatements: any[]) {
+ var flattenedContentNodes;
+ if (this.component.type.isHost) {
+ flattenedContentNodes = appView.projectableNodes;
+ } else {
+ flattenedContentNodes = ListWrapper.createFixedSize(contentNodesByNgContentIndex.length);
+ for (var i = 0; i < contentNodesByNgContentIndex.length; i++) {
+ flattenedContentNodes[i] = flattenArray(contentNodesByNgContentIndex[i], []);
+ }
+ }
+ this.componentViewFactory(component)(renderer, viewManager, appEl, flattenedContentNodes);
+ }
+
+ getProjectedNodes(projectableNodes: any[][], ngContentIndex: number): any[] {
+ return projectableNodes[ngContentIndex];
+ }
+
+ appendProjectedNodes(renderer: Renderer, parent: any, nodes: any[], targetStatements: any[]) {
+ renderer.projectNodes(parent, flattenNestedViewRenderNodes(nodes));
+ }
+
+ createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
+ targetStatements: any[]): Function {
+ var compileProtoView = this.protoViews[embeddedTemplateIndex];
+ var isComponentView = compileProtoView.protoView.type === ViewType.COMPONENT;
+ var renderComponentType = null;
+ return (parentRenderer: ParentRenderer, viewManager: AppViewManager_, containerEl: AppElement,
+ projectableNodes: any[][], rootSelector: string = null,
+ dynamicallyCreatedProviders: ResolvedProvider[] = null,
+ rootInjector: Injector = null) => {
+ checkSlotCount(this.component.type.name, this.component.template.ngContentSelectors.length,
+ projectableNodes);
+ var renderer;
+ if (embeddedTemplateIndex === 0) {
+ if (isBlank(renderComponentType)) {
+ renderComponentType = viewManager.createRenderComponentType(
+ this.component.template.encapsulation, this.styles);
+ }
+ renderer = parentRenderer.renderComponent(renderComponentType);
+ } else {
+ renderer = parentRenderer;
+ }
+ var changeDetector = this.changeDetectorFactories[embeddedTemplateIndex]();
+ var view =
+ new AppView(compileProtoView.protoView, renderer, viewManager, projectableNodes,
+ containerEl, dynamicallyCreatedProviders, rootInjector, changeDetector);
+ var visitor = new ViewBuilderVisitor(
+ renderer, viewManager, projectableNodes, rootSelector, view, compileProtoView, [], this);
+ var parentRenderNode =
+ isComponentView ? renderer.createViewRoot(containerEl.nativeElement) : null;
+ templateVisitAll(visitor, asts, new ParentElement(parentRenderNode, null, null));
+ view.init(flattenArray(visitor.rootNodesOrAppElements, []), visitor.renderNodes,
+ visitor.appDisposables, visitor.appElements);
+ return view;
+ };
+ }
+}
+
+class ParentElement {
+ public contentNodesByNgContentIndex: Array[];
+
+ constructor(public renderNode: EXPRESSION, public appEl: EXPRESSION,
+ public component: CompileDirectiveMetadata) {
+ if (isPresent(component)) {
+ this.contentNodesByNgContentIndex =
+ ListWrapper.createFixedSize(component.template.ngContentSelectors.length);
+ for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
+ this.contentNodesByNgContentIndex[i] = [];
+ }
+ } else {
+ this.contentNodesByNgContentIndex = null;
+ }
+ }
+
+ addContentNode(ngContentIndex: number, nodeExpr: EXPRESSION) {
+ this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
+ }
+}
+
+class ViewBuilderVisitor implements TemplateAstVisitor {
+ renderStmts: Array = [];
+ renderNodes: EXPRESSION[] = [];
+ appStmts: Array = [];
+ appElements: EXPRESSION[] = [];
+ appDisposables: EXPRESSION[] = [];
+
+ rootNodesOrAppElements: EXPRESSION[] = [];
+
+ elementCount: number = 0;
+
+ constructor(public renderer: EXPRESSION, public viewManager: EXPRESSION,
+ public projectableNodes: EXPRESSION, public rootSelector: EXPRESSION,
+ public view: EXPRESSION, public protoView: CompileProtoView,
+ public targetStatements: STATEMENT[],
+ public factory: ViewFactory) {}
+
+ private _addRenderNode(renderNode: EXPRESSION, appEl: EXPRESSION, ngContentIndex: number,
+ parent: ParentElement) {
+ this.renderNodes.push(renderNode);
+ if (isPresent(parent.component)) {
+ if (isPresent(ngContentIndex)) {
+ parent.addContentNode(ngContentIndex, isPresent(appEl) ? appEl : renderNode);
+ }
+ } else if (isBlank(parent.renderNode)) {
+ this.rootNodesOrAppElements.push(isPresent(appEl) ? appEl : renderNode);
+ }
+ }
+
+ private _getParentRenderNode(ngContentIndex: number,
+ parent: ParentElement): EXPRESSION {
+ return isPresent(parent.component) &&
+ parent.component.template.encapsulation !== ViewEncapsulation.Native ?
+ null :
+ parent.renderNode;
+ }
+
+ visitBoundText(ast: BoundTextAst, parent: ParentElement): any {
+ return this._visitText('', ast.ngContentIndex, parent);
+ }
+ visitText(ast: TextAst, parent: ParentElement): any {
+ return this._visitText(ast.value, ast.ngContentIndex, parent);
+ }
+ private _visitText(value: string, ngContentIndex: number, parent: ParentElement) {
+ var renderNode = this.factory.createText(
+ this.renderer, this._getParentRenderNode(ngContentIndex, parent), value, this.renderStmts);
+ this._addRenderNode(renderNode, null, ngContentIndex, parent);
+ return null;
+ }
+
+ visitNgContent(ast: NgContentAst, parent: ParentElement): any {
+ var nodesExpression = this.factory.getProjectedNodes(this.projectableNodes, ast.index);
+ if (isPresent(parent.component)) {
+ if (isPresent(ast.ngContentIndex)) {
+ parent.addContentNode(ast.ngContentIndex, nodesExpression);
+ }
+ } else {
+ if (isPresent(parent.renderNode)) {
+ this.factory.appendProjectedNodes(this.renderer, parent.renderNode, nodesExpression,
+ this.renderStmts);
+ } else {
+ this.rootNodesOrAppElements.push(nodesExpression);
+ }
+ }
+ return null;
+ }
+
+ visitElement(ast: ElementAst, parent: ParentElement): any {
+ var renderNode = this.factory.createElement(
+ this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), ast.name,
+ this.rootSelector, this.renderStmts);
+
+ var component = ast.getComponent();
+ var elementIndex = this.elementCount++;
+ var protoEl = this.protoView.protoElements[elementIndex];
+
+ protoEl.renderEvents.forEach((eventAst) => {
+ if (isPresent(eventAst.target)) {
+ var disposable = this.factory.createGlobalEventListener(
+ this.renderer, this.view, protoEl.boundElementIndex, eventAst, this.renderStmts);
+ this.appDisposables.push(disposable);
+ } else {
+ this.factory.createElementEventListener(this.renderer, this.view, protoEl.boundElementIndex,
+ renderNode, eventAst, this.renderStmts);
+ }
+ });
+ for (var i = 0; i < protoEl.attrNameAndValues.length; i++) {
+ var attrName = protoEl.attrNameAndValues[i][0];
+ var attrValue = protoEl.attrNameAndValues[i][1];
+ this.factory.setElementAttribute(this.renderer, renderNode, attrName, attrValue,
+ this.renderStmts);
+ }
+ var appEl = null;
+ if (isPresent(protoEl.appProtoEl)) {
+ appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode, parent.appEl,
+ null, this.appStmts);
+ this.appElements.push(appEl);
+ }
+ this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
+
+ var newParent = new ParentElement(
+ renderNode, isPresent(appEl) ? appEl : parent.appEl, component);
+ templateVisitAll(this, ast.children, newParent);
+ if (isPresent(appEl) && isPresent(component)) {
+ this.factory.createAndSetComponentView(this.renderer, this.viewManager, this.view, appEl,
+ component, newParent.contentNodesByNgContentIndex,
+ this.appStmts);
+ }
+ return null;
+ }
+
+ visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: ParentElement): any {
+ var renderNode = this.factory.createTemplateAnchor(
+ this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), this.renderStmts);
+
+ var elementIndex = this.elementCount++;
+ var protoEl = this.protoView.protoElements[elementIndex];
+ var embeddedViewFactory = this.factory.createViewFactory(
+ ast.children, protoEl.embeddedTemplateIndex, this.targetStatements);
+
+ var appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode,
+ parent.appEl, embeddedViewFactory, this.appStmts);
+ this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
+ this.appElements.push(appEl);
+ return null;
+ }
+
+ visitVariable(ast: VariableAst, ctx: any): any { return null; }
+ visitAttr(ast: AttrAst, ctx: any): any { return null; }
+ visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
+ visitEvent(ast: BoundEventAst, ctx: any): any { return null; }
+ visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
+ visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
+}
+
+
+function codeGenEventHandler(view: Expression, boundElementIndex: number,
+ eventName: string): string {
+ return codeGenValueFn(
+ ['event'],
+ `${view.expression}.triggerEventHandlers(${escapeValue(eventName)}, event, ${boundElementIndex})`);
+}
+
+function codeGenViewFactoryName(component: CompileDirectiveMetadata,
+ embeddedTemplateIndex: number): string {
+ return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
+}
+
+function codeGenViewEncapsulation(value: ViewEncapsulation): string {
+ if (IS_DART) {
+ return `${METADATA_MODULE_REF}${value}`;
+ } else {
+ return `${value}`;
+ }
+}
diff --git a/modules/angular2/src/core/application_common_providers.ts b/modules/angular2/src/core/application_common_providers.ts
index b0aa60bc71..bb75bf517e 100644
--- a/modules/angular2/src/core/application_common_providers.ts
+++ b/modules/angular2/src/core/application_common_providers.ts
@@ -11,13 +11,11 @@ import {
KeyValueDiffers,
defaultKeyValueDiffers
} from './change_detection/change_detection';
-import {AppViewPool, APP_VIEW_POOL_CAPACITY} from './linker/view_pool';
+import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
import {AppViewManager} from './linker/view_manager';
import {AppViewManager_} from "./linker/view_manager";
-import {AppViewManagerUtils} from './linker/view_manager_utils';
import {ViewResolver} from './linker/view_resolver';
import {AppViewListener} from './linker/view_listener';
-import {ProtoViewFactory} from './linker/proto_view_factory';
import {DirectiveResolver} from './linker/directive_resolver';
import {PipeResolver} from './linker/pipe_resolver';
import {Compiler} from './linker/compiler';
@@ -32,12 +30,9 @@ import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
export const APPLICATION_COMMON_PROVIDERS: Array = CONST_EXPR([
new Provider(Compiler, {useClass: Compiler_}),
APP_ID_RANDOM_PROVIDER,
- AppViewPool,
- new Provider(APP_VIEW_POOL_CAPACITY, {useValue: 10000}),
+ ResolvedMetadataCache,
new Provider(AppViewManager, {useClass: AppViewManager_}),
- AppViewManagerUtils,
AppViewListener,
- ProtoViewFactory,
ViewResolver,
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
diff --git a/modules/angular2/src/core/application_ref.ts b/modules/angular2/src/core/application_ref.ts
index 426c56853f..d4d799bea0 100644
--- a/modules/angular2/src/core/application_ref.ts
+++ b/modules/angular2/src/core/application_ref.ts
@@ -33,11 +33,11 @@ import {
ExceptionHandler,
unimplemented
} from 'angular2/src/facade/exceptions';
-import {internalView} from 'angular2/src/core/linker/view_ref';
import {Console} from 'angular2/src/core/console';
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
import {lockMode} from 'angular2/src/facade/lang';
+import {ElementRef_} from 'angular2/src/core/linker/element_ref';
/**
* Construct providers specific to an individual root component.
@@ -56,10 +56,10 @@ function _componentProviders(appComponentType: Type): Array { appRef._unloadComponent(ref); })
.then((componentRef) => {
ref = componentRef;
- if (isPresent(componentRef.location.nativeElement)) {
+ var testability = injector.getOptional(Testability);
+ if (isPresent(testability)) {
injector.get(TestabilityRegistry)
- .registerApplication(componentRef.location.nativeElement,
- injector.get(Testability));
+ .registerApplication(componentRef.location.nativeElement, testability);
}
return componentRef;
});
@@ -439,7 +439,7 @@ export class ApplicationRef_ extends ApplicationRef {
/** @internal */
_loadComponent(ref): void {
- var appChangeDetector = internalView(ref.hostView).changeDetector;
+ var appChangeDetector = (ref.location).internalElement.parentView.changeDetector;
this._changeDetectorRefs.push(appChangeDetector.ref);
this.tick();
this._rootComponents.push(ref);
@@ -451,7 +451,8 @@ export class ApplicationRef_ extends ApplicationRef {
if (!ListWrapper.contains(this._rootComponents, ref)) {
return;
}
- this.unregisterChangeDetector(internalView(ref.hostView).changeDetector.ref);
+ this.unregisterChangeDetector(
+ (ref.location).internalElement.parentView.changeDetector.ref);
ListWrapper.remove(this._rootComponents, ref);
}
diff --git a/modules/angular2/src/core/change_detection/abstract_change_detector.ts b/modules/angular2/src/core/change_detection/abstract_change_detector.ts
index 4a1ced947e..e3ec39cb87 100644
--- a/modules/angular2/src/core/change_detection/abstract_change_detector.ts
+++ b/modules/angular2/src/core/change_detection/abstract_change_detector.ts
@@ -8,7 +8,9 @@ import {Pipes} from './pipes';
import {
ChangeDetectionError,
ExpressionChangedAfterItHasBeenCheckedException,
- DehydratedException
+ DehydratedException,
+ EventEvaluationErrorContext,
+ EventEvaluationError
} from './exceptions';
import {BindingTarget} from './binding_record';
import {Locals} from './parser/locals';
@@ -43,9 +45,12 @@ export class AbstractChangeDetector implements ChangeDetector {
subscriptions: any[];
streams: any[];
- constructor(public id: string, public dispatcher: ChangeDispatcher,
- public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
- public directiveIndices: DirectiveIndex[], public strategy: ChangeDetectionStrategy) {
+ dispatcher: ChangeDispatcher;
+
+
+ constructor(public id: string, public numberOfPropertyProtoRecords: number,
+ public bindingTargets: BindingTarget[], public directiveIndices: DirectiveIndex[],
+ public strategy: ChangeDetectionStrategy) {
this.ref = new ChangeDetectorRef_(this);
}
@@ -65,10 +70,24 @@ export class AbstractChangeDetector implements ChangeDetector {
remove(): void { this.parent.removeContentChild(this); }
- handleEvent(eventName: string, elIndex: number, locals: Locals): boolean {
- var res = this.handleEventInternal(eventName, elIndex, locals);
- this.markPathToRootAsCheckOnce();
- return res;
+ handleEvent(eventName: string, elIndex: number, event: any): boolean {
+ if (!this.hydrated()) {
+ return true;
+ }
+ try {
+ var locals = new Map();
+ locals.set('$event', event);
+ var res = !this.handleEventInternal(eventName, elIndex, new Locals(this.locals, locals));
+ this.markPathToRootAsCheckOnce();
+ return res;
+ } catch (e) {
+ var c = this.dispatcher.getDebugContext(null, elIndex, null);
+ var context = isPresent(c) ?
+ new EventEvaluationErrorContext(c.element, c.componentElement, c.context,
+ c.locals, c.injector) :
+ null;
+ throw new EventEvaluationError(eventName, e, e.stack, context);
+ }
}
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean { return false; }
@@ -133,7 +152,8 @@ export class AbstractChangeDetector implements ChangeDetector {
// This method is not intended to be overridden. Subclasses should instead provide an
// implementation of `hydrateDirectives`.
- hydrate(context: T, locals: Locals, directives: any, pipes: Pipes): void {
+ hydrate(context: T, locals: Locals, dispatcher: ChangeDispatcher, pipes: Pipes): void {
+ this.dispatcher = dispatcher;
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
this.context = context;
@@ -143,12 +163,12 @@ export class AbstractChangeDetector implements ChangeDetector {
this.locals = locals;
this.pipes = pipes;
- this.hydrateDirectives(directives);
+ this.hydrateDirectives(dispatcher);
this.state = ChangeDetectorState.NeverChecked;
}
// Subclasses should override this method to hydrate any directives.
- hydrateDirectives(directives: any): void {}
+ hydrateDirectives(dispatcher: ChangeDispatcher): void {}
// This method is not intended to be overridden. Subclasses should instead provide an
// implementation of `dehydrateDirectives`.
@@ -160,6 +180,7 @@ export class AbstractChangeDetector implements ChangeDetector {
this._unsubsribeFromObservables();
}
+ this.dispatcher = null;
this.context = null;
this.locals = null;
this.pipes = null;
@@ -171,6 +192,19 @@ export class AbstractChangeDetector implements ChangeDetector {
hydrated(): boolean { return isPresent(this.context); }
+ destroyRecursive(): void {
+ this.dispatcher.notifyOnDestroy();
+ this.dehydrate();
+ var children = this.contentChildren;
+ for (var i = 0; i < children.length; i++) {
+ children[i].destroyRecursive();
+ }
+ children = this.viewChildren;
+ for (var i = 0; i < children.length; i++) {
+ children[i].destroyRecursive();
+ }
+ }
+
afterContentLifecycleCallbacks(): void {
this.dispatcher.notifyAfterContentChecked();
this.afterContentLifecycleCallbacksInternal();
@@ -298,7 +332,7 @@ export class AbstractChangeDetector implements ChangeDetector {
private _throwError(exception: any, stack: any): void {
var error;
try {
- var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
+ var c = this.dispatcher.getDebugContext(null, this._currentBinding().elementIndex, null);
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
c.injector, this._currentBinding().debug) :
null;
diff --git a/modules/angular2/src/core/change_detection/change_detection_jit_generator.ts b/modules/angular2/src/core/change_detection/change_detection_jit_generator.ts
index 14c9cafc7e..98615d94ae 100644
--- a/modules/angular2/src/core/change_detection/change_detection_jit_generator.ts
+++ b/modules/angular2/src/core/change_detection/change_detection_jit_generator.ts
@@ -66,8 +66,8 @@ export class ChangeDetectorJITGenerator {
generate(): Function {
var factorySource = `
${this.generateSource()}
- return function(dispatcher) {
- return new ${this.typeName}(dispatcher);
+ return function() {
+ return new ${this.typeName}();
}
`;
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
@@ -77,9 +77,9 @@ export class ChangeDetectorJITGenerator {
generateSource(): string {
return `
- var ${this.typeName} = function ${this.typeName}(dispatcher) {
+ var ${this.typeName} = function ${this.typeName}() {
${this.abstractChangeDetectorVarName}.call(
- this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
+ this, ${JSON.stringify(this.id)}, ${this.records.length},
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
${codify(this.changeDetectionStrategy)});
this.dehydrateDirectives(false);
@@ -199,13 +199,14 @@ export class ChangeDetectorJITGenerator {
/** @internal */
_maybeGenDehydrateDirectives(): string {
var destroyPipesCode = this._names.genPipeOnDestroy();
- if (destroyPipesCode) {
- destroyPipesCode = `if (destroyPipes) { ${destroyPipesCode} }`;
- }
+ var destroyDirectivesCode = this._logic.genDirectivesOnDestroy(this.directiveRecords);
var dehydrateFieldsCode = this._names.genDehydrateFields();
- if (!destroyPipesCode && !dehydrateFieldsCode) return '';
+ if (!destroyPipesCode && !destroyDirectivesCode && !dehydrateFieldsCode) return '';
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
- ${destroyPipesCode}
+ if (destroyPipes) {
+ ${destroyPipesCode}
+ ${destroyDirectivesCode}
+ }
${dehydrateFieldsCode}
}`;
}
diff --git a/modules/angular2/src/core/change_detection/change_detector_ref.ts b/modules/angular2/src/core/change_detection/change_detector_ref.ts
index e128dcaac2..e34fe44857 100644
--- a/modules/angular2/src/core/change_detection/change_detector_ref.ts
+++ b/modules/angular2/src/core/change_detection/change_detector_ref.ts
@@ -205,4 +205,4 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
this.markForCheck();
}
-}
+}
\ No newline at end of file
diff --git a/modules/angular2/src/core/change_detection/codegen_logic_util.ts b/modules/angular2/src/core/change_detection/codegen_logic_util.ts
index 2c2e2ab741..17cf95cfc2 100644
--- a/modules/angular2/src/core/change_detection/codegen_logic_util.ts
+++ b/modules/angular2/src/core/change_detection/codegen_logic_util.ts
@@ -155,17 +155,49 @@ export class CodegenLogicUtil {
var res = [];
for (var i = 0; i < directiveRecords.length; ++i) {
var r = directiveRecords[i];
- res.push(`${this._names.getDirectiveName(r.directiveIndex)} = ${this._genReadDirective(i)};`);
+ var dirVarName = this._names.getDirectiveName(r.directiveIndex);
+ res.push(`${dirVarName} = ${this._genReadDirective(i)};`);
+ if (isPresent(r.outputs)) {
+ r.outputs.forEach(output => {
+ var eventHandlerExpr = this._genEventHandler(r.directiveIndex.elementIndex, output[1]);
+ if (IS_DART) {
+ res.push(`${dirVarName}.${output[0]}.listen(${eventHandlerExpr});`);
+ } else {
+ res.push(`${dirVarName}.${output[0]}.subscribe({next: ${eventHandlerExpr}});`);
+ }
+ });
+ }
}
return res.join("\n");
}
+ genDirectivesOnDestroy(directiveRecords: DirectiveRecord[]): string {
+ var res = [];
+ for (var i = 0; i < directiveRecords.length; ++i) {
+ var r = directiveRecords[i];
+ if (r.callOnDestroy) {
+ var dirVarName = this._names.getDirectiveName(r.directiveIndex);
+ res.push(`${dirVarName}.ngOnDestroy();`);
+ }
+ }
+ return res.join("\n");
+ }
+
+ private _genEventHandler(boundElementIndex: number, eventName: string): string {
+ if (IS_DART) {
+ return `(event) => this.handleEvent('${eventName}', ${boundElementIndex}, event)`;
+ } else {
+ return `(function(event) { return this.handleEvent('${eventName}', ${boundElementIndex}, event); }).bind(this)`;
+ }
+ }
+
private _genReadDirective(index: number) {
+ var directiveExpr = `this.getDirectiveFor(directives, ${index})`;
// This is an experimental feature. Works only in Dart.
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
- return `this.observeDirective(this.getDirectiveFor(directives, ${index}), ${index})`;
+ return `this.observeDirective(${directiveExpr}, ${index})`;
} else {
- return `this.getDirectiveFor(directives, ${index})`;
+ return directiveExpr;
}
}
diff --git a/modules/angular2/src/core/change_detection/directive_record.ts b/modules/angular2/src/core/change_detection/directive_record.ts
index 967034f07e..53c6f2e6cd 100644
--- a/modules/angular2/src/core/change_detection/directive_record.ts
+++ b/modules/angular2/src/core/change_detection/directive_record.ts
@@ -16,10 +16,14 @@ export class DirectiveRecord {
callOnChanges: boolean;
callDoCheck: boolean;
callOnInit: boolean;
+ callOnDestroy: boolean;
changeDetection: ChangeDetectionStrategy;
+ // array of [emitter property name, eventName]
+ outputs: string[][];
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
- callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, changeDetection}: {
+ callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, callOnDestroy,
+ changeDetection, outputs}: {
directiveIndex?: DirectiveIndex,
callAfterContentInit?: boolean,
callAfterContentChecked?: boolean,
@@ -28,7 +32,9 @@ export class DirectiveRecord {
callOnChanges?: boolean,
callDoCheck?: boolean,
callOnInit?: boolean,
- changeDetection?: ChangeDetectionStrategy
+ callOnDestroy?: boolean,
+ changeDetection?: ChangeDetectionStrategy,
+ outputs?: string[][]
} = {}) {
this.directiveIndex = directiveIndex;
this.callAfterContentInit = normalizeBool(callAfterContentInit);
@@ -38,7 +44,9 @@ export class DirectiveRecord {
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
this.callDoCheck = normalizeBool(callDoCheck);
this.callOnInit = normalizeBool(callOnInit);
+ this.callOnDestroy = normalizeBool(callOnDestroy);
this.changeDetection = changeDetection;
+ this.outputs = outputs;
}
isDefaultChangeDetection(): boolean {
diff --git a/modules/angular2/src/core/change_detection/dynamic_change_detector.ts b/modules/angular2/src/core/change_detection/dynamic_change_detector.ts
index 344b71ff5a..0b07abe62f 100644
--- a/modules/angular2/src/core/change_detection/dynamic_change_detector.ts
+++ b/modules/angular2/src/core/change_detection/dynamic_change_detector.ts
@@ -11,21 +11,21 @@ import {ChangeDispatcher, ChangeDetectorGenConfig} from './interfaces';
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
import {ProtoRecord, RecordType} from './proto_record';
+import {reflector} from 'angular2/src/core/reflection/reflection';
+import {ObservableWrapper} from 'angular2/src/facade/async';
export class DynamicChangeDetector extends AbstractChangeDetector {
values: any[];
changes: any[];
localPipes: any[];
prevContexts: any[];
- directives: any = null;
- constructor(id: string, dispatcher: ChangeDispatcher, numberOfPropertyProtoRecords: number,
+ constructor(id: string, numberOfPropertyProtoRecords: number,
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
private _genConfig: ChangeDetectorGenConfig) {
- super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
- strategy);
+ super(id, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, strategy);
var len = _records.length + 1;
this.values = ListWrapper.createFixedSize(len);
this.localPipes = ListWrapper.createFixedSize(len);
@@ -104,24 +104,41 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
return this._eventBindings.filter(eb => eb.eventName == eventName && eb.elIndex === elIndex);
}
- hydrateDirectives(directives: any): void {
+ hydrateDirectives(dispatcher: ChangeDispatcher): void {
this.values[0] = this.context;
- this.directives = directives;
+ this.dispatcher = dispatcher;
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
for (var i = 0; i < this.directiveIndices.length; ++i) {
var index = this.directiveIndices[i];
- super.observeDirective(directives.getDirectiveFor(index), i);
+ super.observeDirective(this._getDirectiveFor(index), i);
+ }
+ }
+ for (var i = 0; i < this._directiveRecords.length; ++i) {
+ var r = this._directiveRecords[i];
+ if (isPresent(r.outputs)) {
+ r.outputs.forEach(output => {
+ var eventHandler =
+ this._createEventHandler(r.directiveIndex.elementIndex, output[1]);
+ var directive = this._getDirectiveFor(r.directiveIndex);
+ var getter = reflector.getter(output[0]);
+ ObservableWrapper.subscribe(getter(directive), eventHandler);
+ });
}
}
}
+ private _createEventHandler(boundElementIndex: number, eventName: string): Function {
+ return (event) => this.handleEvent(eventName, boundElementIndex, event);
+ }
+
+
dehydrateDirectives(destroyPipes: boolean) {
if (destroyPipes) {
this._destroyPipes();
+ this._destroyDirectives();
}
this.values[0] = null;
- this.directives = null;
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
ListWrapper.fill(this.changes, false);
ListWrapper.fill(this.localPipes, null);
@@ -137,6 +154,16 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
}
+ /** @internal */
+ _destroyDirectives() {
+ for (var i = 0; i < this._directiveRecords.length; ++i) {
+ var record = this._directiveRecords[i];
+ if (record.callOnDestroy) {
+ this._getDirectiveFor(record.directiveIndex).ngOnDestroy();
+ }
+ }
+ }
+
checkNoChanges(): void { this.runDetectChanges(true); }
detectChangesInRecordsInternal(throwOnChange: boolean) {
@@ -241,12 +268,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
}
/** @internal */
- private _getDirectiveFor(directiveIndex) {
- return this.directives.getDirectiveFor(directiveIndex);
+ private _getDirectiveFor(directiveIndex: DirectiveIndex) {
+ return this.dispatcher.getDirectiveFor(directiveIndex);
}
/** @internal */
- private _getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
+ private _getDetectorFor(directiveIndex: DirectiveIndex) {
+ return this.dispatcher.getDetectorFor(directiveIndex);
+ }
/** @internal */
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],
diff --git a/modules/angular2/src/core/change_detection/exceptions.ts b/modules/angular2/src/core/change_detection/exceptions.ts
index b8c7454447..11d04ee273 100644
--- a/modules/angular2/src/core/change_detection/exceptions.ts
+++ b/modules/angular2/src/core/change_detection/exceptions.ts
@@ -93,3 +93,20 @@ export class ChangeDetectionError extends WrappedException {
export class DehydratedException extends BaseException {
constructor() { super('Attempt to detect changes on a dehydrated detector.'); }
}
+
+/**
+ * Wraps an exception thrown by an event handler.
+ */
+export class EventEvaluationError extends WrappedException {
+ constructor(eventName: string, originalException: any, originalStack: any, context: any) {
+ super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
+ }
+}
+
+/**
+ * Error context included when an event handler throws an exception.
+ */
+export class EventEvaluationErrorContext {
+ constructor(public element: any, public componentElement: any, public context: any,
+ public locals: any, public injector: any) {}
+}
diff --git a/modules/angular2/src/core/change_detection/interfaces.ts b/modules/angular2/src/core/change_detection/interfaces.ts
index 803d962c56..0f3c165f56 100644
--- a/modules/angular2/src/core/change_detection/interfaces.ts
+++ b/modules/angular2/src/core/change_detection/interfaces.ts
@@ -1,6 +1,6 @@
import {Locals} from './parser/locals';
import {BindingTarget, BindingRecord} from './binding_record';
-import {DirectiveIndex, DirectiveRecord} from './directive_record';
+import {DirectiveRecord, DirectiveIndex} from './directive_record';
import {ChangeDetectionStrategy} from './constants';
import {ChangeDetectorRef} from './change_detector_ref';
@@ -10,11 +10,14 @@ export class DebugContext {
}
export interface ChangeDispatcher {
- getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
+ getDebugContext(appElement: any, elementIndex: number, directiveIndex: number): DebugContext;
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
notifyAfterContentChecked(): void;
notifyAfterViewChecked(): void;
+ notifyOnDestroy(): void;
+ getDetectorFor(directiveIndex: DirectiveIndex): ChangeDetector;
+ getDirectiveFor(directiveIndex: DirectiveIndex): any;
}
export interface ChangeDetector {
@@ -27,16 +30,18 @@ export interface ChangeDetector {
removeContentChild(cd: ChangeDetector): void;
removeViewChild(cd: ChangeDetector): void;
remove(): void;
- hydrate(context: any, locals: Locals, directives: any, pipes: any): void;
+ hydrate(context: any, locals: Locals, dispatcher: ChangeDispatcher, pipes: any): void;
dehydrate(): void;
markPathToRootAsCheckOnce(): void;
- handleEvent(eventName: string, elIndex: number, locals: Locals);
+ handleEvent(eventName: string, elIndex: number, event: any);
detectChanges(): void;
checkNoChanges(): void;
+ destroyRecursive(): void;
+ markAsCheckOnce(): void;
}
-export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; }
+export interface ProtoChangeDetector { instantiate(): ChangeDetector; }
export class ChangeDetectorGenConfig {
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
diff --git a/modules/angular2/src/core/change_detection/jit_proto_change_detector.dart b/modules/angular2/src/core/change_detection/jit_proto_change_detector.dart
index 46a3251a5b..5fb02a8e4a 100644
--- a/modules/angular2/src/core/change_detection/jit_proto_change_detector.dart
+++ b/modules/angular2/src/core/change_detection/jit_proto_change_detector.dart
@@ -3,11 +3,11 @@ library change_detection.jit_proto_change_detector;
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
class JitProtoChangeDetector implements ProtoChangeDetector {
- JitProtoChangeDetector(definition) : super();
+ JitProtoChangeDetector(definition);
static bool isSupported() => false;
- ChangeDetector instantiate(dispatcher) {
+ ChangeDetector instantiate() {
throw "Jit Change Detection not supported in Dart";
}
}
diff --git a/modules/angular2/src/core/change_detection/jit_proto_change_detector.ts b/modules/angular2/src/core/change_detection/jit_proto_change_detector.ts
index 3d4636e7e3..bd747c4931 100644
--- a/modules/angular2/src/core/change_detection/jit_proto_change_detector.ts
+++ b/modules/angular2/src/core/change_detection/jit_proto_change_detector.ts
@@ -14,7 +14,7 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
static isSupported(): boolean { return true; }
- instantiate(dispatcher: any): ChangeDetector { return this._factory(dispatcher); }
+ instantiate(): ChangeDetector { return this._factory(); }
/** @internal */
_createFactory(definition: ChangeDetectorDefinition) {
diff --git a/modules/angular2/src/core/change_detection/parser/locals.ts b/modules/angular2/src/core/change_detection/parser/locals.ts
index 92b1aaa862..fb7fbd2fbd 100644
--- a/modules/angular2/src/core/change_detection/parser/locals.ts
+++ b/modules/angular2/src/core/change_detection/parser/locals.ts
@@ -41,5 +41,5 @@ export class Locals {
}
}
- clearValues(): void { MapWrapper.clearValues(this.current); }
+ clearLocalValues(): void { MapWrapper.clearValues(this.current); }
}
diff --git a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart b/modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart
index bf95c75e1d..301801ddba 100644
--- a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart
+++ b/modules/angular2/src/core/change_detection/pregen_proto_change_detector.dart
@@ -1,8 +1,5 @@
library angular2.src.change_detection.pregen_proto_change_detector;
-import 'package:angular2/src/core/change_detection/interfaces.dart';
-import 'package:angular2/src/facade/lang.dart' show looseIdentical;
-
export 'dart:core' show List;
export 'package:angular2/src/core/change_detection/abstract_change_detector.dart'
show AbstractChangeDetector;
@@ -20,34 +17,3 @@ export 'package:angular2/src/core/change_detection/proto_record.dart'
export 'package:angular2/src/core/change_detection/change_detection_util.dart'
show ChangeDetectionUtil;
export 'package:angular2/src/facade/lang.dart' show assertionsEnabled, looseIdentical;
-
-typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
- ChangeDetectorDefinition definition);
-
-typedef ChangeDetector InstantiateMethod(dynamic dispatcher);
-
-/// Implementation of [ProtoChangeDetector] for use by pre-generated change
-/// detectors in Angular 2 Dart.
-/// Classes generated by the `TemplateCompiler` use this. The `export`s above
-/// allow the generated code to `import` a single library and get all
-/// dependencies.
-class PregenProtoChangeDetector extends ProtoChangeDetector {
- /// The [ChangeDetectorDefinition#id]. Strictly informational.
- final String id;
-
- /// Closure used to generate an actual [ChangeDetector].
- final InstantiateMethod _instantiateMethod;
-
- /// Internal ctor.
- PregenProtoChangeDetector._(this.id, this._instantiateMethod);
-
- static bool isSupported() => true;
-
- factory PregenProtoChangeDetector(
- InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) {
- return new PregenProtoChangeDetector._(def.id, instantiateMethod);
- }
-
- @override
- instantiate(dynamic dispatcher) => _instantiateMethod(dispatcher);
-}
diff --git a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts b/modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts
index 5ef1eadb5b..da60b225c9 100644
--- a/modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts
+++ b/modules/angular2/src/core/change_detection/pregen_proto_change_detector.ts
@@ -1,14 +1 @@
-import {BaseException} from 'angular2/src/facade/exceptions';
-
-import {ProtoChangeDetector, ChangeDetector} from './interfaces';
-import {coalesce} from './coalesce';
-
-export {Function as PregenProtoChangeDetectorFactory};
-
-export class PregenProtoChangeDetector implements ProtoChangeDetector {
- static isSupported(): boolean { return false; }
-
- instantiate(dispatcher: any): ChangeDetector {
- throw new BaseException('Pregen change detection not supported in Js');
- }
-}
+// empty file as we only need the dart version
\ No newline at end of file
diff --git a/modules/angular2/src/core/change_detection/proto_change_detector.ts b/modules/angular2/src/core/change_detection/proto_change_detector.ts
index c25cc54867..ee0016a9df 100644
--- a/modules/angular2/src/core/change_detection/proto_change_detector.ts
+++ b/modules/angular2/src/core/change_detection/proto_change_detector.ts
@@ -54,12 +54,11 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
this._directiveIndices = this._definition.directiveRecords.map(d => d.directiveIndex);
}
- instantiate(dispatcher: any): ChangeDetector {
+ instantiate(): ChangeDetector {
return new DynamicChangeDetector(
- this._definition.id, dispatcher, this._propertyBindingRecords.length,
- this._propertyBindingTargets, this._directiveIndices, this._definition.strategy,
- this._propertyBindingRecords, this._eventBindingRecords, this._definition.directiveRecords,
- this._definition.genConfig);
+ this._definition.id, this._propertyBindingRecords.length, this._propertyBindingTargets,
+ this._directiveIndices, this._definition.strategy, this._propertyBindingRecords,
+ this._eventBindingRecords, this._definition.directiveRecords, this._definition.genConfig);
}
}
diff --git a/modules/angular2/src/core/debug/debug_element.ts b/modules/angular2/src/core/debug/debug_element.ts
index c26acb8188..88b196258e 100644
--- a/modules/angular2/src/core/debug/debug_element.ts
+++ b/modules/angular2/src/core/debug/debug_element.ts
@@ -1,9 +1,9 @@
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection';
import {unimplemented} from 'angular2/src/facade/exceptions';
-import {ElementInjector} from 'angular2/src/core/linker/element_injector';
-import {AppView, ViewType} from 'angular2/src/core/linker/view';
-import {internalView} from 'angular2/src/core/linker/view_ref';
+
+import {AppElement} from 'angular2/src/core/linker/element';
+import {AppView} from 'angular2/src/core/linker/view';
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
/**
@@ -103,79 +103,68 @@ export abstract class DebugElement {
}
export class DebugElement_ extends DebugElement {
- /** @internal */
- _elementInjector: ElementInjector;
-
- constructor(private _parentView: AppView, private _boundElementIndex: number) {
- super();
- this._elementInjector = this._parentView.elementInjectors[this._boundElementIndex];
- }
+ constructor(private _appElement: AppElement) { super(); }
get componentInstance(): any {
- if (!isPresent(this._elementInjector)) {
+ if (!isPresent(this._appElement)) {
return null;
}
- return this._elementInjector.getComponent();
+ return this._appElement.getComponent();
}
get nativeElement(): any { return this.elementRef.nativeElement; }
- get elementRef(): ElementRef { return this._parentView.elementRefs[this._boundElementIndex]; }
+ get elementRef(): ElementRef { return this._appElement.ref; }
getDirectiveInstance(directiveIndex: number): any {
- return this._elementInjector.getDirectiveAtIndex(directiveIndex);
+ return this._appElement.getDirectiveAtIndex(directiveIndex);
}
get children(): DebugElement[] {
- return this._getChildElements(this._parentView, this._boundElementIndex);
+ return this._getChildElements(this._appElement.parentView, this._appElement);
}
get componentViewChildren(): DebugElement[] {
- var shadowView = this._parentView.getNestedView(this._boundElementIndex);
-
- if (!isPresent(shadowView) || shadowView.proto.type !== ViewType.COMPONENT) {
+ if (!isPresent(this._appElement.componentView)) {
// The current element is not a component.
return [];
}
- return this._getChildElements(shadowView, null);
+ return this._getChildElements(this._appElement.componentView, null);
}
triggerEventHandler(eventName: string, eventObj: Event): void {
- this._parentView.triggerEventHandlers(eventName, eventObj, this._boundElementIndex);
+ this._appElement.parentView.triggerEventHandlers(eventName, eventObj,
+ this._appElement.proto.index);
}
hasDirective(type: Type): boolean {
- if (!isPresent(this._elementInjector)) {
+ if (!isPresent(this._appElement)) {
return false;
}
- return this._elementInjector.hasDirective(type);
+ return this._appElement.hasDirective(type);
}
inject(type: Type): any {
- if (!isPresent(this._elementInjector)) {
+ if (!isPresent(this._appElement)) {
return null;
}
- return this._elementInjector.get(type);
+ return this._appElement.get(type);
}
- getLocal(name: string): any { return this._parentView.locals.get(name); }
+ getLocal(name: string): any { return this._appElement.parentView.locals.get(name); }
/** @internal */
- _getChildElements(view: AppView, parentBoundElementIndex: number): DebugElement[] {
+ _getChildElements(view: AppView, parentAppElement: AppElement): DebugElement[] {
var els = [];
- var parentElementBinder = null;
- if (isPresent(parentBoundElementIndex)) {
- parentElementBinder = view.proto.elementBinders[parentBoundElementIndex - view.elementOffset];
- }
- for (var i = 0; i < view.proto.elementBinders.length; ++i) {
- var binder = view.proto.elementBinders[i];
- if (binder.parent == parentElementBinder) {
- els.push(new DebugElement_(view, view.elementOffset + i));
+ for (var i = 0; i < view.appElements.length; ++i) {
+ var appEl = view.appElements[i];
+ if (appEl.parent == parentAppElement) {
+ els.push(new DebugElement_(appEl));
- var views = view.viewContainers[view.elementOffset + i];
+ var views = appEl.nestedViews;
if (isPresent(views)) {
- views.views.forEach(
+ views.forEach(
(nextView) => { els = els.concat(this._getChildElements(nextView, null)); });
}
}
@@ -191,8 +180,7 @@ export class DebugElement_ extends DebugElement {
* @return {DebugElement}
*/
export function inspectElement(elementRef: ElementRef): DebugElement {
- return new DebugElement_(internalView((elementRef).parentView),
- (elementRef).boundElementIndex);
+ return new DebugElement_((elementRef).internalElement);
}
/**
diff --git a/modules/angular2/src/core/di/injector.ts b/modules/angular2/src/core/di/injector.ts
index 40c7e20493..4d499cc8a6 100644
--- a/modules/angular2/src/core/di/injector.ts
+++ b/modules/angular2/src/core/di/injector.ts
@@ -194,6 +194,11 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
}
export class ProtoInjector {
+ static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
+ var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
+ return new ProtoInjector(bd);
+ }
+
/** @internal */
_strategy: ProtoInjectorStrategy;
numberOfProviders: number;
@@ -215,7 +220,6 @@ export interface InjectorStrategy {
getObjAtIndex(index: number): any;
getMaxNumberOfObjects(): number;
- attach(parent: Injector, isHost: boolean): void;
resetConstructionCounter(): void;
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any;
}
@@ -240,12 +244,6 @@ export class InjectorInlineStrategy implements InjectorStrategy {
return this.injector._new(provider, visibility);
}
- attach(parent: Injector, isHost: boolean): void {
- var inj = this.injector;
- inj._parent = parent;
- inj._isHost = isHost;
- }
-
getObjByKeyId(keyId: number, visibility: Visibility): any {
var p = this.protoStrategy;
var inj = this.injector;
@@ -346,12 +344,6 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
return this.injector._new(provider, visibility);
}
- attach(parent: Injector, isHost: boolean): void {
- var inj = this.injector;
- inj._parent = parent;
- inj._isHost = isHost;
- }
-
getObjByKeyId(keyId: number, visibility: Visibility): any {
var p = this.protoStrategy;
@@ -516,9 +508,7 @@ export class Injector {
* ```
*/
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
- var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
- var proto = new ProtoInjector(bd);
- return new Injector(proto, null, null);
+ return new Injector(ProtoInjector.fromResolvedProviders(providers));
}
/**
@@ -531,8 +521,6 @@ export class Injector {
/** @internal */
_strategy: InjectorStrategy;
/** @internal */
- _isHost: boolean = false;
- /** @internal */
_constructionCounter: number = 0;
/** @internal */
public _proto: any /* ProtoInjector */;
@@ -542,6 +530,7 @@ export class Injector {
* Private
*/
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
+ private _isHostBoundary: boolean = false,
private _depProvider: any /* DependencyProvider */ = null,
private _debugContext: Function = null) {
this._proto = _proto;
@@ -549,6 +538,12 @@ export class Injector {
this._strategy = _proto._strategy.createInjectorStrategy(this);
}
+ /**
+ * Whether this injector is a boundary to a host.
+ * @internal
+ */
+ get hostBoundary() { return this._isHostBoundary; }
+
/**
* @internal
*/
@@ -692,7 +687,7 @@ export class Injector {
createChildFromResolved(providers: ResolvedProvider[]): Injector {
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
var proto = new ProtoInjector(bd);
- var inj = new Injector(proto, null, null);
+ var inj = new Injector(proto);
inj._parent = this;
return inj;
}
@@ -935,7 +930,7 @@ export class Injector {
var inj: Injector = this;
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
- if (inj._isHost) {
+ if (inj._isHostBoundary) {
return this._getPrivateDependency(key, optional, inj);
} else {
inj = inj._parent;
@@ -946,7 +941,7 @@ export class Injector {
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
if (obj !== UNDEFINED) return obj;
- if (isPresent(inj._parent) && inj._isHost) {
+ if (isPresent(inj._parent) && inj._isHostBoundary) {
return this._getPrivateDependency(key, optional, inj);
} else {
inj = inj._parent;
@@ -968,7 +963,7 @@ export class Injector {
var inj: Injector = this;
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
- providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
+ providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
inj = inj._parent;
}
@@ -976,7 +971,7 @@ export class Injector {
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
if (obj !== UNDEFINED) return obj;
- providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
+ providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
inj = inj._parent;
}
diff --git a/modules/angular2/src/core/di/provider.ts b/modules/angular2/src/core/di/provider.ts
index 267381244d..753f0fd4d8 100644
--- a/modules/angular2/src/core/di/provider.ts
+++ b/modules/angular2/src/core/di/provider.ts
@@ -538,50 +538,62 @@ export function resolveFactory(provider: Provider): ResolvedFactory {
* convenience provider syntax.
*/
export function resolveProvider(provider: Provider): ResolvedProvider {
- return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], false);
+ return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], provider.multi);
}
/**
* Resolve a list of Providers.
*/
export function resolveProviders(providers: Array): ResolvedProvider[] {
- var normalized = _createListOfProviders(_normalizeProviders(
- providers, new Map()));
- return normalized.map(b => {
- if (b instanceof _NormalizedProvider) {
- return new ResolvedProvider_(b.key, [b.resolvedFactory], false);
-
- } else {
- var arr = <_NormalizedProvider[]>b;
- return new ResolvedProvider_(arr[0].key, arr.map(_ => _.resolvedFactory), true);
- }
- });
+ var normalized = _normalizeProviders(providers, []);
+ var resolved = normalized.map(resolveProvider);
+ return MapWrapper.values(mergeResolvedProviders(resolved, new Map()));
}
/**
- * The algorithm works as follows:
- *
- * [Provider] -> [_NormalizedProvider|[_NormalizedProvider]] -> [ResolvedProvider]
- *
- * _NormalizedProvider is essentially a resolved provider before it was grouped by key.
+ * Merges a list of ResolvedProviders into a list where
+ * each key is contained exactly once and multi providers
+ * have been merged.
*/
-class _NormalizedProvider {
- constructor(public key: Key, public resolvedFactory: ResolvedFactory) {}
-}
-
-function _createListOfProviders(flattenedProviders: Map): any[] {
- return MapWrapper.values(flattenedProviders);
+export function mergeResolvedProviders(
+ providers: ResolvedProvider[],
+ normalizedProvidersMap: Map): Map {
+ for (var i = 0; i < providers.length; i++) {
+ var provider = providers[i];
+ var existing = normalizedProvidersMap.get(provider.key.id);
+ if (isPresent(existing)) {
+ if (provider.multiProvider !== existing.multiProvider) {
+ throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
+ }
+ if (provider.multiProvider) {
+ for (var j = 0; j < provider.resolvedFactories.length; j++) {
+ existing.resolvedFactories.push(provider.resolvedFactories[j]);
+ }
+ } else {
+ normalizedProvidersMap.set(provider.key.id, provider);
+ }
+ } else {
+ var resolvedProvider;
+ if (provider.multiProvider) {
+ resolvedProvider = new ResolvedProvider_(
+ provider.key, ListWrapper.clone(provider.resolvedFactories), provider.multiProvider);
+ } else {
+ resolvedProvider = provider;
+ }
+ normalizedProvidersMap.set(provider.key.id, resolvedProvider);
+ }
+ }
+ return normalizedProvidersMap;
}
function _normalizeProviders(providers: Array,
- res: Map):
- Map {
+ res: Provider[]): Provider[] {
providers.forEach(b => {
if (b instanceof Type) {
- _normalizeProvider(provide(b, {useClass: b}), res);
+ res.push(provide(b, {useClass: b}));
} else if (b instanceof Provider) {
- _normalizeProvider(b, res);
+ res.push(b);
} else if (b instanceof Array) {
_normalizeProviders(b, res);
@@ -597,36 +609,6 @@ function _normalizeProviders(providers: Array): void {
- var key = Key.get(b.token);
- var factory = resolveFactory(b);
- var normalized = new _NormalizedProvider(key, factory);
-
- if (b.multi) {
- var existingProvider = res.get(key.id);
-
- if (existingProvider instanceof Array) {
- existingProvider.push(normalized);
-
- } else if (isBlank(existingProvider)) {
- res.set(key.id, [normalized]);
-
- } else {
- throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
- }
-
- } else {
- var existingProvider = res.get(key.id);
-
- if (existingProvider instanceof Array) {
- throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
- }
-
- res.set(key.id, normalized);
- }
-}
-
function _constructDependencies(factoryFunction: Function, dependencies: any[]): Dependency[] {
if (isBlank(dependencies)) {
return _dependenciesFor(factoryFunction);
diff --git a/modules/angular2/src/core/linker.ts b/modules/angular2/src/core/linker.ts
index b43b69776f..b04229c4a4 100644
--- a/modules/angular2/src/core/linker.ts
+++ b/modules/angular2/src/core/linker.ts
@@ -17,6 +17,6 @@ export {QueryList} from './linker/query_list';
export {DynamicComponentLoader} from './linker/dynamic_component_loader';
export {ElementRef} from './linker/element_ref';
export {TemplateRef} from './linker/template_ref';
-export {ViewRef, HostViewRef, ProtoViewRef} from './linker/view_ref';
+export {EmbeddedViewRef, HostViewRef, ViewRef, HostViewFactoryRef} from './linker/view_ref';
export {ViewContainerRef} from './linker/view_container_ref';
export {ComponentRef} from './linker/dynamic_component_loader';
\ No newline at end of file
diff --git a/modules/angular2/src/core/linker/compiler.ts b/modules/angular2/src/core/linker/compiler.ts
index c4fdaea971..9447952ffd 100644
--- a/modules/angular2/src/core/linker/compiler.ts
+++ b/modules/angular2/src/core/linker/compiler.ts
@@ -1,12 +1,12 @@
-import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
-import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
+import {HostViewFactoryRef} from 'angular2/src/core/linker/view_ref';
import {Injectable} from 'angular2/src/core/di';
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {reflector} from 'angular2/src/core/reflection/reflection';
-import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
+import {HostViewFactory} from 'angular2/src/core/linker/view';
+import {HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
/**
* Low-level service for compiling {@link Component}s into {@link ProtoViewRef ProtoViews}s, which
@@ -16,37 +16,25 @@ import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
* both compiles and instantiates a Component.
*/
export abstract class Compiler {
- abstract compileInHost(componentType: Type): Promise;
+ abstract compileInHost(componentType: Type): Promise;
abstract clearCache();
}
-function _isCompiledHostTemplate(type: any): boolean {
- return type instanceof CompiledHostTemplate;
+function isHostViewFactory(type: any): boolean {
+ return type instanceof HostViewFactory;
}
@Injectable()
export class Compiler_ extends Compiler {
- constructor(private _protoViewFactory: ProtoViewFactory) { super(); }
-
- compileInHost(componentType: Type): Promise {
+ compileInHost(componentType: Type): Promise {
var metadatas = reflector.annotations(componentType);
- var compiledHostTemplate = metadatas.find(_isCompiledHostTemplate);
+ var hostViewFactory = metadatas.find(isHostViewFactory);
- if (isBlank(compiledHostTemplate)) {
- throw new BaseException(
- `No precompiled template for component ${stringify(componentType)} found`);
+ if (isBlank(hostViewFactory)) {
+ throw new BaseException(`No precompiled component ${stringify(componentType)} found`);
}
- return PromiseWrapper.resolve(this._createProtoView(compiledHostTemplate));
+ return PromiseWrapper.resolve(new HostViewFactoryRef_(hostViewFactory));
}
- private _createProtoView(compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
- return this._protoViewFactory.createHost(compiledHostTemplate).ref;
- }
-
- clearCache() { this._protoViewFactory.clearCache(); }
-}
-
-export function internalCreateProtoView(compiler: Compiler,
- compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
- return (compiler)._createProtoView(compiledHostTemplate);
+ clearCache() {}
}
diff --git a/modules/angular2/src/core/linker/directive_resolver.ts b/modules/angular2/src/core/linker/directive_resolver.ts
index 1d19c4e9fe..ae52c0cfc5 100644
--- a/modules/angular2/src/core/linker/directive_resolver.ts
+++ b/modules/angular2/src/core/linker/directive_resolver.ts
@@ -138,3 +138,5 @@ export class DirectiveResolver {
}
}
}
+
+export var CODEGEN_DIRECTIVE_RESOLVER = new DirectiveResolver();
diff --git a/modules/angular2/src/core/linker/dynamic_component_loader.ts b/modules/angular2/src/core/linker/dynamic_component_loader.ts
index 7613835930..e885ab892f 100644
--- a/modules/angular2/src/core/linker/dynamic_component_loader.ts
+++ b/modules/angular2/src/core/linker/dynamic_component_loader.ts
@@ -3,8 +3,8 @@ import {Compiler} from './compiler';
import {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/async';
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
-import {ElementRef} from './element_ref';
-import {ViewRef, HostViewRef} from './view_ref';
+import {ElementRef, ElementRef_} from './element_ref';
+import {HostViewRef} from './view_ref';
/**
* Represents an instance of a Component created via {@link DynamicComponentLoader}.
@@ -42,7 +42,9 @@ export abstract class ComponentRef {
/**
* The {@link ViewRef} of the Host View of this Component instance.
*/
- get hostView(): HostViewRef { return this.location.parentView; }
+ get hostView(): HostViewRef {
+ return (this.location).internalElement.parentView.ref;
+ }
/**
* @internal
@@ -140,7 +142,7 @@ export abstract class DynamicComponentLoader {
* ```
*/
abstract loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
- onDispose?: () => void): Promise;
+ onDispose?: () => void, projectableNodes?: any[][]): Promise;
/**
* Creates an instance of a Component and attaches it to a View Container located inside of the
@@ -190,7 +192,8 @@ export abstract class DynamicComponentLoader {
* ```
*/
abstract loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
- providers?: ResolvedProvider[]): Promise;
+ providers?: ResolvedProvider[],
+ projectableNodes?: any[][]): Promise;
/**
* Creates an instance of a Component and attaches it to the View Container found at the
@@ -232,19 +235,19 @@ export abstract class DynamicComponentLoader {
* Child
* ```
*/
- abstract loadNextToLocation(type: Type, location: ElementRef,
- providers?: ResolvedProvider[]): Promise;
+ abstract loadNextToLocation(type: Type, location: ElementRef, providers?: ResolvedProvider[],
+ projectableNodes?: any[][]): Promise;
}
@Injectable()
export class DynamicComponentLoader_ extends DynamicComponentLoader {
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); }
- loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
- onDispose?: () => void): Promise {
+ loadAsRoot(type: Type, overrideSelector: string, injector: Injector, onDispose?: () => void,
+ projectableNodes?: any[][]): Promise {
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
- var hostViewRef =
- this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
+ var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector,
+ injector, projectableNodes);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
@@ -259,24 +262,25 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader {
}
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
- providers: ResolvedProvider[] = null): Promise {
+ providers: ResolvedProvider[] = null,
+ projectableNodes: any[][] = null): Promise {
return this.loadNextToLocation(
- type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
- providers);
+ type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), providers,
+ projectableNodes);
}
- loadNextToLocation(type: Type, location: ElementRef,
- providers: ResolvedProvider[] = null): Promise {
+ loadNextToLocation(type: Type, location: ElementRef, providers: ResolvedProvider[] = null,
+ projectableNodes: any[][] = null): Promise {
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
var viewContainer = this._viewManager.getViewContainer(location);
- var hostViewRef =
- viewContainer.createHostView(hostProtoViewRef, viewContainer.length, providers);
+ var hostViewRef = viewContainer.createHostView(hostProtoViewRef, viewContainer.length,
+ providers, projectableNodes);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
var dispose = () => {
- var index = viewContainer.indexOf(hostViewRef);
- if (index !== -1) {
+ var index = viewContainer.indexOf(hostViewRef);
+ if (!hostViewRef.destroyed && index !== -1) {
viewContainer.remove(index);
}
};
diff --git a/modules/angular2/src/core/linker/element.ts b/modules/angular2/src/core/linker/element.ts
new file mode 100644
index 0000000000..7911e35c60
--- /dev/null
+++ b/modules/angular2/src/core/linker/element.ts
@@ -0,0 +1,867 @@
+import {
+ isPresent,
+ isBlank,
+ Type,
+ stringify,
+ CONST_EXPR,
+ StringWrapper
+} from 'angular2/src/facade/lang';
+import {BaseException} from 'angular2/src/facade/exceptions';
+import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
+import {
+ Injector,
+ Key,
+ Dependency,
+ provide,
+ Provider,
+ ResolvedProvider,
+ NoProviderError,
+ AbstractProviderError,
+ CyclicDependencyError,
+ resolveForwardRef,
+ Injectable
+} from 'angular2/src/core/di';
+import {mergeResolvedProviders} from 'angular2/src/core/di/provider';
+import {
+ UNDEFINED,
+ ProtoInjector,
+ Visibility,
+ InjectorInlineStrategy,
+ InjectorDynamicStrategy,
+ ProviderWithVisibility,
+ DependencyProvider
+} from 'angular2/src/core/di/injector';
+import {resolveProvider, ResolvedFactory, ResolvedProvider_} from 'angular2/src/core/di/provider';
+
+import {AttributeMetadata, QueryMetadata} from '../metadata/di';
+
+import {AppView} from './view';
+import {ViewType} from './view_type';
+import {ElementRef_} from './element_ref';
+
+import {ViewContainerRef} from './view_container_ref';
+import {ElementRef} from './element_ref';
+import {Renderer} from 'angular2/src/core/render/api';
+import {TemplateRef, TemplateRef_} from './template_ref';
+import {DirectiveMetadata, ComponentMetadata} from '../metadata/directives';
+import {
+ ChangeDetector,
+ ChangeDetectorRef
+} from 'angular2/src/core/change_detection/change_detection';
+import {QueryList} from './query_list';
+import {reflector} from 'angular2/src/core/reflection/reflection';
+import {SetterFn} from 'angular2/src/core/reflection/types';
+import {AfterViewChecked} from 'angular2/src/core/linker/interfaces';
+import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider';
+
+import {ViewContainerRef_} from "./view_container_ref";
+import {ResolvedMetadataCache} from './resolved_metadata_cache';
+
+var _staticKeys;
+
+export class StaticKeys {
+ templateRefId: number;
+ viewContainerId: number;
+ changeDetectorRefId: number;
+ elementRefId: number;
+ rendererId: number;
+
+ constructor() {
+ this.templateRefId = Key.get(TemplateRef).id;
+ this.viewContainerId = Key.get(ViewContainerRef).id;
+ this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
+ this.elementRefId = Key.get(ElementRef).id;
+ this.rendererId = Key.get(Renderer).id;
+ }
+
+ static instance(): StaticKeys {
+ if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
+ return _staticKeys;
+ }
+}
+
+export class DirectiveDependency extends Dependency {
+ constructor(key: Key, optional: boolean, lowerBoundVisibility: Object,
+ upperBoundVisibility: Object, properties: any[], public attributeName: string,
+ public queryDecorator: QueryMetadata) {
+ super(key, optional, lowerBoundVisibility, upperBoundVisibility, properties);
+ this._verify();
+ }
+
+ /** @internal */
+ _verify(): void {
+ var count = 0;
+ if (isPresent(this.queryDecorator)) count++;
+ if (isPresent(this.attributeName)) count++;
+ if (count > 1)
+ throw new BaseException(
+ 'A directive injectable can contain only one of the following @Attribute or @Query.');
+ }
+
+ static createFrom(d: Dependency): DirectiveDependency {
+ return new DirectiveDependency(
+ d.key, d.optional, d.lowerBoundVisibility, d.upperBoundVisibility, d.properties,
+ DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties));
+ }
+
+ /** @internal */
+ static _attributeName(properties: any[]): string {
+ var p = properties.find(p => p instanceof AttributeMetadata);
+ return isPresent(p) ? p.attributeName : null;
+ }
+
+ /** @internal */
+ static _query(properties: any[]): QueryMetadata {
+ return properties.find(p => p instanceof QueryMetadata);
+ }
+}
+
+export class DirectiveProvider extends ResolvedProvider_ {
+ constructor(key: Key, factory: Function, deps: Dependency[], public isComponent: boolean,
+ public providers: ResolvedProvider[], public viewProviders: ResolvedProvider[],
+ public queries: QueryMetadataWithSetter[]) {
+ super(key, [new ResolvedFactory(factory, deps)], false);
+ }
+
+ get displayName(): string { return this.key.displayName; }
+
+ static createFromType(type: Type, meta: DirectiveMetadata): DirectiveProvider {
+ var provider = new Provider(type, {useClass: type});
+ if (isBlank(meta)) {
+ meta = new DirectiveMetadata();
+ }
+ var rb = resolveProvider(provider);
+ var rf = rb.resolvedFactories[0];
+ var deps: DirectiveDependency[] = rf.dependencies.map(DirectiveDependency.createFrom);
+ var isComponent = meta instanceof ComponentMetadata;
+ var resolvedProviders = isPresent(meta.providers) ? Injector.resolve(meta.providers) : null;
+ var resolvedViewProviders = meta instanceof ComponentMetadata && isPresent(meta.viewProviders) ?
+ Injector.resolve(meta.viewProviders) :
+ null;
+ var queries = [];
+ if (isPresent(meta.queries)) {
+ StringMapWrapper.forEach(meta.queries, (meta, fieldName) => {
+ var setter = reflector.setter(fieldName);
+ queries.push(new QueryMetadataWithSetter(setter, meta));
+ });
+ }
+ // queries passed into the constructor.
+ // TODO: remove this after constructor queries are no longer supported
+ deps.forEach(d => {
+ if (isPresent(d.queryDecorator)) {
+ queries.push(new QueryMetadataWithSetter(null, d.queryDecorator));
+ }
+ });
+ return new DirectiveProvider(rb.key, rf.factory, deps, isComponent, resolvedProviders,
+ resolvedViewProviders, queries);
+ }
+}
+
+export class QueryMetadataWithSetter {
+ constructor(public setter: SetterFn, public metadata: QueryMetadata) {}
+}
+
+
+function setProvidersVisibility(providers: ResolvedProvider[], visibility: Visibility,
+ result: Map) {
+ for (var i = 0; i < providers.length; i++) {
+ result.set(providers[i].key.id, visibility);
+ }
+}
+
+export class AppProtoElement {
+ protoInjector: ProtoInjector;
+
+ static create(metadataCache: ResolvedMetadataCache, index: number,
+ attributes: {[key: string]: string}, directiveTypes: Type[],
+ directiveVariableBindings: {[key: string]: number}): AppProtoElement {
+ var componentDirProvider = null;
+ var mergedProvidersMap: Map = new Map();
+ var providerVisibilityMap: Map = new Map();
+ var providers = ListWrapper.createGrowableSize(directiveTypes.length);
+
+ var protoQueryRefs = [];
+ for (var i = 0; i < directiveTypes.length; i++) {
+ var dirProvider = metadataCache.getResolvedDirectiveMetadata(directiveTypes[i]);
+ providers[i] = new ProviderWithVisibility(
+ dirProvider, dirProvider.isComponent ? Visibility.PublicAndPrivate : Visibility.Public);
+
+ if (dirProvider.isComponent) {
+ componentDirProvider = dirProvider;
+ } else {
+ if (isPresent(dirProvider.providers)) {
+ mergeResolvedProviders(dirProvider.providers, mergedProvidersMap);
+ setProvidersVisibility(dirProvider.providers, Visibility.Public, providerVisibilityMap);
+ }
+ }
+ if (isPresent(dirProvider.viewProviders)) {
+ mergeResolvedProviders(dirProvider.viewProviders, mergedProvidersMap);
+ setProvidersVisibility(dirProvider.viewProviders, Visibility.Private,
+ providerVisibilityMap);
+ }
+ for (var queryIdx = 0; queryIdx < dirProvider.queries.length; queryIdx++) {
+ var q = dirProvider.queries[queryIdx];
+ protoQueryRefs.push(new ProtoQueryRef(i, q.setter, q.metadata));
+ }
+ }
+ if (isPresent(componentDirProvider) && isPresent(componentDirProvider.providers)) {
+ // directive providers need to be prioritized over component providers
+ mergeResolvedProviders(componentDirProvider.providers, mergedProvidersMap);
+ setProvidersVisibility(componentDirProvider.providers, Visibility.Public,
+ providerVisibilityMap);
+ }
+ mergedProvidersMap.forEach((provider, _) => {
+ providers.push(
+ new ProviderWithVisibility(provider, providerVisibilityMap.get(provider.key.id)));
+ });
+
+ return new AppProtoElement(isPresent(componentDirProvider), index, attributes, providers,
+ protoQueryRefs, directiveVariableBindings);
+ }
+
+ constructor(public firstProviderIsComponent: boolean, public index: number,
+ public attributes: {[key: string]: string}, pwvs: ProviderWithVisibility[],
+ public protoQueryRefs: ProtoQueryRef[],
+ public directiveVariableBindings: {[key: string]: number}) {
+ var length = pwvs.length;
+ if (length > 0) {
+ this.protoInjector = new ProtoInjector(pwvs);
+ } else {
+ this.protoInjector = null;
+ this.protoQueryRefs = [];
+ }
+ }
+
+ getProviderAtIndex(index: number): any { return this.protoInjector.getProviderAtIndex(index); }
+}
+
+class _Context {
+ constructor(public element: any, public componentElement: any, public injector: any) {}
+}
+
+export class InjectorWithHostBoundary {
+ constructor(public injector: Injector, public hostInjectorBoundary: boolean) {}
+}
+
+export class AppElement implements DependencyProvider, ElementRef, AfterViewChecked {
+ static getViewParentInjector(parentViewType: ViewType, containerAppElement: AppElement,
+ imperativelyCreatedProviders: ResolvedProvider[],
+ rootInjector: Injector): InjectorWithHostBoundary {
+ var parentInjector;
+ var hostInjectorBoundary;
+ switch (parentViewType) {
+ case ViewType.COMPONENT:
+ parentInjector = containerAppElement._injector;
+ hostInjectorBoundary = true;
+ break;
+ case ViewType.EMBEDDED:
+ parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
+ containerAppElement._injector.parent :
+ containerAppElement._injector;
+ hostInjectorBoundary = containerAppElement._injector.hostBoundary;
+ break;
+ case ViewType.HOST:
+ if (isPresent(containerAppElement)) {
+ // host view is attached to a container
+ parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
+ containerAppElement._injector.parent :
+ containerAppElement._injector;
+ if (isPresent(imperativelyCreatedProviders)) {
+ var imperativeProvidersWithVisibility = imperativelyCreatedProviders.map(
+ p => new ProviderWithVisibility(p, Visibility.Public));
+ // The imperative injector is similar to having an element between
+ // the dynamic-loaded component and its parent => no boundary between
+ // the component and imperativelyCreatedInjector.
+ parentInjector = new Injector(new ProtoInjector(imperativeProvidersWithVisibility),
+ parentInjector, true, null, null);
+ hostInjectorBoundary = false;
+ } else {
+ hostInjectorBoundary = containerAppElement._injector.hostBoundary;
+ }
+ } else {
+ // bootstrap
+ parentInjector = rootInjector;
+ hostInjectorBoundary = true;
+ }
+ break;
+ }
+ return new InjectorWithHostBoundary(parentInjector, hostInjectorBoundary);
+ }
+
+ public nestedViews: AppView[] = null;
+ public componentView: AppView = null;
+
+ private _queryStrategy: _QueryStrategy;
+ private _injector: Injector;
+ private _strategy: _ElementDirectiveStrategy;
+ public ref: ElementRef_;
+
+ constructor(public proto: AppProtoElement, public parentView: AppView, public parent: AppElement,
+ public nativeElement: any, public embeddedViewFactory: Function) {
+ this.ref = new ElementRef_(this);
+ var parentInjector = isPresent(parent) ? parent._injector : parentView.parentInjector;
+ if (isPresent(this.proto.protoInjector)) {
+ var isBoundary;
+ if (isPresent(parent) && isPresent(parent.proto.protoInjector)) {
+ isBoundary = false;
+ } else {
+ isBoundary = parentView.hostInjectorBoundary;
+ }
+ this._queryStrategy = this._buildQueryStrategy();
+ this._injector = new Injector(this.proto.protoInjector, parentInjector, isBoundary, this,
+ () => this._debugContext());
+
+ // we couple ourselves to the injector strategy to avoid polymorphic calls
+ var injectorStrategy = this._injector.internalStrategy;
+ this._strategy = injectorStrategy instanceof InjectorInlineStrategy ?
+ new ElementDirectiveInlineStrategy(injectorStrategy, this) :
+ new ElementDirectiveDynamicStrategy(injectorStrategy, this);
+ this._strategy.init();
+ } else {
+ this._queryStrategy = null;
+ this._injector = parentInjector;
+ this._strategy = null;
+ }
+ }
+
+ attachComponentView(componentView: AppView) { this.componentView = componentView; }
+
+ private _debugContext(): any {
+ var c = this.parentView.getDebugContext(this, null, null);
+ return isPresent(c) ? new _Context(c.element, c.componentElement, c.injector) : null;
+ }
+
+ hasVariableBinding(name: string): boolean {
+ var vb = this.proto.directiveVariableBindings;
+ return isPresent(vb) && StringMapWrapper.contains(vb, name);
+ }
+
+ getVariableBinding(name: string): any {
+ var index = this.proto.directiveVariableBindings[name];
+ return isPresent(index) ? this.getDirectiveAtIndex(index) : this.getElementRef();
+ }
+
+ get(token: any): any { return this._injector.get(token); }
+
+ hasDirective(type: Type): boolean { return isPresent(this._injector.getOptional(type)); }
+
+ getComponent(): any { return isPresent(this._strategy) ? this._strategy.getComponent() : null; }
+
+ getInjector(): Injector { return this._injector; }
+
+ getElementRef(): ElementRef { return this.ref; }
+
+ getViewContainerRef(): ViewContainerRef { return new ViewContainerRef_(this); }
+
+ getTemplateRef(): TemplateRef {
+ if (isPresent(this.embeddedViewFactory)) {
+ return new TemplateRef_(this.ref);
+ }
+ return null;
+ }
+
+ getDependency(injector: Injector, provider: ResolvedProvider, dep: Dependency): any {
+ if (provider instanceof DirectiveProvider) {
+ var dirDep = dep;
+
+ if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep);
+
+ if (isPresent(dirDep.queryDecorator))
+ return this._queryStrategy.findQuery(dirDep.queryDecorator).list;
+
+ if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) {
+ // We provide the component's view change detector to components and
+ // the surrounding component's change detector to directives.
+ if (this.proto.firstProviderIsComponent) {
+ // Note: The component view is not yet created when
+ // this method is called!
+ return new _ComponentViewChangeDetectorRef(this);
+ } else {
+ return this.parentView.changeDetector.ref;
+ }
+ }
+
+ if (dirDep.key.id === StaticKeys.instance().elementRefId) {
+ return this.getElementRef();
+ }
+
+ if (dirDep.key.id === StaticKeys.instance().viewContainerId) {
+ return this.getViewContainerRef();
+ }
+
+ if (dirDep.key.id === StaticKeys.instance().templateRefId) {
+ var tr = this.getTemplateRef();
+ if (isBlank(tr) && !dirDep.optional) {
+ throw new NoProviderError(null, dirDep.key);
+ }
+ return tr;
+ }
+
+ if (dirDep.key.id === StaticKeys.instance().rendererId) {
+ return this.parentView.renderer;
+ }
+
+ } else if (provider instanceof PipeProvider) {
+ if (dep.key.id === StaticKeys.instance().changeDetectorRefId) {
+ // We provide the component's view change detector to components and
+ // the surrounding component's change detector to directives.
+ if (this.proto.firstProviderIsComponent) {
+ // Note: The component view is not yet created when
+ // this method is called!
+ return new _ComponentViewChangeDetectorRef(this);
+ } else {
+ return this.parentView.changeDetector;
+ }
+ }
+ }
+
+ return UNDEFINED;
+ }
+
+ private _buildAttribute(dep: DirectiveDependency): string {
+ var attributes = this.proto.attributes;
+ if (isPresent(attributes) && StringMapWrapper.contains(attributes, dep.attributeName)) {
+ return attributes[dep.attributeName];
+ } else {
+ return null;
+ }
+ }
+
+ addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
+ var templateRef = this.getTemplateRef();
+ if (query.selector === TemplateRef && isPresent(templateRef)) {
+ list.push(templateRef);
+ }
+ if (this._strategy != null) {
+ this._strategy.addDirectivesMatchingQuery(query, list);
+ }
+ }
+
+ private _buildQueryStrategy(): _QueryStrategy {
+ if (this.proto.protoQueryRefs.length === 0) {
+ return _emptyQueryStrategy;
+ } else if (this.proto.protoQueryRefs.length <=
+ InlineQueryStrategy.NUMBER_OF_SUPPORTED_QUERIES) {
+ return new InlineQueryStrategy(this);
+ } else {
+ return new DynamicQueryStrategy(this);
+ }
+ }
+
+
+ getDirectiveAtIndex(index: number): any { return this._injector.getAt(index); }
+
+ ngAfterViewChecked(): void {
+ if (isPresent(this._queryStrategy)) this._queryStrategy.updateViewQueries();
+ }
+
+ ngAfterContentChecked(): void {
+ if (isPresent(this._queryStrategy)) this._queryStrategy.updateContentQueries();
+ }
+
+ traverseAndSetQueriesAsDirty(): void {
+ var inj: AppElement = this;
+ while (isPresent(inj)) {
+ inj._setQueriesAsDirty();
+ inj = inj.parent;
+ }
+ }
+
+ private _setQueriesAsDirty(): void {
+ if (isPresent(this._queryStrategy)) {
+ this._queryStrategy.setContentQueriesAsDirty();
+ }
+ if (this.parentView.proto.type === ViewType.COMPONENT) {
+ this.parentView.containerAppElement._queryStrategy.setViewQueriesAsDirty();
+ }
+ }
+}
+
+interface _QueryStrategy {
+ setContentQueriesAsDirty(): void;
+ setViewQueriesAsDirty(): void;
+ updateContentQueries(): void;
+ updateViewQueries(): void;
+ findQuery(query: QueryMetadata): QueryRef;
+}
+
+class _EmptyQueryStrategy implements _QueryStrategy {
+ setContentQueriesAsDirty(): void {}
+ setViewQueriesAsDirty(): void {}
+ updateContentQueries(): void {}
+ updateViewQueries(): void {}
+ findQuery(query: QueryMetadata): QueryRef {
+ throw new BaseException(`Cannot find query for directive ${query}.`);
+ }
+}
+
+var _emptyQueryStrategy = new _EmptyQueryStrategy();
+
+class InlineQueryStrategy implements _QueryStrategy {
+ static NUMBER_OF_SUPPORTED_QUERIES = 3;
+
+ query0: QueryRef;
+ query1: QueryRef;
+ query2: QueryRef;
+
+ constructor(ei: AppElement) {
+ var protoRefs = ei.proto.protoQueryRefs;
+ if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], ei);
+ if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], ei);
+ if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], ei);
+ }
+
+ setContentQueriesAsDirty(): void {
+ if (isPresent(this.query0) && !this.query0.isViewQuery) this.query0.dirty = true;
+ if (isPresent(this.query1) && !this.query1.isViewQuery) this.query1.dirty = true;
+ if (isPresent(this.query2) && !this.query2.isViewQuery) this.query2.dirty = true;
+ }
+
+ setViewQueriesAsDirty(): void {
+ if (isPresent(this.query0) && this.query0.isViewQuery) this.query0.dirty = true;
+ if (isPresent(this.query1) && this.query1.isViewQuery) this.query1.dirty = true;
+ if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true;
+ }
+
+ updateContentQueries() {
+ if (isPresent(this.query0) && !this.query0.isViewQuery) {
+ this.query0.update();
+ }
+ if (isPresent(this.query1) && !this.query1.isViewQuery) {
+ this.query1.update();
+ }
+ if (isPresent(this.query2) && !this.query2.isViewQuery) {
+ this.query2.update();
+ }
+ }
+
+ updateViewQueries() {
+ if (isPresent(this.query0) && this.query0.isViewQuery) {
+ this.query0.update();
+ }
+ if (isPresent(this.query1) && this.query1.isViewQuery) {
+ this.query1.update();
+ }
+ if (isPresent(this.query2) && this.query2.isViewQuery) {
+ this.query2.update();
+ }
+ }
+
+ findQuery(query: QueryMetadata): QueryRef {
+ if (isPresent(this.query0) && this.query0.protoQueryRef.query === query) {
+ return this.query0;
+ }
+ if (isPresent(this.query1) && this.query1.protoQueryRef.query === query) {
+ return this.query1;
+ }
+ if (isPresent(this.query2) && this.query2.protoQueryRef.query === query) {
+ return this.query2;
+ }
+ throw new BaseException(`Cannot find query for directive ${query}.`);
+ }
+}
+
+class DynamicQueryStrategy implements _QueryStrategy {
+ queries: QueryRef[];
+
+ constructor(ei: AppElement) {
+ this.queries = ei.proto.protoQueryRefs.map(p => new QueryRef(p, ei));
+ }
+
+ setContentQueriesAsDirty(): void {
+ for (var i = 0; i < this.queries.length; ++i) {
+ var q = this.queries[i];
+ if (!q.isViewQuery) q.dirty = true;
+ }
+ }
+
+ setViewQueriesAsDirty(): void {
+ for (var i = 0; i < this.queries.length; ++i) {
+ var q = this.queries[i];
+ if (q.isViewQuery) q.dirty = true;
+ }
+ }
+
+ updateContentQueries() {
+ for (var i = 0; i < this.queries.length; ++i) {
+ var q = this.queries[i];
+ if (!q.isViewQuery) {
+ q.update();
+ }
+ }
+ }
+
+ updateViewQueries() {
+ for (var i = 0; i < this.queries.length; ++i) {
+ var q = this.queries[i];
+ if (q.isViewQuery) {
+ q.update();
+ }
+ }
+ }
+
+ findQuery(query: QueryMetadata): QueryRef {
+ for (var i = 0; i < this.queries.length; ++i) {
+ var q = this.queries[i];
+ if (q.protoQueryRef.query === query) {
+ return q;
+ }
+ }
+ throw new BaseException(`Cannot find query for directive ${query}.`);
+ }
+}
+
+interface _ElementDirectiveStrategy {
+ getComponent(): any;
+ isComponentKey(key: Key): boolean;
+ addDirectivesMatchingQuery(q: QueryMetadata, res: any[]): void;
+ init(): void;
+}
+
+/**
+ * Strategy used by the `ElementInjector` when the number of providers is 10 or less.
+ * In such a case, inlining fields is beneficial for performances.
+ */
+class ElementDirectiveInlineStrategy implements _ElementDirectiveStrategy {
+ constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: AppElement) {}
+
+ init(): void {
+ var i = this.injectorStrategy;
+ var p = i.protoStrategy;
+ i.resetConstructionCounter();
+
+ if (p.provider0 instanceof DirectiveProvider && isPresent(p.keyId0) && i.obj0 === UNDEFINED)
+ i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
+ if (p.provider1 instanceof DirectiveProvider && isPresent(p.keyId1) && i.obj1 === UNDEFINED)
+ i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
+ if (p.provider2 instanceof DirectiveProvider && isPresent(p.keyId2) && i.obj2 === UNDEFINED)
+ i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
+ if (p.provider3 instanceof DirectiveProvider && isPresent(p.keyId3) && i.obj3 === UNDEFINED)
+ i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
+ if (p.provider4 instanceof DirectiveProvider && isPresent(p.keyId4) && i.obj4 === UNDEFINED)
+ i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
+ if (p.provider5 instanceof DirectiveProvider && isPresent(p.keyId5) && i.obj5 === UNDEFINED)
+ i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
+ if (p.provider6 instanceof DirectiveProvider && isPresent(p.keyId6) && i.obj6 === UNDEFINED)
+ i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
+ if (p.provider7 instanceof DirectiveProvider && isPresent(p.keyId7) && i.obj7 === UNDEFINED)
+ i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
+ if (p.provider8 instanceof DirectiveProvider && isPresent(p.keyId8) && i.obj8 === UNDEFINED)
+ i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
+ if (p.provider9 instanceof DirectiveProvider && isPresent(p.keyId9) && i.obj9 === UNDEFINED)
+ i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
+ }
+
+ getComponent(): any { return this.injectorStrategy.obj0; }
+
+ isComponentKey(key: Key): boolean {
+ return this._ei.proto.firstProviderIsComponent && isPresent(key) &&
+ key.id === this.injectorStrategy.protoStrategy.keyId0;
+ }
+
+ addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
+ var i = this.injectorStrategy;
+ var p = i.protoStrategy;
+ if (isPresent(p.provider0) && p.provider0.key.token === query.selector) {
+ if (i.obj0 === UNDEFINED) i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
+ list.push(i.obj0);
+ }
+ if (isPresent(p.provider1) && p.provider1.key.token === query.selector) {
+ if (i.obj1 === UNDEFINED) i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
+ list.push(i.obj1);
+ }
+ if (isPresent(p.provider2) && p.provider2.key.token === query.selector) {
+ if (i.obj2 === UNDEFINED) i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
+ list.push(i.obj2);
+ }
+ if (isPresent(p.provider3) && p.provider3.key.token === query.selector) {
+ if (i.obj3 === UNDEFINED) i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
+ list.push(i.obj3);
+ }
+ if (isPresent(p.provider4) && p.provider4.key.token === query.selector) {
+ if (i.obj4 === UNDEFINED) i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
+ list.push(i.obj4);
+ }
+ if (isPresent(p.provider5) && p.provider5.key.token === query.selector) {
+ if (i.obj5 === UNDEFINED) i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
+ list.push(i.obj5);
+ }
+ if (isPresent(p.provider6) && p.provider6.key.token === query.selector) {
+ if (i.obj6 === UNDEFINED) i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
+ list.push(i.obj6);
+ }
+ if (isPresent(p.provider7) && p.provider7.key.token === query.selector) {
+ if (i.obj7 === UNDEFINED) i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
+ list.push(i.obj7);
+ }
+ if (isPresent(p.provider8) && p.provider8.key.token === query.selector) {
+ if (i.obj8 === UNDEFINED) i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
+ list.push(i.obj8);
+ }
+ if (isPresent(p.provider9) && p.provider9.key.token === query.selector) {
+ if (i.obj9 === UNDEFINED) i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
+ list.push(i.obj9);
+ }
+ }
+}
+
+/**
+ * Strategy used by the `ElementInjector` when the number of bindings is 11 or more.
+ * In such a case, there are too many fields to inline (see ElementInjectorInlineStrategy).
+ */
+class ElementDirectiveDynamicStrategy implements _ElementDirectiveStrategy {
+ constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: AppElement) {}
+
+ init(): void {
+ var inj = this.injectorStrategy;
+ var p = inj.protoStrategy;
+ inj.resetConstructionCounter();
+
+ for (var i = 0; i < p.keyIds.length; i++) {
+ if (p.providers[i] instanceof DirectiveProvider && isPresent(p.keyIds[i]) &&
+ inj.objs[i] === UNDEFINED) {
+ inj.objs[i] = inj.instantiateProvider(p.providers[i], p.visibilities[i]);
+ }
+ }
+ }
+
+ getComponent(): any { return this.injectorStrategy.objs[0]; }
+
+ isComponentKey(key: Key): boolean {
+ var p = this.injectorStrategy.protoStrategy;
+ return this._ei.proto.firstProviderIsComponent && isPresent(key) && key.id === p.keyIds[0];
+ }
+
+ addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
+ var ist = this.injectorStrategy;
+ var p = ist.protoStrategy;
+
+ for (var i = 0; i < p.providers.length; i++) {
+ if (p.providers[i].key.token === query.selector) {
+ if (ist.objs[i] === UNDEFINED) {
+ ist.objs[i] = ist.instantiateProvider(p.providers[i], p.visibilities[i]);
+ }
+ list.push(ist.objs[i]);
+ }
+ }
+ }
+}
+
+export class ProtoQueryRef {
+ constructor(public dirIndex: number, public setter: SetterFn, public query: QueryMetadata) {}
+
+ get usesPropertySyntax(): boolean { return isPresent(this.setter); }
+}
+
+export class QueryRef {
+ public list: QueryList;
+ public dirty: boolean;
+
+ constructor(public protoQueryRef: ProtoQueryRef, private originator: AppElement) {
+ this.list = new QueryList();
+ this.dirty = true;
+ }
+
+ get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; }
+
+ update(): void {
+ if (!this.dirty) return;
+ this._update();
+ this.dirty = false;
+
+ // TODO delete the check once only field queries are supported
+ if (this.protoQueryRef.usesPropertySyntax) {
+ var dir = this.originator.getDirectiveAtIndex(this.protoQueryRef.dirIndex);
+ if (this.protoQueryRef.query.first) {
+ this.protoQueryRef.setter(dir, this.list.length > 0 ? this.list.first : null);
+ } else {
+ this.protoQueryRef.setter(dir, this.list);
+ }
+ }
+
+ this.list.notifyOnChanges();
+ }
+
+ private _update(): void {
+ var aggregator = [];
+ if (this.protoQueryRef.query.isViewQuery) {
+ // intentionally skipping originator for view queries.
+ var nestedView = this.originator.componentView;
+ if (isPresent(nestedView)) this._visitView(nestedView, aggregator);
+ } else {
+ this._visit(this.originator, aggregator);
+ }
+ this.list.reset(aggregator);
+ };
+
+ private _visit(inj: AppElement, aggregator: any[]): void {
+ var view = inj.parentView;
+ var startIdx = inj.proto.index;
+ for (var i = startIdx; i < view.appElements.length; i++) {
+ var curInj = view.appElements[i];
+ // The first injector after inj, that is outside the subtree rooted at
+ // inj has to have a null parent or a parent that is an ancestor of inj.
+ if (i > startIdx && (isBlank(curInj.parent) || curInj.parent.proto.index < startIdx)) {
+ break;
+ }
+
+ if (!this.protoQueryRef.query.descendants &&
+ !(curInj.parent == this.originator || curInj == this.originator))
+ continue;
+
+ // We visit the view container(VC) views right after the injector that contains
+ // the VC. Theoretically, that might not be the right order if there are
+ // child injectors of said injector. Not clear whether if such case can
+ // even be constructed with the current apis.
+ this._visitInjector(curInj, aggregator);
+ this._visitViewContainerViews(curInj.nestedViews, aggregator);
+ }
+ }
+
+ private _visitInjector(inj: AppElement, aggregator: any[]) {
+ if (this.protoQueryRef.query.isVarBindingQuery) {
+ this._aggregateVariableBinding(inj, aggregator);
+ } else {
+ this._aggregateDirective(inj, aggregator);
+ }
+ }
+
+ private _visitViewContainerViews(views: AppView[], aggregator: any[]) {
+ if (isPresent(views)) {
+ for (var j = 0; j < views.length; j++) {
+ this._visitView(views[j], aggregator);
+ }
+ }
+ }
+
+ private _visitView(view: AppView, aggregator: any[]) {
+ for (var i = 0; i < view.appElements.length; i++) {
+ var inj = view.appElements[i];
+ this._visitInjector(inj, aggregator);
+ this._visitViewContainerViews(inj.nestedViews, aggregator);
+ }
+ }
+
+ private _aggregateVariableBinding(inj: AppElement, aggregator: any[]): void {
+ var vb = this.protoQueryRef.query.varBindings;
+ for (var i = 0; i < vb.length; ++i) {
+ if (inj.hasVariableBinding(vb[i])) {
+ aggregator.push(inj.getVariableBinding(vb[i]));
+ }
+ }
+ }
+
+ private _aggregateDirective(inj: AppElement, aggregator: any[]): void {
+ inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator);
+ }
+}
+
+class _ComponentViewChangeDetectorRef extends ChangeDetectorRef {
+ constructor(private _appElement: AppElement) { super(); }
+
+ markForCheck(): void { this._appElement.componentView.changeDetector.ref.markForCheck(); }
+ detach(): void { this._appElement.componentView.changeDetector.ref.detach(); }
+ detectChanges(): void { this._appElement.componentView.changeDetector.ref.detectChanges(); }
+ checkNoChanges(): void { this._appElement.componentView.changeDetector.ref.checkNoChanges(); }
+ reattach(): void { this._appElement.componentView.changeDetector.ref.reattach(); }
+}
diff --git a/modules/angular2/src/core/linker/element_binder.ts b/modules/angular2/src/core/linker/element_binder.ts
deleted file mode 100644
index 00da679b67..0000000000
--- a/modules/angular2/src/core/linker/element_binder.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import {isBlank} from 'angular2/src/facade/lang';
-import {BaseException} from 'angular2/src/facade/exceptions';
-import * as eiModule from './element_injector';
-import {DirectiveProvider} from './element_injector';
-import * as viewModule from './view';
-
-export class ElementBinder {
- constructor(public index: number, public parent: ElementBinder, public distanceToParent: number,
- public protoElementInjector: eiModule.ProtoElementInjector,
- public componentDirective: DirectiveProvider,
- public nestedProtoView: viewModule.AppProtoView) {
- if (isBlank(index)) {
- throw new BaseException('null index not allowed.');
- }
- }
-}
diff --git a/modules/angular2/src/core/linker/element_injector.ts b/modules/angular2/src/core/linker/element_injector.ts
deleted file mode 100644
index cbfa777174..0000000000
--- a/modules/angular2/src/core/linker/element_injector.ts
+++ /dev/null
@@ -1,1086 +0,0 @@
-import {
- isPresent,
- isBlank,
- Type,
- stringify,
- CONST_EXPR,
- StringWrapper
-} from 'angular2/src/facade/lang';
-import {BaseException} from 'angular2/src/facade/exceptions';
-import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
-import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-import {
- Injector,
- Key,
- Dependency,
- provide,
- Provider,
- ResolvedProvider,
- NoProviderError,
- AbstractProviderError,
- CyclicDependencyError,
- resolveForwardRef
-} from 'angular2/src/core/di';
-import {
- UNDEFINED,
- ProtoInjector,
- Visibility,
- InjectorInlineStrategy,
- InjectorDynamicStrategy,
- ProviderWithVisibility,
- DependencyProvider
-} from 'angular2/src/core/di/injector';
-import {resolveProvider, ResolvedFactory, ResolvedProvider_} from 'angular2/src/core/di/provider';
-
-import {AttributeMetadata, QueryMetadata} from '../metadata/di';
-
-import {AppViewContainer, AppView} from './view';
-/* circular */ import * as avmModule from './view_manager';
-import {ViewContainerRef} from './view_container_ref';
-import {ElementRef} from './element_ref';
-import {TemplateRef} from './template_ref';
-import {DirectiveMetadata, ComponentMetadata} from '../metadata/directives';
-import {hasLifecycleHook} from './directive_lifecycle_reflector';
-import {
- ChangeDetector,
- ChangeDetectorRef
-} from 'angular2/src/core/change_detection/change_detection';
-import {QueryList} from './query_list';
-import {reflector} from 'angular2/src/core/reflection/reflection';
-import {SetterFn} from 'angular2/src/core/reflection/types';
-import {EventConfig} from 'angular2/src/core/linker/event_config';
-import {AfterViewChecked} from 'angular2/src/core/linker/interfaces';
-import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider';
-
-import {LifecycleHooks} from './interfaces';
-import {ViewContainerRef_} from "./view_container_ref";
-
-var _staticKeys;
-
-export class StaticKeys {
- viewManagerId: number;
- templateRefId: number;
- viewContainerId: number;
- changeDetectorRefId: number;
- elementRefId: number;
-
- constructor() {
- this.viewManagerId = Key.get(avmModule.AppViewManager).id;
- this.templateRefId = Key.get(TemplateRef).id;
- this.viewContainerId = Key.get(ViewContainerRef).id;
- this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
- this.elementRefId = Key.get(ElementRef).id;
- }
-
- static instance(): StaticKeys {
- if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
- return _staticKeys;
- }
-}
-
-export class TreeNode> {
- /** @internal */
- _parent: T;
- constructor(parent: T) {
- if (isPresent(parent)) {
- parent.addChild(this);
- } else {
- this._parent = null;
- }
- }
-
- addChild(child: T): void { child._parent = this; }
-
- remove(): void { this._parent = null; }
-
- get parent() { return this._parent; }
-}
-
-export class DirectiveDependency extends Dependency {
- constructor(key: Key, optional: boolean, lowerBoundVisibility: Object,
- upperBoundVisibility: Object, properties: any[], public attributeName: string,
- public queryDecorator: QueryMetadata) {
- super(key, optional, lowerBoundVisibility, upperBoundVisibility, properties);
- this._verify();
- }
-
- /** @internal */
- _verify(): void {
- var count = 0;
- if (isPresent(this.queryDecorator)) count++;
- if (isPresent(this.attributeName)) count++;
- if (count > 1)
- throw new BaseException(
- 'A directive injectable can contain only one of the following @Attribute or @Query.');
- }
-
- static createFrom(d: Dependency): Dependency {
- return new DirectiveDependency(
- d.key, d.optional, d.lowerBoundVisibility, d.upperBoundVisibility, d.properties,
- DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties));
- }
-
- /** @internal */
- static _attributeName(properties: any[]): string {
- var p = properties.find(p => p instanceof AttributeMetadata);
- return isPresent(p) ? p.attributeName : null;
- }
-
- /** @internal */
- static _query(properties: any[]): QueryMetadata {
- return properties.find(p => p instanceof QueryMetadata);
- }
-}
-
-export class DirectiveProvider extends ResolvedProvider_ {
- public callOnDestroy: boolean;
-
- constructor(key: Key, factory: Function, deps: Dependency[], public metadata: DirectiveMetadata,
- public providers: Array,
- public viewProviders: Array) {
- super(key, [new ResolvedFactory(factory, deps)], false);
- this.callOnDestroy = hasLifecycleHook(LifecycleHooks.OnDestroy, key.token);
- }
-
- get displayName(): string { return this.key.displayName; }
-
- get queries(): QueryMetadataWithSetter[] {
- if (isBlank(this.metadata.queries)) return [];
-
- var res = [];
- StringMapWrapper.forEach(this.metadata.queries, (meta, fieldName) => {
- var setter = reflector.setter(fieldName);
- res.push(new QueryMetadataWithSetter(setter, meta));
- });
- return res;
- }
-
- get eventEmitters(): string[] {
- return isPresent(this.metadata) && isPresent(this.metadata.outputs) ? this.metadata.outputs :
- [];
- }
-
- static createFromProvider(provider: Provider, meta: DirectiveMetadata): DirectiveProvider {
- if (isBlank(meta)) {
- meta = new DirectiveMetadata();
- }
-
- var rb = resolveProvider(provider);
- var rf = rb.resolvedFactories[0];
- var deps = rf.dependencies.map(DirectiveDependency.createFrom);
-
- var providers = isPresent(meta.providers) ? meta.providers : [];
- var viewBindigs = meta instanceof ComponentMetadata && isPresent(meta.viewProviders) ?
- meta.viewProviders :
- [];
- return new DirectiveProvider(rb.key, rf.factory, deps, meta, providers, viewBindigs);
- }
-
- static createFromType(type: Type, annotation: DirectiveMetadata): DirectiveProvider {
- var provider = new Provider(type, {useClass: type});
- return DirectiveProvider.createFromProvider(provider, annotation);
- }
-}
-
-// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
-export class PreBuiltObjects {
- nestedView: AppView = null;
- constructor(public viewManager: avmModule.AppViewManager, public view: AppView,
- public elementRef: ElementRef, public templateRef: TemplateRef) {}
-}
-
-export class QueryMetadataWithSetter {
- constructor(public setter: SetterFn, public metadata: QueryMetadata) {}
-}
-
-export class EventEmitterAccessor {
- constructor(public eventName: string, public getter: Function) {}
-
- subscribe(view: AppView, boundElementIndex: number, directive: Object): Object {
- var eventEmitter = this.getter(directive);
- return ObservableWrapper.subscribe(
- eventEmitter,
- eventObj => view.triggerEventHandlers(this.eventName, eventObj, boundElementIndex));
- }
-}
-
-function _createEventEmitterAccessors(bwv: ProviderWithVisibility): EventEmitterAccessor[] {
- var provider = bwv.provider;
- if (!(provider instanceof DirectiveProvider)) return [];
- var db = provider;
- return db.eventEmitters.map(eventConfig => {
- var parsedEvent = EventConfig.parse(eventConfig);
- return new EventEmitterAccessor(parsedEvent.eventName, reflector.getter(parsedEvent.fieldName));
- });
-}
-
-function _createProtoQueryRefs(providers: ProviderWithVisibility[]): ProtoQueryRef[] {
- var res = [];
- ListWrapper.forEachWithIndex(providers, (b, i) => {
- if (b.provider instanceof DirectiveProvider) {
- var directiveProvider = b.provider;
- // field queries
- var queries: QueryMetadataWithSetter[] = directiveProvider.queries;
- queries.forEach(q => res.push(new ProtoQueryRef(i, q.setter, q.metadata)));
-
- // queries passed into the constructor.
- // TODO: remove this after constructor queries are no longer supported
- var deps: DirectiveDependency[] =
- directiveProvider.resolvedFactory.dependencies;
- deps.forEach(d => {
- if (isPresent(d.queryDecorator)) res.push(new ProtoQueryRef(i, null, d.queryDecorator));
- });
- }
- });
- return res;
-}
-
-export class ProtoElementInjector {
- view: AppView;
- attributes: Map;
- eventEmitterAccessors: EventEmitterAccessor[][];
- protoQueryRefs: ProtoQueryRef[];
- protoInjector: ProtoInjector;
-
- static create(parent: ProtoElementInjector, index: number, providers: DirectiveProvider[],
- firstProviderIsComponent: boolean, distanceToParent: number,
- directiveVariableBindings: Map): ProtoElementInjector {
- var bd = [];
-
- ProtoElementInjector._createDirectiveProviderWithVisibility(providers, bd,
- firstProviderIsComponent);
- if (firstProviderIsComponent) {
- ProtoElementInjector._createViewProvidersWithVisibility(providers, bd);
- }
-
- ProtoElementInjector._createProvidersWithVisibility(providers, bd);
- return new ProtoElementInjector(parent, index, bd, distanceToParent, firstProviderIsComponent,
- directiveVariableBindings);
- }
-
- private static _createDirectiveProviderWithVisibility(dirProviders: DirectiveProvider[],
- bd: ProviderWithVisibility[],
- firstProviderIsComponent: boolean) {
- dirProviders.forEach(dirProvider => {
- bd.push(ProtoElementInjector._createProviderWithVisibility(
- firstProviderIsComponent, dirProvider, dirProviders, dirProvider));
- });
- }
-
- private static _createProvidersWithVisibility(dirProviders: DirectiveProvider[],
- bd: ProviderWithVisibility[]) {
- var providersFromAllDirectives = [];
- dirProviders.forEach(dirProvider => {
- providersFromAllDirectives =
- ListWrapper.concat(providersFromAllDirectives, dirProvider.providers);
- });
-
- var resolved = Injector.resolve(providersFromAllDirectives);
- resolved.forEach(b => bd.push(new ProviderWithVisibility(b, Visibility.Public)));
- }
-
- private static _createProviderWithVisibility(firstProviderIsComponent: boolean,
- dirProvider: DirectiveProvider,
- dirProviders: DirectiveProvider[],
- provider: ResolvedProvider) {
- var isComponent = firstProviderIsComponent && dirProviders[0] === dirProvider;
- return new ProviderWithVisibility(
- provider, isComponent ? Visibility.PublicAndPrivate : Visibility.Public);
- }
-
- private static _createViewProvidersWithVisibility(dirProviders: DirectiveProvider[],
- bd: ProviderWithVisibility[]) {
- var resolvedViewProviders = Injector.resolve(dirProviders[0].viewProviders);
- resolvedViewProviders.forEach(b => bd.push(new ProviderWithVisibility(b, Visibility.Private)));
- }
-
- /** @internal */
- public _firstProviderIsComponent: boolean;
-
-
- constructor(public parent: ProtoElementInjector, public index: number,
- bwv: ProviderWithVisibility[], public distanceToParent: number,
- _firstProviderIsComponent: boolean,
- public directiveVariableBindings: Map) {
- this._firstProviderIsComponent = _firstProviderIsComponent;
- var length = bwv.length;
- this.protoInjector = new ProtoInjector(bwv);
- this.eventEmitterAccessors = ListWrapper.createFixedSize(length);
- for (var i = 0; i < length; ++i) {
- this.eventEmitterAccessors[i] = _createEventEmitterAccessors(bwv[i]);
- }
- this.protoQueryRefs = _createProtoQueryRefs(bwv);
- }
-
- instantiate(parent: ElementInjector): ElementInjector {
- return new ElementInjector(this, parent);
- }
-
- directParent(): ProtoElementInjector { return this.distanceToParent < 2 ? this.parent : null; }
-
- get hasBindings(): boolean { return this.eventEmitterAccessors.length > 0; }
-
- getProviderAtIndex(index: number): any { return this.protoInjector.getProviderAtIndex(index); }
-}
-
-class _Context {
- constructor(public element: any, public componentElement: any, public injector: any) {}
-}
-
-export class ElementInjector extends TreeNode implements DependencyProvider,
- AfterViewChecked {
- private _host: ElementInjector;
- private _preBuiltObjects: PreBuiltObjects = null;
- private _queryStrategy: _QueryStrategy;
-
- hydrated: boolean;
-
- private _injector: Injector;
- private _strategy: _ElementInjectorStrategy;
- /** @internal */
- public _proto: ProtoElementInjector;
-
- constructor(_proto: ProtoElementInjector, parent: ElementInjector) {
- super(parent);
- this._proto = _proto;
- this._injector =
- new Injector(this._proto.protoInjector, null, this, () => this._debugContext());
-
- // we couple ourselves to the injector strategy to avoid polymoprhic calls
- var injectorStrategy = this._injector.internalStrategy;
- this._strategy = injectorStrategy instanceof InjectorInlineStrategy ?
- new ElementInjectorInlineStrategy(injectorStrategy, this) :
- new ElementInjectorDynamicStrategy(injectorStrategy, this);
-
- this.hydrated = false;
-
- this._queryStrategy = this._buildQueryStrategy();
- }
-
- dehydrate(): void {
- this.hydrated = false;
- this._host = null;
- this._preBuiltObjects = null;
- this._strategy.callOnDestroy();
- this._strategy.dehydrate();
- this._queryStrategy.dehydrate();
- }
-
- hydrate(imperativelyCreatedInjector: Injector, host: ElementInjector,
- preBuiltObjects: PreBuiltObjects): void {
- this._host = host;
- this._preBuiltObjects = preBuiltObjects;
-
- this._reattachInjectors(imperativelyCreatedInjector);
- this._queryStrategy.hydrate();
- this._strategy.hydrate();
-
- this.hydrated = true;
- }
-
- private _debugContext(): any {
- var p = this._preBuiltObjects;
- var index = p.elementRef.boundElementIndex - p.view.elementOffset;
- var c = this._preBuiltObjects.view.getDebugContext(index, null);
- return isPresent(c) ? new _Context(c.element, c.componentElement, c.injector) : null;
- }
-
- private _reattachInjectors(imperativelyCreatedInjector: Injector): void {
- // Dynamically-loaded component in the template. Not a root ElementInjector.
- if (isPresent(this._parent)) {
- if (isPresent(imperativelyCreatedInjector)) {
- // The imperative injector is similar to having an element between
- // the dynamic-loaded component and its parent => no boundaries.
- this._reattachInjector(this._injector, imperativelyCreatedInjector, false);
- this._reattachInjector(imperativelyCreatedInjector, this._parent._injector, false);
- } else {
- this._reattachInjector(this._injector, this._parent._injector, false);
- }
-
- // Dynamically-loaded component in the template. A root ElementInjector.
- } else if (isPresent(this._host)) {
- // The imperative injector is similar to having an element between
- // the dynamic-loaded component and its parent => no boundary between
- // the component and imperativelyCreatedInjector.
- // But since it is a root ElementInjector, we need to create a boundary
- // between imperativelyCreatedInjector and _host.
- if (isPresent(imperativelyCreatedInjector)) {
- this._reattachInjector(this._injector, imperativelyCreatedInjector, false);
- this._reattachInjector(imperativelyCreatedInjector, this._host._injector, true);
- } else {
- this._reattachInjector(this._injector, this._host._injector, true);
- }
-
- // Bootstrap
- } else {
- if (isPresent(imperativelyCreatedInjector)) {
- this._reattachInjector(this._injector, imperativelyCreatedInjector, true);
- }
- }
- }
-
- private _reattachInjector(injector: Injector, parentInjector: Injector, isBoundary: boolean) {
- injector.internalStrategy.attach(parentInjector, isBoundary);
- }
-
- hasVariableBinding(name: string): boolean {
- var vb = this._proto.directiveVariableBindings;
- return isPresent(vb) && vb.has(name);
- }
-
- getVariableBinding(name: string): any {
- var index = this._proto.directiveVariableBindings.get(name);
- return isPresent(index) ? this.getDirectiveAtIndex(index) : this.getElementRef();
- }
-
- get(token: any): any { return this._injector.get(token); }
-
- hasDirective(type: Type): boolean { return isPresent(this._injector.getOptional(type)); }
-
- getEventEmitterAccessors(): EventEmitterAccessor[][] { return this._proto.eventEmitterAccessors; }
-
- getDirectiveVariableBindings(): Map {
- return this._proto.directiveVariableBindings;
- }
-
- getComponent(): any { return this._strategy.getComponent(); }
-
- getInjector(): Injector { return this._injector; }
-
- getElementRef(): ElementRef { return this._preBuiltObjects.elementRef; }
-
- getViewContainerRef(): ViewContainerRef {
- return new ViewContainerRef_(this._preBuiltObjects.viewManager, this.getElementRef());
- }
-
- getNestedView(): AppView { return this._preBuiltObjects.nestedView; }
-
- getView(): AppView { return this._preBuiltObjects.view; }
-
- directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; }
-
- isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); }
-
- getDependency(injector: Injector, provider: ResolvedProvider, dep: Dependency): any {
- var key: Key = dep.key;
-
- if (provider instanceof DirectiveProvider) {
- var dirDep = dep;
- var dirProvider = provider;
- var staticKeys = StaticKeys.instance();
-
-
- if (key.id === staticKeys.viewManagerId) return this._preBuiltObjects.viewManager;
-
- if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep);
-
- if (isPresent(dirDep.queryDecorator))
- return this._queryStrategy.findQuery(dirDep.queryDecorator).list;
-
- if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) {
- // We provide the component's view change detector to components and
- // the surrounding component's change detector to directives.
- if (dirProvider.metadata instanceof ComponentMetadata) {
- var componentView = this._preBuiltObjects.view.getNestedView(
- this._preBuiltObjects.elementRef.boundElementIndex);
- return componentView.changeDetector.ref;
- } else {
- return this._preBuiltObjects.view.changeDetector.ref;
- }
- }
-
- if (dirDep.key.id === StaticKeys.instance().elementRefId) {
- return this.getElementRef();
- }
-
- if (dirDep.key.id === StaticKeys.instance().viewContainerId) {
- return this.getViewContainerRef();
- }
-
- if (dirDep.key.id === StaticKeys.instance().templateRefId) {
- if (isBlank(this._preBuiltObjects.templateRef)) {
- if (dirDep.optional) {
- return null;
- }
-
- throw new NoProviderError(null, dirDep.key);
- }
- return this._preBuiltObjects.templateRef;
- }
-
- } else if (provider instanceof PipeProvider) {
- if (dep.key.id === StaticKeys.instance().changeDetectorRefId) {
- var componentView = this._preBuiltObjects.view.getNestedView(
- this._preBuiltObjects.elementRef.boundElementIndex);
- return componentView.changeDetector.ref;
- }
- }
-
- return UNDEFINED;
- }
-
- private _buildAttribute(dep: DirectiveDependency): string {
- var attributes = this._proto.attributes;
- if (isPresent(attributes) && attributes.has(dep.attributeName)) {
- return attributes.get(dep.attributeName);
- } else {
- return null;
- }
- }
-
- addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
- var templateRef = isBlank(this._preBuiltObjects) ? null : this._preBuiltObjects.templateRef;
- if (query.selector === TemplateRef && isPresent(templateRef)) {
- list.push(templateRef);
- }
- this._strategy.addDirectivesMatchingQuery(query, list);
- }
-
- private _buildQueryStrategy(): _QueryStrategy {
- if (this._proto.protoQueryRefs.length === 0) {
- return _emptyQueryStrategy;
- } else if (this._proto.protoQueryRefs.length <=
- InlineQueryStrategy.NUMBER_OF_SUPPORTED_QUERIES) {
- return new InlineQueryStrategy(this);
- } else {
- return new DynamicQueryStrategy(this);
- }
- }
-
- link(parent: ElementInjector): void { parent.addChild(this); }
-
- unlink(): void { this.remove(); }
-
- getDirectiveAtIndex(index: number): any { return this._injector.getAt(index); }
-
- hasInstances(): boolean { return this._proto.hasBindings && this.hydrated; }
-
- getHost(): ElementInjector { return this._host; }
-
- getBoundElementIndex(): number { return this._proto.index; }
-
- getRootViewInjectors(): ElementInjector[] {
- if (!this.hydrated) return [];
- var view = this._preBuiltObjects.view;
- var nestedView = view.getNestedView(view.elementOffset + this.getBoundElementIndex());
- return isPresent(nestedView) ? nestedView.rootElementInjectors : [];
- }
-
- ngAfterViewChecked(): void { this._queryStrategy.updateViewQueries(); }
-
- ngAfterContentChecked(): void { this._queryStrategy.updateContentQueries(); }
-
- traverseAndSetQueriesAsDirty(): void {
- var inj: ElementInjector = this;
- while (isPresent(inj)) {
- inj._setQueriesAsDirty();
- inj = inj.parent;
- }
- }
-
- private _setQueriesAsDirty(): void {
- this._queryStrategy.setContentQueriesAsDirty();
- if (isPresent(this._host)) this._host._queryStrategy.setViewQueriesAsDirty();
- }
-}
-
-interface _QueryStrategy {
- setContentQueriesAsDirty(): void;
- setViewQueriesAsDirty(): void;
- hydrate(): void;
- dehydrate(): void;
- updateContentQueries(): void;
- updateViewQueries(): void;
- findQuery(query: QueryMetadata): QueryRef;
-}
-
-class _EmptyQueryStrategy implements _QueryStrategy {
- setContentQueriesAsDirty(): void {}
- setViewQueriesAsDirty(): void {}
- hydrate(): void {}
- dehydrate(): void {}
- updateContentQueries(): void {}
- updateViewQueries(): void {}
- findQuery(query: QueryMetadata): QueryRef {
- throw new BaseException(`Cannot find query for directive ${query}.`);
- }
-}
-
-var _emptyQueryStrategy = new _EmptyQueryStrategy();
-
-class InlineQueryStrategy implements _QueryStrategy {
- static NUMBER_OF_SUPPORTED_QUERIES = 3;
-
- query0: QueryRef;
- query1: QueryRef;
- query2: QueryRef;
-
- constructor(ei: ElementInjector) {
- var protoRefs = ei._proto.protoQueryRefs;
- if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], ei);
- if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], ei);
- if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], ei);
- }
-
- setContentQueriesAsDirty(): void {
- if (isPresent(this.query0) && !this.query0.isViewQuery) this.query0.dirty = true;
- if (isPresent(this.query1) && !this.query1.isViewQuery) this.query1.dirty = true;
- if (isPresent(this.query2) && !this.query2.isViewQuery) this.query2.dirty = true;
- }
-
- setViewQueriesAsDirty(): void {
- if (isPresent(this.query0) && this.query0.isViewQuery) this.query0.dirty = true;
- if (isPresent(this.query1) && this.query1.isViewQuery) this.query1.dirty = true;
- if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true;
- }
-
- hydrate(): void {
- if (isPresent(this.query0)) this.query0.hydrate();
- if (isPresent(this.query1)) this.query1.hydrate();
- if (isPresent(this.query2)) this.query2.hydrate();
- }
-
- dehydrate(): void {
- if (isPresent(this.query0)) this.query0.dehydrate();
- if (isPresent(this.query1)) this.query1.dehydrate();
- if (isPresent(this.query2)) this.query2.dehydrate();
- }
-
- updateContentQueries() {
- if (isPresent(this.query0) && !this.query0.isViewQuery) {
- this.query0.update();
- }
- if (isPresent(this.query1) && !this.query1.isViewQuery) {
- this.query1.update();
- }
- if (isPresent(this.query2) && !this.query2.isViewQuery) {
- this.query2.update();
- }
- }
-
- updateViewQueries() {
- if (isPresent(this.query0) && this.query0.isViewQuery) {
- this.query0.update();
- }
- if (isPresent(this.query1) && this.query1.isViewQuery) {
- this.query1.update();
- }
- if (isPresent(this.query2) && this.query2.isViewQuery) {
- this.query2.update();
- }
- }
-
- findQuery(query: QueryMetadata): QueryRef {
- if (isPresent(this.query0) && this.query0.protoQueryRef.query === query) {
- return this.query0;
- }
- if (isPresent(this.query1) && this.query1.protoQueryRef.query === query) {
- return this.query1;
- }
- if (isPresent(this.query2) && this.query2.protoQueryRef.query === query) {
- return this.query2;
- }
- throw new BaseException(`Cannot find query for directive ${query}.`);
- }
-}
-
-class DynamicQueryStrategy implements _QueryStrategy {
- queries: QueryRef[];
-
- constructor(ei: ElementInjector) {
- this.queries = ei._proto.protoQueryRefs.map(p => new QueryRef(p, ei));
- }
-
- setContentQueriesAsDirty(): void {
- for (var i = 0; i < this.queries.length; ++i) {
- var q = this.queries[i];
- if (!q.isViewQuery) q.dirty = true;
- }
- }
-
- setViewQueriesAsDirty(): void {
- for (var i = 0; i < this.queries.length; ++i) {
- var q = this.queries[i];
- if (q.isViewQuery) q.dirty = true;
- }
- }
-
- hydrate(): void {
- for (var i = 0; i < this.queries.length; ++i) {
- var q = this.queries[i];
- q.hydrate();
- }
- }
-
- dehydrate(): void {
- for (var i = 0; i < this.queries.length; ++i) {
- var q = this.queries[i];
- q.dehydrate();
- }
- }
-
- updateContentQueries() {
- for (var i = 0; i < this.queries.length; ++i) {
- var q = this.queries[i];
- if (!q.isViewQuery) {
- q.update();
- }
- }
- }
-
- updateViewQueries() {
- for (var i = 0; i < this.queries.length; ++i) {
- var q = this.queries[i];
- if (q.isViewQuery) {
- q.update();
- }
- }
- }
-
- findQuery(query: QueryMetadata): QueryRef {
- for (var i = 0; i < this.queries.length; ++i) {
- var q = this.queries[i];
- if (q.protoQueryRef.query === query) {
- return q;
- }
- }
- throw new BaseException(`Cannot find query for directive ${query}.`);
- }
-}
-
-interface _ElementInjectorStrategy {
- callOnDestroy(): void;
- getComponent(): any;
- isComponentKey(key: Key): boolean;
- addDirectivesMatchingQuery(q: QueryMetadata, res: any[]): void;
- hydrate(): void;
- dehydrate(): void;
-}
-
-/**
- * Strategy used by the `ElementInjector` when the number of providers is 10 or less.
- * In such a case, inlining fields is beneficial for performances.
- */
-class ElementInjectorInlineStrategy implements _ElementInjectorStrategy {
- constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: ElementInjector) {}
-
- hydrate(): void {
- var i = this.injectorStrategy;
- var p = i.protoStrategy;
- i.resetConstructionCounter();
-
- if (p.provider0 instanceof DirectiveProvider && isPresent(p.keyId0) && i.obj0 === UNDEFINED)
- i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
- if (p.provider1 instanceof DirectiveProvider && isPresent(p.keyId1) && i.obj1 === UNDEFINED)
- i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
- if (p.provider2 instanceof DirectiveProvider && isPresent(p.keyId2) && i.obj2 === UNDEFINED)
- i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
- if (p.provider3 instanceof DirectiveProvider && isPresent(p.keyId3) && i.obj3 === UNDEFINED)
- i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
- if (p.provider4 instanceof DirectiveProvider && isPresent(p.keyId4) && i.obj4 === UNDEFINED)
- i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
- if (p.provider5 instanceof DirectiveProvider && isPresent(p.keyId5) && i.obj5 === UNDEFINED)
- i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
- if (p.provider6 instanceof DirectiveProvider && isPresent(p.keyId6) && i.obj6 === UNDEFINED)
- i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
- if (p.provider7 instanceof DirectiveProvider && isPresent(p.keyId7) && i.obj7 === UNDEFINED)
- i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
- if (p.provider8 instanceof DirectiveProvider && isPresent(p.keyId8) && i.obj8 === UNDEFINED)
- i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
- if (p.provider9 instanceof DirectiveProvider && isPresent(p.keyId9) && i.obj9 === UNDEFINED)
- i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
- }
-
- dehydrate() {
- var i = this.injectorStrategy;
-
- i.obj0 = UNDEFINED;
- i.obj1 = UNDEFINED;
- i.obj2 = UNDEFINED;
- i.obj3 = UNDEFINED;
- i.obj4 = UNDEFINED;
- i.obj5 = UNDEFINED;
- i.obj6 = UNDEFINED;
- i.obj7 = UNDEFINED;
- i.obj8 = UNDEFINED;
- i.obj9 = UNDEFINED;
- }
-
- callOnDestroy(): void {
- var i = this.injectorStrategy;
- var p = i.protoStrategy;
-
- if (p.provider0 instanceof DirectiveProvider &&
- (p.provider0).callOnDestroy) {
- i.obj0.ngOnDestroy();
- }
- if (p.provider1 instanceof DirectiveProvider &&
- (p.provider1).callOnDestroy) {
- i.obj1.ngOnDestroy();
- }
- if (p.provider2 instanceof DirectiveProvider &&
- (p.provider2).callOnDestroy) {
- i.obj2.ngOnDestroy();
- }
- if (p.provider3 instanceof DirectiveProvider &&
- (p.provider3).callOnDestroy) {
- i.obj3.ngOnDestroy();
- }
- if (p.provider4 instanceof DirectiveProvider &&
- (p.provider4).callOnDestroy) {
- i.obj4.ngOnDestroy();
- }
- if (p.provider5 instanceof DirectiveProvider &&
- (p.provider5).callOnDestroy) {
- i.obj5.ngOnDestroy();
- }
- if (p.provider6 instanceof DirectiveProvider &&
- (p.provider6).callOnDestroy) {
- i.obj6.ngOnDestroy();
- }
- if (p.provider7 instanceof DirectiveProvider &&
- (p.provider7).callOnDestroy) {
- i.obj7.ngOnDestroy();
- }
- if (p.provider8 instanceof DirectiveProvider &&
- (p.provider8).callOnDestroy) {
- i.obj8.ngOnDestroy();
- }
- if (p.provider9 instanceof DirectiveProvider &&
- (p.provider9).callOnDestroy) {
- i.obj9.ngOnDestroy();
- }
- }
-
- getComponent(): any { return this.injectorStrategy.obj0; }
-
- isComponentKey(key: Key): boolean {
- return this._ei._proto._firstProviderIsComponent && isPresent(key) &&
- key.id === this.injectorStrategy.protoStrategy.keyId0;
- }
-
- addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
- var i = this.injectorStrategy;
- var p = i.protoStrategy;
-
- if (isPresent(p.provider0) && p.provider0.key.token === query.selector) {
- if (i.obj0 === UNDEFINED) i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
- list.push(i.obj0);
- }
- if (isPresent(p.provider1) && p.provider1.key.token === query.selector) {
- if (i.obj1 === UNDEFINED) i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
- list.push(i.obj1);
- }
- if (isPresent(p.provider2) && p.provider2.key.token === query.selector) {
- if (i.obj2 === UNDEFINED) i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
- list.push(i.obj2);
- }
- if (isPresent(p.provider3) && p.provider3.key.token === query.selector) {
- if (i.obj3 === UNDEFINED) i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
- list.push(i.obj3);
- }
- if (isPresent(p.provider4) && p.provider4.key.token === query.selector) {
- if (i.obj4 === UNDEFINED) i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
- list.push(i.obj4);
- }
- if (isPresent(p.provider5) && p.provider5.key.token === query.selector) {
- if (i.obj5 === UNDEFINED) i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
- list.push(i.obj5);
- }
- if (isPresent(p.provider6) && p.provider6.key.token === query.selector) {
- if (i.obj6 === UNDEFINED) i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
- list.push(i.obj6);
- }
- if (isPresent(p.provider7) && p.provider7.key.token === query.selector) {
- if (i.obj7 === UNDEFINED) i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
- list.push(i.obj7);
- }
- if (isPresent(p.provider8) && p.provider8.key.token === query.selector) {
- if (i.obj8 === UNDEFINED) i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
- list.push(i.obj8);
- }
- if (isPresent(p.provider9) && p.provider9.key.token === query.selector) {
- if (i.obj9 === UNDEFINED) i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
- list.push(i.obj9);
- }
- }
-}
-
-/**
- * Strategy used by the `ElementInjector` when the number of bindings is 11 or more.
- * In such a case, there are too many fields to inline (see ElementInjectorInlineStrategy).
- */
-class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
- constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: ElementInjector) {}
-
- hydrate(): void {
- var inj = this.injectorStrategy;
- var p = inj.protoStrategy;
- inj.resetConstructionCounter();
-
- for (var i = 0; i < p.keyIds.length; i++) {
- if (p.providers[i] instanceof DirectiveProvider && isPresent(p.keyIds[i]) &&
- inj.objs[i] === UNDEFINED) {
- inj.objs[i] = inj.instantiateProvider(p.providers[i], p.visibilities[i]);
- }
- }
- }
-
- dehydrate(): void {
- var inj = this.injectorStrategy;
- ListWrapper.fill(inj.objs, UNDEFINED);
- }
-
- callOnDestroy(): void {
- var ist = this.injectorStrategy;
- var p = ist.protoStrategy;
-
- for (var i = 0; i < p.providers.length; i++) {
- if (p.providers[i] instanceof DirectiveProvider &&
- (p.providers[i]).callOnDestroy) {
- ist.objs[i].ngOnDestroy();
- }
- }
- }
-
- getComponent(): any { return this.injectorStrategy.objs[0]; }
-
- isComponentKey(key: Key): boolean {
- var p = this.injectorStrategy.protoStrategy;
- return this._ei._proto._firstProviderIsComponent && isPresent(key) && key.id === p.keyIds[0];
- }
-
- addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
- var ist = this.injectorStrategy;
- var p = ist.protoStrategy;
-
- for (var i = 0; i < p.providers.length; i++) {
- if (p.providers[i].key.token === query.selector) {
- if (ist.objs[i] === UNDEFINED) {
- ist.objs[i] = ist.instantiateProvider(p.providers[i], p.visibilities[i]);
- }
- list.push(ist.objs[i]);
- }
- }
- }
-}
-
-export class ProtoQueryRef {
- constructor(public dirIndex: number, public setter: SetterFn, public query: QueryMetadata) {}
-
- get usesPropertySyntax(): boolean { return isPresent(this.setter); }
-}
-
-export class QueryRef {
- public list: QueryList;
- public dirty: boolean;
-
- constructor(public protoQueryRef: ProtoQueryRef, private originator: ElementInjector) {}
-
- get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; }
-
- update(): void {
- if (!this.dirty) return;
- this._update();
- this.dirty = false;
-
- // TODO delete the check once only field queries are supported
- if (this.protoQueryRef.usesPropertySyntax) {
- var dir = this.originator.getDirectiveAtIndex(this.protoQueryRef.dirIndex);
- if (this.protoQueryRef.query.first) {
- this.protoQueryRef.setter(dir, this.list.length > 0 ? this.list.first : null);
- } else {
- this.protoQueryRef.setter(dir, this.list);
- }
- }
-
- this.list.notifyOnChanges();
- }
-
- private _update(): void {
- var aggregator = [];
- if (this.protoQueryRef.query.isViewQuery) {
- var view = this.originator.getView();
- // intentionally skipping originator for view queries.
- var nestedView =
- view.getNestedView(view.elementOffset + this.originator.getBoundElementIndex());
- if (isPresent(nestedView)) this._visitView(nestedView, aggregator);
- } else {
- this._visit(this.originator, aggregator);
- }
- this.list.reset(aggregator);
- };
-
- private _visit(inj: ElementInjector, aggregator: any[]): void {
- var view = inj.getView();
- var startIdx = view.elementOffset + inj._proto.index;
- for (var i = startIdx; i < view.elementOffset + view.ownBindersCount; i++) {
- var curInj = view.elementInjectors[i];
- if (isBlank(curInj)) continue;
- // The first injector after inj, that is outside the subtree rooted at
- // inj has to have a null parent or a parent that is an ancestor of inj.
- if (i > startIdx && (isBlank(curInj) || isBlank(curInj.parent) ||
- view.elementOffset + curInj.parent._proto.index < startIdx)) {
- break;
- }
-
- if (!this.protoQueryRef.query.descendants &&
- !(curInj.parent == this.originator || curInj == this.originator))
- continue;
-
- // We visit the view container(VC) views right after the injector that contains
- // the VC. Theoretically, that might not be the right order if there are
- // child injectors of said injector. Not clear whether if such case can
- // even be constructed with the current apis.
- this._visitInjector(curInj, aggregator);
- var vc = view.viewContainers[i];
- if (isPresent(vc)) this._visitViewContainer(vc, aggregator);
- }
- }
-
- private _visitInjector(inj: ElementInjector, aggregator: any[]) {
- if (this.protoQueryRef.query.isVarBindingQuery) {
- this._aggregateVariableBinding(inj, aggregator);
- } else {
- this._aggregateDirective(inj, aggregator);
- }
- }
-
- private _visitViewContainer(vc: AppViewContainer, aggregator: any[]) {
- for (var j = 0; j < vc.views.length; j++) {
- this._visitView(vc.views[j], aggregator);
- }
- }
-
- private _visitView(view: AppView, aggregator: any[]) {
- for (var i = view.elementOffset; i < view.elementOffset + view.ownBindersCount; i++) {
- var inj = view.elementInjectors[i];
- if (isBlank(inj)) continue;
-
- this._visitInjector(inj, aggregator);
-
- var vc = view.viewContainers[i];
- if (isPresent(vc)) this._visitViewContainer(vc, aggregator);
- }
- }
-
- private _aggregateVariableBinding(inj: ElementInjector, aggregator: any[]): void {
- var vb = this.protoQueryRef.query.varBindings;
- for (var i = 0; i < vb.length; ++i) {
- if (inj.hasVariableBinding(vb[i])) {
- aggregator.push(inj.getVariableBinding(vb[i]));
- }
- }
- }
-
- private _aggregateDirective(inj: ElementInjector, aggregator: any[]): void {
- inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator);
- }
-
- dehydrate(): void { this.list = null; }
-
- hydrate(): void {
- this.list = new QueryList();
- this.dirty = true;
- }
-}
diff --git a/modules/angular2/src/core/linker/element_ref.ts b/modules/angular2/src/core/linker/element_ref.ts
index 810fce84cb..a052fb8fa0 100644
--- a/modules/angular2/src/core/linker/element_ref.ts
+++ b/modules/angular2/src/core/linker/element_ref.ts
@@ -1,6 +1,5 @@
-import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
-import {ViewRef, ViewRef_} from './view_ref';
-import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/render/api';
+import {unimplemented} from 'angular2/src/facade/exceptions';
+import {AppElement} from './element';
/**
* Represents a location in a View that has an injection, change-detection and render context
@@ -12,23 +11,7 @@ import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/rende
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
* element.
*/
-export abstract class ElementRef implements RenderElementRef {
- /**
- * @internal
- *
- * Reference to the {@link ViewRef} that this `ElementRef` is part of.
- */
- parentView: ViewRef;
-
- /**
- * @internal
- *
- * Index of the element inside the {@link ViewRef}.
- *
- * This is used internally by the Angular framework to locate elements.
- */
- boundElementIndex: number;
-
+export abstract class ElementRef {
/**
* The underlying native element or `null` if direct access to native elements is not supported
* (e.g. when the application runs in a web worker).
@@ -48,24 +31,13 @@ export abstract class ElementRef implements RenderElementRef {
*
*
*/
- get nativeElement(): any { return unimplemented(); };
-
- get renderView(): RenderViewRef { return unimplemented(); }
+ get nativeElement(): any { return unimplemented(); }
}
-export class ElementRef_ extends ElementRef {
- constructor(public parentView: ViewRef,
+export class ElementRef_ implements ElementRef {
+ constructor(private _appElement: AppElement) {}
- /**
- * Index of the element inside the {@link ViewRef}.
- *
- * This is used internally by the Angular framework to locate elements.
- */
- public boundElementIndex: number, private _renderer: Renderer) {
- super();
- }
+ get internalElement(): AppElement { return this._appElement; }
- get renderView(): RenderViewRef { return (this.parentView).render; }
- set renderView(value) { unimplemented(); }
- get nativeElement(): any { return this._renderer.getNativeElementSync(this); }
+ get nativeElement() { return this._appElement.nativeElement; }
}
diff --git a/modules/angular2/src/core/linker/event_config.ts b/modules/angular2/src/core/linker/event_config.ts
deleted file mode 100644
index fdbcf2baa0..0000000000
--- a/modules/angular2/src/core/linker/event_config.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-export const EVENT_TARGET_SEPARATOR = ':';
-
-export class EventConfig {
- constructor(public fieldName: string, public eventName: string, public isLongForm: boolean) {}
-
- static parse(eventConfig: string): EventConfig {
- var fieldName = eventConfig, eventName = eventConfig, isLongForm = false;
- var separatorIdx = eventConfig.indexOf(EVENT_TARGET_SEPARATOR);
- if (separatorIdx > -1) {
- // long format: 'fieldName: eventName'
- fieldName = eventConfig.substring(0, separatorIdx).trim();
- eventName = eventConfig.substring(separatorIdx + 1).trim();
- isLongForm = true;
- }
- return new EventConfig(fieldName, eventName, isLongForm);
- }
-
- getFullName(): string {
- return this.isLongForm ? `${this.fieldName}${EVENT_TARGET_SEPARATOR}${this.eventName}` :
- this.eventName;
- }
-}
diff --git a/modules/angular2/src/core/linker/pipe_resolver.ts b/modules/angular2/src/core/linker/pipe_resolver.ts
index 67683978c7..3de7a3f23c 100644
--- a/modules/angular2/src/core/linker/pipe_resolver.ts
+++ b/modules/angular2/src/core/linker/pipe_resolver.ts
@@ -31,3 +31,5 @@ export class PipeResolver {
throw new BaseException(`No Pipe decorator found on ${stringify(type)}`);
}
}
+
+export var CODEGEN_PIPE_RESOLVER = new PipeResolver();
diff --git a/modules/angular2/src/core/linker/proto_view_factory.ts b/modules/angular2/src/core/linker/proto_view_factory.ts
deleted file mode 100644
index 30216a44b4..0000000000
--- a/modules/angular2/src/core/linker/proto_view_factory.ts
+++ /dev/null
@@ -1,341 +0,0 @@
-import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/facade/lang';
-
-import {RenderProtoViewRef, RenderComponentTemplate} from 'angular2/src/core/render/api';
-
-import {Optional, Injectable, Provider, resolveForwardRef, Inject} from 'angular2/src/core/di';
-
-import {PipeProvider} from '../pipes/pipe_provider';
-import {ProtoPipes} from '../pipes/pipes';
-
-import {AppProtoView, AppProtoViewMergeInfo, ViewType} from './view';
-import {ElementBinder} from './element_binder';
-import {ProtoElementInjector, DirectiveProvider} from './element_injector';
-import {DirectiveResolver} from './directive_resolver';
-import {ViewResolver} from './view_resolver';
-import {PipeResolver} from './pipe_resolver';
-import {ViewMetadata, ViewEncapsulation} from '../metadata/view';
-import {PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
-
-import {
- visitAllCommands,
- CompiledComponentTemplate,
- CompiledHostTemplate,
- TemplateCmd,
- CommandVisitor,
- EmbeddedTemplateCmd,
- BeginComponentCmd,
- BeginElementCmd,
- IBeginElementCmd,
- TextCmd,
- NgContentCmd
-} from './template_commands';
-
-import {Renderer} from 'angular2/src/core/render/api';
-import {APP_ID} from 'angular2/src/core/application_tokens';
-
-
-@Injectable()
-export class ProtoViewFactory {
- private _cache: Map = new Map();
- private _nextTemplateId: number = 0;
-
- constructor(private _renderer: Renderer,
- @Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Array,
- private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
- private _pipeResolver: PipeResolver, @Inject(APP_ID) private _appId: string) {}
-
- clearCache() { this._cache.clear(); }
-
- createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
- var compiledTemplate = compiledHostTemplate.template;
- var result = this._cache.get(compiledTemplate.id);
- if (isBlank(result)) {
- var emptyMap: {[key: string]: PipeProvider} = {};
- var shortId = `${this._appId}-${this._nextTemplateId++}`;
- this._renderer.registerComponentTemplate(new RenderComponentTemplate(
- compiledTemplate.id, shortId, ViewEncapsulation.None, compiledTemplate.commands, []));
- result =
- new AppProtoView(compiledTemplate.id, compiledTemplate.commands, ViewType.HOST, true,
- compiledTemplate.changeDetectorFactory, null, new ProtoPipes(emptyMap));
- this._cache.set(compiledTemplate.id, result);
- }
- return result;
- }
-
- private _createComponent(cmd: BeginComponentCmd): AppProtoView {
- var nestedProtoView = this._cache.get(cmd.templateId);
- if (isBlank(nestedProtoView)) {
- var component = cmd.directives[0];
- var view = this._viewResolver.resolve(component);
- var compiledTemplate = cmd.templateGetter();
- var styles = _flattenStyleArr(compiledTemplate.styles, []);
- var shortId = `${this._appId}-${this._nextTemplateId++}`;
- this._renderer.registerComponentTemplate(new RenderComponentTemplate(
- compiledTemplate.id, shortId, cmd.encapsulation, compiledTemplate.commands, styles));
- var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
-
- nestedProtoView = new AppProtoView(
- compiledTemplate.id, compiledTemplate.commands, ViewType.COMPONENT, true,
- compiledTemplate.changeDetectorFactory, null, ProtoPipes.fromProviders(boundPipes));
- // Note: The cache is updated before recursing
- // to be able to resolve cycles
- this._cache.set(compiledTemplate.id, nestedProtoView);
- this._initializeProtoView(nestedProtoView, null);
- }
- return nestedProtoView;
- }
-
- private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView {
- var nestedProtoView = new AppProtoView(
- parent.templateId, cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory,
- arrayToMap(cmd.variableNameAndValues, true), new ProtoPipes(parent.pipes.config));
- if (cmd.isMerged) {
- this.initializeProtoViewIfNeeded(nestedProtoView);
- }
- return nestedProtoView;
- }
-
- initializeProtoViewIfNeeded(protoView: AppProtoView) {
- if (!protoView.isInitialized()) {
- var render = this._renderer.createProtoView(protoView.templateId, protoView.templateCmds);
- this._initializeProtoView(protoView, render);
- }
- }
-
- private _initializeProtoView(protoView: AppProtoView, render: RenderProtoViewRef) {
- var initializer = new _ProtoViewInitializer(protoView, this._directiveResolver, this);
- visitAllCommands(initializer, protoView.templateCmds);
- var mergeInfo =
- new AppProtoViewMergeInfo(initializer.mergeEmbeddedViewCount, initializer.mergeElementCount,
- initializer.mergeViewCount);
- protoView.init(render, initializer.elementBinders, initializer.boundTextCount, mergeInfo,
- initializer.variableLocations);
- }
-
- private _bindPipe(typeOrProvider): PipeProvider {
- let meta = this._pipeResolver.resolve(typeOrProvider);
- return PipeProvider.createFromType(typeOrProvider, meta);
- }
-
- private _flattenPipes(view: ViewMetadata): any[] {
- let pipes = [];
- if (isPresent(this._platformPipes)) {
- _flattenArray(this._platformPipes, pipes);
- }
- if (isPresent(view.pipes)) {
- _flattenArray(view.pipes, pipes);
- }
- return pipes;
- }
-}
-
-
-function createComponent(protoViewFactory: ProtoViewFactory, cmd: BeginComponentCmd): AppProtoView {
- return (protoViewFactory)._createComponent(cmd);
-}
-
-function createEmbeddedTemplate(protoViewFactory: ProtoViewFactory, cmd: EmbeddedTemplateCmd,
- parent: AppProtoView): AppProtoView {
- return (protoViewFactory)._createEmbeddedTemplate(cmd, parent);
-}
-
-class _ProtoViewInitializer implements CommandVisitor {
- variableLocations: Map = new Map();
- boundTextCount: number = 0;
- boundElementIndex: number = 0;
- elementBinderStack: ElementBinder[] = [];
- distanceToParentElementBinder: number = 0;
- distanceToParentProtoElementInjector: number = 0;
- elementBinders: ElementBinder[] = [];
- mergeEmbeddedViewCount: number = 0;
- mergeElementCount: number = 0;
- mergeViewCount: number = 1;
-
- constructor(private _protoView: AppProtoView, private _directiveResolver: DirectiveResolver,
- private _protoViewFactory: ProtoViewFactory) {}
-
- visitText(cmd: TextCmd, context: any): any {
- if (cmd.isBound) {
- this.boundTextCount++;
- }
- return null;
- }
- visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
- visitBeginElement(cmd: BeginElementCmd, context: any): any {
- if (cmd.isBound) {
- this._visitBeginBoundElement(cmd, null);
- } else {
- this._visitBeginElement(cmd, null, null);
- }
- return null;
- }
- visitEndElement(context: any): any { return this._visitEndElement(); }
- visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
- var nestedProtoView = createComponent(this._protoViewFactory, cmd);
- return this._visitBeginBoundElement(cmd, nestedProtoView);
- }
- visitEndComponent(context: any): any { return this._visitEndElement(); }
- visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
- var nestedProtoView = createEmbeddedTemplate(this._protoViewFactory, cmd, this._protoView);
- if (cmd.isMerged) {
- this.mergeEmbeddedViewCount++;
- }
- this._visitBeginBoundElement(cmd, nestedProtoView);
- return this._visitEndElement();
- }
-
- private _visitBeginBoundElement(cmd: IBeginElementCmd, nestedProtoView: AppProtoView): any {
- if (isPresent(nestedProtoView) && nestedProtoView.isMergable) {
- this.mergeElementCount += nestedProtoView.mergeInfo.elementCount;
- this.mergeViewCount += nestedProtoView.mergeInfo.viewCount;
- this.mergeEmbeddedViewCount += nestedProtoView.mergeInfo.embeddedViewCount;
- }
- var elementBinder = _createElementBinder(
- this._directiveResolver, nestedProtoView, this.elementBinderStack, this.boundElementIndex,
- this.distanceToParentElementBinder, this.distanceToParentProtoElementInjector, cmd);
- this.elementBinders.push(elementBinder);
- var protoElementInjector = elementBinder.protoElementInjector;
- for (var i = 0; i < cmd.variableNameAndValues.length; i += 2) {
- this.variableLocations.set(cmd.variableNameAndValues[i], this.boundElementIndex);
- }
- this.boundElementIndex++;
- this.mergeElementCount++;
- return this._visitBeginElement(cmd, elementBinder, protoElementInjector);
- }
-
- private _visitBeginElement(cmd: IBeginElementCmd, elementBinder: ElementBinder,
- protoElementInjector: ProtoElementInjector): any {
- this.distanceToParentElementBinder =
- isPresent(elementBinder) ? 1 : this.distanceToParentElementBinder + 1;
- this.distanceToParentProtoElementInjector =
- isPresent(protoElementInjector) ? 1 : this.distanceToParentProtoElementInjector + 1;
- this.elementBinderStack.push(elementBinder);
- return null;
- }
-
- private _visitEndElement(): any {
- var parentElementBinder = this.elementBinderStack.pop();
- var parentProtoElementInjector =
- isPresent(parentElementBinder) ? parentElementBinder.protoElementInjector : null;
- this.distanceToParentElementBinder = isPresent(parentElementBinder) ?
- parentElementBinder.distanceToParent :
- this.distanceToParentElementBinder - 1;
- this.distanceToParentProtoElementInjector = isPresent(parentProtoElementInjector) ?
- parentProtoElementInjector.distanceToParent :
- this.distanceToParentProtoElementInjector - 1;
- return null;
- }
-}
-
-
-function _createElementBinder(directiveResolver: DirectiveResolver, nestedProtoView: AppProtoView,
- elementBinderStack: ElementBinder[], boundElementIndex: number,
- distanceToParentBinder: number, distanceToParentPei: number,
- beginElementCmd: IBeginElementCmd): ElementBinder {
- var parentElementBinder: ElementBinder = null;
- var parentProtoElementInjector: ProtoElementInjector = null;
- if (distanceToParentBinder > 0) {
- parentElementBinder = elementBinderStack[elementBinderStack.length - distanceToParentBinder];
- }
- if (isBlank(parentElementBinder)) {
- distanceToParentBinder = -1;
- }
- if (distanceToParentPei > 0) {
- var peiBinder = elementBinderStack[elementBinderStack.length - distanceToParentPei];
- if (isPresent(peiBinder)) {
- parentProtoElementInjector = peiBinder.protoElementInjector;
- }
- }
- if (isBlank(parentProtoElementInjector)) {
- distanceToParentPei = -1;
- }
- var componentDirectiveProvider: DirectiveProvider = null;
- var isEmbeddedTemplate = false;
- var directiveProviders: DirectiveProvider[] =
- beginElementCmd.directives.map(type => provideDirective(directiveResolver, type));
- if (beginElementCmd instanceof BeginComponentCmd) {
- componentDirectiveProvider = directiveProviders[0];
- } else if (beginElementCmd instanceof EmbeddedTemplateCmd) {
- isEmbeddedTemplate = true;
- }
-
- var protoElementInjector = null;
- // Create a protoElementInjector for any element that either has bindings *or* has one
- // or more var- defined *or* for elements:
- // - Elements with a var- defined need a their own element injector
- // so that, when hydrating, $implicit can be set to the element.
- // - elements need their own ElementInjector so that we can query their TemplateRef
- var hasVariables = beginElementCmd.variableNameAndValues.length > 0;
- if (directiveProviders.length > 0 || hasVariables || isEmbeddedTemplate) {
- var directiveVariableBindings = new Map();
- if (!isEmbeddedTemplate) {
- directiveVariableBindings = createDirectiveVariableBindings(
- beginElementCmd.variableNameAndValues, directiveProviders);
- }
- protoElementInjector = ProtoElementInjector.create(
- parentProtoElementInjector, boundElementIndex, directiveProviders,
- isPresent(componentDirectiveProvider), distanceToParentPei, directiveVariableBindings);
- protoElementInjector.attributes = arrayToMap(beginElementCmd.attrNameAndValues, false);
- }
-
- return new ElementBinder(boundElementIndex, parentElementBinder, distanceToParentBinder,
- protoElementInjector, componentDirectiveProvider, nestedProtoView);
-}
-
-function provideDirective(directiveResolver: DirectiveResolver, type: Type): DirectiveProvider {
- let annotation = directiveResolver.resolve(type);
- return DirectiveProvider.createFromType(type, annotation);
-}
-
-export function createDirectiveVariableBindings(
- variableNameAndValues: Array,
- directiveProviders: DirectiveProvider[]): Map {
- var directiveVariableBindings = new Map();
- for (var i = 0; i < variableNameAndValues.length; i += 2) {
- var templateName = variableNameAndValues[i];
- var dirIndex = variableNameAndValues[i + 1];
- if (isNumber(dirIndex)) {
- directiveVariableBindings.set(templateName, dirIndex);
- } else {
- // a variable without a directive index -> reference the element
- directiveVariableBindings.set(templateName, null);
- }
- }
- return directiveVariableBindings;
-}
-
-
-function arrayToMap(arr: string[], inverse: boolean): Map {
- var result = new Map();
- for (var i = 0; i < arr.length; i += 2) {
- if (inverse) {
- result.set(arr[i + 1], arr[i]);
- } else {
- result.set(arr[i], arr[i + 1]);
- }
- }
- return result;
-}
-
-function _flattenArray(tree: any[], out: Array): void {
- for (var i = 0; i < tree.length; i++) {
- var item = resolveForwardRef(tree[i]);
- if (isArray(item)) {
- _flattenArray(item, out);
- } else {
- out.push(item);
- }
- }
-}
-
-function _flattenStyleArr(arr: Array, out: string[]): string[] {
- for (var i = 0; i < arr.length; i++) {
- var entry = arr[i];
- if (isArray(entry)) {
- _flattenStyleArr(entry, out);
- } else {
- out.push(entry);
- }
- }
- return out;
-}
diff --git a/modules/angular2/src/core/linker/resolved_metadata_cache.ts b/modules/angular2/src/core/linker/resolved_metadata_cache.ts
new file mode 100644
index 0000000000..370c2005e6
--- /dev/null
+++ b/modules/angular2/src/core/linker/resolved_metadata_cache.ts
@@ -0,0 +1,35 @@
+import {Injectable} from '../di';
+import {Type, isBlank} from 'angular2/src/facade/lang';
+import {DirectiveProvider} from './element';
+import {DirectiveResolver, CODEGEN_DIRECTIVE_RESOLVER} from './directive_resolver';
+import {PipeProvider} from '../pipes/pipe_provider';
+import {PipeResolver, CODEGEN_PIPE_RESOLVER} from './pipe_resolver';
+
+@Injectable()
+export class ResolvedMetadataCache {
+ private _directiveCache: Map = new Map();
+ private _pipeCache: Map = new Map();
+
+ constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver) {}
+
+ getResolvedDirectiveMetadata(type: Type): DirectiveProvider {
+ var result = this._directiveCache.get(type);
+ if (isBlank(result)) {
+ result = DirectiveProvider.createFromType(type, this._directiveResolver.resolve(type));
+ this._directiveCache.set(type, result);
+ }
+ return result;
+ }
+
+ getResolvedPipeMetadata(type: Type): PipeProvider {
+ var result = this._pipeCache.get(type);
+ if (isBlank(result)) {
+ result = PipeProvider.createFromType(type, this._pipeResolver.resolve(type));
+ this._pipeCache.set(type, result);
+ }
+ return result;
+ }
+}
+
+export var CODEGEN_RESOLVED_METADATA_CACHE =
+ new ResolvedMetadataCache(CODEGEN_DIRECTIVE_RESOLVER, CODEGEN_PIPE_RESOLVER);
diff --git a/modules/angular2/src/core/linker/template_commands.ts b/modules/angular2/src/core/linker/template_commands.ts
deleted file mode 100644
index 20d32cbfb4..0000000000
--- a/modules/angular2/src/core/linker/template_commands.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/facade/lang';
-import {unimplemented} from 'angular2/src/facade/exceptions';
-import {
- RenderTemplateCmd,
- RenderCommandVisitor,
- RenderBeginElementCmd,
- RenderTextCmd,
- RenderNgContentCmd,
- RenderBeginComponentCmd,
- RenderEmbeddedTemplateCmd
-} from 'angular2/src/core/render/api';
-import {ViewEncapsulation} from 'angular2/src/core/metadata';
-// Export ViewEncapsulation so that compiled templates only need to depend
-// on template_commands.
-export {ViewEncapsulation} from 'angular2/src/core/metadata';
-
-/**
- * A compiled host template.
- *
- * This is const as we are storing it as annotation
- * for the compiled component type.
- */
-@CONST()
-export class CompiledHostTemplate {
- constructor(public template: CompiledComponentTemplate) {}
-}
-
-/**
- * A compiled template.
- */
-@CONST()
-export class CompiledComponentTemplate {
- constructor(public id: string, public changeDetectorFactory: Function,
- public commands: TemplateCmd[], public styles: string[]) {}
-}
-
-const EMPTY_ARR = CONST_EXPR([]);
-
-export interface TemplateCmd extends RenderTemplateCmd {
- visit(visitor: RenderCommandVisitor, context: any): any;
-}
-
-@CONST()
-export class TextCmd implements TemplateCmd, RenderTextCmd {
- constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitText(this, context);
- }
-}
-
-@CONST()
-export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
- isBound: boolean = false;
- constructor(public index: number, public ngContentIndex: number) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitNgContent(this, context);
- }
-}
-
-export abstract class IBeginElementCmd extends RenderBeginElementCmd implements TemplateCmd {
- get variableNameAndValues(): Array { return unimplemented(); }
- get eventTargetAndNames(): string[] { return unimplemented(); }
- get directives(): Type[] { return unimplemented(); }
- abstract visit(visitor: RenderCommandVisitor, context: any): any;
-}
-
-@CONST()
-export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
- constructor(public name: string, public attrNameAndValues: string[],
- public eventTargetAndNames: string[],
- public variableNameAndValues: Array, public directives: Type[],
- public isBound: boolean, public ngContentIndex: number) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitBeginElement(this, context);
- }
-}
-
-
-@CONST()
-export class EndElementCmd implements TemplateCmd {
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitEndElement(context);
- }
-}
-
-@CONST()
-export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
- isBound: boolean = true;
- constructor(public name: string, public attrNameAndValues: string[],
- public eventTargetAndNames: string[],
- public variableNameAndValues: Array, public directives: Type[],
- public encapsulation: ViewEncapsulation, public ngContentIndex: number,
- // Note: the template needs to be stored as a function
- // so that we can resolve cycles
- public templateGetter: Function /*() => CompiledComponentTemplate*/) {}
-
- get templateId(): string { return this.templateGetter().id; }
-
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitBeginComponent(this, context);
- }
-}
-
-@CONST()
-export class EndComponentCmd implements TemplateCmd {
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitEndComponent(context);
- }
-}
-
-@CONST()
-export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
- RenderEmbeddedTemplateCmd {
- isBound: boolean = true;
- name: string = null;
- eventTargetAndNames: string[] = EMPTY_ARR;
- constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
- public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
- public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitEmbeddedTemplate(this, context);
- }
-}
-
-
-export interface CommandVisitor extends RenderCommandVisitor {
- visitText(cmd: TextCmd, context: any): any;
- visitNgContent(cmd: NgContentCmd, context: any): any;
- visitBeginElement(cmd: BeginElementCmd, context: any): any;
- visitEndElement(context: any): any;
- visitBeginComponent(cmd: BeginComponentCmd, context: any): any;
- visitEndComponent(context: any): any;
- visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any;
-}
-
-export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
- context: any = null) {
- for (var i = 0; i < cmds.length; i++) {
- cmds[i].visit(visitor, context);
- }
-}
diff --git a/modules/angular2/src/core/linker/template_ref.ts b/modules/angular2/src/core/linker/template_ref.ts
index 4330f2ef64..c26df74a94 100644
--- a/modules/angular2/src/core/linker/template_ref.ts
+++ b/modules/angular2/src/core/linker/template_ref.ts
@@ -1,6 +1,4 @@
-import {internalView, ProtoViewRef} from './view_ref';
import {ElementRef, ElementRef_} from './element_ref';
-import * as viewModule from './view';
/**
* Represents an Embedded Template that can be used to instantiate Embedded Views.
@@ -28,33 +26,10 @@ export abstract class TemplateRef {
*/
// TODO(i): rename to anchor or location
elementRef: ElementRef;
-
- /**
- * Allows you to check if this Embedded Template defines Local Variable with name matching `name`.
- */
- abstract hasLocal(name: string): boolean;
}
export class TemplateRef_ extends TemplateRef {
- constructor(elementRef: ElementRef) {
- super();
- this.elementRef = elementRef;
- }
+ constructor(private _elementRef: ElementRef_) { super(); }
- private _getProtoView(): viewModule.AppProtoView {
- let elementRef = this.elementRef;
- var parentView = internalView(elementRef.parentView);
- return parentView.proto.elementBinders[elementRef.boundElementIndex - parentView.elementOffset]
- .nestedProtoView;
- }
-
- /**
- * Reference to the ProtoView used for creating Embedded Views that are based on the compiled
- * Embedded Template.
- */
- get protoViewRef(): ProtoViewRef { return this._getProtoView().ref; }
-
- hasLocal(name: string): boolean {
- return this._getProtoView().templateVariableBindings.has(name);
- }
+ get elementRef(): ElementRef_ { return this._elementRef; }
}
diff --git a/modules/angular2/src/core/linker/view.ts b/modules/angular2/src/core/linker/view.ts
index 5eec1fcbe5..f91fb09b5c 100644
--- a/modules/angular2/src/core/linker/view.ts
+++ b/modules/angular2/src/core/linker/view.ts
@@ -10,86 +10,53 @@ import {
DirectiveIndex,
BindingTarget,
Locals,
- ProtoChangeDetector
+ ProtoChangeDetector,
+ ChangeDetectorRef
} from 'angular2/src/core/change_detection/change_detection';
+import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
import {DebugContext} from 'angular2/src/core/change_detection/interfaces';
+import {AppProtoElement, AppElement, DirectiveProvider} from './element';
import {
- ProtoElementInjector,
- ElementInjector,
- PreBuiltObjects,
- DirectiveProvider
-} from './element_injector';
-import {ElementBinder} from './element_binder';
-import {isPresent} from 'angular2/src/facade/lang';
+ isPresent,
+ isBlank,
+ Type,
+ isArray,
+ isNumber,
+ CONST,
+ CONST_EXPR
+} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
-import * as renderApi from 'angular2/src/core/render/api';
-import {RenderEventDispatcher} from 'angular2/src/core/render/api';
-import {ViewRef, ProtoViewRef, internalView} from './view_ref';
-import {ElementRef} from './element_ref';
+import {Renderer, RootRenderer} from 'angular2/src/core/render/api';
+import {ViewRef_, HostViewFactoryRef} from './view_ref';
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
import {camelCaseToDashCase} from 'angular2/src/core/render/util';
-import {TemplateCmd} from './template_commands';
-import {ViewRef_, ProtoViewRef_} from "./view_ref";
export {DebugContext} from 'angular2/src/core/change_detection/interfaces';
+import {Pipes} from 'angular2/src/core/pipes/pipes';
+import {AppViewManager_, AppViewManager} from './view_manager';
+import {ResolvedMetadataCache} from './resolved_metadata_cache';
+import {ViewType} from './view_type';
const REFLECT_PREFIX: string = 'ng-reflect-';
-export enum ViewType {
- // A view that contains the host element with bound component directive.
- // Contains a COMPONENT view
- HOST,
- // The view of the component
- // Can contain 0 to n EMBEDDED views
- COMPONENT,
- // A view that is embedded into another View via a element
- // inside of a COMPONENT view
- EMBEDDED
-}
-
-export class AppViewContainer {
- // The order in this list matches the DOM order.
- views: AppView[] = [];
-}
+const EMPTY_CONTEXT = CONST_EXPR(new Object());
/**
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
*
*/
-export class AppView implements ChangeDispatcher, RenderEventDispatcher {
- // AppViews that have been merged in depth first order.
- // This list is shared between all merged views. Use this.elementOffset to get the local
- // entries.
- views: AppView[] = null;
- // root elementInjectors of this AppView
- // This list is local to this AppView and not shared with other Views.
- rootElementInjectors: ElementInjector[];
- // ElementInjectors of all AppViews in views grouped by view.
- // This list is shared between all merged views. Use this.elementOffset to get the local
- // entries.
- elementInjectors: ElementInjector[] = null;
- // ViewContainers of all AppViews in views grouped by view.
- // This list is shared between all merged views. Use this.elementOffset to get the local
- // entries.
- viewContainers: AppViewContainer[] = null;
- // PreBuiltObjects of all AppViews in views grouped by view.
- // This list is shared between all merged views. Use this.elementOffset to get the local
- // entries.
- preBuiltObjects: PreBuiltObjects[] = null;
- // ElementRef of all AppViews in views grouped by view.
- // This list is shared between all merged views. Use this.elementOffset to get the local
- // entries.
- elementRefs: ElementRef[];
-
- ref: ViewRef;
- changeDetector: ChangeDetector = null;
+export class AppView implements ChangeDispatcher {
+ ref: ViewRef_;
+ rootNodesOrAppElements: any[];
+ allNodes: any[];
+ disposables: Function[];
+ appElements: AppElement[];
/**
* The context against which data-binding expressions in this view are evaluated against.
* This is always a component instance.
*/
-
context: any = null;
/**
@@ -99,70 +66,133 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
*/
locals: Locals;
- constructor(public renderer: renderApi.Renderer, public proto: AppProtoView,
- public viewOffset: number, public elementOffset: number, public textOffset: number,
- protoLocals: Map, public render: renderApi.RenderViewRef,
- public renderFragment: renderApi.RenderFragmentRef,
- public containerElementInjector: ElementInjector) {
- this.ref = new ViewRef_(this);
+ pipes: Pipes;
- this.locals = new Locals(null, MapWrapper.clone(protoLocals)); // TODO optimize this
+ parentInjector: Injector;
+
+ /**
+ * Whether root injectors of this view
+ * have a hostBoundary.
+ */
+ hostInjectorBoundary: boolean;
+
+ destroyed: boolean = false;
+
+ constructor(public proto: AppProtoView, public renderer: Renderer,
+ public viewManager: AppViewManager_, public projectableNodes: Array,
+ public containerAppElement: AppElement,
+ imperativelyCreatedProviders: ResolvedProvider[], rootInjector: Injector,
+ public changeDetector: ChangeDetector) {
+ this.ref = new ViewRef_(this);
+ var injectorWithHostBoundary = AppElement.getViewParentInjector(
+ this.proto.type, containerAppElement, imperativelyCreatedProviders, rootInjector);
+ this.parentInjector = injectorWithHostBoundary.injector;
+ this.hostInjectorBoundary = injectorWithHostBoundary.hostInjectorBoundary;
+ var pipes;
+ var context;
+ switch (proto.type) {
+ case ViewType.COMPONENT:
+ pipes = new Pipes(proto.protoPipes, containerAppElement.getInjector());
+ context = containerAppElement.getComponent();
+ break;
+ case ViewType.EMBEDDED:
+ pipes = containerAppElement.parentView.pipes;
+ context = containerAppElement.parentView.context;
+ break;
+ case ViewType.HOST:
+ pipes = null;
+ context = EMPTY_CONTEXT;
+ break;
+ }
+ this.pipes = pipes;
+ this.context = context;
}
- init(changeDetector: ChangeDetector, elementInjectors: ElementInjector[],
- rootElementInjectors: ElementInjector[], preBuiltObjects: PreBuiltObjects[],
- views: AppView[], elementRefs: ElementRef[], viewContainers: AppViewContainer[]) {
- this.changeDetector = changeDetector;
- this.elementInjectors = elementInjectors;
- this.rootElementInjectors = rootElementInjectors;
- this.preBuiltObjects = preBuiltObjects;
- this.views = views;
- this.elementRefs = elementRefs;
- this.viewContainers = viewContainers;
+ init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[],
+ appElements: AppElement[]) {
+ this.rootNodesOrAppElements = rootNodesOrAppElements;
+ this.allNodes = allNodes;
+ this.disposables = disposables;
+ this.appElements = appElements;
+ var localsMap = new Map();
+ StringMapWrapper.forEach(this.proto.templateVariableBindings,
+ (templateName, _) => { localsMap.set(templateName, null); });
+ for (var i = 0; i < appElements.length; i++) {
+ var appEl = appElements[i];
+ StringMapWrapper.forEach(appEl.proto.directiveVariableBindings, (directiveIndex, name) => {
+ if (isBlank(directiveIndex)) {
+ localsMap.set(name, appEl.nativeElement);
+ } else {
+ localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
+ }
+ });
+ }
+ var parentLocals = null;
+ if (this.proto.type !== ViewType.COMPONENT) {
+ parentLocals =
+ isPresent(this.containerAppElement) ? this.containerAppElement.parentView.locals : null;
+ }
+ if (this.proto.type === ViewType.COMPONENT) {
+ // Note: the render nodes have been attached to their host element
+ // in the ViewFactory already.
+ this.containerAppElement.attachComponentView(this);
+ this.containerAppElement.parentView.changeDetector.addViewChild(this.changeDetector);
+ }
+ this.locals = new Locals(parentLocals, localsMap);
+ this.changeDetector.hydrate(this.context, this.locals, this, this.pipes);
+ this.viewManager.onViewCreated(this);
+ }
+
+ destroy() {
+ if (this.destroyed) {
+ throw new BaseException('This view has already been destroyed!');
+ }
+ this.changeDetector.destroyRecursive();
+ }
+
+ notifyOnDestroy() {
+ this.destroyed = true;
+ var hostElement =
+ this.proto.type === ViewType.COMPONENT ? this.containerAppElement.nativeElement : null;
+ this.renderer.destroyView(hostElement, this.allNodes);
+ for (var i = 0; i < this.disposables.length; i++) {
+ this.disposables[i]();
+ }
+ this.viewManager.onViewDestroyed(this);
+ }
+
+ get changeDetectorRef(): ChangeDetectorRef { return this.changeDetector.ref; }
+
+ get flatRootNodes(): any[] { return flattenNestedViewRenderNodes(this.rootNodesOrAppElements); }
+
+ hasLocal(contextName: string): boolean {
+ return StringMapWrapper.contains(this.proto.templateVariableBindings, contextName);
}
setLocal(contextName: string, value: any): void {
- if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
- if (!this.proto.templateVariableBindings.has(contextName)) {
+ if (!this.hasLocal(contextName)) {
return;
}
- var templateName = this.proto.templateVariableBindings.get(contextName);
+ var templateName = this.proto.templateVariableBindings[contextName];
this.locals.set(templateName, value);
}
- hydrated(): boolean { return isPresent(this.context); }
-
- /**
- * Triggers the event handlers for the element and the directives.
- *
- * This method is intended to be called from directive EventEmitters.
- *
- * @param {string} eventName
- * @param {*} eventObj
- * @param {number} boundElementIndex
- */
- triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): void {
- var locals = new Map();
- locals.set('$event', eventObj);
- this.dispatchEvent(boundElementIndex, eventName, locals);
- }
-
// dispatch to element injector or text nodes based on context
notifyOnBinding(b: BindingTarget, currentValue: any): void {
if (b.isTextNode()) {
- this.renderer.setText(this.render, b.elementIndex + this.textOffset, currentValue);
+ this.renderer.setText(this.allNodes[b.elementIndex], currentValue);
} else {
- var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
+ var nativeElement = this.appElements[b.elementIndex].nativeElement;
if (b.isElementProperty()) {
- this.renderer.setElementProperty(elementRef, b.name, currentValue);
+ this.renderer.setElementProperty(nativeElement, b.name, currentValue);
} else if (b.isElementAttribute()) {
- this.renderer.setElementAttribute(elementRef, b.name,
+ this.renderer.setElementAttribute(nativeElement, b.name,
isPresent(currentValue) ? `${currentValue}` : null);
} else if (b.isElementClass()) {
- this.renderer.setElementClass(elementRef, b.name, currentValue);
+ this.renderer.setElementClass(nativeElement, b.name, currentValue);
} else if (b.isElementStyle()) {
var unit = isPresent(b.unit) ? b.unit : '';
- this.renderer.setElementStyle(elementRef, b.name,
+ this.renderer.setElementStyle(nativeElement, b.name,
isPresent(currentValue) ? `${currentValue}${unit}` : null);
} else {
throw new BaseException('Unsupported directive record');
@@ -172,57 +202,39 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
logBindingUpdate(b: BindingTarget, value: any): void {
if (b.isDirective() || b.isElementProperty()) {
- var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
+ var nativeElement = this.appElements[b.elementIndex].nativeElement;
this.renderer.setBindingDebugInfo(
- elementRef, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
+ nativeElement, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
}
}
notifyAfterContentChecked(): void {
- var eiCount = this.proto.elementBinders.length;
- var ei = this.elementInjectors;
- for (var i = eiCount - 1; i >= 0; i--) {
- if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterContentChecked();
+ var count = this.appElements.length;
+ for (var i = count - 1; i >= 0; i--) {
+ this.appElements[i].ngAfterContentChecked();
}
}
notifyAfterViewChecked(): void {
- var eiCount = this.proto.elementBinders.length;
- var ei = this.elementInjectors;
- for (var i = eiCount - 1; i >= 0; i--) {
- if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterViewChecked();
+ var count = this.appElements.length;
+ for (var i = count - 1; i >= 0; i--) {
+ this.appElements[i].ngAfterViewChecked();
}
}
- getDirectiveFor(directive: DirectiveIndex): any {
- var elementInjector = this.elementInjectors[this.elementOffset + directive.elementIndex];
- return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
- }
-
- getNestedView(boundElementIndex: number): AppView {
- var eli = this.elementInjectors[boundElementIndex];
- return isPresent(eli) ? eli.getNestedView() : null;
- }
-
- getContainerElement(): ElementRef {
- return isPresent(this.containerElementInjector) ?
- this.containerElementInjector.getElementRef() :
- null;
- }
-
- getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext {
+ getDebugContext(appElement: AppElement, elementIndex: number,
+ directiveIndex: number): DebugContext {
try {
- var offsettedIndex = this.elementOffset + elementIndex;
- var hasRefForIndex = offsettedIndex < this.elementRefs.length;
+ if (isBlank(appElement) && elementIndex < this.appElements.length) {
+ appElement = this.appElements[elementIndex];
+ }
+ var container = this.containerAppElement;
- var elementRef = hasRefForIndex ? this.elementRefs[this.elementOffset + elementIndex] : null;
- var container = this.getContainerElement();
- var ei = hasRefForIndex ? this.elementInjectors[this.elementOffset + elementIndex] : null;
-
- var element = isPresent(elementRef) ? elementRef.nativeElement : null;
+ var element = isPresent(appElement) ? appElement.nativeElement : null;
var componentElement = isPresent(container) ? container.nativeElement : null;
- var directive = isPresent(directiveIndex) ? this.getDirectiveFor(directiveIndex) : null;
- var injector = isPresent(ei) ? ei.getInjector() : null;
+ var directive =
+ isPresent(directiveIndex) ? appElement.getDirectiveAtIndex(directiveIndex) : null;
+ var injector = isPresent(appElement) ? appElement.getInjector() : null;
return new DebugContext(element, componentElement, directive, this.context,
_localsToStringMap(this.locals), injector);
@@ -234,43 +246,28 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
}
}
+ getDirectiveFor(directive: DirectiveIndex): any {
+ return this.appElements[directive.elementIndex].getDirectiveAtIndex(directive.directiveIndex);
+ }
+
getDetectorFor(directive: DirectiveIndex): any {
- var childView = this.getNestedView(this.elementOffset + directive.elementIndex);
- return isPresent(childView) ? childView.changeDetector : null;
+ var componentView = this.appElements[directive.elementIndex].componentView;
+ return isPresent(componentView) ? componentView.changeDetector : null;
}
- invokeElementMethod(elementIndex: number, methodName: string, args: any[]) {
- this.renderer.invokeElementMethod(this.elementRefs[elementIndex], methodName, args);
+ /**
+ * Triggers the event handlers for the element and the directives.
+ *
+ * This method is intended to be called from directive EventEmitters.
+ *
+ * @param {string} eventName
+ * @param {*} eventObj
+ * @param {number} boundElementIndex
+ * @return false if preventDefault must be applied to the DOM event
+ */
+ triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): boolean {
+ return this.changeDetector.handleEvent(eventName, boundElementIndex, eventObj);
}
-
- // implementation of RenderEventDispatcher#dispatchRenderEvent
- dispatchRenderEvent(boundElementIndex: number, eventName: string,
- locals: Map): boolean {
- var elementRef = this.elementRefs[boundElementIndex];
- var view = internalView(elementRef.parentView);
- return view.dispatchEvent(elementRef.boundElementIndex, eventName, locals);
- }
-
-
- // returns false if preventDefault must be applied to the DOM event
- dispatchEvent(boundElementIndex: number, eventName: string, locals: Map): boolean {
- try {
- if (this.hydrated()) {
- return !this.changeDetector.handleEvent(eventName, boundElementIndex - this.elementOffset,
- new Locals(this.locals, locals));
- } else {
- return true;
- }
- } catch (e) {
- var c = this.getDebugContext(boundElementIndex - this.elementOffset, null);
- var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
- c.injector) :
- null;
- throw new EventEvaluationError(eventName, e, e.stack, context);
- }
- }
-
- get ownBindersCount(): number { return this.proto.elementBinders.length; }
}
function _localsToStringMap(locals: Locals): {[key: string]: any} {
@@ -283,69 +280,61 @@ function _localsToStringMap(locals: Locals): {[key: string]: any} {
return res;
}
-/**
- * Error context included when an event handler throws an exception.
- */
-class _Context {
- constructor(public element: any, public componentElement: any, public context: any,
- public locals: any, public injector: any) {}
-}
-
-/**
- * Wraps an exception thrown by an event handler.
- */
-class EventEvaluationError extends WrappedException {
- constructor(eventName: string, originalException: any, originalStack: any, context: any) {
- super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
- }
-}
-
-export class AppProtoViewMergeInfo {
- constructor(public embeddedViewCount: number, public elementCount: number,
- public viewCount: number) {}
-}
-
/**
*
*/
export class AppProtoView {
- ref: ProtoViewRef;
- protoLocals: Map;
-
- elementBinders: ElementBinder[] = null;
- mergeInfo: AppProtoViewMergeInfo = null;
- variableLocations: Map = null;
- textBindingCount = null;
- render: renderApi.RenderProtoViewRef = null;
-
- constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
- public isMergable: boolean, public changeDetectorFactory: Function,
- public templateVariableBindings: Map, public pipes: ProtoPipes) {
- this.ref = new ProtoViewRef_(this);
+ static create(metadataCache: ResolvedMetadataCache, type: ViewType, pipes: Type[],
+ templateVariableBindings: {[key: string]: string}): AppProtoView {
+ var protoPipes = null;
+ if (isPresent(pipes) && pipes.length > 0) {
+ var boundPipes = ListWrapper.createFixedSize(pipes.length);
+ for (var i = 0; i < pipes.length; i++) {
+ boundPipes[i] = metadataCache.getResolvedPipeMetadata(pipes[i]);
+ }
+ protoPipes = ProtoPipes.fromProviders(boundPipes);
+ }
+ return new AppProtoView(type, protoPipes, templateVariableBindings);
}
- init(render: renderApi.RenderProtoViewRef, elementBinders: ElementBinder[],
- textBindingCount: number, mergeInfo: AppProtoViewMergeInfo,
- variableLocations: Map) {
- this.render = render;
- this.elementBinders = elementBinders;
- this.textBindingCount = textBindingCount;
- this.mergeInfo = mergeInfo;
- this.variableLocations = variableLocations;
- this.protoLocals = new Map();
- if (isPresent(this.templateVariableBindings)) {
- this.templateVariableBindings.forEach(
- (templateName, _) => { this.protoLocals.set(templateName, null); });
- }
- if (isPresent(variableLocations)) {
- // The view's locals needs to have a full set of variable names at construction time
- // in order to prevent new variables from being set later in the lifecycle. Since we don't
- // want
- // to actually create variable bindings for the $implicit bindings, add to the
- // protoLocals manually.
- variableLocations.forEach((_, templateName) => { this.protoLocals.set(templateName, null); });
- }
- }
-
- isInitialized(): boolean { return isPresent(this.elementBinders); }
+ constructor(public type: ViewType, public protoPipes: ProtoPipes,
+ public templateVariableBindings: {[key: string]: string}) {}
+}
+
+
+@CONST()
+export class HostViewFactory {
+ constructor(public selector: string, public viewFactory: Function) {}
+}
+
+export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
+ return _flattenNestedViewRenderNodes(nodes, []);
+}
+
+function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
+ for (var i = 0; i < nodes.length; i++) {
+ var node = nodes[i];
+ if (node instanceof AppElement) {
+ var appEl = node;
+ renderNodes.push(appEl.nativeElement);
+ if (isPresent(appEl.nestedViews)) {
+ for (var k = 0; k < appEl.nestedViews.length; k++) {
+ _flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
+ }
+ }
+ } else {
+ renderNodes.push(node);
+ }
+ }
+ return renderNodes;
+}
+
+export function checkSlotCount(componentName: string, expectedSlotCount: number,
+ projectableNodes: any[][]): void {
+ var givenSlotCount = isPresent(projectableNodes) ? projectableNodes.length : 0;
+ if (givenSlotCount < expectedSlotCount) {
+ throw new BaseException(
+ `The component ${componentName} has ${expectedSlotCount} elements,` +
+ ` but only ${givenSlotCount} slots were provided.`);
+ }
}
diff --git a/modules/angular2/src/core/linker/view_container_ref.ts b/modules/angular2/src/core/linker/view_container_ref.ts
index 45f2fd008a..b8561ed108 100644
--- a/modules/angular2/src/core/linker/view_container_ref.ts
+++ b/modules/angular2/src/core/linker/view_container_ref.ts
@@ -3,12 +3,18 @@ import {unimplemented} from 'angular2/src/facade/exceptions';
import {ResolvedProvider} from 'angular2/src/core/di';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
-import * as avmModule from './view_manager';
-import * as viewModule from './view';
+import {AppElement} from './element';
import {ElementRef, ElementRef_} from './element_ref';
-import {TemplateRef} from './template_ref';
-import {ViewRef, HostViewRef, ProtoViewRef, internalView} from './view_ref';
+import {TemplateRef, TemplateRef_} from './template_ref';
+import {
+ EmbeddedViewRef,
+ HostViewRef,
+ HostViewFactoryRef,
+ HostViewFactoryRef_,
+ ViewRef,
+ ViewRef_
+} from './view_ref';
/**
* Represents a container where one or more Views can be attached.
@@ -35,7 +41,7 @@ export abstract class ViewContainerRef {
* Anchor element that specifies the location of this container in the containing View.
*
*/
- public element: ElementRef;
+ get element(): ElementRef { return unimplemented(); }
/**
* Destroys all Views in this container.
@@ -64,7 +70,7 @@ export abstract class ViewContainerRef {
*
* Returns the {@link ViewRef} for the newly created View.
*/
- abstract createEmbeddedView(templateRef: TemplateRef, index?: number): ViewRef;
+ abstract createEmbeddedView(templateRef: TemplateRef, index?: number): EmbeddedViewRef;
/**
* Instantiates a single {@link Component} and inserts its Host View into this container at the
@@ -80,8 +86,9 @@ export abstract class ViewContainerRef {
*
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
*/
- abstract createHostView(protoViewRef?: ProtoViewRef, index?: number,
- dynamicallyCreatedProviders?: ResolvedProvider[]): HostViewRef;
+ abstract createHostView(hostViewFactoryRef: HostViewFactoryRef, index?: number,
+ dynamicallyCreatedProviders?: ResolvedProvider[],
+ projectableNodes?: any[][]): HostViewRef;
/**
* Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
@@ -90,7 +97,7 @@ export abstract class ViewContainerRef {
*
* Returns the inserted {@link ViewRef}.
*/
- abstract insert(viewRef: ViewRef, index?: number): ViewRef;
+ abstract insert(viewRef: EmbeddedViewRef, index?: number): EmbeddedViewRef;
/**
* Returns the index of the View, specified via {@link ViewRef}, within the current container or
@@ -110,58 +117,60 @@ export abstract class ViewContainerRef {
*
* If the `index` param is omitted, the last {@link ViewRef} is detached.
*/
- abstract detach(index?: number): ViewRef;
+ abstract detach(index?: number): EmbeddedViewRef;
}
export class ViewContainerRef_ extends ViewContainerRef {
- constructor(public viewManager: avmModule.AppViewManager, element: ElementRef) {
- super();
- this.element = element;
+ constructor(private _element: AppElement) { super(); }
+
+ get(index: number): EmbeddedViewRef { return this._element.nestedViews[index].ref; }
+ get length(): number {
+ var views = this._element.nestedViews;
+ return isPresent(views) ? views.length : 0;
}
- private _getViews(): Array {
- let element = this.element;
- var vc = internalView(element.parentView).viewContainers[element.boundElementIndex];
- return isPresent(vc) ? vc.views : [];
- }
-
- get(index: number): ViewRef { return this._getViews()[index].ref; }
- get length(): number { return this._getViews().length; }
+ get element(): ElementRef_ { return this._element.ref; }
// TODO(rado): profile and decide whether bounds checks should be added
// to the methods below.
- createEmbeddedView(templateRef: TemplateRef, index: number = -1): ViewRef {
+ createEmbeddedView(templateRef: TemplateRef_, index: number = -1): EmbeddedViewRef {
if (index == -1) index = this.length;
- return this.viewManager.createEmbeddedViewInContainer(this.element, index, templateRef);
+ var vm = this._element.parentView.viewManager;
+ return vm.createEmbeddedViewInContainer(this._element.ref, index, templateRef);
}
- createHostView(protoViewRef: ProtoViewRef = null, index: number = -1,
- dynamicallyCreatedProviders: ResolvedProvider[] = null): HostViewRef {
+ createHostView(hostViewFactoryRef: HostViewFactoryRef_, index: number = -1,
+ dynamicallyCreatedProviders: ResolvedProvider[] = null,
+ projectableNodes: any[][] = null): HostViewRef {
if (index == -1) index = this.length;
- return this.viewManager.createHostViewInContainer(this.element, index, protoViewRef,
- dynamicallyCreatedProviders);
+ var vm = this._element.parentView.viewManager;
+ return vm.createHostViewInContainer(this._element.ref, index, hostViewFactoryRef,
+ dynamicallyCreatedProviders, projectableNodes);
}
// TODO(i): refactor insert+remove into move
- insert(viewRef: ViewRef, index: number = -1): ViewRef {
+ insert(viewRef: ViewRef_, index: number = -1): EmbeddedViewRef {
if (index == -1) index = this.length;
- return this.viewManager.attachViewInContainer(this.element, index, viewRef);
+ var vm = this._element.parentView.viewManager;
+ return vm.attachViewInContainer(this._element.ref, index, viewRef);
}
- indexOf(viewRef: ViewRef): number {
- return ListWrapper.indexOf(this._getViews(), internalView(viewRef));
+ indexOf(viewRef: ViewRef_): number {
+ return ListWrapper.indexOf(this._element.nestedViews, viewRef.internalView);
}
// TODO(i): rename to destroy
remove(index: number = -1): void {
if (index == -1) index = this.length - 1;
- this.viewManager.destroyViewInContainer(this.element, index);
+ var vm = this._element.parentView.viewManager;
+ return vm.destroyViewInContainer(this._element.ref, index);
// view is intentionally not returned to the client.
}
// TODO(i): refactor insert+remove into move
- detach(index: number = -1): ViewRef {
+ detach(index: number = -1): EmbeddedViewRef {
if (index == -1) index = this.length - 1;
- return this.viewManager.detachViewInContainer(this.element, index);
+ var vm = this._element.parentView.viewManager;
+ return vm.detachViewInContainer(this._element.ref, index);
}
}
diff --git a/modules/angular2/src/core/linker/view_manager.ts b/modules/angular2/src/core/linker/view_manager.ts
index 300608f41b..57a0a82da8 100644
--- a/modules/angular2/src/core/linker/view_manager.ts
+++ b/modules/angular2/src/core/linker/view_manager.ts
@@ -6,24 +6,27 @@ import {
ResolvedProvider,
forwardRef
} from 'angular2/src/core/di';
-import {isPresent, isBlank} from 'angular2/src/facade/lang';
+import {isPresent, isBlank, isArray} from 'angular2/src/facade/lang';
+import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {BaseException} from 'angular2/src/facade/exceptions';
-import * as viewModule from './view';
+import {AppView, HostViewFactory, flattenNestedViewRenderNodes} from './view';
+import {AppElement} from './element';
import {ElementRef, ElementRef_} from './element_ref';
-import {ProtoViewRef, ViewRef, HostViewRef, internalView, internalProtoView} from './view_ref';
+import {
+ HostViewFactoryRef,
+ HostViewFactoryRef_,
+ EmbeddedViewRef,
+ HostViewRef,
+ ViewRef_
+} from './view_ref';
import {ViewContainerRef} from './view_container_ref';
import {TemplateRef, TemplateRef_} from './template_ref';
-import {
- Renderer,
- RenderViewRef,
- RenderFragmentRef,
- RenderViewWithFragments
-} from 'angular2/src/core/render/api';
-import {AppViewManagerUtils} from './view_manager_utils';
-import {AppViewPool} from './view_pool';
import {AppViewListener} from './view_listener';
+import {RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
-import {ProtoViewFactory} from './proto_view_factory';
+import {APP_ID} from 'angular2/src/core/application_tokens';
+import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
+import {ViewType} from './view_type';
/**
* Service exposing low level API for creating, moving and destroying Views.
@@ -40,13 +43,7 @@ export abstract class AppViewManager {
/**
* Returns the {@link ElementRef} that makes up the specified Host View.
*/
- getHostElement(hostViewRef: HostViewRef): ElementRef {
- var hostView = internalView(hostViewRef);
- if (hostView.proto.type !== viewModule.ViewType.HOST) {
- throw new BaseException('This operation is only allowed on host views');
- }
- return hostView.elementRefs[hostView.elementOffset];
- }
+ abstract getHostElement(hostViewRef: HostViewRef): ElementRef;
/**
* Searches the Component View of the Component specified via `hostLocation` and returns the
@@ -70,7 +67,8 @@ export abstract class AppViewManager {
* This as a low-level way to bootstrap an application and upgrade an existing Element to a
* Host Element. Most applications should use {@link DynamicComponentLoader#loadAsRoot} instead.
*
- * The Component and its View are created based on the `hostProtoViewRef` which can be obtained
+ * The Component and its View are created based on the `hostProtoComponentRef` which can be
+ * obtained
* by compiling the component with {@link Compiler#compileInHost}.
*
* Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host
@@ -101,7 +99,7 @@ export abstract class AppViewManager {
* viewRef: ng.ViewRef;
*
* constructor(public appViewManager: ng.AppViewManager, compiler: ng.Compiler) {
- * compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoViewRef) => {
+ * compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoComponentRef) => {
* this.viewRef = appViewManager.createRootHostView(protoView, 'some-component', null);
* })
* }
@@ -115,8 +113,8 @@ export abstract class AppViewManager {
* ng.bootstrap(MyApp);
* ```
*/
- abstract createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
- injector: Injector): HostViewRef;
+ abstract createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string,
+ injector: Injector, projectableNodes?: any[][]): HostViewRef;
/**
* Destroys the Host View created via {@link AppViewManager#createRootHostView}.
@@ -140,7 +138,7 @@ export abstract class AppViewManager {
// TODO(i): this low-level version of ViewContainerRef#createEmbeddedView doesn't add anything new
// we should make it private, otherwise we have two apis to do the same thing.
abstract createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
- templateRef: TemplateRef): ViewRef;
+ templateRef: TemplateRef): EmbeddedViewRef;
/**
* Instantiates a single {@link Component} and inserts its Host View into the View Container
@@ -150,16 +148,16 @@ export abstract class AppViewManager {
* The component is instantiated using its {@link ProtoViewRef `protoViewRef`} which can be
* obtained via {@link Compiler#compileInHost}.
*
- * You can optionally specify `imperativelyCreatedInjector`, which configure the {@link Injector}
+ * You can optionally specify `dynamicallyCreatedProviders`, which configure the {@link Injector}
* that will be created for the Host View.
*
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
*
* Use {@link AppViewManager#destroyViewInContainer} to destroy the created Host View.
*/
- abstract createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
- protoViewRef: ProtoViewRef,
- imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef;
+ abstract createHostViewInContainer(
+ viewContainerLocation: ElementRef, index: number, hostViewFactoryRef: HostViewFactoryRef,
+ dynamicallyCreatedProviders: ResolvedProvider[], projectableNodes: any[][]): HostViewRef;
/**
* Destroys an Embedded or Host View attached to a View Container at the specified `index`.
@@ -174,85 +172,75 @@ export abstract class AppViewManager {
*/
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
abstract attachViewInContainer(viewContainerLocation: ElementRef, index: number,
- viewRef: ViewRef): ViewRef;
+ viewRef: EmbeddedViewRef): EmbeddedViewRef;
/**
* See {@link AppViewManager#attachViewInContainer}.
*/
- abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef;
+ abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): EmbeddedViewRef;
}
@Injectable()
export class AppViewManager_ extends AppViewManager {
- private _protoViewFactory: ProtoViewFactory;
+ private _nextCompTypeId: number = 0;
- constructor(private _viewPool: AppViewPool, private _viewListener: AppViewListener,
- private _utils: AppViewManagerUtils, private _renderer: Renderer,
- @Inject(forwardRef(() => ProtoViewFactory)) _protoViewFactory) {
+ constructor(private _renderer: RootRenderer, private _viewListener: AppViewListener,
+ @Inject(APP_ID) private _appId: string) {
super();
- this._protoViewFactory = _protoViewFactory;
}
- getViewContainer(location: ElementRef): ViewContainerRef {
- var hostView = internalView((location).parentView);
- return hostView.elementInjectors[(location).boundElementIndex]
- .getViewContainerRef();
+ getViewContainer(location: ElementRef_): ViewContainerRef {
+ return location.internalElement.getViewContainerRef();
}
- getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
- var hostView = internalView((hostLocation).parentView);
- var boundElementIndex = (hostLocation).boundElementIndex;
- var componentView = hostView.getNestedView(boundElementIndex);
+ getHostElement(hostViewRef: ViewRef_): ElementRef {
+ var hostView = hostViewRef.internalView;
+ if (hostView.proto.type !== ViewType.HOST) {
+ throw new BaseException('This operation is only allowed on host views');
+ }
+ return hostView.appElements[0].ref;
+ }
+
+ getNamedElementInComponentView(hostLocation: ElementRef_, variableName: string): ElementRef {
+ var appEl = hostLocation.internalElement;
+ var componentView = appEl.componentView;
if (isBlank(componentView)) {
- throw new BaseException(`There is no component directive at element ${boundElementIndex}`);
+ throw new BaseException(`There is no component directive at element ${hostLocation}`);
}
- var binderIdx = componentView.proto.variableLocations.get(variableName);
- if (isBlank(binderIdx)) {
- throw new BaseException(`Could not find variable ${variableName}`);
+ for (var i = 0; i < componentView.appElements.length; i++) {
+ var compAppEl = componentView.appElements[i];
+ if (StringMapWrapper.contains(compAppEl.proto.directiveVariableBindings, variableName)) {
+ return compAppEl.ref;
+ }
}
- return componentView.elementRefs[componentView.elementOffset + binderIdx];
+ throw new BaseException(`Could not find variable ${variableName}`);
}
- getComponent(hostLocation: ElementRef): any {
- var hostView = internalView((hostLocation).parentView);
- var boundElementIndex = (hostLocation).boundElementIndex;
- return this._utils.getComponentInstance(hostView, boundElementIndex);
+ getComponent(hostLocation: ElementRef_): any {
+ return hostLocation.internalElement.getComponent();
}
/** @internal */
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()');
- createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
- injector: Injector): HostViewRef {
+ createRootHostView(hostViewFactoryRef: HostViewFactoryRef_, overrideSelector: string,
+ injector: Injector, projectableNodes: any[][] = null): HostViewRef {
var s = this._createRootHostViewScope();
- var hostProtoView: viewModule.AppProtoView = internalProtoView(hostProtoViewRef);
- this._protoViewFactory.initializeProtoViewIfNeeded(hostProtoView);
- var hostElementSelector = overrideSelector;
- if (isBlank(hostElementSelector)) {
- hostElementSelector = hostProtoView.elementBinders[0].componentDirective.metadata.selector;
- }
- var renderViewWithFragments = this._renderer.createRootHostView(
- hostProtoView.render, hostProtoView.mergeInfo.embeddedViewCount + 1, hostElementSelector);
- var hostView = this._createMainView(hostProtoView, renderViewWithFragments);
-
- this._renderer.hydrateView(hostView.render);
- this._utils.hydrateRootHostView(hostView, injector);
- return wtfLeave(s, hostView.ref);
+ var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
+ var selector = isPresent(overrideSelector) ? overrideSelector : hostViewFactory.selector;
+ var view = hostViewFactory.viewFactory(this._renderer, this, null, projectableNodes, selector,
+ null, injector);
+ return wtfLeave(s, view.ref);
}
/** @internal */
_destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()');
- destroyRootHostView(hostViewRef: HostViewRef) {
- // Note: Don't put the hostView into the view pool
- // as it is depending on the element for which it was created.
+ destroyRootHostView(hostViewRef: ViewRef_) {
var s = this._destroyRootHostViewScope();
- var hostView = internalView(hostViewRef);
- this._renderer.detachFragment(hostView.renderFragment);
- this._renderer.dehydrateView(hostView.render);
- this._viewDehydrateRecurse(hostView);
- this._viewListener.onViewDestroyed(hostView);
- this._renderer.destroyView(hostView.render);
+ var hostView = hostViewRef.internalView;
+ hostView.renderer.detachView(flattenNestedViewRenderNodes(hostView.rootNodesOrAppElements));
+ hostView.destroy();
wtfLeave(s);
}
@@ -260,97 +248,43 @@ export class AppViewManager_ extends AppViewManager {
_createEmbeddedViewInContainerScope: WtfScopeFn =
wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()');
- createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
- templateRef: TemplateRef): ViewRef {
+ createEmbeddedViewInContainer(viewContainerLocation: ElementRef_, index: number,
+ templateRef: TemplateRef_): EmbeddedViewRef {
var s = this._createEmbeddedViewInContainerScope();
- var protoView = internalProtoView((templateRef).protoViewRef);
- if (protoView.type !== viewModule.ViewType.EMBEDDED) {
- throw new BaseException('This method can only be called with embedded ProtoViews!');
- }
- this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
- return wtfLeave(s, this._createViewInContainer(viewContainerLocation, index, protoView,
- templateRef.elementRef, null));
+ var contextEl = templateRef.elementRef.internalElement;
+ var view: AppView =
+ contextEl.embeddedViewFactory(contextEl.parentView.renderer, this, contextEl,
+ contextEl.parentView.projectableNodes, null, null, null);
+ this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
+ return wtfLeave(s, view.ref);
}
/** @internal */
_createHostViewInContainerScope: WtfScopeFn =
wtfCreateScope('AppViewManager#createHostViewInContainer()');
- createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
- protoViewRef: ProtoViewRef,
- imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef {
+ createHostViewInContainer(viewContainerLocation: ElementRef_, index: number,
+ hostViewFactoryRef: HostViewFactoryRef_,
+ dynamicallyCreatedProviders: ResolvedProvider[],
+ projectableNodes: any[][]): HostViewRef {
var s = this._createHostViewInContainerScope();
- var protoView = internalProtoView(protoViewRef);
- if (protoView.type !== viewModule.ViewType.HOST) {
- throw new BaseException('This method can only be called with host ProtoViews!');
- }
- this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
- return wtfLeave(
- s, this._createViewInContainer(viewContainerLocation, index, protoView,
- viewContainerLocation, imperativelyCreatedInjector));
- }
-
- /**
- *
- * See {@link AppViewManager#destroyViewInContainer}.
- * @internal
- */
- _createViewInContainer(viewContainerLocation: ElementRef, index: number,
- protoView: viewModule.AppProtoView, context: ElementRef,
- imperativelyCreatedInjector: ResolvedProvider[]): ViewRef {
- var parentView = internalView((viewContainerLocation).parentView);
- var boundElementIndex = (viewContainerLocation).boundElementIndex;
- var contextView = internalView((context).parentView);
- var contextBoundElementIndex = (context).boundElementIndex;
- var embeddedFragmentView = contextView.getNestedView(contextBoundElementIndex);
- var view;
- if (protoView.type === viewModule.ViewType.EMBEDDED && isPresent(embeddedFragmentView) &&
- !embeddedFragmentView.hydrated()) {
- // Case 1: instantiate the first view of a template that has been merged into a parent
- view = embeddedFragmentView;
- this._attachRenderView(parentView, boundElementIndex, index, view);
- } else {
- // Case 2: instantiate another copy of the template or a host ProtoView.
- // This is a separate case
- // as we only inline one copy of the template into the parent view.
- view = this._createPooledView(protoView);
- this._attachRenderView(parentView, boundElementIndex, index, view);
- this._renderer.hydrateView(view.render);
- }
- this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
- contextBoundElementIndex, index, view);
-
- try {
- this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
- contextBoundElementIndex, index,
- imperativelyCreatedInjector);
- } catch (e) {
- this._utils.detachViewInContainer(parentView, boundElementIndex, index);
- throw e;
- }
- return view.ref;
- }
-
- /** @internal */
- _attachRenderView(parentView: viewModule.AppView, boundElementIndex: number, index: number,
- view: viewModule.AppView) {
- var elementRef = parentView.elementRefs[boundElementIndex];
- if (index === 0) {
- this._renderer.attachFragmentAfterElement(elementRef, view.renderFragment);
- } else {
- var prevView = parentView.viewContainers[boundElementIndex].views[index - 1];
- this._renderer.attachFragmentAfterFragment(prevView.renderFragment, view.renderFragment);
- }
+ // TODO(tbosch): This should be specifiable via an additional argument!
+ var contextEl = viewContainerLocation.internalElement;
+ var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
+ var view = hostViewFactory.viewFactory(
+ contextEl.parentView.renderer, contextEl.parentView.viewManager, contextEl,
+ projectableNodes, null, dynamicallyCreatedProviders, null);
+ this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
+ return wtfLeave(s, view.ref);
}
/** @internal */
_destroyViewInContainerScope = wtfCreateScope('AppViewMananger#destroyViewInContainer()');
- destroyViewInContainer(viewContainerLocation: ElementRef, index: number) {
+ destroyViewInContainer(viewContainerLocation: ElementRef_, index: number) {
var s = this._destroyViewInContainerScope();
- var parentView = internalView((viewContainerLocation).parentView);
- var boundElementIndex = (viewContainerLocation).boundElementIndex;
- this._destroyViewInContainer(parentView, boundElementIndex, index);
+ var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
+ view.destroy();
wtfLeave(s);
}
@@ -358,20 +292,10 @@ export class AppViewManager_ extends AppViewManager {
_attachViewInContainerScope = wtfCreateScope('AppViewMananger#attachViewInContainer()');
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
- attachViewInContainer(viewContainerLocation: ElementRef, index: number,
- viewRef: ViewRef): ViewRef {
+ attachViewInContainer(viewContainerLocation: ElementRef_, index: number,
+ viewRef: ViewRef_): EmbeddedViewRef {
var s = this._attachViewInContainerScope();
- var view = internalView(viewRef);
- var parentView = internalView((viewContainerLocation).parentView);
- var boundElementIndex = (viewContainerLocation).boundElementIndex;
- // TODO(tbosch): the public methods attachViewInContainer/detachViewInContainer
- // are used for moving elements without the same container.
- // We will change this into an atomic `move` operation, which should preserve the
- // previous parent injector (see https://github.com/angular/angular/issues/1377).
- // Right now we are destroying any special
- // context view that might have been used.
- this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, index, view);
- this._attachRenderView(parentView, boundElementIndex, index, view);
+ this._attachViewToContainer(viewRef.internalView, viewContainerLocation.internalElement, index);
return wtfLeave(s, viewRef);
}
@@ -379,88 +303,72 @@ export class AppViewManager_ extends AppViewManager {
_detachViewInContainerScope = wtfCreateScope('AppViewMananger#detachViewInContainer()');
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
- detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef {
+ detachViewInContainer(viewContainerLocation: ElementRef_, index: number): EmbeddedViewRef {
var s = this._detachViewInContainerScope();
- var parentView = internalView((viewContainerLocation).parentView);
- var boundElementIndex = (viewContainerLocation).boundElementIndex;
- var viewContainer = parentView.viewContainers[boundElementIndex];
- var view = viewContainer.views[index];
- this._utils.detachViewInContainer(parentView, boundElementIndex, index);
- this._renderer.detachFragment(view.renderFragment);
+ var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
return wtfLeave(s, view.ref);
}
/** @internal */
- _createMainView(protoView: viewModule.AppProtoView,
- renderViewWithFragments: RenderViewWithFragments): viewModule.AppView {
- var mergedParentView =
- this._utils.createView(protoView, renderViewWithFragments, this, this._renderer);
- this._renderer.setEventDispatcher(mergedParentView.render, mergedParentView);
- this._viewListener.onViewCreated(mergedParentView);
- return mergedParentView;
- }
+ onViewCreated(view: AppView) { this._viewListener.onViewCreated(view); }
/** @internal */
- _createPooledView(protoView: viewModule.AppProtoView): viewModule.AppView {
- var view = this._viewPool.getView(protoView);
- if (isBlank(view)) {
- view = this._createMainView(
- protoView,
- this._renderer.createView(protoView.render, protoView.mergeInfo.embeddedViewCount + 1));
+ onViewDestroyed(view: AppView) { this._viewListener.onViewDestroyed(view); }
+
+ /** @internal */
+ createRenderComponentType(encapsulation: ViewEncapsulation,
+ styles: Array): RenderComponentType {
+ return new RenderComponentType(`${this._appId}-${this._nextCompTypeId++}`, encapsulation,
+ styles);
+ }
+
+ private _attachViewToContainer(view: AppView, vcAppElement: AppElement, viewIndex: number) {
+ if (view.proto.type === ViewType.COMPONENT) {
+ throw new BaseException(`Component views can't be moved!`);
}
+ var nestedViews = vcAppElement.nestedViews;
+ if (nestedViews == null) {
+ nestedViews = [];
+ vcAppElement.nestedViews = nestedViews;
+ }
+ ListWrapper.insert(nestedViews, viewIndex, view);
+ var refNode;
+ if (viewIndex > 0) {
+ var prevView = nestedViews[viewIndex - 1];
+ refNode = prevView.rootNodesOrAppElements.length > 0 ?
+ prevView.rootNodesOrAppElements[prevView.rootNodesOrAppElements.length - 1] :
+ null;
+ } else {
+ refNode = vcAppElement.nativeElement;
+ }
+ if (isPresent(refNode)) {
+ var refRenderNode;
+ if (refNode instanceof AppElement) {
+ refRenderNode = (refNode).nativeElement;
+ } else {
+ refRenderNode = refNode;
+ }
+ view.renderer.attachViewAfter(refRenderNode,
+ flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
+ }
+ // TODO: This is only needed when a view is destroyed,
+ // not when it is detached for reordering with ng-for...
+ vcAppElement.parentView.changeDetector.addContentChild(view.changeDetector);
+ vcAppElement.traverseAndSetQueriesAsDirty();
+ }
+
+ private _detachViewInContainer(vcAppElement: AppElement, viewIndex: number): AppView {
+ var view = ListWrapper.removeAt(vcAppElement.nestedViews, viewIndex);
+ if (view.proto.type === ViewType.COMPONENT) {
+ throw new BaseException(`Component views can't be moved!`);
+ }
+ vcAppElement.traverseAndSetQueriesAsDirty();
+
+ view.renderer.detachView(flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
+
+ // TODO: This is only needed when a view is destroyed,
+ // not when it is detached for reordering with ng-for...
+ view.changeDetector.remove();
return view;
}
-
- /** @internal */
- _destroyPooledView(view: viewModule.AppView) {
- var wasReturned = this._viewPool.returnView(view);
- if (!wasReturned) {
- this._viewListener.onViewDestroyed(view);
- this._renderer.destroyView(view.render);
- }
- }
-
- /** @internal */
- _destroyViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
- index: number) {
- var viewContainer = parentView.viewContainers[boundElementIndex];
- var view = viewContainer.views[index];
-
- this._viewDehydrateRecurse(view);
- this._utils.detachViewInContainer(parentView, boundElementIndex, index);
- if (view.viewOffset > 0) {
- // Case 1: a view that is part of another view.
- // Just detach the fragment
- this._renderer.detachFragment(view.renderFragment);
- } else {
- // Case 2: a view that is not part of another view.
- // dehydrate and destroy it.
- this._renderer.dehydrateView(view.render);
- this._renderer.detachFragment(view.renderFragment);
- this._destroyPooledView(view);
- }
- }
-
- /** @internal */
- _viewDehydrateRecurse(view: viewModule.AppView) {
- if (view.hydrated()) {
- this._utils.dehydrateView(view);
- }
- var viewContainers = view.viewContainers;
- var startViewOffset = view.viewOffset;
- var endViewOffset = view.viewOffset + view.proto.mergeInfo.viewCount - 1;
- var elementOffset = view.elementOffset;
- for (var viewIdx = startViewOffset; viewIdx <= endViewOffset; viewIdx++) {
- var currView = view.views[viewIdx];
- for (var binderIdx = 0; binderIdx < currView.proto.elementBinders.length;
- binderIdx++, elementOffset++) {
- var vc = viewContainers[elementOffset];
- if (isPresent(vc)) {
- for (var j = vc.views.length - 1; j >= 0; j--) {
- this._destroyViewInContainer(currView, elementOffset, j);
- }
- }
- }
- }
- }
}
diff --git a/modules/angular2/src/core/linker/view_manager_utils.ts b/modules/angular2/src/core/linker/view_manager_utils.ts
deleted file mode 100644
index 821f1b31dd..0000000000
--- a/modules/angular2/src/core/linker/view_manager_utils.ts
+++ /dev/null
@@ -1,266 +0,0 @@
-import {Injector, Provider, Injectable, ResolvedProvider} from 'angular2/src/core/di';
-import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facade/collection';
-import * as eli from './element_injector';
-import {isPresent, isBlank} from 'angular2/src/facade/lang';
-import * as viewModule from './view';
-import * as avmModule from './view_manager';
-import {ElementRef, ElementRef_} from './element_ref';
-import {TemplateRef, TemplateRef_} from './template_ref';
-import {Renderer, RenderViewWithFragments} from 'angular2/src/core/render/api';
-import {Locals} from 'angular2/src/core/change_detection/change_detection';
-import {Pipes} from 'angular2/src/core/pipes/pipes';
-
-@Injectable()
-export class AppViewManagerUtils {
- constructor() {}
-
- getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
- var eli = parentView.elementInjectors[boundElementIndex];
- return eli.getComponent();
- }
-
- createView(mergedParentViewProto: viewModule.AppProtoView,
- renderViewWithFragments: RenderViewWithFragments,
- viewManager: avmModule.AppViewManager, renderer: Renderer): viewModule.AppView {
- var renderFragments = renderViewWithFragments.fragmentRefs;
- var renderView = renderViewWithFragments.viewRef;
-
- var elementCount = mergedParentViewProto.mergeInfo.elementCount;
- var viewCount = mergedParentViewProto.mergeInfo.viewCount;
- var elementRefs: ElementRef[] = ListWrapper.createFixedSize(elementCount);
- var viewContainers = ListWrapper.createFixedSize(elementCount);
- var preBuiltObjects: eli.PreBuiltObjects[] = ListWrapper.createFixedSize(elementCount);
- var elementInjectors: eli.ElementInjector[] = ListWrapper.createFixedSize(elementCount);
- var views = ListWrapper.createFixedSize(viewCount);
-
- var elementOffset = 0;
- var textOffset = 0;
- var fragmentIdx = 0;
- var containerElementIndicesByViewIndex: number[] = ListWrapper.createFixedSize(viewCount);
- for (var viewOffset = 0; viewOffset < viewCount; viewOffset++) {
- var containerElementIndex = containerElementIndicesByViewIndex[viewOffset];
- var containerElementInjector =
- isPresent(containerElementIndex) ? elementInjectors[containerElementIndex] : null;
- var parentView =
- isPresent(containerElementInjector) ? preBuiltObjects[containerElementIndex].view : null;
- var protoView =
- isPresent(containerElementIndex) ?
- parentView.proto.elementBinders[containerElementIndex - parentView.elementOffset]
- .nestedProtoView :
- mergedParentViewProto;
- var renderFragment = null;
- if (viewOffset === 0 || protoView.type === viewModule.ViewType.EMBEDDED) {
- renderFragment = renderFragments[fragmentIdx++];
- }
- var currentView = new viewModule.AppView(renderer, protoView, viewOffset, elementOffset,
- textOffset, protoView.protoLocals, renderView,
- renderFragment, containerElementInjector);
- views[viewOffset] = currentView;
- if (isPresent(containerElementIndex)) {
- preBuiltObjects[containerElementIndex].nestedView = currentView;
- }
- var rootElementInjectors = [];
- var nestedViewOffset = viewOffset + 1;
- for (var binderIdx = 0; binderIdx < protoView.elementBinders.length; binderIdx++) {
- var binder = protoView.elementBinders[binderIdx];
- var boundElementIndex = elementOffset + binderIdx;
- var elementInjector = null;
-
- if (isPresent(binder.nestedProtoView) && binder.nestedProtoView.isMergable) {
- containerElementIndicesByViewIndex[nestedViewOffset] = boundElementIndex;
- nestedViewOffset += binder.nestedProtoView.mergeInfo.viewCount;
- }
-
- // elementInjectors and rootElementInjectors
- var protoElementInjector = binder.protoElementInjector;
- if (isPresent(protoElementInjector)) {
- if (isPresent(protoElementInjector.parent)) {
- var parentElementInjector =
- elementInjectors[elementOffset + protoElementInjector.parent.index];
- elementInjector = protoElementInjector.instantiate(parentElementInjector);
- } else {
- elementInjector = protoElementInjector.instantiate(null);
- rootElementInjectors.push(elementInjector);
- }
- }
- elementInjectors[boundElementIndex] = elementInjector;
-
- // elementRefs
- var el = new ElementRef_(currentView.ref, boundElementIndex, renderer);
- elementRefs[el.boundElementIndex] = el;
-
- // preBuiltObjects
- if (isPresent(elementInjector)) {
- var templateRef = isPresent(binder.nestedProtoView) &&
- binder.nestedProtoView.type === viewModule.ViewType.EMBEDDED ?
- new TemplateRef_(el) :
- null;
- preBuiltObjects[boundElementIndex] =
- new eli.PreBuiltObjects(viewManager, currentView, el, templateRef);
- }
- }
- currentView.init(protoView.changeDetectorFactory(currentView), elementInjectors,
- rootElementInjectors, preBuiltObjects, views, elementRefs, viewContainers);
- if (isPresent(parentView) && protoView.type === viewModule.ViewType.COMPONENT) {
- parentView.changeDetector.addViewChild(currentView.changeDetector);
- }
- elementOffset += protoView.elementBinders.length;
- textOffset += protoView.textBindingCount;
- }
- return views[0];
- }
-
- hydrateRootHostView(hostView: viewModule.AppView, injector: Injector) {
- this._hydrateView(hostView, injector, null, new Object(), null);
- }
-
- // Misnomer: this method is attaching next to the view container.
- attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
- contextView: viewModule.AppView, contextBoundElementIndex: number,
- index: number, view: viewModule.AppView) {
- if (isBlank(contextView)) {
- contextView = parentView;
- contextBoundElementIndex = boundElementIndex;
- }
- parentView.changeDetector.addContentChild(view.changeDetector);
- var viewContainer = parentView.viewContainers[boundElementIndex];
- if (isBlank(viewContainer)) {
- viewContainer = new viewModule.AppViewContainer();
- parentView.viewContainers[boundElementIndex] = viewContainer;
- }
- ListWrapper.insert(viewContainer.views, index, view);
- var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
-
- for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
- if (isPresent(elementInjector.parent)) {
- view.rootElementInjectors[i].link(elementInjector.parent);
- }
- }
- elementInjector.traverseAndSetQueriesAsDirty();
- }
-
- detachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number, index: number) {
- var viewContainer = parentView.viewContainers[boundElementIndex];
- var view = viewContainer.views[index];
-
- parentView.elementInjectors[boundElementIndex].traverseAndSetQueriesAsDirty();
-
- view.changeDetector.remove();
- ListWrapper.removeAt(viewContainer.views, index);
- for (var i = 0; i < view.rootElementInjectors.length; ++i) {
- var inj = view.rootElementInjectors[i];
- inj.unlink();
- }
- }
-
-
- hydrateViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
- contextView: viewModule.AppView, contextBoundElementIndex: number,
- index: number, imperativelyCreatedProviders: ResolvedProvider[]) {
- if (isBlank(contextView)) {
- contextView = parentView;
- contextBoundElementIndex = boundElementIndex;
- }
- var viewContainer = parentView.viewContainers[boundElementIndex];
- var view = viewContainer.views[index];
- var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
-
- var injector = isPresent(imperativelyCreatedProviders) ?
- Injector.fromResolvedProviders(imperativelyCreatedProviders) :
- null;
- this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
- contextView.locals);
- }
-
- /** @internal */
- _hydrateView(initView: viewModule.AppView, imperativelyCreatedInjector: Injector,
- hostElementInjector: eli.ElementInjector, context: Object, parentLocals: Locals) {
- var viewIdx = initView.viewOffset;
- var endViewOffset = viewIdx + initView.proto.mergeInfo.viewCount - 1;
- while (viewIdx <= endViewOffset) {
- var currView = initView.views[viewIdx];
- var currProtoView = currView.proto;
- if (currView !== initView && currView.proto.type === viewModule.ViewType.EMBEDDED) {
- // Don't hydrate components of embedded fragment views.
- viewIdx += currView.proto.mergeInfo.viewCount;
- } else {
- if (currView !== initView) {
- // hydrate a nested component view
- imperativelyCreatedInjector = null;
- parentLocals = null;
- hostElementInjector = currView.containerElementInjector;
- context = hostElementInjector.getComponent();
- }
- currView.context = context;
- currView.locals.parent = parentLocals;
- var binders = currProtoView.elementBinders;
- for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
- var boundElementIndex = binderIdx + currView.elementOffset;
- var elementInjector = initView.elementInjectors[boundElementIndex];
-
- if (isPresent(elementInjector)) {
- elementInjector.hydrate(imperativelyCreatedInjector, hostElementInjector,
- currView.preBuiltObjects[boundElementIndex]);
- this._populateViewLocals(currView, elementInjector, boundElementIndex);
- this._setUpEventEmitters(currView, elementInjector, boundElementIndex);
- }
- }
- var pipes = isPresent(hostElementInjector) ?
- new Pipes(currView.proto.pipes, hostElementInjector.getInjector()) :
- null;
- currView.changeDetector.hydrate(currView.context, currView.locals, currView, pipes);
- viewIdx++;
- }
- }
- }
-
- /** @internal */
- _populateViewLocals(view: viewModule.AppView, elementInjector: eli.ElementInjector,
- boundElementIdx: number): void {
- if (isPresent(elementInjector.getDirectiveVariableBindings())) {
- elementInjector.getDirectiveVariableBindings().forEach((directiveIndex, name) => {
- if (isBlank(directiveIndex)) {
- view.locals.set(name, view.elementRefs[boundElementIdx].nativeElement);
- } else {
- view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
- }
- });
- }
- }
-
- /** @internal */
- _setUpEventEmitters(view: viewModule.AppView, elementInjector: eli.ElementInjector,
- boundElementIndex: number) {
- var emitters = elementInjector.getEventEmitterAccessors();
- for (var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
- var directiveEmitters = emitters[directiveIndex];
- var directive = elementInjector.getDirectiveAtIndex(directiveIndex);
-
- for (var eventIndex = 0; eventIndex < directiveEmitters.length; ++eventIndex) {
- var eventEmitterAccessor = directiveEmitters[eventIndex];
- eventEmitterAccessor.subscribe(view, boundElementIndex, directive);
- }
- }
- }
-
- dehydrateView(initView: viewModule.AppView) {
- var endViewOffset = initView.viewOffset + initView.proto.mergeInfo.viewCount - 1;
- for (var viewIdx = initView.viewOffset; viewIdx <= endViewOffset; viewIdx++) {
- var currView = initView.views[viewIdx];
- if (currView.hydrated()) {
- if (isPresent(currView.locals)) {
- currView.locals.clearValues();
- }
- currView.context = null;
- currView.changeDetector.dehydrate();
- var binders = currView.proto.elementBinders;
- for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
- var eli = initView.elementInjectors[currView.elementOffset + binderIdx];
- if (isPresent(eli)) {
- eli.dehydrate();
- }
- }
- }
- }
- }
-}
diff --git a/modules/angular2/src/core/linker/view_pool.ts b/modules/angular2/src/core/linker/view_pool.ts
deleted file mode 100644
index 17598fddf5..0000000000
--- a/modules/angular2/src/core/linker/view_pool.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
-
-import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
-import {MapWrapper, Map} from 'angular2/src/facade/collection';
-
-import * as viewModule from './view';
-
-export const APP_VIEW_POOL_CAPACITY = CONST_EXPR(new OpaqueToken('AppViewPool.viewPoolCapacity'));
-
-@Injectable()
-export class AppViewPool {
- /** @internal */
- _poolCapacityPerProtoView: number;
- /** @internal */
- _pooledViewsPerProtoView = new Map>();
-
- constructor(@Inject(APP_VIEW_POOL_CAPACITY) poolCapacityPerProtoView) {
- this._poolCapacityPerProtoView = poolCapacityPerProtoView;
- }
-
- getView(protoView: viewModule.AppProtoView): viewModule.AppView {
- var pooledViews = this._pooledViewsPerProtoView.get(protoView);
- if (isPresent(pooledViews) && pooledViews.length > 0) {
- return pooledViews.pop();
- }
- return null;
- }
-
- returnView(view: viewModule.AppView): boolean {
- var protoView = view.proto;
- var pooledViews = this._pooledViewsPerProtoView.get(protoView);
- if (isBlank(pooledViews)) {
- pooledViews = [];
- this._pooledViewsPerProtoView.set(protoView, pooledViews);
- }
- var haveRemainingCapacity = pooledViews.length < this._poolCapacityPerProtoView;
- if (haveRemainingCapacity) {
- pooledViews.push(view);
- }
- return haveRemainingCapacity;
- }
-}
diff --git a/modules/angular2/src/core/linker/view_ref.ts b/modules/angular2/src/core/linker/view_ref.ts
index 7cf3af02ad..8e823ce380 100644
--- a/modules/angular2/src/core/linker/view_ref.ts
+++ b/modules/angular2/src/core/linker/view_ref.ts
@@ -1,20 +1,16 @@
-import {isPresent} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
-import * as viewModule from './view';
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
-import {RenderViewRef, RenderFragmentRef} from 'angular2/src/core/render/api';
+import {AppView, HostViewFactory} from './view';
-// This is a workaround for privacy in Dart as we don't have library parts
-export function internalView(viewRef: ViewRef): viewModule.AppView {
- return (viewRef)._view;
+export abstract class ViewRef {
+ /**
+ * @internal
+ */
+ get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); };
+
+ get destroyed(): boolean { return unimplemented(); }
}
-// This is a workaround for privacy in Dart as we don't have library parts
-export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppProtoView {
- return isPresent(protoViewRef) ? (protoViewRef)._protoView : null;
-}
-
-
/**
* Represents a View containing a single Element that is the Host Element of a {@link Component}
* instance.
@@ -24,11 +20,8 @@ export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppPro
* of the higher-level APIs: {@link AppViewManager#createRootHostView},
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
*/
-export interface HostViewRef {
- /**
- * @internal
- */
- changeDetectorRef: ChangeDetectorRef;
+export abstract class HostViewRef extends ViewRef {
+ get rootNodes(): any[] { return unimplemented(); };
}
/**
@@ -84,96 +77,43 @@ export interface HostViewRef {
*
* ```
*/
-export abstract class ViewRef implements HostViewRef {
+export abstract class EmbeddedViewRef extends ViewRef {
/**
* Sets `value` of local variable called `variableName` in this View.
*/
abstract setLocal(variableName: string, value: any): void;
- get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }
- set changeDetectorRef(value: ChangeDetectorRef) {
- unimplemented(); // TODO: https://github.com/Microsoft/TypeScript/issues/12
- }
+ /**
+ * Checks whether this view has a local variable called `variableName`.
+ */
+ abstract hasLocal(variableName: string): boolean;
+
+ get rootNodes(): any[] { return unimplemented(); };
}
-export class ViewRef_ extends ViewRef {
- private _changeDetectorRef: ChangeDetectorRef = null;
- /** @internal */
- public _view: viewModule.AppView;
- constructor(_view: viewModule.AppView) {
- super();
- this._view = _view;
- }
+export class ViewRef_ implements EmbeddedViewRef, HostViewRef {
+ constructor(private _view: AppView) { this._view = _view; }
- /**
- * Return `RenderViewRef`
- */
- get render(): RenderViewRef { return this._view.render; }
-
- /**
- * Return `RenderFragmentRef`
- */
- get renderFragment(): RenderFragmentRef { return this._view.renderFragment; }
+ get internalView(): AppView { return this._view; }
/**
* Return `ChangeDetectorRef`
*/
- get changeDetectorRef(): ChangeDetectorRef {
- if (this._changeDetectorRef === null) {
- this._changeDetectorRef = this._view.changeDetector.ref;
- }
- return this._changeDetectorRef;
- }
+ get changeDetectorRef(): ChangeDetectorRef { return this._view.changeDetector.ref; }
+
+ get rootNodes(): any[] { return this._view.flatRootNodes; }
setLocal(variableName: string, value: any): void { this._view.setLocal(variableName, value); }
+
+ hasLocal(variableName: string): boolean { return this._view.hasLocal(variableName); }
+
+ get destroyed(): boolean { return this._view.destroyed; }
}
-/**
- * Represents an Angular ProtoView.
- *
- * A ProtoView is a prototypical {@link ViewRef View} that is the result of Template compilation and
- * is used by Angular to efficiently create an instance of this View based on the compiled Template.
- *
- * Most ProtoViews are created and used internally by Angular and you don't need to know about them,
- * except in advanced use-cases where you compile components yourself via the low-level
- * {@link Compiler#compileInHost} API.
- *
- *
- * ### Example
- *
- * Given this template:
- *
- * ```
- * Count: {{items.length}}
- *
- * ```
- *
- * Angular desugars and compiles the template into two ProtoViews:
- *
- * Outer ProtoView:
- * ```
- * Count: {{items.length}}
- *
- * ```
- *
- * Inner ProtoView:
- * ```
- * {{item}}
- * ```
- *
- * Notice that the original template is broken down into two separate ProtoViews.
- */
-export abstract class ProtoViewRef {}
+export abstract class HostViewFactoryRef {}
-export class ProtoViewRef_ extends ProtoViewRef {
- /** @internal */
- public _protoView: viewModule.AppProtoView;
- constructor(_protoView: viewModule.AppProtoView) {
- super();
- this._protoView = _protoView;
- }
-}
+export class HostViewFactoryRef_ implements HostViewFactoryRef {
+ constructor(private _hostViewFactory: HostViewFactory) {}
+
+ get internalHostViewFactory(): HostViewFactory { return this._hostViewFactory; }
+}
\ No newline at end of file
diff --git a/modules/angular2/src/core/linker/view_type.ts b/modules/angular2/src/core/linker/view_type.ts
new file mode 100644
index 0000000000..ef4ceefc6c
--- /dev/null
+++ b/modules/angular2/src/core/linker/view_type.ts
@@ -0,0 +1,11 @@
+export enum ViewType {
+ // A view that contains the host element with bound component directive.
+ // Contains a COMPONENT view
+ HOST,
+ // The view of the component
+ // Can contain 0 to n EMBEDDED views
+ COMPONENT,
+ // A view that is embedded into another View via a element
+ // inside of a COMPONENT view
+ EMBEDDED
+}
diff --git a/modules/angular2/src/core/pipes/pipes.ts b/modules/angular2/src/core/pipes/pipes.ts
index 52f2482621..08bbbb9c92 100644
--- a/modules/angular2/src/core/pipes/pipes.ts
+++ b/modules/angular2/src/core/pipes/pipes.ts
@@ -45,7 +45,6 @@ export class Pipes implements cd.Pipes {
get(name: string): cd.SelectedPipe {
var cached = StringMapWrapper.get(this._config, name);
if (isPresent(cached)) return cached;
-
var p = this.proto.get(name);
var transform = this.injector.instantiateResolved(p);
var res = new cd.SelectedPipe(transform, p.pure);
diff --git a/modules/angular2/src/core/render.ts b/modules/angular2/src/core/render.ts
index 7976f27931..e7bbe6139c 100644
--- a/modules/angular2/src/core/render.ts
+++ b/modules/angular2/src/core/render.ts
@@ -1,19 +1,2 @@
// Public API for render
-export {
- RenderEventDispatcher,
- Renderer,
- RenderElementRef,
- RenderViewRef,
- RenderProtoViewRef,
- RenderFragmentRef,
- RenderViewWithFragments,
- RenderTemplateCmd,
- RenderCommandVisitor,
- RenderTextCmd,
- RenderNgContentCmd,
- RenderBeginElementCmd,
- RenderBeginComponentCmd,
- RenderEmbeddedTemplateCmd,
- RenderBeginCmd,
- RenderComponentTemplate
-} from './render/api';
\ No newline at end of file
+export {RootRenderer, Renderer, RenderComponentType} from './render/api';
diff --git a/modules/angular2/src/core/render/api.ts b/modules/angular2/src/core/render/api.ts
index c5bcc1c02b..834ef9e11c 100644
--- a/modules/angular2/src/core/render/api.ts
+++ b/modules/angular2/src/core/render/api.ts
@@ -1,196 +1,54 @@
-import {unimplemented} from 'angular2/src/facade/exceptions';
-import {Map} from 'angular2/src/facade/collection';
-import {ViewEncapsulation} from 'angular2/src/core/metadata';
+import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
-/**
- * Represents an Angular ProtoView in the Rendering Context.
- *
- * When you implement a custom {@link Renderer}, `RenderProtoViewRef` specifies what Render View
- * your renderer should create.
- *
- * `RenderProtoViewRef` is a counterpart to {@link ProtoViewRef} available in the Application
- * Context. But unlike `ProtoViewRef`, `RenderProtoViewRef` contains all static nested Proto Views
- * that are recursively merged into a single Render Proto View.
-
- *
- *
- */
-export class RenderProtoViewRef {}
-
-/**
- * Represents a list of sibling Nodes that can be moved by the {@link Renderer} independently of
- * other Render Fragments.
- *
- * Any {@link RenderViewRef} has one Render Fragment.
- *
- * Additionally any View with an Embedded View that contains a {@link NgContentAst View Projection}
- * results in additional Render Fragment.
- */
-/*
- foo
- {{bar}}
-
-
- foo
-> view 1 / fragment 1
-
-
- - {{fg}}
-> view 2 / fragment 1
-
-
- {{bar}}
-
-
- foo
-> view 1 / fragment 1
-
-
- - >
-> view 1 / fragment 2
-
-
- - >
->
- -> view 1 / fragment 2 + view 2 / fragment 1..n-1
-
-
- {{bar}}
- */
-// TODO(i): refactor into an interface
-export class RenderFragmentRef {}
-
-
-/**
- * Represents an Angular View in the Rendering Context.
- *
- * `RenderViewRef` specifies to the {@link Renderer} what View to update or destroy.
- *
- * Unlike a {@link ViewRef} available in the Application Context, Render View contains all the
- * static Component Views that have been recursively merged into a single Render View.
- *
- * Each `RenderViewRef` contains one or more {@link RenderFragmentRef Render Fragments}, these
- * Fragments are created, hydrated, dehydrated and destroyed as a single unit together with the
- * View.
- */
-// TODO(i): refactor into an interface
-export class RenderViewRef {}
-
-/**
- * Abstract base class for commands to the Angular renderer, using the visitor pattern.
- */
-export abstract class RenderTemplateCmd {
- abstract visit(visitor: RenderCommandVisitor, context: any): any;
+export class RenderComponentType {
+ constructor(public id: string, public encapsulation: ViewEncapsulation,
+ public styles: Array) {}
}
-/**
- * Command to begin rendering.
- */
-export abstract class RenderBeginCmd extends RenderTemplateCmd {
- get ngContentIndex(): number { return unimplemented(); };
- get isBound(): boolean { return unimplemented(); };
-}
+export interface ParentRenderer { renderComponent(componentType: RenderComponentType): Renderer; }
-/**
- * Command to render text.
- */
-export abstract class RenderTextCmd extends RenderBeginCmd {
- get value(): string { return unimplemented(); };
-}
+export abstract class Renderer implements ParentRenderer {
+ abstract renderComponent(componentType: RenderComponentType): Renderer;
-/**
- * Command to render projected content.
- */
-export abstract class RenderNgContentCmd extends RenderTemplateCmd {
- // The index of this NgContent element
- get index(): number { return unimplemented(); };
- // The index of the NgContent element into which this
- // NgContent element should be projected (if any)
- get ngContentIndex(): number { return unimplemented(); };
-}
+ abstract selectRootElement(selector: string): any;
-/**
- * Command to begin rendering an element.
- */
-export abstract class RenderBeginElementCmd extends RenderBeginCmd {
- get name(): string { return unimplemented(); };
- get attrNameAndValues(): string[] { return unimplemented(); };
- get eventTargetAndNames(): string[] { return unimplemented(); };
-}
+ abstract createElement(parentElement: any, name: string): any;
-/**
- * Command to begin rendering a component.
- */
-export abstract class RenderBeginComponentCmd extends RenderBeginElementCmd {
- get templateId(): string { return unimplemented(); };
-}
+ abstract createViewRoot(hostElement: any): any;
-/**
- * Command to render a component's template.
- */
-export abstract class RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
- get isMerged(): boolean { return unimplemented(); };
- get children(): RenderTemplateCmd[] { return unimplemented(); };
-}
+ abstract createTemplateAnchor(parentElement: any): any;
-/**
- * Visitor for a {@link RenderTemplateCmd}.
- */
-export interface RenderCommandVisitor {
- visitText(cmd: RenderTextCmd, context: any): any;
- visitNgContent(cmd: RenderNgContentCmd, context: any): any;
- visitBeginElement(cmd: RenderBeginElementCmd, context: any): any;
- visitEndElement(context: any): any;
- visitBeginComponent(cmd: RenderBeginComponentCmd, context: any): any;
- visitEndComponent(context: any): any;
- visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: any): any;
-}
+ abstract createText(parentElement: any, value: string): any;
+ abstract projectNodes(parentElement: any, nodes: any[]);
-/**
- * Container class produced by a {@link Renderer} when creating a Render View.
- *
- * An instance of `RenderViewWithFragments` contains a {@link RenderViewRef} and an array of
- * {@link RenderFragmentRef}s belonging to this Render View.
- */
-// TODO(i): refactor this by RenderViewWithFragments and adding fragments directly to RenderViewRef
-export class RenderViewWithFragments {
- constructor(
- /**
- * Reference to the {@link RenderViewRef}.
- */
- public viewRef: RenderViewRef,
- /**
- * Array of {@link RenderFragmentRef}s ordered in the depth-first order.
- */
- public fragmentRefs: RenderFragmentRef[]) {}
-}
+ abstract attachViewAfter(node: any, viewRootNodes: any[]);
-/**
- * Represents an Element that is part of a {@link RenderViewRef Render View}.
- *
- * `RenderElementRef` is a counterpart to {@link ElementRef} available in the Application Context.
- *
- * When using `Renderer` from the Application Context, `ElementRef` can be used instead of
- * `RenderElementRef`.
- */
-export interface RenderElementRef {
- /**
- * Reference to the Render View that contains this Element.
- */
- renderView: RenderViewRef;
+ abstract detachView(viewRootNodes: any[]);
+
+ abstract destroyView(hostElement: any, viewAllNodes: any[]);
+
+ abstract listen(renderElement: any, name: string, callback: Function);
+
+ abstract listenGlobal(target: string, name: string, callback: Function): Function;
+
+ abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: any);
+
+ abstract setElementAttribute(renderElement: any, attributeName: string, attributeValue: string);
/**
- * @internal
- *
- * Index of the Element (in the depth-first order) inside the Render View.
- *
- * This index is used internally by Angular to locate elements.
+ * Used only in debug mode to serialize property changes to comment nodes,
+ * such as placeholders.
*/
- boundElementIndex: number;
-}
+ abstract setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string);
-/**
- * Template for rendering a component, including commands and styles.
- */
-export class RenderComponentTemplate {
- constructor(public id: string, public shortId: string, public encapsulation: ViewEncapsulation,
- public commands: RenderTemplateCmd[], public styles: string[]) {}
+ abstract setElementClass(renderElement: any, className: string, isAdd: boolean);
+
+ abstract setElementStyle(renderElement: any, styleName: string, styleValue: string);
+
+ abstract invokeElementMethod(renderElement: any, methodName: string, args: any[]);
+
+ abstract setText(renderNode: any, text: string);
}
/**
@@ -205,184 +63,7 @@ export class RenderComponentTemplate {
*
* The default Renderer implementation is `DomRenderer`. Also available is `WebWorkerRenderer`.
*/
-export abstract class Renderer {
- /**
- * Registers a component template represented as arrays of {@link RenderTemplateCmd}s and styles
- * with the Renderer.
- *
- * Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when
- * {@link #createProtoView creating Render ProtoView}.
- */
- abstract registerComponentTemplate(template: RenderComponentTemplate);
- /**
- * Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s.
- */
- abstract createProtoView(componentTemplateId: string,
- cmds: RenderTemplateCmd[]): RenderProtoViewRef;
-
- /**
- * Creates a Root Host View based on the provided `hostProtoViewRef`.
- *
- * `fragmentCount` is the number of nested {@link RenderFragmentRef}s in this View. This parameter
- * is non-optional so that the renderer can create a result synchronously even when application
- * runs in a different context (e.g. in a Web Worker).
- *
- * `hostElementSelector` is a (CSS) selector for querying the main document to find the Host
- * Element. The newly created Root Host View should be attached to this element.
- *
- * Returns an instance of {@link RenderViewWithFragments}, representing the Render View.
- */
- abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
- hostElementSelector: string): RenderViewWithFragments;
-
- /**
- * Creates a Render View based on the provided `protoViewRef`.
- *
- * `fragmentCount` is the number of nested {@link RenderFragmentRef}s in this View. This parameter
- * is non-optional so that the renderer can create a result synchronously even when application
- * runs in a different context (e.g. in a Web Worker).
- *
- * Returns an instance of {@link RenderViewWithFragments}, representing the Render View.
- */
- abstract createView(protoViewRef: RenderProtoViewRef,
- fragmentCount: number): RenderViewWithFragments;
-
- /**
- * Destroys a Render View specified via `viewRef`.
- *
- * This operation should be performed only on a View that has already been dehydrated and
- * all of its Render Fragments have been detached.
- *
- * Destroying a View indicates to the Renderer that this View is not going to be referenced in any
- * future operations. If the Renderer created any renderer-specific objects for this View, these
- * objects should now be destroyed to prevent memory leaks.
- */
- abstract destroyView(viewRef: RenderViewRef);
-
- /**
- * Attaches the Nodes of a Render Fragment after the last Node of `previousFragmentRef`.
- */
- abstract attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
- fragmentRef: RenderFragmentRef);
-
- /**
- * Attaches the Nodes of the Render Fragment after an Element.
- */
- abstract attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef);
-
- /**
- * Detaches the Nodes of a Render Fragment from their parent.
- *
- * This operations should be called only on a View that has been already
- * {@link #dehydrateView dehydrated}.
- */
- abstract detachFragment(fragmentRef: RenderFragmentRef);
-
- /**
- * Notifies a custom Renderer to initialize a Render View.
- *
- * This method is called by Angular after a Render View has been created, or when a previously
- * dehydrated Render View is about to be reused.
- */
- abstract hydrateView(viewRef: RenderViewRef);
-
- /**
- * Notifies a custom Renderer that a Render View is no longer active.
- *
- * This method is called by Angular before a Render View will be destroyed, or when a hydrated
- * Render View is about to be put into a pool for future reuse.
- */
- abstract dehydrateView(viewRef: RenderViewRef);
-
- /**
- * Returns the underlying native element at the specified `location`, or `null` if direct access
- * to native elements is not supported (e.g. when the application runs in a web worker).
- *
- *
- *
- *
- * Use this api as the last resort when direct access to DOM is needed. Use templating and
- * data-binding, or other {@link Renderer} methods instead.
- *
- *
- * Relying on direct DOM access creates tight coupling between your application and rendering
- * layers which will make it impossible to separate the two and deploy your application into a
- * web worker.
- *
- *
- */
- abstract getNativeElementSync(location: RenderElementRef): any;
-
- /**
- * Sets a property on the Element specified via `location`.
- */
- abstract setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any);
-
- /**
- * Sets an attribute on the Element specified via `location`.
- *
- * If `attributeValue` is `null`, the attribute is removed.
- */
- abstract setElementAttribute(location: RenderElementRef, attributeName: string,
- attributeValue: string);
-
- abstract setBindingDebugInfo(location: RenderElementRef, propertyName: string,
- propertyValue: string);
-
- /**
- * Sets a (CSS) class on the Element specified via `location`.
- *
- * `isAdd` specifies if the class should be added or removed.
- */
- abstract setElementClass(location: RenderElementRef, className: string, isAdd: boolean);
-
- /**
- * Sets a (CSS) inline style on the Element specified via `location`.
- *
- * If `styleValue` is `null`, the style is removed.
- */
- abstract setElementStyle(location: RenderElementRef, styleName: string, styleValue: string);
-
- /**
- * Calls a method on the Element specified via `location`.
- */
- abstract invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]);
-
- /**
- * Sets the value of an interpolated TextNode at the specified index to the `text` value.
- *
- * `textNodeIndex` is the depth-first index of the Node among interpolated Nodes in the Render
- * View.
- */
- abstract setText(viewRef: RenderViewRef, textNodeIndex: number, text: string);
-
- /**
- * Sets a dispatcher to relay all events triggered in the given Render View.
- *
- * Each Render View can have only one Event Dispatcher, if this method is called multiple times,
- * the last provided dispatcher will be used.
- */
- abstract setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher);
-}
-
-/**
- * A dispatcher that relays all events that occur in a Render View.
- *
- * Use {@link Renderer#setEventDispatcher} to register a dispatcher for a particular Render View.
- */
-export interface RenderEventDispatcher {
- /**
- * Called when Event called `eventName` was triggered on an Element with an Event Binding for this
- * Event.
- *
- * `elementIndex` specifies the depth-first index of the Element in the Render View.
- *
- * `locals` is a map for local variable to value mapping that should be used when evaluating the
- * Event Binding expression.
- *
- * Returns `false` if `preventDefault` should be called to stop the default behavior of the Event
- * in the Rendering Context.
- */
- dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map): boolean;
+export abstract class RootRenderer implements ParentRenderer {
+ abstract renderComponent(componentType: RenderComponentType): Renderer;
}
diff --git a/modules/angular2/src/core/render/view.ts b/modules/angular2/src/core/render/view.ts
deleted file mode 100644
index 0e47b7f3ab..0000000000
--- a/modules/angular2/src/core/render/view.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import {BaseException} from 'angular2/src/facade/exceptions';
-import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facade/collection';
-import {isPresent, isBlank, stringify} from 'angular2/src/facade/lang';
-
-import {
- RenderComponentTemplate,
- RenderViewRef,
- RenderEventDispatcher,
- RenderTemplateCmd,
- RenderProtoViewRef,
- RenderFragmentRef
-} from './api';
-
-export class DefaultProtoViewRef extends RenderProtoViewRef {
- constructor(public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
- super();
- }
-}
-
-export class DefaultRenderFragmentRef extends RenderFragmentRef {
- constructor(public nodes: N[]) { super(); }
-}
-
-export class DefaultRenderView extends RenderViewRef {
- hydrated: boolean = false;
- eventDispatcher: RenderEventDispatcher = null;
- globalEventRemovers: Function[] = null;
-
- constructor(public fragments: DefaultRenderFragmentRef[], public boundTextNodes: N[],
- public boundElements: N[], public nativeShadowRoots: N[],
- public globalEventAdders: Function[], public rootContentInsertionPoints: N[]) {
- super();
- }
-
- hydrate() {
- if (this.hydrated) throw new BaseException('The view is already hydrated.');
- this.hydrated = true;
- this.globalEventRemovers = ListWrapper.createFixedSize(this.globalEventAdders.length);
- for (var i = 0; i < this.globalEventAdders.length; i++) {
- this.globalEventRemovers[i] = this.globalEventAdders[i]();
- }
- }
-
- dehydrate() {
- if (!this.hydrated) throw new BaseException('The view is already dehydrated.');
- for (var i = 0; i < this.globalEventRemovers.length; i++) {
- this.globalEventRemovers[i]();
- }
- this.globalEventRemovers = null;
- this.hydrated = false;
- }
-
- setEventDispatcher(dispatcher: RenderEventDispatcher) { this.eventDispatcher = dispatcher; }
-
- dispatchRenderEvent(boundElementIndex: number, eventName: string, event: any): boolean {
- var allowDefaultBehavior = true;
- if (isPresent(this.eventDispatcher)) {
- var locals = new Map();
- locals.set('$event', event);
- allowDefaultBehavior =
- this.eventDispatcher.dispatchRenderEvent(boundElementIndex, eventName, locals);
- }
- return allowDefaultBehavior;
- }
-}
diff --git a/modules/angular2/src/core/render/view_factory.ts b/modules/angular2/src/core/render/view_factory.ts
deleted file mode 100644
index d401c265fd..0000000000
--- a/modules/angular2/src/core/render/view_factory.ts
+++ /dev/null
@@ -1,321 +0,0 @@
-import {isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
-import {
- RenderEventDispatcher,
- RenderTemplateCmd,
- RenderCommandVisitor,
- RenderBeginElementCmd,
- RenderBeginComponentCmd,
- RenderNgContentCmd,
- RenderTextCmd,
- RenderEmbeddedTemplateCmd,
- RenderComponentTemplate
-} from './api';
-import {DefaultRenderView, DefaultRenderFragmentRef} from './view';
-import {ViewEncapsulation} from 'angular2/src/core/metadata';
-import {ListWrapper} from 'angular2/src/facade/collection';
-
-
-export function encapsulateStyles(componentTemplate: RenderComponentTemplate): string[] {
- var processedStyles = componentTemplate.styles;
- if (componentTemplate.encapsulation === ViewEncapsulation.Emulated) {
- processedStyles = ListWrapper.createFixedSize(componentTemplate.styles.length);
- for (var i = 0; i < componentTemplate.styles.length; i++) {
- processedStyles[i] = StringWrapper.replaceAll(componentTemplate.styles[i], COMPONENT_REGEX,
- componentTemplate.shortId);
- }
- }
- return processedStyles;
-}
-
-export function createRenderView(componentTemplate: RenderComponentTemplate,
- cmds: RenderTemplateCmd[], inplaceElement: any,
- nodeFactory: NodeFactory): DefaultRenderView {
- var view: DefaultRenderView;
- var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) =>
- view.dispatchRenderEvent(boundElementIndex, eventName, event);
- var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement);
- context.build(componentTemplate, cmds);
- var fragments: DefaultRenderFragmentRef[] = [];
- for (var i = 0; i < context.fragments.length; i++) {
- fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
- }
- view = new DefaultRenderView(fragments, context.boundTextNodes, context.boundElements,
- context.nativeShadowRoots, context.globalEventAdders,
- context.rootContentInsertionPoints);
- return view;
-}
-
-export interface NodeFactory {
- resolveComponentTemplate(templateId: string): RenderComponentTemplate;
- createTemplateAnchor(attrNameAndValues: string[]): N;
- createElement(name: string, attrNameAndValues: string[]): N;
- createRootContentInsertionPoint(): N;
- mergeElement(existing: N, attrNameAndValues: string[]);
- createShadowRoot(host: N, templateId: string): N;
- createText(value: string): N;
- appendChild(parent: N, child: N);
- on(element: N, eventName: string, callback: Function);
- globalOn(target: string, eventName: string, callback: Function): Function;
-}
-
-class BuildContext {
- constructor(private _eventDispatcher: Function, public factory: NodeFactory,
- private _inplaceElement: N) {
- this.isHost = isPresent((_inplaceElement));
- }
- private _builders: RenderViewBuilder[] = [];
-
- globalEventAdders: Function[] = [];
- boundElements: N[] = [];
- boundTextNodes: N[] = [];
- nativeShadowRoots: N[] = [];
- fragments: N[][] = [];
- rootContentInsertionPoints: N[] = [];
- componentCount: number = 0;
- isHost: boolean;
-
- build(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
- this.enqueueRootBuilder(template, cmds);
- this._build(this._builders[0]);
- }
-
- private _build(builder: RenderViewBuilder) {
- this._builders = [];
- builder.build(this);
- var enqueuedBuilders = this._builders;
- for (var i = 0; i < enqueuedBuilders.length; i++) {
- this._build(enqueuedBuilders[i]);
- }
- }
-
- enqueueComponentBuilder(component: Component) {
- this.componentCount++;
- this._builders.push(
- new RenderViewBuilder(component, null, component.template, component.template.commands));
- }
-
- enqueueFragmentBuilder(parentComponent: Component, parentTemplate: RenderComponentTemplate,
- commands: RenderTemplateCmd[]) {
- var rootNodes = [];
- this.fragments.push(rootNodes);
- this._builders.push(
- new RenderViewBuilder(parentComponent, rootNodes, parentTemplate, commands));
- }
-
- enqueueRootBuilder(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
- var rootNodes = [];
- this.fragments.push(rootNodes);
- this._builders.push(new RenderViewBuilder(null, rootNodes, template, cmds));
- }
-
- consumeInplaceElement(): N {
- var result = this._inplaceElement;
- this._inplaceElement = null;
- return result;
- }
-
- addEventListener(boundElementIndex: number, target: string, eventName: string) {
- if (isPresent(target)) {
- var handler =
- createEventHandler(boundElementIndex, `${target}:${eventName}`, this._eventDispatcher);
- this.globalEventAdders.push(createGlobalEventAdder(target, eventName, handler, this.factory));
- } else {
- var handler = createEventHandler(boundElementIndex, eventName, this._eventDispatcher);
- this.factory.on(this.boundElements[boundElementIndex], eventName, handler);
- }
- }
-}
-
-
-function createEventHandler(boundElementIndex: number, eventName: string,
- eventDispatcher: Function): Function {
- return ($event) => eventDispatcher(boundElementIndex, eventName, $event);
-}
-
-function createGlobalEventAdder(target: string, eventName: string, eventHandler: Function,
- nodeFactory: NodeFactory): Function {
- return () => nodeFactory.globalOn(target, eventName, eventHandler);
-}
-
-class RenderViewBuilder implements RenderCommandVisitor {
- parentStack: Array>;
-
- constructor(public parentComponent: Component, public fragmentRootNodes: N[],
- public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
- var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot;
- this.parentStack = [rootNodesParent];
- }
-
- build(context: BuildContext) {
- var cmds = this.cmds;
- for (var i = 0; i < cmds.length; i++) {
- cmds[i].visit(this, context);
- }
- }
-
- get parent(): N | Component { return this.parentStack[this.parentStack.length - 1]; }
-
- visitText(cmd: RenderTextCmd, context: BuildContext): any {
- var text = context.factory.createText(cmd.value);
- this._addChild(text, cmd.ngContentIndex, context);
- if (cmd.isBound) {
- context.boundTextNodes.push(text);
- }
- return null;
- }
- visitNgContent(cmd: RenderNgContentCmd, context: BuildContext): any {
- if (isPresent(this.parentComponent)) {
- if (this.parentComponent.isRoot) {
- var insertionPoint = context.factory.createRootContentInsertionPoint();
- if (this.parent instanceof Component) {
- context.factory.appendChild((>this.parent).shadowRoot, insertionPoint);
- } else {
- context.factory.appendChild(this.parent, insertionPoint);
- }
- context.rootContentInsertionPoints.push(insertionPoint);
- } else {
- var projectedNodes = this.parentComponent.project(cmd.index);
- for (var i = 0; i < projectedNodes.length; i++) {
- var node = projectedNodes[i];
- this._addChild(node, cmd.ngContentIndex, context);
- }
- }
- }
- return null;
- }
- visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext): any {
- this.parentStack.push(this._beginElement(cmd, context, null));
- return null;
- }
- visitEndElement(context: BuildContext): any {
- this._endElement();
- return null;
- }
- visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext): any {
- var templateId = cmd.templateId;
- var tpl = context.factory.resolveComponentTemplate(templateId);
- var el = this._beginElement(cmd, context, tpl);
- var root = el;
-
- if (tpl.encapsulation === ViewEncapsulation.Native) {
- root = context.factory.createShadowRoot(el, templateId);
- context.nativeShadowRoots.push(root);
- }
- var isRoot = context.componentCount === 0 && context.isHost;
- var component = new Component(el, root, isRoot, tpl);
- context.enqueueComponentBuilder(component);
- this.parentStack.push(component);
- return null;
- }
- visitEndComponent(context: BuildContext): any {
- this._endElement();
- return null;
- }
- visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: BuildContext): any {
- var el = context.factory.createTemplateAnchor(cmd.attrNameAndValues);
- this._addChild(el, cmd.ngContentIndex, context);
- context.boundElements.push(el);
- if (cmd.isMerged) {
- context.enqueueFragmentBuilder(this.parentComponent, this.template, cmd.children);
- }
- return null;
- }
-
- private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext,
- componentTemplate: RenderComponentTemplate): N {
- var el: N = context.consumeInplaceElement();
- var attrNameAndValues = cmd.attrNameAndValues;
- var templateEmulatedEncapsulation = this.template.encapsulation === ViewEncapsulation.Emulated;
- var componentEmulatedEncapsulation =
- isPresent(componentTemplate) &&
- componentTemplate.encapsulation === ViewEncapsulation.Emulated;
- var newAttrLength = attrNameAndValues.length + (templateEmulatedEncapsulation ? 2 : 0) +
- (componentEmulatedEncapsulation ? 2 : 0);
- if (newAttrLength > attrNameAndValues.length) {
- // Note: Need to clone attrNameAndValues to make it writable!
- var newAttrNameAndValues = ListWrapper.createFixedSize(newAttrLength);
- var attrIndex;
- for (attrIndex = 0; attrIndex < attrNameAndValues.length; attrIndex++) {
- newAttrNameAndValues[attrIndex] = attrNameAndValues[attrIndex];
- }
- if (templateEmulatedEncapsulation) {
- newAttrNameAndValues[attrIndex++] = _shimContentAttribute(this.template.shortId);
- newAttrNameAndValues[attrIndex++] = '';
- }
- if (componentEmulatedEncapsulation) {
- newAttrNameAndValues[attrIndex++] = _shimHostAttribute(componentTemplate.shortId);
- newAttrNameAndValues[attrIndex++] = '';
- }
- attrNameAndValues = newAttrNameAndValues;
- }
- if (isPresent(el)) {
- context.factory.mergeElement(el, attrNameAndValues);
- this.fragmentRootNodes.push(el);
- } else {
- el = context.factory.createElement(cmd.name, attrNameAndValues);
- this._addChild(el, cmd.ngContentIndex, context);
- }
- if (cmd.isBound) {
- var boundElementIndex = context.boundElements.length;
- context.boundElements.push(el);
- for (var i = 0; i < cmd.eventTargetAndNames.length; i += 2) {
- var target = cmd.eventTargetAndNames[i];
- var eventName = cmd.eventTargetAndNames[i + 1];
- context.addEventListener(boundElementIndex, target, eventName);
- }
- }
- return el;
- }
-
- private _endElement() { this.parentStack.pop(); }
-
- private _addChild(node: N, ngContentIndex: number, context: BuildContext) {
- var parent = this.parent;
- if (isPresent(parent)) {
- if (parent instanceof Component) {
- parent.addContentNode(ngContentIndex, node, context);
- } else {
- context.factory.appendChild(parent, node);
- }
- } else {
- this.fragmentRootNodes.push(node);
- }
- }
-}
-
-class Component {
- private contentNodesByNgContentIndex: N[][] = [];
-
- constructor(public hostElement: N, public shadowRoot: N, public isRoot: boolean,
- public template: RenderComponentTemplate) {}
- addContentNode(ngContentIndex: number, node: N, context: BuildContext) {
- if (isBlank(ngContentIndex)) {
- if (this.template.encapsulation === ViewEncapsulation.Native) {
- context.factory.appendChild(this.hostElement, node);
- }
- } else {
- while (this.contentNodesByNgContentIndex.length <= ngContentIndex) {
- this.contentNodesByNgContentIndex.push([]);
- }
- this.contentNodesByNgContentIndex[ngContentIndex].push(node);
- }
- }
- project(ngContentIndex: number): N[] {
- return ngContentIndex < this.contentNodesByNgContentIndex.length ?
- this.contentNodesByNgContentIndex[ngContentIndex] :
- [];
- }
-}
-
-var COMPONENT_REGEX = /%COMP%/g;
-export const COMPONENT_VARIABLE = '%COMP%';
-export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
-export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
-
-function _shimContentAttribute(componentShortId: string): string {
- return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentShortId);
-}
-
-function _shimHostAttribute(componentShortId: string): string {
- return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentShortId);
-}
diff --git a/modules/angular2/src/core/util/decorators.ts b/modules/angular2/src/core/util/decorators.ts
index 5944745240..54f50fa565 100644
--- a/modules/angular2/src/core/util/decorators.ts
+++ b/modules/angular2/src/core/util/decorators.ts
@@ -1,5 +1,7 @@
import {ConcreteType, global, Type, isFunction, stringify} from 'angular2/src/facade/lang';
+var _nextClassId = 0;
+
/**
* Declares the interface to be used with {@link Class}.
*/
@@ -228,6 +230,10 @@ export function Class(clsDef: ClassDefinition): ConcreteType {
Reflect.defineMetadata('annotations', this.annotations, constructor);
}
+ if (!constructor['name']) {
+ constructor['overriddenName'] = `class${_nextClassId++}`;
+ }
+
return constructor;
}
diff --git a/modules/angular2/src/facade/async.dart b/modules/angular2/src/facade/async.dart
index bc4f56d90a..5f8a196b5f 100644
--- a/modules/angular2/src/facade/async.dart
+++ b/modules/angular2/src/facade/async.dart
@@ -50,7 +50,7 @@ class ObservableWrapper {
static void callNext(EventEmitter emitter, value) {
emitter.add(value);
}
-
+
static void callEmit(EventEmitter emitter, value) {
emitter.add(value);
}
@@ -90,7 +90,7 @@ class EventEmitter extends Stream {
void add(value) {
_controller.add(value);
}
-
+
void emit(value) {
_controller.add(value);
}
diff --git a/modules/angular2/src/facade/lang.dart b/modules/angular2/src/facade/lang.dart
index c8067485f6..55f3f7ca28 100644
--- a/modules/angular2/src/facade/lang.dart
+++ b/modules/angular2/src/facade/lang.dart
@@ -331,3 +331,7 @@ class DateWrapper {
// needed to match the exports from lang.js
var global = null;
+
+dynamic evalExpression(String sourceUrl, String expr, String declarations, Map vars) {
+ throw "Dart does not support evaluating expression during runtime!";
+}
\ No newline at end of file
diff --git a/modules/angular2/src/facade/lang.ts b/modules/angular2/src/facade/lang.ts
index 3d741d1dc1..7c83209ec0 100644
--- a/modules/angular2/src/facade/lang.ts
+++ b/modules/angular2/src/facade/lang.ts
@@ -141,6 +141,9 @@ export function stringify(token): string {
if (token.name) {
return token.name;
}
+ if (token.overriddenName) {
+ return token.overriddenName;
+ }
var res = token.toString();
var newLineIndex = res.indexOf("\n");
@@ -412,3 +415,15 @@ export function getSymbolIterator(): string | symbol {
}
return _symbolIterator;
}
+
+export function evalExpression(sourceUrl: string, expr: string, declarations: string,
+ vars: {[key: string]: any}): any {
+ var fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`;
+ var fnArgNames = [];
+ var fnArgValues = [];
+ for (var argName in vars) {
+ fnArgNames.push(argName);
+ fnArgValues.push(vars[argName]);
+ }
+ return new Function(...fnArgNames.concat(fnBody))(...fnArgValues);
+}
diff --git a/modules/angular2/src/platform/browser_common.ts b/modules/angular2/src/platform/browser_common.ts
index d99a2d756a..8dd0b3dfe3 100644
--- a/modules/angular2/src/platform/browser_common.ts
+++ b/modules/angular2/src/platform/browser_common.ts
@@ -9,7 +9,7 @@ import {
platform,
ExceptionHandler,
Reflector,
- Renderer,
+ RootRenderer,
reflector,
APPLICATION_COMMON_PROVIDERS,
PLATFORM_COMMON_PROVIDERS
@@ -21,7 +21,7 @@ import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
import {KeyEventsPlugin} from 'angular2/src/platform/dom/events/key_events';
import {HammerGesturesPlugin} from 'angular2/src/platform/dom/events/hammer_gestures';
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
-import {DomRenderer, DomRenderer_} from 'angular2/src/platform/dom/dom_renderer';
+import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
import {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
import {SharedStylesHost} from "angular2/src/platform/dom/shared_styles_host";
import {BrowserDetails} from "angular2/src/animate/browser_details";
@@ -77,8 +77,8 @@ export const BROWSER_APP_COMMON_PROVIDERS: Array {
- abstract registerComponentTemplate(template: RenderComponentTemplate);
+export abstract class DomRootRenderer implements RootRenderer {
+ private _registeredComponents: Map = new Map();
- abstract resolveComponentTemplate(templateId: string): RenderComponentTemplate;
+ constructor(public document: any, public eventManager: EventManager,
+ public sharedStylesHost: DomSharedStylesHost, public animate: AnimationBuilder) {}
- abstract createProtoView(componentTemplateId: string,
- cmds: RenderTemplateCmd[]): RenderProtoViewRef;
-
- abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
- hostElementSelector: string): RenderViewWithFragments;
-
- abstract createView(protoViewRef: RenderProtoViewRef,
- fragmentCount: number): RenderViewWithFragments;
-
- abstract destroyView(viewRef: RenderViewRef);
-
- abstract createRootContentInsertionPoint();
-
- getNativeElementSync(location: RenderElementRef): any {
- return resolveInternalDomView(location.renderView).boundElements[location.boundElementIndex];
+ renderComponent(componentProto: RenderComponentType): Renderer {
+ var renderer = this._registeredComponents.get(componentProto.id);
+ if (isBlank(renderer)) {
+ renderer = new DomRenderer(this, componentProto);
+ this._registeredComponents.set(componentProto.id, renderer);
+ }
+ return renderer;
}
+}
- getRootNodes(fragment: RenderFragmentRef): Node[] { return resolveInternalDomFragment(fragment); }
+@Injectable()
+export class DomRootRenderer_ extends DomRootRenderer {
+ constructor(@Inject(DOCUMENT) _document: any, _eventManager: EventManager,
+ sharedStylesHost: DomSharedStylesHost, animate: AnimationBuilder) {
+ super(_document, _eventManager, sharedStylesHost, animate);
+ }
+}
- attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
- fragmentRef: RenderFragmentRef) {
- var previousFragmentNodes = resolveInternalDomFragment(previousFragmentRef);
- if (previousFragmentNodes.length > 0) {
- var sibling = previousFragmentNodes[previousFragmentNodes.length - 1];
- let nodes = resolveInternalDomFragment(fragmentRef);
- moveNodesAfterSibling(sibling, nodes);
- this.animateNodesEnter(nodes);
+export class DomRenderer implements Renderer {
+ private _contentAttr: string;
+ private _hostAttr: string;
+ private _styles: string[];
+
+ constructor(private _rootRenderer: DomRootRenderer, private componentProto: RenderComponentType) {
+ this._styles = _flattenStyles(componentProto.id, componentProto.styles, []);
+ if (componentProto.encapsulation !== ViewEncapsulation.Native) {
+ this._rootRenderer.sharedStylesHost.addStyles(this._styles);
+ }
+ if (this.componentProto.encapsulation === ViewEncapsulation.Emulated) {
+ this._contentAttr = _shimContentAttribute(componentProto.id);
+ this._hostAttr = _shimHostAttribute(componentProto.id);
+ } else {
+ this._contentAttr = null;
+ this._hostAttr = null;
}
}
- /**
- * Iterates through all nodes being added to the DOM and animates them if necessary
- * @param nodes
- */
- animateNodesEnter(nodes: Node[]) {
- for (let i = 0; i < nodes.length; i++) this.animateNodeEnter(nodes[i]);
+ renderComponent(componentProto: RenderComponentType): Renderer {
+ return this._rootRenderer.renderComponent(componentProto);
}
+ selectRootElement(selector: string): Element {
+ var el = DOM.querySelector(this._rootRenderer.document, selector);
+ if (isBlank(el)) {
+ throw new BaseException(`The selector "${selector}" did not match any elements`);
+ }
+ DOM.clearNodes(el);
+ return el;
+ }
+
+ createElement(parent: Element, name: string): Node {
+ var nsAndName = splitNamespace(name);
+ var el = isPresent(nsAndName[0]) ?
+ DOM.createElementNS(NAMESPACE_URIS[nsAndName[0]], nsAndName[1]) :
+ DOM.createElement(nsAndName[1]);
+ if (isPresent(this._contentAttr)) {
+ DOM.setAttribute(el, this._contentAttr, '');
+ }
+ if (isPresent(parent)) {
+ DOM.appendChild(parent, el);
+ }
+ return el;
+ }
+
+ createViewRoot(hostElement: any): any {
+ var nodesParent;
+ if (this.componentProto.encapsulation === ViewEncapsulation.Native) {
+ nodesParent = DOM.createShadowRoot(hostElement);
+ this._rootRenderer.sharedStylesHost.addHost(nodesParent);
+ for (var i = 0; i < this._styles.length; i++) {
+ DOM.appendChild(nodesParent, DOM.createStyleElement(this._styles[i]));
+ }
+ } else {
+ if (isPresent(this._hostAttr)) {
+ DOM.setAttribute(hostElement, this._hostAttr, '');
+ }
+ nodesParent = hostElement;
+ }
+ return nodesParent;
+ }
+
+ createTemplateAnchor(parentElement: any): any {
+ var comment = DOM.createComment(TEMPLATE_COMMENT_TEXT);
+ if (isPresent(parentElement)) {
+ DOM.appendChild(parentElement, comment);
+ }
+ return comment;
+ }
+
+ createText(parentElement: any, value: string): any {
+ var node = DOM.createTextNode(value);
+ if (isPresent(parentElement)) {
+ DOM.appendChild(parentElement, node);
+ }
+ return node;
+ }
+
+ projectNodes(parentElement: any, nodes: any[]) {
+ if (isBlank(parentElement)) return;
+ appendNodes(parentElement, nodes);
+ }
+
+ attachViewAfter(node: any, viewRootNodes: any[]) {
+ moveNodesAfterSibling(node, viewRootNodes);
+ for (let i = 0; i < viewRootNodes.length; i++) this.animateNodeEnter(viewRootNodes[i]);
+ }
+
+ detachView(viewRootNodes: any[]) {
+ for (var i = 0; i < viewRootNodes.length; i++) {
+ var node = viewRootNodes[i];
+ DOM.remove(node);
+ this.animateNodeLeave(node);
+ }
+ }
+
+ destroyView(hostElement: any, viewAllNodes: any[]) {
+ if (this.componentProto.encapsulation === ViewEncapsulation.Native && isPresent(hostElement)) {
+ this._rootRenderer.sharedStylesHost.removeHost(DOM.getShadowRoot(hostElement));
+ }
+ }
+
+ listen(renderElement: any, name: string, callback: Function) {
+ this._rootRenderer.eventManager.addEventListener(renderElement, name,
+ decoratePreventDefault(callback));
+ }
+
+ listenGlobal(target: string, name: string, callback: Function): Function {
+ return this._rootRenderer.eventManager.addGlobalEventListener(target, name,
+ decoratePreventDefault(callback));
+ }
+
+ setElementProperty(renderElement: any, propertyName: string, propertyValue: any): void {
+ DOM.setProperty(renderElement, propertyName, propertyValue);
+ }
+
+ setElementAttribute(renderElement: any, attributeName: string, attributeValue: string): void {
+ var attrNs;
+ var nsAndName = splitNamespace(attributeName);
+ if (isPresent(nsAndName[0])) {
+ attributeName = nsAndName[0] + ':' + nsAndName[1];
+ attrNs = NAMESPACE_URIS[nsAndName[0]];
+ }
+ if (isPresent(attributeValue)) {
+ if (isPresent(attrNs)) {
+ DOM.setAttributeNS(renderElement, attrNs, attributeName, attributeValue);
+ } else {
+ DOM.setAttribute(renderElement, nsAndName[1], attributeValue);
+ }
+ } else {
+ DOM.removeAttribute(renderElement, attributeName);
+ }
+ }
+
+ setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string): void {
+ var dashCasedPropertyName = camelCaseToDashCase(propertyName);
+ if (DOM.isCommentNode(renderElement)) {
+ var existingBindings = RegExpWrapper.firstMatch(
+ TEMPLATE_BINDINGS_EXP, StringWrapper.replaceAll(DOM.getText(renderElement), /\n/g, ''));
+ var parsedBindings = Json.parse(existingBindings[1]);
+ parsedBindings[dashCasedPropertyName] = propertyValue;
+ DOM.setText(renderElement, StringWrapper.replace(TEMPLATE_COMMENT_TEXT, '{}',
+ Json.stringify(parsedBindings)));
+ } else {
+ this.setElementAttribute(renderElement, propertyName, propertyValue);
+ }
+ }
+
+ setElementClass(renderElement: any, className: string, isAdd: boolean): void {
+ if (isAdd) {
+ DOM.addClass(renderElement, className);
+ } else {
+ DOM.removeClass(renderElement, className);
+ }
+ }
+
+ setElementStyle(renderElement: any, styleName: string, styleValue: string): void {
+ if (isPresent(styleValue)) {
+ DOM.setStyle(renderElement, styleName, stringify(styleValue));
+ } else {
+ DOM.removeStyle(renderElement, styleName);
+ }
+ }
+
+ invokeElementMethod(renderElement: any, methodName: string, args: any[]): void {
+ DOM.invoke(renderElement, methodName, args);
+ }
+
+ setText(renderNode: any, text: string): void { DOM.setText(renderNode, text); }
+
/**
* Performs animations if necessary
* @param node
*/
- abstract animateNodeEnter(node: Node);
-
- /**
- * If animations are necessary, performs animations then removes the element; otherwise, it just
- * removes the element.
- * @param node
- */
- abstract animateNodeLeave(node: Node);
-
- attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
- var parentView = resolveInternalDomView(elementRef.renderView);
- var element = parentView.boundElements[elementRef.boundElementIndex];
- var nodes = resolveInternalDomFragment(fragmentRef);
- moveNodesAfterSibling(element, nodes);
- this.animateNodesEnter(nodes);
- }
-
- abstract detachFragment(fragmentRef: RenderFragmentRef);
-
- hydrateView(viewRef: RenderViewRef) { resolveInternalDomView(viewRef).hydrate(); }
-
- dehydrateView(viewRef: RenderViewRef) { resolveInternalDomView(viewRef).dehydrate(); }
-
- createTemplateAnchor(attrNameAndValues: string[]): Node {
- return DOM.createComment(TEMPLATE_COMMENT_TEXT);
- }
- abstract createElement(name: string, attrNameAndValues: string[]): Node;
- abstract mergeElement(existing: Node, attrNameAndValues: string[]);
- abstract createShadowRoot(host: Node, templateId: string): Node;
- createText(value: string): Node { return DOM.createTextNode(isPresent(value) ? value : ''); }
- appendChild(parent: Node, child: Node) { DOM.appendChild(parent, child); }
- abstract on(element: Node, eventName: string, callback: Function);
- abstract globalOn(target: string, eventName: string, callback: Function): Function;
-
- setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any): void {
- var view = resolveInternalDomView(location.renderView);
- DOM.setProperty(view.boundElements[location.boundElementIndex], propertyName,
- propertyValue);
- }
-
- setElementAttribute(location: RenderElementRef, attributeName: string,
- attributeValue: string): void {
- var view = resolveInternalDomView(location.renderView);
- var element = view.boundElements[location.boundElementIndex];
- if (isPresent(attributeValue)) {
- DOM.setAttribute(element, attributeName, stringify(attributeValue));
- } else {
- DOM.removeAttribute(element, attributeName);
- }
- }
-
- /**
- * Used only in debug mode to serialize property changes to comment nodes,
- * such as placeholders.
- */
- setBindingDebugInfo(location: RenderElementRef, propertyName: string,
- propertyValue: string): void {
- var view: DefaultRenderView = resolveInternalDomView(location.renderView);
- var element = view.boundElements[location.boundElementIndex];
- var dashCasedPropertyName = camelCaseToDashCase(propertyName);
- if (DOM.isCommentNode(element)) {
- var existingBindings = RegExpWrapper.firstMatch(
- TEMPLATE_BINDINGS_EXP, StringWrapper.replaceAll(DOM.getText(element), /\n/g, ''));
- var parsedBindings = Json.parse(existingBindings[1]);
- parsedBindings[dashCasedPropertyName] = propertyValue;
- DOM.setText(element, StringWrapper.replace(TEMPLATE_COMMENT_TEXT, '{}',
- Json.stringify(parsedBindings)));
- } else {
- this.setElementAttribute(location, propertyName, propertyValue);
- }
- }
-
- setElementClass(location: RenderElementRef, className: string, isAdd: boolean): void {
- var view = resolveInternalDomView(location.renderView);
- var element = view.boundElements[location.boundElementIndex];
- if (isAdd) {
- DOM.addClass(element, className);
- } else {
- DOM.removeClass(element, className);
- }
- }
-
- setElementStyle(location: RenderElementRef, styleName: string, styleValue: string): void {
- var view = resolveInternalDomView(location.renderView);
- var element = view.boundElements[location.boundElementIndex];
- if (isPresent(styleValue)) {
- DOM.setStyle(element, styleName, stringify(styleValue));
- } else {
- DOM.removeStyle(element, styleName);
- }
- }
-
- invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]): void {
- var view = resolveInternalDomView(location.renderView);
- var element = view.boundElements[location.boundElementIndex];
- DOM.invoke(element, methodName, args);
- }
-
- setText(viewRef: RenderViewRef, textNodeIndex: number, text: string): void {
- var view = resolveInternalDomView(viewRef);
- DOM.setText(view.boundTextNodes[textNodeIndex], text);
- }
-
- setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher): void {
- resolveInternalDomView(viewRef).setEventDispatcher(dispatcher);
- }
-}
-
-@Injectable()
-export class DomRenderer_ extends DomRenderer {
- private _componentTpls: Map =
- new Map();
- private _document;
-
- constructor(private _eventManager: EventManager,
- private _domSharedStylesHost: DomSharedStylesHost, private _animate: AnimationBuilder,
- @Inject(DOCUMENT) document) {
- super();
- this._document = document;
- }
-
- registerComponentTemplate(template: RenderComponentTemplate) {
- this._componentTpls.set(template.id, template);
- if (template.encapsulation !== ViewEncapsulation.Native) {
- var encapsulatedStyles = encapsulateStyles(template);
- this._domSharedStylesHost.addStyles(encapsulatedStyles);
- }
- }
-
- createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
- return new DefaultProtoViewRef(this._componentTpls.get(componentTemplateId), cmds);
- }
-
- resolveComponentTemplate(templateId: string): RenderComponentTemplate {
- return this._componentTpls.get(templateId);
- }
-
- /** @internal */
- _createRootHostViewScope: WtfScopeFn = wtfCreateScope('DomRenderer#createRootHostView()');
- createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
- hostElementSelector: string): RenderViewWithFragments {
- var s = this._createRootHostViewScope();
- var element = DOM.querySelector(this._document, hostElementSelector);
- if (isBlank(element)) {
- wtfLeave(s);
- throw new BaseException(`The selector "${hostElementSelector}" did not match any elements`);
- }
- return wtfLeave(s, this._createView(hostProtoViewRef, element));
- }
-
- /** @internal */
- _createViewScope = wtfCreateScope('DomRenderer#createView()');
- createView(protoViewRef: RenderProtoViewRef, fragmentCount: number): RenderViewWithFragments {
- var s = this._createViewScope();
- return wtfLeave(s, this._createView(protoViewRef, null));
- }
-
- private _createView(protoViewRef: RenderProtoViewRef,
- inplaceElement: HTMLElement): RenderViewWithFragments {
- var dpvr = protoViewRef;
- var view = createRenderView(dpvr.template, dpvr.cmds, inplaceElement, this);
- var sdRoots = view.nativeShadowRoots;
- for (var i = 0; i < sdRoots.length; i++) {
- this._domSharedStylesHost.addHost(sdRoots[i]);
- }
- return new RenderViewWithFragments(view, view.fragments);
- }
-
- destroyView(viewRef: RenderViewRef) {
- var view = >viewRef;
- var sdRoots = view.nativeShadowRoots;
- for (var i = 0; i < sdRoots.length; i++) {
- this._domSharedStylesHost.removeHost(sdRoots[i]);
- }
- }
-
animateNodeEnter(node: Node) {
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
DOM.addClass(node, 'ng-enter');
- this._animate.css()
+ this._rootRenderer.animate.css()
.addAnimationClass('ng-enter-active')
.start(node)
.onComplete(() => { DOM.removeClass(node, 'ng-enter'); });
}
}
+
+ /**
+ * If animations are necessary, performs animations then removes the element; otherwise, it just
+ * removes the element.
+ * @param node
+ */
animateNodeLeave(node: Node) {
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
DOM.addClass(node, 'ng-leave');
- this._animate.css()
+ this._rootRenderer.animate.css()
.addAnimationClass('ng-leave-active')
.start(node)
.onComplete(() => {
@@ -295,73 +257,6 @@ export class DomRenderer_ extends DomRenderer {
DOM.remove(node);
}
}
-
- /** @internal */
- _detachFragmentScope = wtfCreateScope('DomRenderer#detachFragment()');
- detachFragment(fragmentRef: RenderFragmentRef) {
- var s = this._detachFragmentScope();
- var fragmentNodes = resolveInternalDomFragment(fragmentRef);
- for (var i = 0; i < fragmentNodes.length; i++) {
- this.animateNodeLeave(fragmentNodes[i]);
- }
- wtfLeave(s);
- }
- createElement(name: string, attrNameAndValues: string[]): Node {
- var nsAndName = splitNamespace(name);
- var el = isPresent(nsAndName[0]) ?
- DOM.createElementNS(NAMESPACE_URIS[nsAndName[0]], nsAndName[1]) :
- DOM.createElement(nsAndName[1]);
- this._setAttributes(el, attrNameAndValues);
- return el;
- }
- mergeElement(existing: Node, attrNameAndValues: string[]) {
- DOM.clearNodes(existing);
- this._setAttributes(existing, attrNameAndValues);
- }
- private _setAttributes(node: Node, attrNameAndValues: string[]) {
- for (var attrIdx = 0; attrIdx < attrNameAndValues.length; attrIdx += 2) {
- var attrNs;
- var attrName = attrNameAndValues[attrIdx];
- var nsAndName = splitNamespace(attrName);
- if (isPresent(nsAndName[0])) {
- attrName = nsAndName[0] + ':' + nsAndName[1];
- attrNs = NAMESPACE_URIS[nsAndName[0]];
- }
- var attrValue = attrNameAndValues[attrIdx + 1];
- if (isPresent(attrNs)) {
- DOM.setAttributeNS(node, attrNs, attrName, attrValue);
- } else {
- DOM.setAttribute(node, nsAndName[1], attrValue);
- }
- }
- }
- createRootContentInsertionPoint(): Node {
- return DOM.createComment('root-content-insertion-point');
- }
- createShadowRoot(host: Node, templateId: string): Node {
- var sr = DOM.createShadowRoot(host);
- var tpl = this._componentTpls.get(templateId);
- for (var i = 0; i < tpl.styles.length; i++) {
- DOM.appendChild(sr, DOM.createStyleElement(tpl.styles[i]));
- }
- return sr;
- }
- on(element: Node, eventName: string, callback: Function) {
- this._eventManager.addEventListener(element, eventName,
- decoratePreventDefault(callback));
- }
- globalOn(target: string, eventName: string, callback: Function): Function {
- return this._eventManager.addGlobalEventListener(target, eventName,
- decoratePreventDefault(callback));
- }
-}
-
-function resolveInternalDomView(viewRef: RenderViewRef): DefaultRenderView {
- return >viewRef;
-}
-
-function resolveInternalDomFragment(fragmentRef: RenderFragmentRef): Node[] {
- return (>fragmentRef).nodes;
}
function moveNodesAfterSibling(sibling, nodes) {
@@ -380,16 +275,48 @@ function moveNodesAfterSibling(sibling, nodes) {
}
}
+function appendNodes(parent, nodes) {
+ for (var i = 0; i < nodes.length; i++) {
+ DOM.appendChild(parent, nodes[i]);
+ }
+}
+
function decoratePreventDefault(eventHandler: Function): Function {
return (event) => {
var allowDefaultBehavior = eventHandler(event);
- if (!allowDefaultBehavior) {
+ if (allowDefaultBehavior === false) {
// TODO(tbosch): move preventDefault into event plugins...
DOM.preventDefault(event);
}
};
}
+var COMPONENT_REGEX = /%COMP%/g;
+export const COMPONENT_VARIABLE = '%COMP%';
+export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
+export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
+
+function _shimContentAttribute(componentShortId: string): string {
+ return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentShortId);
+}
+
+function _shimHostAttribute(componentShortId: string): string {
+ return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentShortId);
+}
+
+function _flattenStyles(compId: string, styles: Array, target: string[]): string[] {
+ for (var i = 0; i < styles.length; i++) {
+ var style = styles[i];
+ if (isArray(style)) {
+ _flattenStyles(compId, style, target);
+ } else {
+ style = StringWrapper.replaceAll(style, COMPONENT_REGEX, compId);
+ target.push(style);
+ }
+ }
+ return target;
+}
+
var NS_PREFIX_RE = /^@([^:]+):(.+)/g;
function splitNamespace(name: string): string[] {
@@ -398,4 +325,4 @@ function splitNamespace(name: string): string[] {
}
let match = RegExpWrapper.firstMatch(NS_PREFIX_RE, name);
return [match[1], match[2]];
-}
+}
\ No newline at end of file
diff --git a/modules/angular2/src/platform/worker_app_common.ts b/modules/angular2/src/platform/worker_app_common.ts
index 209b48aff9..9bcdd28b54 100644
--- a/modules/angular2/src/platform/worker_app_common.ts
+++ b/modules/angular2/src/platform/worker_app_common.ts
@@ -1,8 +1,8 @@
import {XHR} from 'angular2/src/compiler/xhr';
import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl';
-import {WebWorkerRenderer} from 'angular2/src/web_workers/worker/renderer';
+import {WebWorkerRootRenderer} from 'angular2/src/web_workers/worker/renderer';
import {print, Type, CONST_EXPR, isPresent} from 'angular2/src/facade/lang';
-import {Renderer} from 'angular2/src/core/render/api';
+import {RootRenderer} from 'angular2/src/core/render/api';
import {
PLATFORM_DIRECTIVES,
PLATFORM_PIPES,
@@ -23,11 +23,7 @@ import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler';
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
import {Provider} from 'angular2/src/core/di';
-import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
-import {
- RenderViewWithFragmentsStore
-} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
-import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
+import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
class PrintLogger {
log = print;
@@ -48,15 +44,13 @@ export const WORKER_APP_APPLICATION_COMMON: Array =
new Provider(EVENT_MANAGER_PLUGINS, {useClass: DomEventsPlugin, multi: true}),
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
- new Provider(DomRenderer, {useClass: DomRenderer_}),
- new Provider(Renderer, {useExisting: DomRenderer}),
+ new Provider(DomRootRenderer, {useClass: DomRootRenderer_}),
+ new Provider(RootRenderer, {useExisting: DomRootRenderer}),
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
new Provider(XHR, {useClass: XHRImpl}),
MessageBasedXHRImpl,
@@ -81,8 +78,7 @@ export const WORKER_RENDER_APP_COMMON: Array =
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
Serializer,
new Provider(ON_WEB_WORKER, {useValue: false}),
- RenderViewWithFragmentsStore,
- RenderProtoViewRefStore,
+ RenderStore,
DomSharedStylesHost,
Testability,
BrowserDetails,
diff --git a/modules/angular2/src/testing/test_component_builder.ts b/modules/angular2/src/testing/test_component_builder.ts
index 3a40e2db5d..a09645cbdc 100644
--- a/modules/angular2/src/testing/test_component_builder.ts
+++ b/modules/angular2/src/testing/test_component_builder.ts
@@ -6,7 +6,7 @@ import {
Injector,
Injectable,
ViewMetadata,
- ViewRef,
+ EmbeddedViewRef,
ViewResolver,
provide
} from 'angular2/core';
@@ -15,8 +15,8 @@ import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/async';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
+import {ViewRef_} from 'angular2/src/core/linker/view_ref';
import {AppView} from 'angular2/src/core/linker/view';
-import {internalView} from 'angular2/src/core/linker/view_ref';
import {el} from './utils';
@@ -65,10 +65,10 @@ export class ComponentFixture_ extends ComponentFixture {
constructor(componentRef: ComponentRef) {
super();
- this.debugElement = new DebugElement_(internalView(componentRef.hostView), 0);
+ this._componentParentView = (componentRef.hostView).internalView;
+ this.debugElement = new DebugElement_(this._componentParentView.appElements[0]);
this.componentInstance = this.debugElement.componentInstance;
this.nativeElement = this.debugElement.nativeElement;
- this._componentParentView = internalView(componentRef.hostView);
this._componentRef = componentRef;
}
diff --git a/modules/angular2/src/testing/test_injector.ts b/modules/angular2/src/testing/test_injector.ts
index 05fddf45f8..6176caba99 100644
--- a/modules/angular2/src/testing/test_injector.ts
+++ b/modules/angular2/src/testing/test_injector.ts
@@ -14,7 +14,7 @@ import {
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
-import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
+import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
import {
IterableDiffers,
@@ -46,11 +46,10 @@ import {
import {ListWrapper} from 'angular2/src/facade/collection';
import {FunctionWrapper, Type} from 'angular2/src/facade/lang';
-import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/linker/view_pool';
-import {AppViewManagerUtils} from 'angular2/src/core/linker/view_manager_utils';
+import {RootRenderer} from 'angular2/src/core/render/api';
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
-import {DomRenderer} from 'angular2/src/platform/dom/dom_renderer';
+import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
import {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
import {SharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
@@ -58,7 +57,6 @@ import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
import {Serializer} from "angular2/src/web_workers/shared/serializer";
import {Log} from './utils';
import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler';
-import {DomRenderer_} from "angular2/src/platform/dom/dom_renderer";
import {DynamicComponentLoader_} from "angular2/src/core/linker/dynamic_component_loader";
import {AppViewManager_} from "angular2/src/core/linker/view_manager";
@@ -92,20 +90,17 @@ function _getAppBindings() {
return [
APPLICATION_COMMON_PROVIDERS,
- provide(ChangeDetectorGenConfig, {useValue: new ChangeDetectorGenConfig(true, false, true)}),
+ provide(ChangeDetectorGenConfig, {useValue: new ChangeDetectorGenConfig(true, false, false)}),
provide(DOCUMENT, {useValue: appDoc}),
- provide(DomRenderer, {useClass: DomRenderer_}),
- provide(Renderer, {useExisting: DomRenderer}),
+ provide(DomRootRenderer, {useClass: DomRootRenderer_}),
+ provide(RootRenderer, {useExisting: DomRootRenderer}),
provide(APP_ID, {useValue: 'a'}),
DomSharedStylesHost,
provide(SharedStylesHost, {useExisting: DomSharedStylesHost}),
- AppViewPool,
provide(AppViewManager, {useClass: AppViewManager_}),
- AppViewManagerUtils,
Serializer,
ELEMENT_PROBE_PROVIDERS,
- provide(APP_VIEW_POOL_CAPACITY, {useValue: 500}),
- ProtoViewFactory,
+ ResolvedMetadataCache,
provide(DirectiveResolver, {useClass: MockDirectiveResolver}),
provide(ViewResolver, {useClass: MockViewResolver}),
provide(IterableDiffers, {useValue: defaultIterableDiffers}),
diff --git a/modules/angular2/src/upgrade/constants.ts b/modules/angular2/src/upgrade/constants.ts
index b3046c3376..5934bea4e5 100644
--- a/modules/angular2/src/upgrade/constants.ts
+++ b/modules/angular2/src/upgrade/constants.ts
@@ -1,7 +1,7 @@
export const NG2_APP_VIEW_MANAGER = 'ng2.AppViewManager';
export const NG2_COMPILER = 'ng2.Compiler';
export const NG2_INJECTOR = 'ng2.Injector';
-export const NG2_PROTO_VIEW_REF_MAP = 'ng2.ProtoViewRefMap';
+export const NG2_HOST_VIEW_FACTORY_REF_MAP = 'ng2.HostViewFactoryRefMap';
export const NG2_ZONE = 'ng2.NgZone';
export const NG1_CONTROLLER = '$controller';
diff --git a/modules/angular2/src/upgrade/downgrade_ng2_adapter.ts b/modules/angular2/src/upgrade/downgrade_ng2_adapter.ts
index b1172a2e08..a4984c48b2 100644
--- a/modules/angular2/src/upgrade/downgrade_ng2_adapter.ts
+++ b/modules/angular2/src/upgrade/downgrade_ng2_adapter.ts
@@ -5,7 +5,7 @@ import {
HostViewRef,
Injector,
OnChanges,
- ProtoViewRef,
+ HostViewFactoryRef,
SimpleChange
} from 'angular2/core';
import {NG1_SCOPE} from './constants';
@@ -25,13 +25,13 @@ export class DowngradeNg2ComponentAdapter {
changeDetector: ChangeDetectorRef = null;
componentScope: angular.IScope;
childNodes: Node[];
- contentInserctionPoint: Node = null;
+ contentInsertionPoint: Node = null;
constructor(private id: string, private info: ComponentInfo,
private element: angular.IAugmentedJQuery, private attrs: angular.IAttributes,
private scope: angular.IScope, private parentInjector: Injector,
private parse: angular.IParseService, private viewManager: AppViewManager,
- private protoView: ProtoViewRef) {
+ private hostViewFactory: HostViewFactoryRef) {
(this.element[0]).id = id;
this.componentScope = scope.$new();
this.childNodes = element.contents();
@@ -40,13 +40,13 @@ export class DowngradeNg2ComponentAdapter {
bootstrapNg2() {
var childInjector = this.parentInjector.resolveAndCreateChild(
[provide(NG1_SCOPE, {useValue: this.componentScope})]);
- this.hostViewRef =
- this.viewManager.createRootHostView(this.protoView, '#' + this.id, childInjector);
- var renderer: any = (this.hostViewRef).render;
+ this.contentInsertionPoint = document.createComment('ng1 insertion point');
+
+ this.hostViewRef = this.viewManager.createRootHostView(
+ this.hostViewFactory, '#' + this.id, childInjector, [[this.contentInsertionPoint]]);
var hostElement = this.viewManager.getHostElement(this.hostViewRef);
this.changeDetector = this.hostViewRef.changeDetectorRef;
this.component = this.viewManager.getComponent(hostElement);
- this.contentInserctionPoint = renderer.rootContentInsertionPoints[0];
}
setupInputs(): void {
@@ -105,10 +105,10 @@ export class DowngradeNg2ComponentAdapter {
projectContent() {
var childNodes = this.childNodes;
- if (this.contentInserctionPoint) {
- var parent = this.contentInserctionPoint.parentNode;
+ var parent = this.contentInsertionPoint.parentNode;
+ if (parent) {
for (var i = 0, ii = childNodes.length; i < ii; i++) {
- parent.insertBefore(childNodes[i], this.contentInserctionPoint);
+ parent.insertBefore(childNodes[i], this.contentInsertionPoint);
}
}
}
diff --git a/modules/angular2/src/upgrade/upgrade_adapter.ts b/modules/angular2/src/upgrade/upgrade_adapter.ts
index baccd8dc3e..a282d7aeef 100644
--- a/modules/angular2/src/upgrade/upgrade_adapter.ts
+++ b/modules/angular2/src/upgrade/upgrade_adapter.ts
@@ -7,7 +7,7 @@ import {
Injector,
NgZone,
PlatformRef,
- ProtoViewRef,
+ HostViewFactoryRef,
Provider,
Type,
APPLICATION_COMMON_PROVIDERS
@@ -26,7 +26,7 @@ import {
NG2_APP_VIEW_MANAGER,
NG2_COMPILER,
NG2_INJECTOR,
- NG2_PROTO_VIEW_REF_MAP,
+ NG2_HOST_VIEW_FACTORY_REF_MAP,
NG2_ZONE,
REQUIRE_INJECTOR
} from './constants';
@@ -307,13 +307,13 @@ export class UpgradeAdapter {
var original$applyFn: Function;
var rootScopePrototype: any;
var rootScope: angular.IRootScopeService;
- var protoViewRefMap: ProtoViewRefMap = {};
+ var hostViewFactoryRefMap: HostViewFactoryRefMap = {};
var ng1Module = angular.module(this.idPrefix, modules);
var ng1compilePromise: Promise = null;
ng1Module.value(NG2_INJECTOR, injector)
.value(NG2_ZONE, ngZone)
.value(NG2_COMPILER, compiler)
- .value(NG2_PROTO_VIEW_REF_MAP, protoViewRefMap)
+ .value(NG2_HOST_VIEW_FACTORY_REF_MAP, hostViewFactoryRefMap)
.value(NG2_APP_VIEW_MANAGER, injector.get(AppViewManager))
.config([
'$provide',
@@ -347,7 +347,7 @@ export class UpgradeAdapter {
angular.element(element).data(controllerKey(NG2_INJECTOR), injector);
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
- Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise])
+ Promise.all([this.compileNg2Components(compiler, hostViewFactoryRefMap), ng1compilePromise])
.then(() => {
ngZone.run(() => {
if (rootScopePrototype) {
@@ -470,33 +470,35 @@ export class UpgradeAdapter {
}
/* @internal */
- private compileNg2Components(compiler: Compiler,
- protoViewRefMap: ProtoViewRefMap): Promise {
- var promises: Array> = [];
+ private compileNg2Components(compiler: Compiler, hostViewFactoryRefMap: HostViewFactoryRefMap):
+ Promise {
+ var promises: Array> = [];
var types = this.upgradedComponents;
for (var i = 0; i < types.length; i++) {
promises.push(compiler.compileInHost(types[i]));
}
- return Promise.all(promises).then((protoViews: Array) => {
+ return Promise.all(promises).then((hostViewFactories: Array) => {
var types = this.upgradedComponents;
- for (var i = 0; i < protoViews.length; i++) {
- protoViewRefMap[getComponentInfo(types[i]).selector] = protoViews[i];
+ for (var i = 0; i < hostViewFactories.length; i++) {
+ hostViewFactoryRefMap[getComponentInfo(types[i]).selector] = hostViewFactories[i];
}
- return protoViewRefMap;
+ return hostViewFactoryRefMap;
}, onError);
}
}
-interface ProtoViewRefMap {
- [selector: string]: ProtoViewRef;
+interface HostViewFactoryRefMap {
+ [selector: string]: HostViewFactoryRef;
}
function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function {
- (directiveFactory).$inject = [NG2_PROTO_VIEW_REF_MAP, NG2_APP_VIEW_MANAGER, NG1_PARSE];
- function directiveFactory(protoViewRefMap: ProtoViewRefMap, viewManager: AppViewManager,
+ (directiveFactory).$inject =
+ [NG2_HOST_VIEW_FACTORY_REF_MAP, NG2_APP_VIEW_MANAGER, NG1_PARSE];
+ function directiveFactory(hostViewFactoryRefMap: HostViewFactoryRefMap,
+ viewManager: AppViewManager,
parse: angular.IParseService): angular.IDirective {
- var protoView: ProtoViewRef = protoViewRefMap[info.selector];
- if (!protoView) throw new Error('Expecting ProtoViewRef for: ' + info.selector);
+ var hostViewFactory: HostViewFactoryRef = hostViewFactoryRefMap[info.selector];
+ if (!hostViewFactory) throw new Error('Expecting HostViewFactoryRef for: ' + info.selector);
var idCount = 0;
return {
restrict: 'E',
@@ -507,7 +509,7 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
var domElement = element[0];
var facade = new DowngradeNg2ComponentAdapter(idPrefix + (idCount++), info, element,
attrs, scope, parentInjector,
- parse, viewManager, protoView);
+ parse, viewManager, hostViewFactory);
facade.setupInputs();
facade.bootstrapNg2();
facade.projectContent();
diff --git a/modules/angular2/src/web_workers/shared/api.ts b/modules/angular2/src/web_workers/shared/api.ts
index 268668d461..34a757459c 100644
--- a/modules/angular2/src/web_workers/shared/api.ts
+++ b/modules/angular2/src/web_workers/shared/api.ts
@@ -1,75 +1,4 @@
import {CONST_EXPR} from "angular2/src/facade/lang";
import {OpaqueToken} from "angular2/src/core/di";
-import {
- RenderElementRef,
- RenderViewRef,
- RenderTemplateCmd,
- RenderTextCmd,
- RenderNgContentCmd,
- RenderBeginElementCmd,
- RenderBeginComponentCmd,
- RenderEmbeddedTemplateCmd,
- RenderCommandVisitor
-} from "angular2/src/core/render/api";
export const ON_WEB_WORKER = CONST_EXPR(new OpaqueToken('WebWorker.onWebWorker'));
-
-export class WebWorkerElementRef implements RenderElementRef {
- constructor(public renderView: RenderViewRef, public boundElementIndex: number) {}
-}
-
-export class WebWorkerTemplateCmd implements RenderTemplateCmd {
- visit(visitor: RenderCommandVisitor, context: any): any { return null; }
-}
-
-export class WebWorkerTextCmd implements RenderTextCmd {
- constructor(public isBound: boolean, public ngContentIndex: number, public value: string) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitText(this, context);
- }
-}
-
-export class WebWorkerNgContentCmd implements RenderNgContentCmd {
- constructor(public index: number, public ngContentIndex: number) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitNgContent(this, context);
- }
-}
-
-export class WebWorkerBeginElementCmd implements RenderBeginElementCmd {
- constructor(public isBound: boolean, public ngContentIndex: number, public name: string,
- public attrNameAndValues: string[], public eventTargetAndNames: string[]) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitBeginElement(this, context);
- }
-}
-
-export class WebWorkerEndElementCmd implements RenderTemplateCmd {
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitEndElement(context);
- }
-}
-
-export class WebWorkerBeginComponentCmd implements RenderBeginComponentCmd {
- constructor(public isBound: boolean, public ngContentIndex: number, public name: string,
- public attrNameAndValues: string[], public eventTargetAndNames: string[],
- public templateId: string) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitBeginComponent(this, context);
- }
-}
-
-export class WebWorkerEndComponentCmd implements RenderTemplateCmd {
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitEndComponent(context);
- }
-}
-
-export class WebWorkerEmbeddedTemplateCmd implements RenderEmbeddedTemplateCmd {
- constructor(public isBound: boolean, public ngContentIndex: number, public name: string,
- public attrNameAndValues: string[], public eventTargetAndNames: string[],
- public isMerged: boolean, public children: RenderTemplateCmd[]) {}
- visit(visitor: RenderCommandVisitor, context: any): any {
- return visitor.visitEmbeddedTemplate(this, context);
- }
-}
diff --git a/modules/angular2/src/web_workers/shared/render_proto_view_ref_store.ts b/modules/angular2/src/web_workers/shared/render_proto_view_ref_store.ts
deleted file mode 100644
index 5a278e3e9f..0000000000
--- a/modules/angular2/src/web_workers/shared/render_proto_view_ref_store.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import {Injectable, Inject} from "angular2/src/core/di";
-import {RenderProtoViewRef} from "angular2/src/core/render/api";
-import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
-
-@Injectable()
-export class RenderProtoViewRefStore {
- private _lookupByIndex: Map = new Map();
- private _lookupByProtoView: Map =
- new Map();
- private _nextIndex: number = 0;
- private _onWebworker: boolean;
-
- constructor(@Inject(ON_WEB_WORKER) onWebworker) { this._onWebworker = onWebworker; }
-
- allocate(): RenderProtoViewRef {
- var index = this._nextIndex++;
- var result = new WebWorkerRenderProtoViewRef(index);
- this.store(result, index);
- return result;
- }
-
- store(ref: RenderProtoViewRef, index: number): void {
- this._lookupByProtoView.set(ref, index);
- this._lookupByIndex.set(index, ref);
- }
-
- deserialize(index: number): RenderProtoViewRef {
- if (index == null) {
- return null;
- }
- return this._lookupByIndex.get(index);
- }
-
- serialize(ref: RenderProtoViewRef): number {
- if (ref == null) {
- return null;
- }
- if (this._onWebworker) {
- return (ref).refNumber;
- } else {
- return this._lookupByProtoView.get(ref);
- }
- }
-}
-
-export class WebWorkerRenderProtoViewRef extends RenderProtoViewRef {
- constructor(public refNumber: number) { super(); }
-}
diff --git a/modules/angular2/src/web_workers/shared/render_store.ts b/modules/angular2/src/web_workers/shared/render_store.ts
new file mode 100644
index 0000000000..3b96c2ef26
--- /dev/null
+++ b/modules/angular2/src/web_workers/shared/render_store.ts
@@ -0,0 +1,45 @@
+import {Injectable} from "angular2/src/core/di";
+
+@Injectable()
+export class RenderStore {
+ private _nextIndex: number = 0;
+ private _lookupById: Map;
+ private _lookupByObject: Map;
+
+ constructor() {
+ this._lookupById = new Map();
+ this._lookupByObject = new Map();
+ }
+
+ allocateId(): number { return this._nextIndex++; }
+
+ store(obj: any, id: number): void {
+ this._lookupById.set(id, obj);
+ this._lookupByObject.set(obj, id);
+ }
+
+ remove(obj: any): void {
+ var index = this._lookupByObject.get(obj);
+ this._lookupByObject.delete(obj);
+ this._lookupById.delete(index);
+ }
+
+ deserialize(id: number): any {
+ if (id == null) {
+ return null;
+ }
+
+ if (!this._lookupById.has(id)) {
+ return null;
+ }
+
+ return this._lookupById.get(id);
+ }
+
+ serialize(obj: any): number {
+ if (obj == null) {
+ return null;
+ }
+ return this._lookupByObject.get(obj);
+ }
+}
diff --git a/modules/angular2/src/web_workers/shared/render_view_with_fragments_store.ts b/modules/angular2/src/web_workers/shared/render_view_with_fragments_store.ts
deleted file mode 100644
index 99efb73925..0000000000
--- a/modules/angular2/src/web_workers/shared/render_view_with_fragments_store.ts
+++ /dev/null
@@ -1,162 +0,0 @@
-import {Injectable, Inject} from "angular2/src/core/di";
-import {
- RenderViewRef,
- RenderFragmentRef,
- RenderViewWithFragments
-} from "angular2/src/core/render/api";
-import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
-import {MapWrapper, ListWrapper} from "angular2/src/facade/collection";
-
-@Injectable()
-export class RenderViewWithFragmentsStore {
- private _nextIndex: number = 0;
- private _onWebWorker: boolean;
- private _lookupByIndex: Map;
- private _lookupByView: Map;
- private _viewFragments: Map;
-
- constructor(@Inject(ON_WEB_WORKER) onWebWorker) {
- this._onWebWorker = onWebWorker;
- this._lookupByIndex = new Map();
- this._lookupByView = new Map();
- this._viewFragments = new Map();
- }
-
- allocate(fragmentCount: number): RenderViewWithFragments {
- var initialIndex = this._nextIndex;
-
- var viewRef = new WebWorkerRenderViewRef(this._nextIndex++);
- var fragmentRefs = ListWrapper.createGrowableSize(fragmentCount);
-
- for (var i = 0; i < fragmentCount; i++) {
- fragmentRefs[i] = new WebWorkerRenderFragmentRef(this._nextIndex++);
- }
- var renderViewWithFragments = new RenderViewWithFragments(viewRef, fragmentRefs);
- this.store(renderViewWithFragments, initialIndex);
- return renderViewWithFragments;
- }
-
- store(view: RenderViewWithFragments, startIndex: number): void {
- this._lookupByIndex.set(startIndex, view.viewRef);
- this._lookupByView.set(view.viewRef, startIndex);
- startIndex++;
-
- view.fragmentRefs.forEach(ref => {
- this._lookupByIndex.set(startIndex, ref);
- this._lookupByView.set(ref, startIndex);
- startIndex++;
- });
-
- this._viewFragments.set(view.viewRef, view.fragmentRefs);
- }
-
- remove(view: RenderViewRef): void {
- this._removeRef(view);
- var fragments = this._viewFragments.get(view);
- fragments.forEach((fragment) => { this._removeRef(fragment); });
- this._viewFragments.delete(view);
- }
-
- private _removeRef(ref: RenderViewRef | RenderFragmentRef) {
- var index = this._lookupByView.get(ref);
- this._lookupByView.delete(ref);
- this._lookupByIndex.delete(index);
- }
-
- serializeRenderViewRef(viewRef: RenderViewRef): number {
- return this._serializeRenderFragmentOrViewRef(viewRef);
- }
-
- serializeRenderFragmentRef(fragmentRef: RenderFragmentRef): number {
- return this._serializeRenderFragmentOrViewRef(fragmentRef);
- }
-
- deserializeRenderViewRef(ref: number): RenderViewRef {
- if (ref == null) {
- return null;
- }
-
- return this._retrieve(ref);
- }
-
- deserializeRenderFragmentRef(ref: number): RenderFragmentRef {
- if (ref == null) {
- return null;
- }
-
- return this._retrieve(ref);
- }
-
- private _retrieve(ref: number): RenderViewRef | RenderFragmentRef {
- if (ref == null) {
- return null;
- }
-
- if (!this._lookupByIndex.has(ref)) {
- return null;
- }
-
- return this._lookupByIndex.get(ref);
- }
-
-
- private _serializeRenderFragmentOrViewRef(ref: RenderViewRef | RenderFragmentRef): number {
- if (ref == null) {
- return null;
- }
-
- if (this._onWebWorker) {
- return (ref).serialize();
- } else {
- return this._lookupByView.get(ref);
- }
- }
-
- serializeViewWithFragments(view: RenderViewWithFragments): {[key: string]: any} {
- if (view == null) {
- return null;
- }
-
- if (this._onWebWorker) {
- return {
- 'viewRef': (view.viewRef).serialize(),
- 'fragmentRefs': view.fragmentRefs.map(val => (val).serialize())
- };
- } else {
- return {
- 'viewRef': this._lookupByView.get(view.viewRef),
- 'fragmentRefs': view.fragmentRefs.map(val => this._lookupByView.get(val))
- };
- }
- }
-
- deserializeViewWithFragments(obj: {[key: string]: any}): RenderViewWithFragments {
- if (obj == null) {
- return null;
- }
-
- var viewRef = this.deserializeRenderViewRef(obj['viewRef']);
- var fragments = (obj['fragmentRefs']).map(val => this.deserializeRenderFragmentRef(val));
-
- return new RenderViewWithFragments(viewRef, fragments);
- }
-}
-
-export class WebWorkerRenderViewRef extends RenderViewRef {
- constructor(public refNumber: number) { super(); }
- serialize(): number { return this.refNumber; }
-
- static deserialize(ref: number): WebWorkerRenderViewRef {
- return new WebWorkerRenderViewRef(ref);
- }
-}
-
-export class WebWorkerRenderFragmentRef extends RenderFragmentRef {
- constructor(public refNumber: number) { super(); }
-
- serialize(): number { return this.refNumber; }
-
- static deserialize(ref: number): WebWorkerRenderFragmentRef {
- return new WebWorkerRenderFragmentRef(ref);
- }
-}
diff --git a/modules/angular2/src/web_workers/shared/serializer.ts b/modules/angular2/src/web_workers/shared/serializer.ts
index 98b6b2aa6f..7de8f39f9d 100644
--- a/modules/angular2/src/web_workers/shared/serializer.ts
+++ b/modules/angular2/src/web_workers/shared/serializer.ts
@@ -2,36 +2,9 @@ import {Type, isArray, isPresent, serializeEnum, deserializeEnum} from "angular2
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
-import {
- RenderProtoViewRef,
- RenderViewRef,
- RenderFragmentRef,
- RenderElementRef,
- RenderTemplateCmd,
- RenderCommandVisitor,
- RenderTextCmd,
- RenderNgContentCmd,
- RenderBeginElementCmd,
- RenderBeginComponentCmd,
- RenderEmbeddedTemplateCmd,
- RenderComponentTemplate
-} from "angular2/src/core/render/api";
-import {
- WebWorkerElementRef,
- WebWorkerTemplateCmd,
- WebWorkerTextCmd,
- WebWorkerNgContentCmd,
- WebWorkerBeginElementCmd,
- WebWorkerEndElementCmd,
- WebWorkerBeginComponentCmd,
- WebWorkerEndComponentCmd,
- WebWorkerEmbeddedTemplateCmd
-} from 'angular2/src/web_workers/shared/api';
+import {RenderComponentType} from "angular2/src/core/render/api";
import {Injectable} from "angular2/src/core/di";
-import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
-import {
- RenderViewWithFragmentsStore
-} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
+import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
// PRIMITIVE is any type that does not need to be serialized (string, number, boolean)
@@ -40,8 +13,7 @@ export const PRIMITIVE: Type = String;
@Injectable()
export class Serializer {
- constructor(private _protoViewStore: RenderProtoViewRefStore,
- private _renderViewStore: RenderViewWithFragmentsStore) {}
+ constructor(private _renderStore: RenderStore) {}
serialize(obj: any, type: any): Object {
if (!isPresent(obj)) {
@@ -53,18 +25,10 @@ export class Serializer {
if (type == PRIMITIVE) {
return obj;
}
- if (type == RenderProtoViewRef) {
- return this._protoViewStore.serialize(obj);
- } else if (type == RenderViewRef) {
- return this._renderViewStore.serializeRenderViewRef(obj);
- } else if (type == RenderFragmentRef) {
- return this._renderViewStore.serializeRenderFragmentRef(obj);
- } else if (type == WebWorkerElementRef) {
- return this._serializeWorkerElementRef(obj);
- } else if (type == WebWorkerTemplateCmd) {
- return serializeTemplateCmd(obj);
- } else if (type === RenderComponentTemplate) {
- return this._serializeRenderTemplate(obj);
+ if (type == RenderStoreObject) {
+ return this._renderStore.serialize(obj);
+ } else if (type === RenderComponentType) {
+ return this._serializeRenderComponentType(obj);
} else if (type === ViewEncapsulation) {
return serializeEnum(obj);
} else {
@@ -85,18 +49,10 @@ export class Serializer {
return map;
}
- if (type == RenderProtoViewRef) {
- return this._protoViewStore.deserialize(map);
- } else if (type == RenderViewRef) {
- return this._renderViewStore.deserializeRenderViewRef(map);
- } else if (type == RenderFragmentRef) {
- return this._renderViewStore.deserializeRenderFragmentRef(map);
- } else if (type == WebWorkerElementRef) {
- return this._deserializeWorkerElementRef(map);
- } else if (type == WebWorkerTemplateCmd) {
- return deserializeTemplateCmd(map);
- } else if (type === RenderComponentTemplate) {
- return this._deserializeRenderTemplate(map);
+ if (type == RenderStoreObject) {
+ return this._renderStore.deserialize(map);
+ } else if (type === RenderComponentType) {
+ return this._deserializeRenderComponentType(map);
} else if (type === ViewEncapsulation) {
return VIEW_ENCAPSULATION_VALUES[map];
} else {
@@ -134,114 +90,20 @@ export class Serializer {
}
}
- allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); }
-
- private _serializeWorkerElementRef(elementRef: RenderElementRef): {[key: string]: any} {
- return {
- 'renderView': this.serialize(elementRef.renderView, RenderViewRef),
- 'boundElementIndex': elementRef.boundElementIndex
- };
- }
-
- private _deserializeWorkerElementRef(map: {[key: string]: any}): RenderElementRef {
- return new WebWorkerElementRef(this.deserialize(map['renderView'], RenderViewRef),
- map['boundElementIndex']);
- }
-
-
- private _serializeRenderTemplate(obj: RenderComponentTemplate): Object {
+ private _serializeRenderComponentType(obj: RenderComponentType): Object {
return {
'id': obj.id,
- 'shortId': obj.shortId,
'encapsulation': this.serialize(obj.encapsulation, ViewEncapsulation),
- 'commands': this.serialize(obj.commands, WebWorkerTemplateCmd),
'styles': this.serialize(obj.styles, PRIMITIVE)
};
}
- private _deserializeRenderTemplate(map: {[key: string]: any}): RenderComponentTemplate {
- return new RenderComponentTemplate(map['id'], map['shortId'],
- this.deserialize(map['encapsulation'], ViewEncapsulation),
- this.deserialize(map['commands'], WebWorkerTemplateCmd),
- this.deserialize(map['styles'], PRIMITIVE));
+ private _deserializeRenderComponentType(map: {[key: string]: any}): RenderComponentType {
+ return new RenderComponentType(map['id'],
+ this.deserialize(map['encapsulation'], ViewEncapsulation),
+ this.deserialize(map['styles'], PRIMITIVE));
}
}
-function serializeTemplateCmd(cmd: RenderTemplateCmd): Object {
- return cmd.visit(RENDER_TEMPLATE_CMD_SERIALIZER, null);
-}
-
-function deserializeTemplateCmd(data: {[key: string]: any}): RenderTemplateCmd {
- return RENDER_TEMPLATE_CMD_DESERIALIZERS[data['deserializerIndex']](data);
-}
-
-class RenderTemplateCmdSerializer implements RenderCommandVisitor {
- visitText(cmd: RenderTextCmd, context: any): any {
- return {
- 'deserializerIndex': 0,
- 'isBound': cmd.isBound,
- 'ngContentIndex': cmd.ngContentIndex,
- 'value': cmd.value
- };
- }
- visitNgContent(cmd: RenderNgContentCmd, context: any): any {
- return {'deserializerIndex': 1, 'index': cmd.index, 'ngContentIndex': cmd.ngContentIndex};
- }
- visitBeginElement(cmd: RenderBeginElementCmd, context: any): any {
- return {
- 'deserializerIndex': 2,
- 'isBound': cmd.isBound,
- 'ngContentIndex': cmd.ngContentIndex,
- 'name': cmd.name,
- 'attrNameAndValues': cmd.attrNameAndValues,
- 'eventTargetAndNames': cmd.eventTargetAndNames
- };
- }
- visitEndElement(context: any): any { return {'deserializerIndex': 3}; }
- visitBeginComponent(cmd: RenderBeginComponentCmd, context: any): any {
- return {
- 'deserializerIndex': 4,
- 'isBound': cmd.isBound,
- 'ngContentIndex': cmd.ngContentIndex,
- 'name': cmd.name,
- 'attrNameAndValues': cmd.attrNameAndValues,
- 'eventTargetAndNames': cmd.eventTargetAndNames,
- 'templateId': cmd.templateId
- };
- }
- visitEndComponent(context: any): any { return {'deserializerIndex': 5}; }
- visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: any): any {
- var children = cmd.children.map(child => child.visit(this, null));
- return {
- 'deserializerIndex': 6,
- 'isBound': cmd.isBound,
- 'ngContentIndex': cmd.ngContentIndex,
- 'name': cmd.name,
- 'attrNameAndValues': cmd.attrNameAndValues,
- 'eventTargetAndNames': cmd.eventTargetAndNames,
- 'isMerged': cmd.isMerged,
- 'children': children
- };
- }
-}
-
-var RENDER_TEMPLATE_CMD_SERIALIZER = new RenderTemplateCmdSerializer();
-
-var RENDER_TEMPLATE_CMD_DESERIALIZERS = [
- (data: {[key: string]: any}) =>
- new WebWorkerTextCmd(data['isBound'], data['ngContentIndex'], data['value']),
- (data: {[key: string]: any}) => new WebWorkerNgContentCmd(data['index'], data['ngContentIndex']),
- (data: {[key: string]: any}) =>
- new WebWorkerBeginElementCmd(data['isBound'], data['ngContentIndex'], data['name'],
- data['attrNameAndValues'], data['eventTargetAndNames']),
- (data: {[key: string]: any}) => new WebWorkerEndElementCmd(),
- (data: {[key: string]: any}) => new WebWorkerBeginComponentCmd(
- data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'],
- data['eventTargetAndNames'], data['templateId']),
- (data: {[key: string]: any}) => new WebWorkerEndComponentCmd(),
- (data: {[key: string]: any}) => new WebWorkerEmbeddedTemplateCmd(
- data['isBound'], data['ngContentIndex'], data['name'], data['attrNameAndValues'],
- data['eventTargetAndNames'], data['isMerged'],
- (data['children']).map(childData => deserializeTemplateCmd(childData))),
-];
+export class RenderStoreObject {}
\ No newline at end of file
diff --git a/modules/angular2/src/web_workers/ui/event_dispatcher.ts b/modules/angular2/src/web_workers/ui/event_dispatcher.ts
index 66bd879833..e31657785f 100644
--- a/modules/angular2/src/web_workers/ui/event_dispatcher.ts
+++ b/modules/angular2/src/web_workers/ui/event_dispatcher.ts
@@ -1,8 +1,4 @@
-import {
- RenderViewRef,
- RenderEventDispatcher,
-} from 'angular2/src/core/render/api';
-import {Serializer} from 'angular2/src/web_workers/shared/serializer';
+import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
import {
serializeMouseEvent,
serializeKeyboardEvent,
@@ -13,15 +9,13 @@ import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
-export class EventDispatcher implements RenderEventDispatcher {
- constructor(private _viewRef: RenderViewRef, private _sink: EventEmitter,
- private _serializer: Serializer) {}
+export class EventDispatcher {
+ constructor(private _sink: EventEmitter, private _serializer: Serializer) {}
- dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map): boolean {
- var e = locals.get('$event');
+ dispatchRenderEvent(element: any, eventTarget: string, eventName: string, event: any): boolean {
var serializedEvent;
// TODO (jteplitz602): support custom events #3350
- switch (e.type) {
+ switch (event.type) {
case "click":
case "mouseup":
case "mousedown":
@@ -33,17 +27,17 @@ export class EventDispatcher implements RenderEventDispatcher {
case "mouseout":
case "mouseover":
case "show":
- serializedEvent = serializeMouseEvent(e);
+ serializedEvent = serializeMouseEvent(event);
break;
case "keydown":
case "keypress":
case "keyup":
- serializedEvent = serializeKeyboardEvent(e);
+ serializedEvent = serializeKeyboardEvent(event);
break;
case "input":
case "change":
case "blur":
- serializedEvent = serializeEventWithTarget(e);
+ serializedEvent = serializeEventWithTarget(event);
break;
case "abort":
case "afterprint":
@@ -93,19 +87,16 @@ export class EventDispatcher implements RenderEventDispatcher {
case "visibilitychange":
case "volumechange":
case "waiting":
- serializedEvent = serializeGenericEvent(e);
+ serializedEvent = serializeGenericEvent(event);
break;
default:
throw new BaseException(eventName + " not supported on WebWorkers");
}
- var serializedLocals = StringMapWrapper.create();
- StringMapWrapper.set(serializedLocals, '$event', serializedEvent);
-
ObservableWrapper.callEmit(this._sink, {
- "viewRef": this._serializer.serialize(this._viewRef, RenderViewRef),
- "elementIndex": elementIndex,
+ "element": this._serializer.serialize(element, RenderStoreObject),
"eventName": eventName,
- "locals": serializedLocals
+ "eventTarget": eventTarget,
+ "event": serializedEvent
});
// TODO(kegluneq): Eventually, we want the user to indicate from the UI side whether the event
diff --git a/modules/angular2/src/web_workers/ui/renderer.ts b/modules/angular2/src/web_workers/ui/renderer.ts
index 9a9a36e370..c34bd6de25 100644
--- a/modules/angular2/src/web_workers/ui/renderer.ts
+++ b/modules/angular2/src/web_workers/ui/renderer.ts
@@ -1,99 +1,172 @@
import {Injectable} from 'angular2/src/core/di';
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
-import {Serializer, PRIMITIVE} from 'angular2/src/web_workers/shared/serializer';
-import {
- RenderViewRef,
- RenderFragmentRef,
- RenderProtoViewRef,
- Renderer,
- RenderTemplateCmd,
- RenderComponentTemplate
-} from 'angular2/src/core/render/api';
-import {WebWorkerElementRef, WebWorkerTemplateCmd} from 'angular2/src/web_workers/shared/api';
+import {Serializer, PRIMITIVE, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
+import {RootRenderer, Renderer, RenderComponentType} from 'angular2/src/core/render/api';
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
import {Type} from 'angular2/src/facade/lang';
import {bind} from './bind';
import {EventDispatcher} from 'angular2/src/web_workers/ui/event_dispatcher';
-import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
-import {
- RenderViewWithFragmentsStore
-} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
+import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
@Injectable()
export class MessageBasedRenderer {
+ private _eventDispatcher: EventDispatcher;
+
constructor(private _brokerFactory: ServiceMessageBrokerFactory, private _bus: MessageBus,
- private _serializer: Serializer,
- private _renderProtoViewRefStore: RenderProtoViewRefStore,
- private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore,
- private _renderer: Renderer) {}
+ private _serializer: Serializer, private _renderStore: RenderStore,
+ private _rootRenderer: RootRenderer) {}
start(): void {
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
this._bus.initChannel(EVENT_CHANNEL);
+ this._eventDispatcher = new EventDispatcher(this._bus.to(EVENT_CHANNEL), this._serializer);
- broker.registerMethod("registerComponentTemplate", [RenderComponentTemplate],
- bind(this._renderer.registerComponentTemplate, this._renderer));
- broker.registerMethod("createProtoView", [PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE],
- bind(this._createProtoView, this));
- broker.registerMethod("createRootHostView",
- [RenderProtoViewRef, PRIMITIVE, PRIMITIVE, PRIMITIVE],
- bind(this._createRootHostView, this));
- broker.registerMethod("createView", [RenderProtoViewRef, PRIMITIVE, PRIMITIVE],
- bind(this._createView, this));
- broker.registerMethod("destroyView", [RenderViewRef], bind(this._destroyView, this));
- broker.registerMethod("attachFragmentAfterFragment", [RenderFragmentRef, RenderFragmentRef],
- bind(this._renderer.attachFragmentAfterFragment, this._renderer));
- broker.registerMethod("attachFragmentAfterElement", [WebWorkerElementRef, RenderFragmentRef],
- bind(this._renderer.attachFragmentAfterElement, this._renderer));
- broker.registerMethod("detachFragment", [RenderFragmentRef],
- bind(this._renderer.detachFragment, this._renderer));
- broker.registerMethod("hydrateView", [RenderViewRef],
- bind(this._renderer.hydrateView, this._renderer));
- broker.registerMethod("dehydrateView", [RenderViewRef],
- bind(this._renderer.dehydrateView, this._renderer));
- broker.registerMethod("setText", [RenderViewRef, PRIMITIVE, PRIMITIVE],
- bind(this._renderer.setText, this._renderer));
- broker.registerMethod("setElementProperty", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
- bind(this._renderer.setElementProperty, this._renderer));
- broker.registerMethod("setElementAttribute", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
- bind(this._renderer.setElementAttribute, this._renderer));
- broker.registerMethod("setBindingDebugInfo", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
- bind(this._renderer.setBindingDebugInfo, this._renderer));
- broker.registerMethod("setElementClass", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
- bind(this._renderer.setElementClass, this._renderer));
- broker.registerMethod("setElementStyle", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
- bind(this._renderer.setElementStyle, this._renderer));
- broker.registerMethod("invokeElementMethod", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
- bind(this._renderer.invokeElementMethod, this._renderer));
- broker.registerMethod("setEventDispatcher", [RenderViewRef],
- bind(this._setEventDispatcher, this));
+ broker.registerMethod("renderComponent", [RenderComponentType, PRIMITIVE],
+ bind(this._renderComponent, this));
+
+ broker.registerMethod("selectRootElement", [RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._selectRootElement, this));
+ broker.registerMethod("createElement",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._createElement, this));
+ broker.registerMethod("createViewRoot", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
+ bind(this._createViewRoot, this));
+ broker.registerMethod("createTemplateAnchor", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
+ bind(this._createTemplateAnchor, this));
+ broker.registerMethod("createText",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._createText, this));
+ broker.registerMethod("projectNodes", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
+ bind(this._projectNodes, this));
+ broker.registerMethod("attachViewAfter",
+ [RenderStoreObject, RenderStoreObject, RenderStoreObject],
+ bind(this._attachViewAfter, this));
+ broker.registerMethod("detachView", [RenderStoreObject, RenderStoreObject],
+ bind(this._detachView, this));
+ broker.registerMethod("destroyView", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
+ bind(this._destroyView, this));
+ broker.registerMethod("setElementProperty",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._setElementProperty, this));
+ broker.registerMethod("setElementAttribute",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._setElementAttribute, this));
+ broker.registerMethod("setBindingDebugInfo",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._setBindingDebugInfo, this));
+ broker.registerMethod("setElementClass",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._setElementClass, this));
+ broker.registerMethod("setElementStyle",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._setElementStyle, this));
+ broker.registerMethod("invokeElementMethod",
+ [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
+ bind(this._invokeElementMethod, this));
+ broker.registerMethod("setText", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
+ bind(this._setText, this));
+ broker.registerMethod("listen", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
+ bind(this._listen, this));
+ broker.registerMethod("listenGlobal", [RenderStoreObject, PRIMITIVE, PRIMITIVE, PRIMITIVE],
+ bind(this._listenGlobal, this));
+ broker.registerMethod("listenGlobalDone", [RenderStoreObject, RenderStoreObject],
+ bind(this._listenGlobalDone, this));
}
- private _destroyView(viewRef: RenderViewRef): void {
- this._renderer.destroyView(viewRef);
- this._renderViewWithFragmentsStore.remove(viewRef);
+ private _renderComponent(renderComponentType: RenderComponentType, rendererId: number) {
+ var renderer = this._rootRenderer.renderComponent(renderComponentType);
+ this._renderStore.store(renderer, rendererId);
}
- private _createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[],
- refIndex: number) {
- var protoViewRef = this._renderer.createProtoView(componentTemplateId, cmds);
- this._renderProtoViewRefStore.store(protoViewRef, refIndex);
+ private _selectRootElement(renderer: Renderer, selector: string, elId: number) {
+ this._renderStore.store(renderer.selectRootElement(selector), elId);
}
- private _createRootHostView(ref: RenderProtoViewRef, fragmentCount: number, selector: string,
- startIndex: number) {
- var renderViewWithFragments = this._renderer.createRootHostView(ref, fragmentCount, selector);
- this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex);
+ private _createElement(renderer: Renderer, parentElement: any, name: string, elId: number) {
+ this._renderStore.store(renderer.createElement(parentElement, name), elId);
}
- private _createView(ref: RenderProtoViewRef, fragmentCount: number, startIndex: number) {
- var renderViewWithFragments = this._renderer.createView(ref, fragmentCount);
- this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex);
+ private _createViewRoot(renderer: Renderer, hostElement: any, elId: number) {
+ var viewRoot = renderer.createViewRoot(hostElement);
+ if (this._renderStore.serialize(hostElement) !== elId) {
+ this._renderStore.store(viewRoot, elId);
+ }
}
- private _setEventDispatcher(viewRef: RenderViewRef) {
- var dispatcher = new EventDispatcher(viewRef, this._bus.to(EVENT_CHANNEL), this._serializer);
- this._renderer.setEventDispatcher(viewRef, dispatcher);
+ private _createTemplateAnchor(renderer: Renderer, parentElement: any, elId: number) {
+ this._renderStore.store(renderer.createTemplateAnchor(parentElement), elId);
}
+
+ private _createText(renderer: Renderer, parentElement: any, value: string, elId: number) {
+ this._renderStore.store(renderer.createText(parentElement, value), elId);
+ }
+
+ private _projectNodes(renderer: Renderer, parentElement: any, nodes: any[]) {
+ renderer.projectNodes(parentElement, nodes);
+ }
+
+ private _attachViewAfter(renderer: Renderer, node: any, viewRootNodes: any[]) {
+ renderer.attachViewAfter(node, viewRootNodes);
+ }
+
+ private _detachView(renderer: Renderer, viewRootNodes: any[]) {
+ renderer.detachView(viewRootNodes);
+ }
+
+ private _destroyView(renderer: Renderer, hostElement: any, viewAllNodes: any[]) {
+ renderer.destroyView(hostElement, viewAllNodes);
+ for (var i = 0; i < viewAllNodes.length; i++) {
+ this._renderStore.remove(viewAllNodes[i]);
+ }
+ }
+
+ private _setElementProperty(renderer: Renderer, renderElement: any, propertyName: string,
+ propertyValue: any) {
+ renderer.setElementProperty(renderElement, propertyName, propertyValue);
+ }
+
+ private _setElementAttribute(renderer: Renderer, renderElement: any, attributeName: string,
+ attributeValue: string) {
+ renderer.setElementAttribute(renderElement, attributeName, attributeValue);
+ }
+
+ private _setBindingDebugInfo(renderer: Renderer, renderElement: any, propertyName: string,
+ propertyValue: string) {
+ renderer.setBindingDebugInfo(renderElement, propertyName, propertyValue);
+ }
+
+ private _setElementClass(renderer: Renderer, renderElement: any, className: string,
+ isAdd: boolean) {
+ renderer.setElementClass(renderElement, className, isAdd);
+ }
+
+ private _setElementStyle(renderer: Renderer, renderElement: any, styleName: string,
+ styleValue: string) {
+ renderer.setElementStyle(renderElement, styleName, styleValue);
+ }
+
+ private _invokeElementMethod(renderer: Renderer, renderElement: any, methodName: string,
+ args: any[]) {
+ renderer.invokeElementMethod(renderElement, methodName, args);
+ }
+
+ private _setText(renderer: Renderer, renderNode: any, text: string) {
+ renderer.setText(renderNode, text);
+ }
+
+ private _listen(renderer: Renderer, renderElement: any, eventName: string) {
+ renderer.listen(renderElement, eventName, (event) => this._eventDispatcher.dispatchRenderEvent(
+ renderElement, null, eventName, event));
+ }
+
+ private _listenGlobal(renderer: Renderer, eventTarget: string, eventName: string,
+ unlistenId: number) {
+ var unregisterCallback = renderer.listenGlobal(
+ eventTarget, eventName,
+ (event) => this._eventDispatcher.dispatchRenderEvent(null, eventTarget, eventName, event));
+ this._renderStore.store(unregisterCallback, unlistenId);
+ }
+
+ private _listenGlobalDone(renderer: Renderer, unlistenCallback: Function) { unlistenCallback(); }
}
diff --git a/modules/angular2/src/web_workers/worker/event_dispatcher.ts b/modules/angular2/src/web_workers/worker/event_dispatcher.ts
deleted file mode 100644
index 287995baca..0000000000
--- a/modules/angular2/src/web_workers/worker/event_dispatcher.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import {Injectable} from 'angular2/src/core/di';
-import {Map, MapWrapper} from 'angular2/src/facade/collection';
-import {RenderViewRef, RenderEventDispatcher} from 'angular2/src/core/render/api';
-import {Serializer} from 'angular2/src/web_workers/shared/serializer';
-import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
-import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
-import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
-import {deserializeGenericEvent} from './event_deserializer';
-
-@Injectable()
-export class WebWorkerEventDispatcher {
- private _eventDispatchRegistry: Map =
- new Map();
-
- constructor(bus: MessageBus, private _serializer: Serializer) {
- bus.initChannel(EVENT_CHANNEL);
- var source = bus.from(EVENT_CHANNEL);
- ObservableWrapper.subscribe(
- source, (message) => this._dispatchEvent(new RenderEventData(message, _serializer)));
- }
-
-
- private _dispatchEvent(eventData: RenderEventData): void {
- var dispatcher = this._eventDispatchRegistry.get(eventData.viewRef);
- eventData.locals['$event'] = deserializeGenericEvent(eventData.locals['$event']);
- dispatcher.dispatchRenderEvent(eventData.elementIndex, eventData.eventName, eventData.locals);
- }
-
- registerEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher): void {
- this._eventDispatchRegistry.set(viewRef, dispatcher);
- }
-}
-
-
-class RenderEventData {
- viewRef: RenderViewRef;
- elementIndex: number;
- eventName: string;
- locals: Map;
-
- constructor(message: {[key: string]: any}, serializer: Serializer) {
- this.viewRef = serializer.deserialize(message['viewRef'], RenderViewRef);
- this.elementIndex = message['elementIndex'];
- this.eventName = message['eventName'];
- this.locals = MapWrapper.createFromStringMap(message['locals']);
- }
-}
diff --git a/modules/angular2/src/web_workers/worker/renderer.ts b/modules/angular2/src/web_workers/worker/renderer.ts
index 3e3fca3354..7d1377762f 100644
--- a/modules/angular2/src/web_workers/worker/renderer.ts
+++ b/modules/angular2/src/web_workers/worker/renderer.ts
@@ -1,269 +1,270 @@
-import {
- Renderer,
- RenderProtoViewRef,
- RenderViewRef,
- RenderElementRef,
- RenderEventDispatcher,
- RenderViewWithFragments,
- RenderFragmentRef,
- RenderTemplateCmd,
- RenderComponentTemplate
-} from 'angular2/src/core/render/api';
+import {Renderer, RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
import {
ClientMessageBroker,
ClientMessageBrokerFactory,
FnArg,
UiArguments
} from "angular2/src/web_workers/shared/client_message_broker";
-import {isPresent, print} from "angular2/src/facade/lang";
+import {isPresent, isBlank, print} from "angular2/src/facade/lang";
+import {ListWrapper} from 'angular2/src/facade/collection';
import {Injectable} from "angular2/src/core/di";
-import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
-import {
- RenderViewWithFragmentsStore,
- WebWorkerRenderViewRef
-} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
-import {WebWorkerElementRef, WebWorkerTemplateCmd} from 'angular2/src/web_workers/shared/api';
+import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
import {RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
-import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
+import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
+import {EVENT_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
+import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
+import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
+import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
+import {deserializeGenericEvent} from './event_deserializer';
@Injectable()
-export class WebWorkerRenderer implements Renderer {
+export class WebWorkerRootRenderer implements RootRenderer {
private _messageBroker;
- constructor(messageBrokerFactory: ClientMessageBrokerFactory,
- private _renderProtoViewRefStore: RenderProtoViewRefStore,
- private _renderViewStore: RenderViewWithFragmentsStore,
- private _eventDispatcher: WebWorkerEventDispatcher) {
+ public globalEvents: NamedEventEmitter = new NamedEventEmitter();
+ private _componentRenderers: Map =
+ new Map();
+
+ constructor(messageBrokerFactory: ClientMessageBrokerFactory, bus: MessageBus,
+ private _serializer: Serializer, private _renderStore: RenderStore) {
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
+ bus.initChannel(EVENT_CHANNEL);
+ var source = bus.from(EVENT_CHANNEL);
+ ObservableWrapper.subscribe(source, (message) => this._dispatchEvent(message));
}
- registerComponentTemplate(template: RenderComponentTemplate) {
- var fnArgs = [new FnArg(template, RenderComponentTemplate)];
- var args = new UiArguments("registerComponentTemplate", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
- var renderProtoViewRef = this._renderProtoViewRefStore.allocate();
-
- var fnArgs: FnArg[] = [
- new FnArg(componentTemplateId, null),
- new FnArg(cmds, WebWorkerTemplateCmd),
- new FnArg(renderProtoViewRef, RenderProtoViewRef)
- ];
- var args: UiArguments = new UiArguments("createProtoView", fnArgs);
- this._messageBroker.runOnService(args, null);
- return renderProtoViewRef;
- }
-
- /**
- * Creates a root host view that includes the given element.
- * Note that the fragmentCount needs to be passed in so that we can create a result
- * synchronously even when dealing with webworkers!
- *
- * @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type
- * ProtoViewDto.HOST_VIEW_TYPE
- * @param {any} hostElementSelector css selector for the host element (will be queried against the
- * main document)
- * @return {RenderViewRef} the created view
- */
- createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
- hostElementSelector: string): RenderViewWithFragments {
- return this._createViewHelper(hostProtoViewRef, fragmentCount, hostElementSelector);
- }
-
- /**
- * Creates a regular view out of the given ProtoView
- * Note that the fragmentCount needs to be passed in so that we can create a result
- * synchronously even when dealing with webworkers!
- */
- createView(protoViewRef: RenderProtoViewRef, fragmentCount: number): RenderViewWithFragments {
- return this._createViewHelper(protoViewRef, fragmentCount);
- }
-
- private _createViewHelper(protoViewRef: RenderProtoViewRef, fragmentCount: number,
- hostElementSelector?: string): RenderViewWithFragments {
- var renderViewWithFragments = this._renderViewStore.allocate(fragmentCount);
-
- var startIndex = ((renderViewWithFragments.viewRef)).refNumber;
- var fnArgs: FnArg[] = [
- new FnArg(protoViewRef, RenderProtoViewRef),
- new FnArg(fragmentCount, null),
- ];
- var method = "createView";
- if (isPresent(hostElementSelector) && hostElementSelector != null) {
- fnArgs.push(new FnArg(hostElementSelector, null));
- method = "createRootHostView";
+ private _dispatchEvent(message: {[key: string]: any}): void {
+ var eventName = message['eventName'];
+ var target = message['eventTarget'];
+ var event = deserializeGenericEvent(message['event']);
+ if (isPresent(target)) {
+ this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event);
+ } else {
+ var element =
+ this._serializer.deserialize(message['element'], RenderStoreObject);
+ element.events.dispatchEvent(eventName, event);
}
- fnArgs.push(new FnArg(startIndex, null));
-
- var args = new UiArguments(method, fnArgs);
- this._messageBroker.runOnService(args, null);
-
- return renderViewWithFragments;
}
- /**
- * Destroys the given view after it has been dehydrated and detached
- */
- destroyView(viewRef: RenderViewRef) {
- var fnArgs = [new FnArg(viewRef, RenderViewRef)];
- var args = new UiArguments("destroyView", fnArgs);
- this._messageBroker.runOnService(args, null);
- this._renderViewStore.remove(viewRef);
+ renderComponent(componentType: RenderComponentType): Renderer {
+ var result = this._componentRenderers.get(componentType.id);
+ if (isBlank(result)) {
+ result = new WebWorkerRenderer(this, componentType);
+ this._componentRenderers.set(componentType.id, result);
+ var id = this._renderStore.allocateId();
+ this._renderStore.store(result, id);
+ this.runOnService('renderComponent', [
+ new FnArg(componentType, RenderComponentType),
+ new FnArg(result, RenderStoreObject),
+ ]);
+ }
+ return result;
}
- /**
- * Attaches a fragment after another fragment.
- */
- attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
- fragmentRef: RenderFragmentRef) {
- var fnArgs = [
- new FnArg(previousFragmentRef, RenderFragmentRef),
- new FnArg(fragmentRef, RenderFragmentRef)
- ];
- var args = new UiArguments("attachFragmentAfterFragment", fnArgs);
+ runOnService(fnName: string, fnArgs: FnArg[]) {
+ var args = new UiArguments(fnName, fnArgs);
this._messageBroker.runOnService(args, null);
}
- /**
- * Attaches a fragment after an element.
- */
- attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
- var fnArgs =
- [new FnArg(elementRef, WebWorkerElementRef), new FnArg(fragmentRef, RenderFragmentRef)];
- var args = new UiArguments("attachFragmentAfterElement", fnArgs);
- this._messageBroker.runOnService(args, null);
+ allocateNode(): WebWorkerRenderNode {
+ var result = new WebWorkerRenderNode();
+ var id = this._renderStore.allocateId();
+ this._renderStore.store(result, id);
+ return result;
}
- /**
- * Detaches a fragment.
- */
- detachFragment(fragmentRef: RenderFragmentRef) {
- var fnArgs = [new FnArg(fragmentRef, RenderFragmentRef)];
- var args = new UiArguments("detachFragment", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
+ allocateId(): number { return this._renderStore.allocateId(); }
- /**
- * Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views
- * inside of the view pool.
- */
- hydrateView(viewRef: RenderViewRef) {
- var fnArgs = [new FnArg(viewRef, RenderViewRef)];
- var args = new UiArguments("hydrateView", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- /**
- * Dehydrates a view after it has been attached. Hydration/dehydration is used for reusing views
- * inside of the view pool.
- */
- dehydrateView(viewRef: RenderViewRef) {
- var fnArgs = [new FnArg(viewRef, RenderViewRef)];
- var args = new UiArguments("dehydrateView", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- /**
- * Returns the native element at the given location.
- * Attention: In a WebWorker scenario, this should always return null!
- */
- getNativeElementSync(location: RenderElementRef): any { return null; }
-
- /**
- * Sets a property on an element.
- */
- setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any) {
- var fnArgs = [
- new FnArg(location, WebWorkerElementRef),
- new FnArg(propertyName, null),
- new FnArg(propertyValue, null)
- ];
- var args = new UiArguments("setElementProperty", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- /**
- * Sets an attribute on an element.
- */
- setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string) {
- var fnArgs = [
- new FnArg(location, WebWorkerElementRef),
- new FnArg(attributeName, null),
- new FnArg(attributeValue, null)
- ];
- var args = new UiArguments("setElementAttribute", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- setBindingDebugInfo(location: RenderElementRef, propertyName: string,
- propertyValue: string): void {
- var fnArgs = [
- new FnArg(location, WebWorkerElementRef),
- new FnArg(propertyName, null),
- new FnArg(propertyValue, null)
- ];
- var args = new UiArguments("setBindingDebugInfo", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- /**
- * Sets a class on an element.
- */
- setElementClass(location: RenderElementRef, className: string, isAdd: boolean) {
- var fnArgs = [
- new FnArg(location, WebWorkerElementRef),
- new FnArg(className, null),
- new FnArg(isAdd, null)
- ];
- var args = new UiArguments("setElementClass", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- /**
- * Sets a style on an element.
- */
- setElementStyle(location: RenderElementRef, styleName: string, styleValue: string) {
- var fnArgs = [
- new FnArg(location, WebWorkerElementRef),
- new FnArg(styleName, null),
- new FnArg(styleValue, null)
- ];
- var args = new UiArguments("setElementStyle", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- /**
- * Calls a method on an element.
- * Note: For now we're assuming that everything in the args list are primitive
- */
- invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]) {
- var fnArgs = [
- new FnArg(location, WebWorkerElementRef),
- new FnArg(methodName, null),
- new FnArg(args, null)
- ];
- var uiArgs = new UiArguments("invokeElementMethod", fnArgs);
- this._messageBroker.runOnService(uiArgs, null);
- }
-
- /**
- * Sets the value of a text node.
- */
- setText(viewRef: RenderViewRef, textNodeIndex: number, text: string) {
- var fnArgs =
- [new FnArg(viewRef, RenderViewRef), new FnArg(textNodeIndex, null), new FnArg(text, null)];
- var args = new UiArguments("setText", fnArgs);
- this._messageBroker.runOnService(args, null);
- }
-
- /**
- * Sets the dispatcher for all events of the given view
- */
- setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher) {
- var fnArgs = [new FnArg(viewRef, RenderViewRef)];
- var args = new UiArguments("setEventDispatcher", fnArgs);
- this._eventDispatcher.registerEventDispatcher(viewRef, dispatcher);
- this._messageBroker.runOnService(args, null);
+ destroyNodes(nodes: any[]) {
+ for (var i = 0; i < nodes.length; i++) {
+ this._renderStore.remove(nodes[i]);
+ }
}
}
+
+export class WebWorkerRenderer implements Renderer, RenderStoreObject {
+ constructor(private _rootRenderer: WebWorkerRootRenderer,
+ private _componentType: RenderComponentType) {}
+
+ renderComponent(componentType: RenderComponentType): Renderer {
+ return this._rootRenderer.renderComponent(componentType);
+ }
+
+ private _runOnService(fnName: string, fnArgs: FnArg[]) {
+ var fnArgsWithRenderer = [new FnArg(this, RenderStoreObject)].concat(fnArgs);
+ this._rootRenderer.runOnService(fnName, fnArgsWithRenderer);
+ }
+
+ selectRootElement(selector: string): any {
+ var node = this._rootRenderer.allocateNode();
+ this._runOnService('selectRootElement',
+ [new FnArg(selector, null), new FnArg(node, RenderStoreObject)]);
+ return node;
+ }
+
+ createElement(parentElement: any, name: string): any {
+ var node = this._rootRenderer.allocateNode();
+ this._runOnService('createElement', [
+ new FnArg(parentElement, RenderStoreObject),
+ new FnArg(name, null),
+ new FnArg(node, RenderStoreObject)
+ ]);
+ return node;
+ }
+
+ createViewRoot(hostElement: any): any {
+ var viewRoot = this._componentType.encapsulation === ViewEncapsulation.Native ?
+ this._rootRenderer.allocateNode() :
+ hostElement;
+ this._runOnService(
+ 'createViewRoot',
+ [new FnArg(hostElement, RenderStoreObject), new FnArg(viewRoot, RenderStoreObject)]);
+ return viewRoot;
+ }
+
+ createTemplateAnchor(parentElement: any): any {
+ var node = this._rootRenderer.allocateNode();
+ this._runOnService(
+ 'createTemplateAnchor',
+ [new FnArg(parentElement, RenderStoreObject), new FnArg(node, RenderStoreObject)]);
+ return node;
+ }
+
+ createText(parentElement: any, value: string): any {
+ var node = this._rootRenderer.allocateNode();
+ this._runOnService('createText', [
+ new FnArg(parentElement, RenderStoreObject),
+ new FnArg(value, null),
+ new FnArg(node, RenderStoreObject)
+ ]);
+ return node;
+ }
+
+ projectNodes(parentElement: any, nodes: any[]) {
+ this._runOnService(
+ 'projectNodes',
+ [new FnArg(parentElement, RenderStoreObject), new FnArg(nodes, RenderStoreObject)]);
+ }
+
+ attachViewAfter(node: any, viewRootNodes: any[]) {
+ this._runOnService(
+ 'attachViewAfter',
+ [new FnArg(node, RenderStoreObject), new FnArg(viewRootNodes, RenderStoreObject)]);
+ }
+
+ detachView(viewRootNodes: any[]) {
+ this._runOnService('detachView', [new FnArg(viewRootNodes, RenderStoreObject)]);
+ }
+
+ destroyView(hostElement: any, viewAllNodes: any[]) {
+ this._runOnService(
+ 'destroyView',
+ [new FnArg(hostElement, RenderStoreObject), new FnArg(viewAllNodes, RenderStoreObject)]);
+ this._rootRenderer.destroyNodes(viewAllNodes);
+ }
+
+ setElementProperty(renderElement: any, propertyName: string, propertyValue: any) {
+ this._runOnService('setElementProperty', [
+ new FnArg(renderElement, RenderStoreObject),
+ new FnArg(propertyName, null),
+ new FnArg(propertyValue, null)
+ ]);
+ }
+
+ setElementAttribute(renderElement: any, attributeName: string, attributeValue: string) {
+ this._runOnService('setElementAttribute', [
+ new FnArg(renderElement, RenderStoreObject),
+ new FnArg(attributeName, null),
+ new FnArg(attributeValue, null)
+ ]);
+ }
+
+ setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string) {
+ this._runOnService('setBindingDebugInfo', [
+ new FnArg(renderElement, RenderStoreObject),
+ new FnArg(propertyName, null),
+ new FnArg(propertyValue, null)
+ ]);
+ }
+
+ setElementClass(renderElement: any, className: string, isAdd: boolean) {
+ this._runOnService('setElementClass', [
+ new FnArg(renderElement, RenderStoreObject),
+ new FnArg(className, null),
+ new FnArg(isAdd, null)
+ ]);
+ }
+
+ setElementStyle(renderElement: any, styleName: string, styleValue: string) {
+ this._runOnService('setElementStyle', [
+ new FnArg(renderElement, RenderStoreObject),
+ new FnArg(styleName, null),
+ new FnArg(styleValue, null)
+ ]);
+ }
+
+ invokeElementMethod(renderElement: any, methodName: string, args: any[]) {
+ this._runOnService('invokeElementMethod', [
+ new FnArg(renderElement, RenderStoreObject),
+ new FnArg(methodName, null),
+ new FnArg(args, null)
+ ]);
+ }
+
+ setText(renderNode: any, text: string) {
+ this._runOnService('setText',
+ [new FnArg(renderNode, RenderStoreObject), new FnArg(text, null)]);
+ }
+
+ listen(renderElement: WebWorkerRenderNode, name: string, callback: Function) {
+ renderElement.events.listen(name, callback);
+ this._runOnService('listen',
+ [new FnArg(renderElement, RenderStoreObject), new FnArg(name, null)]);
+ }
+
+ listenGlobal(target: string, name: string, callback: Function): Function {
+ this._rootRenderer.globalEvents.listen(eventNameWithTarget(target, name), callback);
+ var unlistenCallbackId = this._rootRenderer.allocateId();
+ this._runOnService(
+ 'listenGlobal',
+ [new FnArg(target, null), new FnArg(name, null), new FnArg(unlistenCallbackId, null)]);
+ return () => {
+ this._rootRenderer.globalEvents.unlisten(eventNameWithTarget(target, name), callback);
+ this._runOnService('listenGlobalDone', [new FnArg(unlistenCallbackId, null)]);
+ };
+ }
+}
+
+export class NamedEventEmitter {
+ private _listeners: Map;
+
+ private _getListeners(eventName: string): Function[] {
+ if (isBlank(this._listeners)) {
+ this._listeners = new Map();
+ }
+ var listeners = this._listeners.get(eventName);
+ if (isBlank(listeners)) {
+ listeners = [];
+ this._listeners.set(eventName, listeners);
+ }
+ return listeners;
+ }
+
+ listen(eventName: string, callback: Function) { this._getListeners(eventName).push(callback); }
+
+ unlisten(eventName: string, callback: Function) {
+ ListWrapper.remove(this._getListeners(eventName), callback);
+ }
+
+ dispatchEvent(eventName: string, event: any) {
+ var listeners = this._getListeners(eventName);
+ for (var i = 0; i < listeners.length; i++) {
+ listeners[i](event);
+ }
+ }
+}
+
+function eventNameWithTarget(target: string, eventName: string): string {
+ return `${target}:${eventName}`;
+}
+
+export class WebWorkerRenderNode { events: NamedEventEmitter = new NamedEventEmitter(); }
diff --git a/modules/angular2/test/common/directives/ng_class_spec.ts b/modules/angular2/test/common/directives/ng_class_spec.ts
index b3c7a6c9d3..beff6db583 100644
--- a/modules/angular2/test/common/directives/ng_class_spec.ts
+++ b/modules/angular2/test/common/directives/ng_class_spec.ts
@@ -18,7 +18,6 @@ import {ListWrapper, StringMapWrapper, SetWrapper} from 'angular2/src/facade/col
import {Component, View, provide} from 'angular2/core';
import {NgFor} from 'angular2/common';
import {NgClass} from 'angular2/src/common/directives/ng_class';
-import {APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/linker/view_pool';
function detectChangesAndCheck(fixture: ComponentFixture, classes: string, elIndex: number = 0) {
fixture.detectChanges();
@@ -30,8 +29,6 @@ export function main() {
describe('binding to CSS class list', () => {
describe('viewpool support', () => {
- beforeEachProviders(() => { return [provide(APP_VIEW_POOL_CAPACITY, {useValue: 100})]; });
-
it('should clean up when the directive is destroyed',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var template = '';
diff --git a/modules/angular2/test/common/spies.ts b/modules/angular2/test/common/spies.ts
index 65d5a047e5..28edae54d6 100644
--- a/modules/angular2/test/common/spies.ts
+++ b/modules/angular2/test/common/spies.ts
@@ -1,8 +1,11 @@
-import {ChangeDetectorRef_} from 'angular2/src/core/change_detection/change_detector_ref';
+import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
import {SpyObject, proxy} from 'angular2/testing_internal';
export class SpyChangeDetectorRef extends SpyObject {
- constructor() { super(ChangeDetectorRef_); }
+ constructor() {
+ super(ChangeDetectorRef);
+ this.spy('markForCheck');
+ }
}
export class SpyNgControl extends SpyObject {}
diff --git a/modules/angular2/test/compiler/change_definition_factory_spec.ts b/modules/angular2/test/compiler/change_definition_factory_spec.ts
index e7ba9eabcd..300b326b11 100644
--- a/modules/angular2/test/compiler/change_definition_factory_spec.ts
+++ b/modules/angular2/test/compiler/change_definition_factory_spec.ts
@@ -47,7 +47,6 @@ export function main() {
var directive: TestDirective;
var locals: Locals;
var pipes: Pipes;
- var eventLocals: Locals;
beforeEach(inject([TemplateParser], (_templateParser) => {
parser = _templateParser;
@@ -55,7 +54,6 @@ export function main() {
directive = new TestDirective();
dispatcher = new TestDispatcher([directive], []);
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
- eventLocals = new Locals(null, MapWrapper.createFromStringMap({'$event': null}));
pipes = new TestPipes();
}));
@@ -65,9 +63,9 @@ export function main() {
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
ChangeDetectionStrategy.Default,
new ChangeDetectorGenConfig(true, false, false),
- parser.parse(template, directives, 'TestComp'))
+ parser.parse(template, directives, [], 'TestComp'))
.map(definition => new DynamicProtoChangeDetector(definition));
- var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
+ var changeDetector = protoChangeDetectors[protoViewIndex].instantiate();
changeDetector.hydrate(context, locals, dispatcher, pipes);
return changeDetector;
}
@@ -91,8 +89,7 @@ export function main() {
it('should handle events on regular elements', () => {
var changeDetector = createChangeDetector('