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:
parent
a08f50badd
commit
7ae23adaff
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -80,7 +80,6 @@ export function selectValueAccessor(dir: NgControl,
|
|||
var defaultAccessor;
|
||||
var builtinAccessor;
|
||||
var customAccessor;
|
||||
|
||||
valueAccessors.forEach(v => {
|
||||
if (v instanceof DefaultValueAccessor) {
|
||||
defaultAccessor = v;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
}
|
|
@ -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}),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
}`;
|
||||
}
|
||||
|
|
|
@ -205,4 +205,4 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
|
|||
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
||||
this.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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[],
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -41,5 +41,5 @@ export class Locals {
|
|||
}
|
||||
}
|
||||
|
||||
clearValues(): void { MapWrapper.clearValues(this.current); }
|
||||
clearLocalValues(): void { MapWrapper.clearValues(this.current); }
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -138,3 +138,5 @@ export class DirectiveResolver {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export var CODEGEN_DIRECTIVE_RESOLVER = new DirectiveResolver();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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(); }
|
||||
}
|
|
@ -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
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -31,3 +31,5 @@ export class PipeResolver {
|
|||
throw new BaseException(`No Pipe decorator found on ${stringify(type)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export var CODEGEN_PIPE_RESOLVER = new PipeResolver();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -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.`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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!";
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]];
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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}),
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 {}
|
|
@ -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
|
||||
|
|
|
@ -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(); }
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
}
|
|
@ -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(); }
|
||||
|
|
|
@ -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>';
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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']);
|
||||
});
|
||||
|
||||
|
|
|
@ -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}));
|
||||
|
|
|
@ -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()); }
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
]);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue