feat(core): speed up view creation via code gen for view factories.

BREAKING CHANGE:
- Platform pipes can only contain types and arrays of types,
  but no bindings any more.
- When using transformers, platform pipes need to be specified explicitly
  in the pubspec.yaml via the new config option
  `platform_pipes`.
- `Compiler.compileInHost` now returns a `HostViewFactoryRef`
- Component view is not yet created when component constructor is called.
  -> use `onInit` lifecycle callback to access the view of a component
- `ViewRef#setLocal` has been moved to new type `EmbeddedViewRef`
- `internalView` is gone, use `EmbeddedViewRef.rootNodes` to access
  the root nodes of an embedded view
- `renderer.setElementProperty`, `..setElementStyle`, `..setElementAttribute` now
  take a native element instead of an ElementRef
- `Renderer` interface now operates on plain native nodes,
  instead of `RenderElementRef`s or `RenderViewRef`s

Closes #5993
This commit is contained in:
Tobias Bosch 2015-12-02 10:35:51 -08:00
parent a08f50badd
commit 7ae23adaff
191 changed files with 6476 additions and 10232 deletions

View File

@ -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);
}
}
}

View File

@ -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 = <EmbeddedViewRef>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;

View File

@ -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);
}
}

View File

@ -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; }

View File

@ -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; }

View File

@ -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 {

View File

@ -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; }

View File

@ -80,7 +80,6 @@ export function selectValueAccessor(dir: NgControl,
var defaultAccessor;
var builtinAccessor;
var customAccessor;
valueAccessors.forEach(v => {
if (v instanceof DefaultValueAccessor) {
defaultAccessor = v;

View File

@ -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);

View File

@ -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++;

View File

@ -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<R> {
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<TemplateCmd> {
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<Expression> {
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<R> implements TemplateAstVisitor {
result: R[] = [];
transitiveNgContentCount: number = 0;
constructor(public commandFactory: CommandFactory<R>, 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}`;
}
}

View File

@ -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<Type | Provider | any[]> = CONST_EXPR([
RuntimeMetadataResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
CommandCompiler,
ProtoViewCompiler,
ViewCompiler,
ChangeDetectionCompiler,
new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}),
TemplateCompiler,

View File

@ -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
};

View File

@ -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<AppProtoView, AppProtoElement, any> {
var protoViewFactory = new RuntimeProtoViewFactory(metadataCache, component, pipes);
var allProtoViews = [];
protoViewFactory.createCompileProtoView(template, [], [], allProtoViews);
return new CompileProtoViews<AppProtoView, AppProtoElement, any>([], allProtoViews);
}
compileProtoViewCodeGen(resolvedMetadataCacheExpr: Expression,
component: CompileDirectiveMetadata, template: TemplateAst[],
pipes: CompilePipeMetadata[]):
CompileProtoViews<Expression, Expression, string> {
var protoViewFactory = new CodeGenProtoViewFactory(resolvedMetadataCacheExpr, component, pipes);
var allProtoViews = [];
var allStatements = [];
protoViewFactory.createCompileProtoView(template, [], allStatements, allProtoViews);
return new CompileProtoViews<Expression, Expression, string>(
allStatements.map(stmt => stmt.statement), allProtoViews);
}
}
export class CompileProtoViews<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
constructor(public declarations: STATEMENT[],
public protoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
}
export class CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
constructor(public embeddedTemplateIndex: number,
public protoElements: CompileProtoElement<APP_PROTO_EL>[],
public protoView: APP_PROTO_VIEW) {}
}
export class CompileProtoElement<APP_PROTO_EL> {
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<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
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<APP_PROTO_VIEW, APP_PROTO_EL>[]):
CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
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<APP_PROTO_VIEW, APP_PROTO_EL, any>(
this, targetStatements, targetProtoViews);
templateVisitAll(builder, template);
var viewType = getViewType(this.component, embeddedTemplateIndex);
var appProtoView = this.createAppProtoView(embeddedTemplateIndex, viewType,
templateVariableBindings, targetStatements);
var cpv = new CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>(
embeddedTemplateIndex, builder.protoElements, appProtoView);
targetProtoViews[embeddedTemplateIndex] = cpv;
return cpv;
}
}
class CodeGenProtoViewFactory extends ProtoViewFactory<Expression, Expression, Statement> {
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<AppProtoView, AppProtoElement, any> {
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<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> implements
TemplateAstVisitor {
protoElements: CompileProtoElement<APP_PROTO_EL>[] = [];
boundElementCount: number = 0;
constructor(public factory: ProtoViewFactory<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT>,
public allStatements: STATEMENT[],
public allProtoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
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<string, BoundEventAst> =
visitAndReturnContext(this, ast.outputs, new Map<string, BoundEventAst>());
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<string, BoundEventAst>(), [],
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<APP_PROTO_EL>(
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<string, BoundEventAst>): 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<string, BoundEventAst>,
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;
}
}

View File

@ -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<HostViewFactoryRef>;
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<ProtoViewRef> {
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
return this._templateCompiler.compileHostComponentRuntime(componentType)
.then(compiledHostTemplate => internalCreateProtoView(this, compiledHostTemplate));
.then(hostViewFactory => new HostViewFactoryRef_(hostViewFactory));
}
clearCache() {

View File

@ -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<Type, cpl.CompileDirectiveMetadata>();
private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>();
private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>();
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<Type | any[]>): void {
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
@ -104,7 +147,7 @@ function flattenArray(tree: any[], out: Array<Type | any[]>): void {
}
}
function isValidDirective(value: Type): boolean {
function isValidType(value: Type): boolean {
return isPresent(value) && (value instanceof Type);
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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<Type, any>();
private _compiledTemplateCache = new Map<any, CompiledComponentTemplate>();
private _compiledTemplateDone = new Map<any, Promise<CompiledComponentTemplate>>();
private _nextTemplateId: number = 0;
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
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<CompileDirectiveMetadata> {
@ -75,99 +113,29 @@ export class TemplateCompiler {
}));
}
compileHostComponentRuntime(type: Type): Promise<CompiledHostTemplate> {
compileHostComponentRuntime(type: Type): Promise<HostViewFactory> {
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<any>): 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([<any>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<Type>,
childPromises: Promise<any>[]): 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 = <CompileDirectiveMetadata>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}',${(<any[]>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<any>): CompiledTemplate {
let uniqViewDirectives = <CompileDirectiveMetadata[]>removeDuplicates(viewDirectives);
let uniqViewPipes = <CompilePipeMetadata[]>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([<any>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<Type>,
childPromises: Promise<any>[]) {
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 = <CompileDirectiveMetadata[]>removeDuplicates(directives);
let uniqPipes = <CompilePipeMetadata[]>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<string, BoundEventAst>): 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<string, BoundEventAst>): 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;
}
}

View File

@ -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<CompileDirectiveMetadata, number>();
ngContentCount: number = 0;
pipesByName: Map<string, CompilePipeMetadata>;
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<string, CompilePipeMetadata>();
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<string> = new Set<string>();
visitPipe(ast: BindingPipe): any {
this.pipes.add(ast.name);
ast.exp.visit(this);
this.visitAll(ast.args);
return null;
}
}

View File

@ -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 && (<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;
}

View File

@ -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<string | any[]>,
protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
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<Expression, Expression>[],
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<EXPRESSION, STATEMENT> {
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<Expression, Statement> {
private _nextVarId: number = 0;
constructor(public component: CompileDirectiveMetadata, public styles: SourceExpression,
public protoViews: CompileProtoView<Expression, Expression>[],
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<Expression, Statement>(
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<any, any> {
constructor(public component: CompileDirectiveMetadata, public styles: Array<string | any[]>,
public protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
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<Array<any | any[]>>,
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 = <Renderer>parentRenderer;
}
var changeDetector = this.changeDetectorFactories[embeddedTemplateIndex]();
var view =
new AppView(compileProtoView.protoView, renderer, viewManager, projectableNodes,
containerEl, dynamicallyCreatedProviders, rootInjector, changeDetector);
var visitor = new ViewBuilderVisitor<any, any>(
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<EXPRESSION> {
public contentNodesByNgContentIndex: Array<EXPRESSION>[];
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<EXPRESSION, STATEMENT> implements TemplateAstVisitor {
renderStmts: Array<STATEMENT> = [];
renderNodes: EXPRESSION[] = [];
appStmts: Array<STATEMENT> = [];
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<EXPRESSION, EXPRESSION>,
public targetStatements: STATEMENT[],
public factory: ViewFactory<EXPRESSION, STATEMENT>) {}
private _addRenderNode(renderNode: EXPRESSION, appEl: EXPRESSION, ngContentIndex: number,
parent: ParentElement<EXPRESSION>) {
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>): EXPRESSION {
return isPresent(parent.component) &&
parent.component.template.encapsulation !== ViewEncapsulation.Native ?
null :
parent.renderNode;
}
visitBoundText(ast: BoundTextAst, parent: ParentElement<EXPRESSION>): any {
return this._visitText('', ast.ngContentIndex, parent);
}
visitText(ast: TextAst, parent: ParentElement<EXPRESSION>): any {
return this._visitText(ast.value, ast.ngContentIndex, parent);
}
private _visitText(value: string, ngContentIndex: number, parent: ParentElement<EXPRESSION>) {
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<EXPRESSION>): 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<EXPRESSION>): 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<EXPRESSION>(
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<EXPRESSION>): 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}`;
}
}

View File

@ -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<Type | Provider | any[]> = 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}),

View File

@ -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<Type | Provider | an
() => { 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 = (<ElementRef_>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(
(<ElementRef_>ref.location).internalElement.parentView.changeDetector.ref);
ListWrapper.remove(this._rootComponents, ref);
}

View File

@ -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<T> 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<T> 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<string, any>();
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<T> 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<T> 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<T> implements ChangeDetector {
this._unsubsribeFromObservables();
}
this.dispatcher = null;
this.context = null;
this.locals = null;
this.pipes = null;
@ -171,6 +192,19 @@ export class AbstractChangeDetector<T> 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<T> 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;

View File

@ -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}
}`;
}

View File

@ -205,4 +205,4 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
this.markForCheck();
}
}
}

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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<any> {
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<any> {
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 =
<any>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<any> {
}
}
/** @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<any> {
}
/** @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[],

View File

@ -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) {}
}

View File

@ -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,

View File

@ -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";
}
}

View File

@ -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) {

View File

@ -41,5 +41,5 @@ export class Locals {
}
}
clearValues(): void { MapWrapper.clearValues(this.current); }
clearLocalValues(): void { MapWrapper.clearValues(this.current); }
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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_>elementRef).parentView),
(<ElementRef_>elementRef).boundElementIndex);
return new DebugElement_((<ElementRef_>elementRef).internalElement);
}
/**

View File

@ -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;
}

View File

@ -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<Type | Provider | any[]>): ResolvedProvider[] {
var normalized = _createListOfProviders(_normalizeProviders(
providers, new Map<number, _NormalizedProvider | _NormalizedProvider[]>()));
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<number, ResolvedProvider>()));
}
/**
* 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<number, any>): any[] {
return MapWrapper.values(flattenedProviders);
export function mergeResolvedProviders(
providers: ResolvedProvider[],
normalizedProvidersMap: Map<number, ResolvedProvider>): Map<number, ResolvedProvider> {
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<Type | Provider | ProviderBuilder | any[]>,
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>):
Map<number, _NormalizedProvider | _NormalizedProvider[]> {
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<Type | Provider | ProviderBuilder
return res;
}
function _normalizeProvider(b: Provider,
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>): 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);

View File

@ -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';

View File

@ -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<ProtoViewRef>;
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
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<ProtoViewRef> {
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
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 (<any>compiler)._createProtoView(compiledHostTemplate);
clearCache() {}
}

View File

@ -138,3 +138,5 @@ export class DirectiveResolver {
}
}
}
export var CODEGEN_DIRECTIVE_RESOLVER = new DirectiveResolver();

View File

@ -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 (<ElementRef_>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<ComponentRef>;
onDispose?: () => void, projectableNodes?: any[][]): Promise<ComponentRef>;
/**
* 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<ComponentRef>;
providers?: ResolvedProvider[],
projectableNodes?: any[][]): Promise<ComponentRef>;
/**
* 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-component>Child</child-component>
* ```
*/
abstract loadNextToLocation(type: Type, location: ElementRef,
providers?: ResolvedProvider[]): Promise<ComponentRef>;
abstract loadNextToLocation(type: Type, location: ElementRef, providers?: ResolvedProvider[],
projectableNodes?: any[][]): Promise<ComponentRef>;
}
@Injectable()
export class DynamicComponentLoader_ extends DynamicComponentLoader {
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); }
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
onDispose?: () => void): Promise<ComponentRef> {
loadAsRoot(type: Type, overrideSelector: string, injector: Injector, onDispose?: () => void,
projectableNodes?: any[][]): Promise<ComponentRef> {
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<ComponentRef> {
providers: ResolvedProvider[] = null,
projectableNodes: any[][] = null): Promise<ComponentRef> {
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<ComponentRef> {
loadNextToLocation(type: Type, location: ElementRef, providers: ResolvedProvider[] = null,
projectableNodes: any[][] = null): Promise<ComponentRef> {
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(<ViewRef>hostViewRef);
if (index !== -1) {
var index = viewContainer.indexOf(hostViewRef);
if (!hostViewRef.destroyed && index !== -1) {
viewContainer.remove(index);
}
};

View File

@ -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 = <AttributeMetadata>properties.find(p => p instanceof AttributeMetadata);
return isPresent(p) ? p.attributeName : null;
}
/** @internal */
static _query(properties: any[]): QueryMetadata {
return <QueryMetadata>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<number, Visibility>) {
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<number, ResolvedProvider> = new Map<number, ResolvedProvider>();
var providerVisibilityMap: Map<number, Visibility> = new Map<number, Visibility>();
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 = <any>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(<number>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 = <DirectiveDependency>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<any>;
public dirty: boolean;
constructor(public protoQueryRef: ProtoQueryRef, private originator: AppElement) {
this.list = new QueryList<any>();
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(); }
}

View File

@ -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.');
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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 {
* </p>
* </div>
*/
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 (<ViewRef_>this.parentView).render; }
set renderView(value) { unimplemented(); }
get nativeElement(): any { return this._renderer.getNativeElementSync(this); }
get nativeElement() { return this._appElement.nativeElement; }
}

View File

@ -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;
}
}

View File

@ -31,3 +31,5 @@ export class PipeResolver {
throw new BaseException(`No Pipe decorator found on ${stringify(type)}`);
}
}
export var CODEGEN_PIPE_RESOLVER = new PipeResolver();

View File

@ -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<string, AppProtoView> = new Map<string, AppProtoView>();
private _nextTemplateId: number = 0;
constructor(private _renderer: Renderer,
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Array<Type | any[]>,
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 (<any>protoViewFactory)._createComponent(cmd);
}
function createEmbeddedTemplate(protoViewFactory: ProtoViewFactory, cmd: EmbeddedTemplateCmd,
parent: AppProtoView): AppProtoView {
return (<any>protoViewFactory)._createEmbeddedTemplate(cmd, parent);
}
class _ProtoViewInitializer implements CommandVisitor {
variableLocations: Map<string, number> = new Map<string, number>();
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(<string>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 <template> elements:
// - Elements with a var- defined need a their own element injector
// so that, when hydrating, $implicit can be set to the element.
// - <template> 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<string, number>();
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<string | number>,
directiveProviders: DirectiveProvider[]): Map<string, number> {
var directiveVariableBindings = new Map<string, number>();
for (var i = 0; i < variableNameAndValues.length; i += 2) {
var templateName = <string>variableNameAndValues[i];
var dirIndex = <number>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<string, string> {
var result = new Map<string, string>();
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<Type | Provider | any[]>): 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<string | any[]>, out: string[]): string[] {
for (var i = 0; i < arr.length; i++) {
var entry = arr[i];
if (isArray(entry)) {
_flattenStyleArr(<any[]>entry, out);
} else {
out.push(<string>entry);
}
}
return out;
}

View File

@ -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<Type, DirectiveProvider> = new Map<Type, DirectiveProvider>();
private _pipeCache: Map<Type, PipeProvider> = new Map<Type, PipeProvider>();
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);

View File

@ -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<string | number> { 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<string | number>, 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<string | number>, 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);
}
}

View File

@ -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 = <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; }
}

View File

@ -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 <template> 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<string, any>, 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<any | any[]>,
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<string, any>();
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<string, any>();
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<string, any>): 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<string, any>): 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<string, any>;
elementBinders: ElementBinder[] = null;
mergeInfo: AppProtoViewMergeInfo = null;
variableLocations: Map<string, number> = 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<string, string>, 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<string, number>) {
this.render = render;
this.elementBinders = elementBinders;
this.textBindingCount = textBindingCount;
this.mergeInfo = mergeInfo;
this.variableLocations = variableLocations;
this.protoLocals = new Map<string, any>();
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 = <AppElement>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} <ng-content> elements,` +
` but only ${givenSlotCount} slots were provided.`);
}
}

View File

@ -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.
* <!-- TODO: rename to anchorElement -->
*/
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<viewModule.AppView> {
let element = <ElementRef_>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);
}
}

View File

@ -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(<ViewRef>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((<ElementRef_>location).parentView);
return hostView.elementInjectors[(<ElementRef_>location).boundElementIndex]
.getViewContainerRef();
getViewContainer(location: ElementRef_): ViewContainerRef {
return location.internalElement.getViewContainerRef();
}
getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
var hostView = internalView((<ElementRef_>hostLocation).parentView);
var boundElementIndex = (<ElementRef_>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((<ElementRef_>hostLocation).parentView);
var boundElementIndex = (<ElementRef_>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(<ViewRef>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_>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((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
var contextView = internalView((<ElementRef_>context).parentView);
var contextBoundElementIndex = (<ElementRef_>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((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>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((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>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((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>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<string | any[]>): 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 = (<AppElement>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);
}
}
}
}
}
}

View File

@ -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();
}
}
}
}
}
}

View File

@ -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<viewModule.AppProtoView, Array<viewModule.AppView>>();
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;
}
}

View File

@ -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_>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_>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 {
* <!-- /ViewRef: outer-0 -->
* ```
*/
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}}
* <ul>
* <li *ngFor="var item of items">{{item}}</li>
* </ul>
* ```
*
* Angular desugars and compiles the template into two ProtoViews:
*
* Outer ProtoView:
* ```
* Count: {{items.length}}
* <ul>
* <template ngFor var-item [ngForOf]="items"></template>
* </ul>
* ```
*
* Inner ProtoView:
* ```
* <li>{{item}}</li>
* ```
*
* 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; }
}

View File

@ -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 <template> element
// inside of a COMPONENT view
EMBEDDED
}

View File

@ -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);

View File

@ -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';
export {RootRenderer, Renderer, RenderComponentType} from './render/api';

View File

@ -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.
*
* <!-- TODO: this is created by Renderer#createProtoView in the new compiler -->
*/
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.
*/
/*
<div>foo</div>
{{bar}}
<div>foo</div> -> view 1 / fragment 1
<ul>
<template ngFor>
<li>{{fg}}</li> -> view 2 / fragment 1
</template>
</ul>
{{bar}}
<div>foo</div> -> view 1 / fragment 1
<ul>
<template ngIf>
<li><ng-content></></li> -> view 1 / fragment 2
</template>
<template ngFor>
<li><ng-content></></li> ->
<li></li> -> view 1 / fragment 2 + view 2 / fragment 1..n-1
</template>
</ul>
{{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<string | any[]>) {}
}
/**
* 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 <template> 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).
*
* <div class="callout is-critical">
* <header>Use with caution</header>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* </div>
*/
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<string, any>): boolean;
export abstract class RootRenderer implements ParentRenderer {
abstract renderComponent(componentType: RenderComponentType): Renderer;
}

View File

@ -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<N> extends RenderFragmentRef {
constructor(public nodes: N[]) { super(); }
}
export class DefaultRenderView<N> extends RenderViewRef {
hydrated: boolean = false;
eventDispatcher: RenderEventDispatcher = null;
globalEventRemovers: Function[] = null;
constructor(public fragments: DefaultRenderFragmentRef<N>[], 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<string, any>();
locals.set('$event', event);
allowDefaultBehavior =
this.eventDispatcher.dispatchRenderEvent(boundElementIndex, eventName, locals);
}
return allowDefaultBehavior;
}
}

View File

@ -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<any>): DefaultRenderView<any> {
var view: DefaultRenderView<any>;
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<any>[] = [];
for (var i = 0; i < context.fragments.length; i++) {
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
}
view = new DefaultRenderView<any>(fragments, context.boundTextNodes, context.boundElements,
context.nativeShadowRoots, context.globalEventAdders,
context.rootContentInsertionPoints);
return view;
}
export interface NodeFactory<N> {
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<N> {
constructor(private _eventDispatcher: Function, public factory: NodeFactory<N>,
private _inplaceElement: N) {
this.isHost = isPresent((_inplaceElement));
}
private _builders: RenderViewBuilder<N>[] = [];
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<N>) {
this._builders = [];
builder.build(this);
var enqueuedBuilders = this._builders;
for (var i = 0; i < enqueuedBuilders.length; i++) {
this._build(enqueuedBuilders[i]);
}
}
enqueueComponentBuilder(component: Component<N>) {
this.componentCount++;
this._builders.push(
new RenderViewBuilder<N>(component, null, component.template, component.template.commands));
}
enqueueFragmentBuilder(parentComponent: Component<N>, parentTemplate: RenderComponentTemplate,
commands: RenderTemplateCmd[]) {
var rootNodes = [];
this.fragments.push(rootNodes);
this._builders.push(
new RenderViewBuilder<N>(parentComponent, rootNodes, parentTemplate, commands));
}
enqueueRootBuilder(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
var rootNodes = [];
this.fragments.push(rootNodes);
this._builders.push(new RenderViewBuilder<N>(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<any>): Function {
return () => nodeFactory.globalOn(target, eventName, eventHandler);
}
class RenderViewBuilder<N> implements RenderCommandVisitor {
parentStack: Array<N | Component<N>>;
constructor(public parentComponent: Component<N>, public fragmentRootNodes: N[],
public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot;
this.parentStack = [rootNodesParent];
}
build(context: BuildContext<N>) {
var cmds = this.cmds;
for (var i = 0; i < cmds.length; i++) {
cmds[i].visit(this, context);
}
}
get parent(): N | Component<N> { return this.parentStack[this.parentStack.length - 1]; }
visitText(cmd: RenderTextCmd, context: BuildContext<N>): 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<N>): any {
if (isPresent(this.parentComponent)) {
if (this.parentComponent.isRoot) {
var insertionPoint = context.factory.createRootContentInsertionPoint();
if (this.parent instanceof Component) {
context.factory.appendChild((<Component<N>>this.parent).shadowRoot, insertionPoint);
} else {
context.factory.appendChild(<N>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<N>): any {
this.parentStack.push(this._beginElement(cmd, context, null));
return null;
}
visitEndElement(context: BuildContext<N>): any {
this._endElement();
return null;
}
visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext<N>): 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<N>): any {
this._endElement();
return null;
}
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: BuildContext<N>): 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<N>,
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<N>) {
var parent = this.parent;
if (isPresent(parent)) {
if (parent instanceof Component) {
parent.addContentNode(ngContentIndex, node, context);
} else {
context.factory.appendChild(<N>parent, node);
}
} else {
this.fragmentRootNodes.push(node);
}
}
}
class Component<N> {
private contentNodesByNgContentIndex: N[][] = [];
constructor(public hostElement: N, public shadowRoot: N, public isRoot: boolean,
public template: RenderComponentTemplate) {}
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
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);
}

View File

@ -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 <ConcreteType>constructor;
}

View File

@ -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<T> extends Stream<T> {
void add(value) {
_controller.add(value);
}
void emit(value) {
_controller.add(value);
}

View File

@ -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<String, String> vars) {
throw "Dart does not support evaluating expression during runtime!";
}

View File

@ -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);
}

View File

@ -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<any /*Type | Provider | any[]*/
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}),
DomSharedStylesHost,
Testability,

View File

@ -4,7 +4,6 @@ import {Injectable, provide, Provider} from 'angular2/src/core/di';
import {AppViewListener} from 'angular2/src/core/linker/view_listener';
import {AppView} from 'angular2/src/core/linker/view';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {Renderer} from 'angular2/src/core/render/api';
import {DebugElement, DebugElement_} from 'angular2/src/core/debug/debug_element';
const NG_ID_PROPERTY = 'ngid';
@ -43,7 +42,7 @@ export function inspectNativeElement(element): DebugElement {
if (isPresent(elId)) {
var view = _allViewsById.get(elId[0]);
if (isPresent(view)) {
return new DebugElement_(view, elId[1]);
return new DebugElement_(view.appElements[elId[1]]);
}
}
return null;
@ -51,17 +50,15 @@ export function inspectNativeElement(element): DebugElement {
@Injectable()
export class DebugElementViewListener implements AppViewListener {
constructor(private _renderer: Renderer) {
DOM.setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement);
}
constructor() { DOM.setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement); }
onViewCreated(view: AppView) {
var viewId = _nextId++;
_allViewsById.set(viewId, view);
_allIdsByView.set(view, viewId);
for (var i = 0; i < view.elementRefs.length; i++) {
var el = view.elementRefs[i];
_setElementId(this._renderer.getNativeElementSync(el), [viewId, i]);
for (var i = 0; i < view.appElements.length; i++) {
var el = view.appElements[i];
_setElementId(el.nativeElement, [viewId, i]);
}
}

View File

@ -7,38 +7,18 @@ import {
RegExpWrapper,
CONST_EXPR,
stringify,
StringWrapper
StringWrapper,
isArray
} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import {DomSharedStylesHost} from './shared_styles_host';
import {WtfScopeFn, wtfLeave, wtfCreateScope} from 'angular2/src/core/profile/profile';
import {
Renderer,
RenderProtoViewRef,
RenderViewRef,
RenderElementRef,
RenderFragmentRef,
RenderViewWithFragments,
RenderTemplateCmd,
RenderEventDispatcher,
RenderComponentTemplate
} from 'angular2/core';
import {Renderer, RootRenderer, RenderComponentType} from 'angular2/core';
import {EventManager} from './events/event_manager';
import {DOCUMENT} from './dom_tokens';
import {
createRenderView,
NodeFactory,
encapsulateStyles
} from 'angular2/src/core/render/view_factory';
import {
DefaultRenderView,
DefaultRenderFragmentRef,
DefaultProtoViewRef
} from 'angular2/src/core/render/view';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {camelCaseToDashCase} from './util';
@ -48,243 +28,225 @@ const NAMESPACE_URIS =
const TEMPLATE_COMMENT_TEXT = 'template bindings={}';
var TEMPLATE_BINDINGS_EXP = /^template bindings=(.*)$/g;
export abstract class DomRenderer extends Renderer implements NodeFactory<Node> {
abstract registerComponentTemplate(template: RenderComponentTemplate);
export abstract class DomRootRenderer implements RootRenderer {
private _registeredComponents: Map<string, DomRenderer> = new Map<string, DomRenderer>();
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(<Element>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 <template> placeholders.
*/
setBindingDebugInfo(location: RenderElementRef, propertyName: string,
propertyValue: string): void {
var view: DefaultRenderView<Node> = 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 = <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<string, RenderComponentTemplate> =
new Map<string, RenderComponentTemplate>();
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 = <DefaultProtoViewRef>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 = <DefaultRenderView<Node>>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(<HTMLElement>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(<HTMLElement>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(<HTMLElement>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<Node> {
return <DefaultRenderView<Node>>viewRef;
}
function resolveInternalDomFragment(fragmentRef: RenderFragmentRef): Node[] {
return (<DefaultRenderFragmentRef<Node>>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<any | any[]>, 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]];
}
}

View File

@ -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<any /*Type | Provider | any[]*
new Provider(PLATFORM_DIRECTIVES, {useValue: COMMON_DIRECTIVES, multi: true}),
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
new Provider(ServiceMessageBrokerFactory, {useClass: ServiceMessageBrokerFactory_}),
WebWorkerRenderer,
new Provider(Renderer, {useExisting: WebWorkerRenderer}),
WebWorkerRootRenderer,
new Provider(RootRenderer, {useExisting: WebWorkerRootRenderer}),
new Provider(ON_WEB_WORKER, {useValue: true}),
RenderViewWithFragmentsStore,
RenderProtoViewRefStore,
RenderStore,
new Provider(ExceptionHandler, {useFactory: _exceptionHandler, deps: []}),
WebWorkerXHRImpl,
new Provider(XHR, {useExisting: WebWorkerXHRImpl}),
WebWorkerEventDispatcher
new Provider(XHR, {useExisting: WebWorkerXHRImpl})
]);
function _exceptionHandler(): ExceptionHandler {

View File

@ -11,7 +11,7 @@ import {
reflector,
APPLICATION_COMMON_PROVIDERS,
PLATFORM_COMMON_PROVIDERS,
Renderer,
RootRenderer,
PLATFORM_INITIALIZER,
APP_INITIALIZER
} from 'angular2/core';
@ -23,7 +23,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';
@ -46,10 +46,7 @@ import {
} from 'angular2/src/web_workers/shared/client_message_broker';
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
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';
export const WORKER_SCRIPT: OpaqueToken = CONST_EXPR(new OpaqueToken("WebWorkerScript"));
@ -72,8 +69,8 @@ export const WORKER_RENDER_APP_COMMON: Array<any /*Type | Provider | any[]*/> =
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<any /*Type | Provider | any[]*/> =
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
Serializer,
new Provider(ON_WEB_WORKER, {useValue: false}),
RenderViewWithFragmentsStore,
RenderProtoViewRefStore,
RenderStore,
DomSharedStylesHost,
Testability,
BrowserDetails,

View File

@ -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(<ViewRef>componentRef.hostView), 0);
this._componentParentView = (<ViewRef_>componentRef.hostView).internalView;
this.debugElement = new DebugElement_(this._componentParentView.appElements[0]);
this.componentInstance = this.debugElement.componentInstance;
this.nativeElement = this.debugElement.nativeElement;
this._componentParentView = internalView(<ViewRef>componentRef.hostView);
this._componentRef = componentRef;
}

View File

@ -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}),

View File

@ -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';

View File

@ -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) {
(<any>this.element[0]).id = id;
this.componentScope = scope.$new();
this.childNodes = <Node[]><any>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 = (<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);
}
}
}

View File

@ -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<any> = 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<ProtoViewRefMap> {
var promises: Array<Promise<ProtoViewRef>> = [];
private compileNg2Components(compiler: Compiler, hostViewFactoryRefMap: HostViewFactoryRefMap):
Promise<HostViewFactoryRefMap> {
var promises: Array<Promise<HostViewFactoryRef>> = [];
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<ProtoViewRef>) => {
return Promise.all(promises).then((hostViewFactories: Array<HostViewFactoryRef>) => {
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 {
(<any>directiveFactory).$inject = [NG2_PROTO_VIEW_REF_MAP, NG2_APP_VIEW_MANAGER, NG1_PARSE];
function directiveFactory(protoViewRefMap: ProtoViewRefMap, viewManager: AppViewManager,
(<any>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 = <any>element[0];
var facade = new DowngradeNg2ComponentAdapter(idPrefix + (idCount++), info, element,
attrs, scope, <Injector>parentInjector,
parse, viewManager, protoView);
parse, viewManager, hostViewFactory);
facade.setupInputs();
facade.bootstrapNg2();
facade.projectContent();

View File

@ -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);
}
}

View File

@ -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<number, RenderProtoViewRef> = new Map<number, RenderProtoViewRef>();
private _lookupByProtoView: Map<RenderProtoViewRef, number> =
new Map<RenderProtoViewRef, number>();
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 (<WebWorkerRenderProtoViewRef>ref).refNumber;
} else {
return this._lookupByProtoView.get(ref);
}
}
}
export class WebWorkerRenderProtoViewRef extends RenderProtoViewRef {
constructor(public refNumber: number) { super(); }
}

View File

@ -0,0 +1,45 @@
import {Injectable} from "angular2/src/core/di";
@Injectable()
export class RenderStore {
private _nextIndex: number = 0;
private _lookupById: Map<number, any>;
private _lookupByObject: Map<any, number>;
constructor() {
this._lookupById = new Map<number, any>();
this._lookupByObject = new Map<any, number>();
}
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);
}
}

View File

@ -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<number, RenderViewRef | RenderFragmentRef>;
private _lookupByView: Map<RenderViewRef | RenderFragmentRef, number>;
private _viewFragments: Map<RenderViewRef, RenderFragmentRef[]>;
constructor(@Inject(ON_WEB_WORKER) onWebWorker) {
this._onWebWorker = onWebWorker;
this._lookupByIndex = new Map<number, RenderViewRef | RenderFragmentRef>();
this._lookupByView = new Map<RenderViewRef | RenderFragmentRef, number>();
this._viewFragments = new Map<RenderViewRef, RenderFragmentRef[]>();
}
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 (<WebWorkerRenderFragmentRef | WebWorkerRenderViewRef>ref).serialize();
} else {
return this._lookupByView.get(ref);
}
}
serializeViewWithFragments(view: RenderViewWithFragments): {[key: string]: any} {
if (view == null) {
return null;
}
if (this._onWebWorker) {
return {
'viewRef': (<WebWorkerRenderViewRef>view.viewRef).serialize(),
'fragmentRefs': view.fragmentRefs.map(val => (<any>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 = (<any[]>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);
}
}

View File

@ -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'],
(<any[]>data['children']).map(childData => deserializeTemplateCmd(childData))),
];
export class RenderStoreObject {}

View File

@ -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<any>,
private _serializer: Serializer) {}
export class EventDispatcher {
constructor(private _sink: EventEmitter<any>, private _serializer: Serializer) {}
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>): 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

View File

@ -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(); }
}

View File

@ -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<RenderViewRef, RenderEventDispatcher> =
new Map<RenderViewRef, RenderEventDispatcher>();
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<string, any>;
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']);
}
}

View File

@ -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<string, WebWorkerRenderer> =
new Map<string, WebWorkerRenderer>();
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 = (<WebWorkerRenderViewRef>(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 =
<WebWorkerRenderNode>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<string, Function[]>;
private _getListeners(eventName: string): Function[] {
if (isBlank(this._listeners)) {
this._listeners = new Map<string, Function[]>();
}
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(); }

View File

@ -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 = '<div *ngFor="var item of items" [ngClass]="item"></div>';

View File

@ -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 {}

View File

@ -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('<div on-click="onEvent($event)">', [], 0);
eventLocals.set('$event', 'click');
changeDetector.handleEvent('click', 0, eventLocals);
changeDetector.handleEvent('click', 0, 'click');
expect(context.eventLog).toEqual(['click']);
});
@ -105,16 +102,14 @@ export function main() {
var changeDetector =
createChangeDetector('<template on-click="onEvent($event)">', [dirMeta], 0);
eventLocals.set('$event', 'click');
changeDetector.handleEvent('click', 0, eventLocals);
changeDetector.handleEvent('click', 0, 'click');
expect(context.eventLog).toEqual(['click']);
});
it('should handle events with targets', () => {
var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0);
eventLocals.set('$event', 'click');
changeDetector.handleEvent('window:click', 0, eventLocals);
changeDetector.handleEvent('window:click', 0, 'click');
expect(context.eventLog).toEqual(['click']);
});
@ -177,8 +172,7 @@ export function main() {
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
eventLocals.set('$event', 'click');
changeDetector.handleEvent('click', 0, eventLocals);
changeDetector.handleEvent('click', 0, 'click');
expect(directive.eventLog).toEqual(['click']);
});

View File

@ -56,10 +56,18 @@ var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
export function main() {
describe('ChangeDetectorCompiler', () => {
beforeEachProviders(() => TEST_PROVIDERS);
beforeEachProviders(() => [
TEST_PROVIDERS,
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, false)})
]);
var parser: TemplateParser;
var compiler: ChangeDetectionCompiler;
beforeEachProviders(() => [
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, false)})
]);
beforeEach(inject([TemplateParser, ChangeDetectionCompiler], (_parser, _compiler) => {
parser = _parser;
@ -71,35 +79,16 @@ export function main() {
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
var type =
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
var parsedTemplate = parser.parse(template, directives, 'TestComp');
var parsedTemplate = parser.parse(template, directives, [], 'TestComp');
var factories =
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
return testChangeDetector(factories[0]);
}
describe('no jit', () => {
beforeEachProviders(() => [
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, false)})
]);
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
describe('jit', () => {
beforeEachProviders(() => [
provide(ChangeDetectorGenConfig,
{useValue: new ChangeDetectorGenConfig(true, false, true)})
]);
it('should watch element properties', () => {
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
.toEqual(['elementProperty(elProp)=someValue']);
});
});
});
describe('compileComponentCodeGen', () => {
@ -108,7 +97,7 @@ export function main() {
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): Promise<string[]> {
var type =
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
var parsedTemplate = parser.parse(template, directives, 'TestComp');
var parsedTemplate = parser.parse(template, directives, [], 'TestComp');
var sourceExpressions =
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
@ -140,7 +129,7 @@ function createTestableModule(source: SourceExpressions,
export function testChangeDetector(changeDetectorFactory: Function): string[] {
var dispatcher = new TestDispatcher([], []);
var cd = changeDetectorFactory(dispatcher);
var cd = changeDetectorFactory();
var ctx = new SomeComponent();
ctx.someProp = 'someValue';
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));

View File

@ -1,7 +1,8 @@
import {isBlank} from 'angular2/src/facade/lang';
import {Pipes} from 'angular2/src/core/change_detection/pipes';
import {EventEmitter} from 'angular2/src/facade/async';
import {
ProtoChangeDetector,
ChangeDetector,
ChangeDispatcher,
DirectiveIndex,
BindingTarget
@ -10,6 +11,7 @@ import {
export class TestDirective {
eventLog: string[] = [];
dirProp: string;
click: EventEmitter<any> = new EventEmitter<any>();
onEvent(value: string) { this.eventLog.push(value); }
}
@ -17,7 +19,7 @@ export class TestDirective {
export class TestDispatcher implements ChangeDispatcher {
log: string[];
constructor(public directives: any[], public detectors: ProtoChangeDetector[]) { this.clear(); }
constructor(public directives: any[], public detectors: ChangeDetector[]) { this.clear(); }
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
@ -34,7 +36,9 @@ export class TestDispatcher implements ChangeDispatcher {
notifyAfterContentChecked() {}
notifyAfterViewChecked() {}
getDebugContext(a, b) { return null; }
notifyOnDestroy() {}
getDebugContext(a, b, c) { return null; }
_asString(value) { return (isBlank(value) ? 'null' : value.toString()); }
}

View File

@ -1,597 +0,0 @@
import {
ddescribe,
describe,
xdescribe,
it,
iit,
xit,
expect,
beforeEach,
afterEach,
AsyncTestCompleter,
inject,
beforeEachProviders
} from 'angular2/testing_internal';
import {
CONST_EXPR,
stringify,
isType,
Type,
isBlank,
serializeEnum,
IS_DART
} from 'angular2/src/facade/lang';
import {MapWrapper} from 'angular2/src/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {TemplateParser} from 'angular2/src/compiler/template_parser';
import {
CommandVisitor,
TextCmd,
NgContentCmd,
BeginElementCmd,
BeginComponentCmd,
EmbeddedTemplateCmd,
TemplateCmd,
visitAllCommands,
CompiledComponentTemplate
} from 'angular2/src/core/linker/template_commands';
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
CompileTemplateMetadata
} from 'angular2/src/compiler/directive_metadata';
import {SourceModule, SourceExpression, moduleRef} from 'angular2/src/compiler/source_module';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {evalModule} from './eval_module';
import {
escapeSingleQuoteString,
codeGenValueFn,
codeGenExportVariable,
codeGenConstConstructorCall,
MODULE_SUFFIX
} from 'angular2/src/compiler/util';
import {TEST_PROVIDERS} from './test_bindings';
const BEGIN_ELEMENT = 'BEGIN_ELEMENT';
const END_ELEMENT = 'END_ELEMENT';
const BEGIN_COMPONENT = 'BEGIN_COMPONENT';
const END_COMPONENT = 'END_COMPONENT';
const TEXT = 'TEXT';
const NG_CONTENT = 'NG_CONTENT';
const EMBEDDED_TEMPLATE = 'EMBEDDED_TEMPLATE';
// Attention: These module names have to correspond to real modules!
var THIS_MODULE_URL = `package:angular2/test/compiler/command_compiler_spec${MODULE_SUFFIX}`;
var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
var TEMPLATE_COMMANDS_MODULE_REF =
moduleRef(`package:angular2/src/core/linker/template_commands${MODULE_SUFFIX}`);
// Attention: read by eval!
export class RootComp {}
export class SomeDir {}
export class AComp {}
var RootCompTypeMeta =
new CompileTypeMetadata({name: 'RootComp', runtime: RootComp, moduleUrl: THIS_MODULE_URL});
var SomeDirTypeMeta =
new CompileTypeMetadata({name: 'SomeDir', runtime: SomeDir, moduleUrl: THIS_MODULE_URL});
var ACompTypeMeta =
new CompileTypeMetadata({name: 'AComp', runtime: AComp, moduleUrl: THIS_MODULE_URL});
var compTypeTemplateId: Map<CompileTypeMetadata, string> = MapWrapper.createFromPairs(
[[RootCompTypeMeta, 'rootCompId'], [SomeDirTypeMeta, 'someDirId'], [ACompTypeMeta, 'aCompId']]);
export function main() {
describe('CommandCompiler', () => {
beforeEachProviders(() => TEST_PROVIDERS);
var parser: TemplateParser;
var commandCompiler: CommandCompiler;
var componentTemplateFactory: Function;
beforeEach(inject([TemplateParser, CommandCompiler], (_templateParser, _commandCompiler) => {
parser = _templateParser;
commandCompiler = _commandCompiler;
}));
function createComp({type, selector, template, encapsulation, ngContentSelectors}: {
type?: CompileTypeMetadata,
selector?: string,
template?: string,
encapsulation?: ViewEncapsulation,
ngContentSelectors?: string[]
}): CompileDirectiveMetadata {
if (isBlank(encapsulation)) {
encapsulation = ViewEncapsulation.None;
}
if (isBlank(selector)) {
selector = 'root';
}
if (isBlank(ngContentSelectors)) {
ngContentSelectors = [];
}
if (isBlank(template)) {
template = '';
}
return CompileDirectiveMetadata.create({
selector: selector,
isComponent: true,
type: type,
template: new CompileTemplateMetadata({
template: template,
ngContentSelectors: ngContentSelectors,
encapsulation: encapsulation
})
});
}
function createDirective(type: CompileTypeMetadata, selector: string,
exportAs: string = null): CompileDirectiveMetadata {
return CompileDirectiveMetadata.create(
{selector: selector, exportAs: exportAs, isComponent: false, type: type});
}
function createTests(run: Function) {
describe('text', () => {
it('should create unbound text commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: 'a'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([[TEXT, 'a', false, null]]);
async.done();
});
}));
it('should create bound text commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '{{a}}'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([[TEXT, null, true, null]]);
async.done();
});
}));
});
describe('elements', () => {
it('should create unbound element commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<div a="b">'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[BEGIN_ELEMENT, 'div', ['a', 'b'], [], [], [], false, null],
[END_ELEMENT]
]);
async.done();
});
}));
it('should create bound element commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<div a="b" #someVar (click)="someHandler" (window:scroll)="scrollTo()">'
});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[
BEGIN_ELEMENT,
'div',
['a', 'b'],
[null, 'click', 'window', 'scroll'],
['someVar', null],
[],
true,
null
],
[END_ELEMENT]
]);
async.done();
});
}));
it('should create element commands with directives',
inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<div a #someVar="someExport">'});
var dir = CompileDirectiveMetadata.create({
selector: '[a]',
exportAs: 'someExport',
isComponent: false,
type: SomeDirTypeMeta,
host: {'(click)': 'doIt()', '(window:scroll)': 'doIt()', 'role': 'button'}
});
run(rootComp, [dir])
.then((data) => {
expect(data).toEqual([
[
BEGIN_ELEMENT,
'div',
['a', '', 'role', 'button'],
[null, 'click', 'window', 'scroll'],
['someVar', 0],
['SomeDirType'],
true,
null
],
[END_ELEMENT]
]);
async.done();
});
}));
it('should merge element attributes with host attributes',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<div class="origclass" style="color: red;" role="origrole" attr1>'
});
var dir = CompileDirectiveMetadata.create({
selector: 'div',
isComponent: false,
type: SomeDirTypeMeta,
host: {'class': 'newclass', 'style': 'newstyle', 'role': 'newrole', 'attr2': ''}
});
run(rootComp, [dir])
.then((data) => {
expect(data).toEqual([
[
BEGIN_ELEMENT,
'div',
[
'attr1',
'',
'attr2',
'',
'class',
'origclass newclass',
'role',
'newrole',
'style',
'color: red; newstyle'
],
[],
[],
['SomeDirType'],
true,
null
],
[END_ELEMENT]
]);
async.done();
});
}));
it('should create nested nodes', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<div>a</div>'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([
[BEGIN_ELEMENT, 'div', [], [], [], [], false, null],
[TEXT, 'a', false, null],
[END_ELEMENT]
]);
async.done();
});
}));
});
describe('components', () => {
it('should create component commands', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp(
{type: RootCompTypeMeta, template: '<a a="b" #someVar (click)="someHandler">'});
var comp = createComp({type: ACompTypeMeta, selector: 'a'});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[
BEGIN_COMPONENT,
'a',
['a', 'b'],
[null, 'click'],
['someVar', 0],
['ACompType'],
serializeEnum(ViewEncapsulation.None),
null,
'aCompId'
],
[END_COMPONENT]
]);
async.done();
});
}));
it('should store viewEncapsulation', inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<a></a>'});
var comp = createComp(
{type: ACompTypeMeta, selector: 'a', encapsulation: ViewEncapsulation.Native});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[
BEGIN_COMPONENT,
'a',
[],
[],
[],
['ACompType'],
serializeEnum(ViewEncapsulation.Native),
null,
'aCompId'
],
[END_COMPONENT]
]);
async.done();
});
}));
it('should create nested nodes and set ngContentIndex',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({type: RootCompTypeMeta, template: '<a>t</a>'});
var comp = createComp({type: ACompTypeMeta, selector: 'a', ngContentSelectors: ['*']});
run(rootComp, [comp])
.then((data) => {
expect(data).toEqual([
[
BEGIN_COMPONENT,
'a',
[],
[],
[],
['ACompType'],
serializeEnum(ViewEncapsulation.None),
null,
'aCompId'
],
[TEXT, 't', false, 0],
[END_COMPONENT]
]);
async.done();
});
}));
});
describe('embedded templates', () => {
it('should create embedded template commands', inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<template a="b"></template>'});
var dir = createDirective(SomeDirTypeMeta, '[a]');
run(rootComp, [dir], 1)
.then((data) => {
expect(data).toEqual([
[EMBEDDED_TEMPLATE, ['a', 'b'], [], ['SomeDirType'], false, null, 'cd1', []]
]);
async.done();
});
}));
it('should keep variable name and value for <template> elements',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<template #someVar="someValue" #someEmptyVar></template>'
});
var dir = createDirective(SomeDirTypeMeta, '[a]');
run(rootComp, [dir], 1)
.then((data) => {
expect(data[0][2])
.toEqual(['someVar', 'someValue', 'someEmptyVar', '$implicit']);
async.done();
});
}));
it('should keep variable name and value for template attributes',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<div template="var someVar=someValue; var someEmptyVar"></div>'
});
var dir = createDirective(SomeDirTypeMeta, '[a]');
run(rootComp, [dir], 1)
.then((data) => {
expect(data[0][2])
.toEqual(['someVar', 'someValue', 'someEmptyVar', '$implicit']);
async.done();
});
}));
it('should created nested nodes', inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<template>t</template>'});
run(rootComp, [], 1)
.then((data) => {
expect(data).toEqual([
[
EMBEDDED_TEMPLATE,
[],
[],
[],
false,
null,
'cd1',
[[TEXT, 't', false, null]]
]
]);
async.done();
});
}));
it('should calculate wether the template is merged based on nested ng-content elements',
inject([AsyncTestCompleter], (async) => {
var rootComp = createComp({
type: RootCompTypeMeta,
template: '<template><ng-content></ng-content></template>'
});
run(rootComp, [], 1)
.then((data) => {
expect(data).toEqual(
[[EMBEDDED_TEMPLATE, [], [], [], true, null, 'cd1', [[NG_CONTENT, null]]]]);
async.done();
});
}));
});
describe('ngContent', () => {
it('should create ng-content commands', inject([AsyncTestCompleter], (async) => {
var rootComp =
createComp({type: RootCompTypeMeta, template: '<ng-content></ng-content>'});
run(rootComp, [])
.then((data) => {
expect(data).toEqual([[NG_CONTENT, null]]);
async.done();
});
}));
});
}
describe('compileComponentRuntime', () => {
beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return () => new CompiledComponentTemplate(compTypeTemplateId.get(directive.type), null,
null, null);
};
});
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
embeddedTemplateCount: number = 0): Promise<any[][]> {
var changeDetectorFactories = [];
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
(function(i) { changeDetectorFactories.push((_) => `cd${i}`); })(i);
}
var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name);
var commands = commandCompiler.compileComponentRuntime(
component, parsedTemplate, changeDetectorFactories, componentTemplateFactory);
return PromiseWrapper.resolve(humanize(commands));
}
createTests(run);
});
describe('compileComponentCodeGen', () => {
beforeEach(() => {
componentTemplateFactory = (directive: CompileDirectiveMetadata) => {
return `${directive.type.name}TemplateGetter`;
};
});
function run(component: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
embeddedTemplateCount: number = 0): Promise<any[][]> {
var testDeclarations = [];
var changeDetectorFactoryExpressions = [];
for (var i = 0; i < embeddedTemplateCount + 1; i++) {
var fnName = `cd${i}`;
testDeclarations.push(`${codeGenValueFn(['_'], ` 'cd${i}' `, fnName)};`);
changeDetectorFactoryExpressions.push(fnName);
}
for (var i = 0; i < directives.length; i++) {
var directive = directives[i];
if (directive.isComponent) {
var nestedTemplate =
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'CompiledComponentTemplate')}('${compTypeTemplateId.get(directive.type)}', null, null, null)`;
var getterName = `${directive.type.name}TemplateGetter`;
testDeclarations.push(`${codeGenValueFn([], nestedTemplate, getterName)};`)
}
}
var parsedTemplate =
parser.parse(component.template.template, directives, component.type.name);
var sourceExpression = commandCompiler.compileComponentCodeGen(
component, parsedTemplate, changeDetectorFactoryExpressions, componentTemplateFactory);
testDeclarations.forEach(decl => sourceExpression.declarations.push(decl));
var testableModule = createTestableModule(sourceExpression).getSourceWithImports();
return evalModule(testableModule.source, testableModule.imports, null);
}
createTests(run);
});
});
}
// Attention: read by eval!
export function humanize(cmds: TemplateCmd[]): any[][] {
var visitor = new CommandHumanizer();
visitAllCommands(visitor, cmds);
return visitor.result;
}
function checkAndStringifyType(type: Type): string {
expect(isType(type)).toBe(true);
return `${stringify(type)}Type`;
}
class CommandHumanizer implements CommandVisitor {
result: any[][] = [];
visitText(cmd: TextCmd, context: any): any {
this.result.push([TEXT, cmd.value, cmd.isBound, cmd.ngContentIndex]);
return null;
}
visitNgContent(cmd: NgContentCmd, context: any): any {
this.result.push([NG_CONTENT, cmd.ngContentIndex]);
return null;
}
visitBeginElement(cmd: BeginElementCmd, context: any): any {
this.result.push([
BEGIN_ELEMENT,
cmd.name,
cmd.attrNameAndValues,
cmd.eventTargetAndNames,
cmd.variableNameAndValues,
cmd.directives.map(checkAndStringifyType),
cmd.isBound,
cmd.ngContentIndex
]);
return null;
}
visitEndElement(context: any): any {
this.result.push([END_ELEMENT]);
return null;
}
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
this.result.push([
BEGIN_COMPONENT,
cmd.name,
cmd.attrNameAndValues,
cmd.eventTargetAndNames,
cmd.variableNameAndValues,
cmd.directives.map(checkAndStringifyType),
serializeEnum(cmd.encapsulation),
cmd.ngContentIndex,
cmd.templateId
]);
return null;
}
visitEndComponent(context: any): any {
this.result.push([END_COMPONENT]);
return null;
}
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
this.result.push([
EMBEDDED_TEMPLATE,
cmd.attrNameAndValues,
cmd.variableNameAndValues,
cmd.directives.map(checkAndStringifyType),
cmd.isMerged,
cmd.ngContentIndex,
cmd.changeDetectorFactory(null),
humanize(cmd.children)
]);
return null;
}
}
function createTestableModule(source: SourceExpression): SourceModule {
var resultExpression = `${THIS_MODULE_REF}humanize(${source.expression})`;
var testableSource = `${source.declarations.join('\n')}
${codeGenValueFn(['_'], resultExpression, '_run')};
${codeGenExportVariable('run')}_run;
`;
return new SourceModule(null, testableSource);
}

View File

@ -15,73 +15,40 @@ import {
import {Component, View, provide} from 'angular2/core';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {SpyProtoViewFactory} from '../core/spies';
import {
CompiledHostTemplate,
CompiledComponentTemplate,
BeginComponentCmd
} from 'angular2/src/core/linker/template_commands';
import {RuntimeCompiler} from 'angular2/src/compiler/runtime_compiler';
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
import {AppProtoView} from 'angular2/src/core/linker/view';
import {SpyTemplateCompiler} from './spies';
import {TemplateCompiler} from 'angular2/src/compiler/compiler';
import {RuntimeCompiler, RuntimeCompiler_} from 'angular2/src/compiler/runtime_compiler';
import {HostViewFactory} from 'angular2/src/core/linker/view';
export function main() {
describe('RuntimeCompiler', () => {
var compiler: RuntimeCompiler;
var compiler: RuntimeCompiler_;
var templateCompilerSpy;
var someHostViewFactory;
beforeEachProviders(() => {
templateCompilerSpy = new SpyTemplateCompiler();
someHostViewFactory = new HostViewFactory(null, null);
templateCompilerSpy.spy('compileHostComponentRuntime')
.andReturn(PromiseWrapper.resolve(someHostViewFactory));
return [provide(TemplateCompiler, {useValue: templateCompilerSpy})];
});
beforeEach(inject([RuntimeCompiler], (_compiler) => { compiler = _compiler; }));
describe('compileInHost', () => {
var protoViewFactorySpy;
var someProtoView;
beforeEachProviders(() => {
protoViewFactorySpy = new SpyProtoViewFactory();
someProtoView = new AppProtoView(null, null, null, null, null, null, null);
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
return [provide(ProtoViewFactory, {useValue: protoViewFactorySpy})];
});
it('should compile the template via TemplateCompiler',
inject([AsyncTestCompleter], (async) => {
var cht: CompiledHostTemplate;
protoViewFactorySpy.spy('createHost')
.andCallFake((_cht) => {
cht = _cht;
return someProtoView;
});
compiler.compileInHost(SomeComponent)
.then((_) => {
var beginComponentCmd = <BeginComponentCmd>cht.template.commands[0];
expect(beginComponentCmd.name).toEqual('some-comp');
async.done();
});
}));
});
it('should cache the result', inject([AsyncTestCompleter], (async) => {
PromiseWrapper
.all([compiler.compileInHost(SomeComponent), compiler.compileInHost(SomeComponent)])
.then((protoViewRefs) => {
expect(protoViewRefs[0]).toBe(protoViewRefs[1]);
it('compileInHost should compile the template via TemplateCompiler',
inject([AsyncTestCompleter], (async) => {
compiler.compileInHost(SomeComponent)
.then((hostViewFactoryRef) => {
expect(hostViewFactoryRef.internalHostViewFactory).toBe(someHostViewFactory);
async.done();
});
}));
it('should clear the cache',
inject([AsyncTestCompleter], (async) => {compiler.compileInHost(SomeComponent)
.then((protoViewRef1) => {
compiler.clearCache();
compiler.compileInHost(SomeComponent)
.then((protoViewRef2) => {
expect(protoViewRef1)
.not.toBe(protoViewRef2);
async.done();
});
})}));
it('should clear the cache', () => {
compiler.clearCache();
expect(templateCompilerSpy.spy('clearCache')).toHaveBeenCalled();
});
});
}

View File

@ -46,7 +46,7 @@ export function main() {
describe('getMetadata', () => {
it('should read metadata',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
var meta = resolver.getMetadata(ComponentWithEverything);
var meta = resolver.getDirectiveMetadata(ComponentWithEverything);
expect(meta.selector).toEqual('someSelector');
expect(meta.exportAs).toEqual('someExportAs');
expect(meta.isComponent).toBe(true);
@ -70,7 +70,8 @@ export function main() {
it('should use the moduleUrl from the reflector if none is given',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
var value: string = resolver.getMetadata(ComponentWithoutModuleId).type.moduleUrl;
var value: string =
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl;
var expectedEndValue =
IS_DART ? 'base/dist/dart/angular2/test/compiler/runtime_metadata_spec.dart' : './';
expect(value.endsWith(expectedEndValue)).toBe(true);
@ -82,7 +83,7 @@ export function main() {
it('should return the directive metadatas',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
.toEqual([resolver.getMetadata(SomeDirective)]);
.toEqual([resolver.getDirectiveMetadata(SomeDirective)]);
}));
describe("platform directives", () => {
@ -91,7 +92,10 @@ export function main() {
it('should include platform directives when available',
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
.toEqual([resolver.getMetadata(ADirective), resolver.getMetadata(SomeDirective)]);
.toEqual([
resolver.getDirectiveMetadata(ADirective),
resolver.getDirectiveMetadata(SomeDirective)
]);
}));
});
});

View File

@ -1,9 +1,15 @@
library core.spies;
library compiler.spies;
import 'package:angular2/src/compiler/xhr.dart';
import 'package:angular2/testing_internal.dart';
import 'package:angular2/src/compiler/template_compiler.dart';
@proxy
class SpyXHR extends SpyObject implements XHR {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyTemplateCompiler extends SpyObject implements TemplateCompiler {
noSuchMethod(m) => super.noSuchMethod(m);
}

View File

@ -1,7 +1,12 @@
import {XHR} from 'angular2/src/compiler/xhr';
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
import {SpyObject, proxy} from 'angular2/testing_internal';
export class SpyXHR extends SpyObject {
constructor() { super(XHR); }
}
export class SpyTemplateCompiler extends SpyObject {
constructor() { super(TemplateCompiler); }
}

Some files were not shown because too many files have changed in this diff Show More