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:
@ -169,10 +169,10 @@ export class NgClass implements DoCheck, OnDestroy {
|
|||||||
if (className.indexOf(' ') > -1) {
|
if (className.indexOf(' ') > -1) {
|
||||||
var classes = className.split(/\s+/g);
|
var classes = className.split(/\s+/g);
|
||||||
for (var i = 0, len = classes.length; i < len; i++) {
|
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 {
|
} else {
|
||||||
this._renderer.setElementClass(this._ngEl, className, enabled);
|
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
IterableDiffers,
|
IterableDiffers,
|
||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
TemplateRef,
|
TemplateRef,
|
||||||
ViewRef
|
EmbeddedViewRef
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
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++) {
|
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 {
|
class RecordViewTuple {
|
||||||
view: ViewRef;
|
view: EmbeddedViewRef;
|
||||||
record: any;
|
record: any;
|
||||||
constructor(record, view) {
|
constructor(record, view) {
|
||||||
this.record = record;
|
this.record = record;
|
||||||
|
@ -92,6 +92,6 @@ export class NgStyle implements DoCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _setStyle(name: string, val: string): void {
|
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) {}
|
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||||
|
|
||||||
writeValue(value: any): void {
|
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; }
|
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
||||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||||
|
@ -31,7 +31,7 @@ export class DefaultValueAccessor implements ControlValueAccessor {
|
|||||||
|
|
||||||
writeValue(value: any): void {
|
writeValue(value: any): void {
|
||||||
var normalizedValue = isBlank(value) ? '' : value;
|
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; }
|
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
|
||||||
|
@ -31,7 +31,7 @@ export class NumberValueAccessor implements ControlValueAccessor {
|
|||||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||||
|
|
||||||
writeValue(value: number): void {
|
writeValue(value: number): void {
|
||||||
this._renderer.setElementProperty(this._elementRef, 'value', value);
|
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(fn: (_: number) => void): void {
|
registerOnChange(fn: (_: number) => void): void {
|
||||||
|
@ -51,7 +51,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
|||||||
|
|
||||||
writeValue(value: any): void {
|
writeValue(value: any): void {
|
||||||
this.value = value;
|
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; }
|
registerOnChange(fn: () => any): void { this.onChange = fn; }
|
||||||
|
@ -80,7 +80,6 @@ export function selectValueAccessor(dir: NgControl,
|
|||||||
var defaultAccessor;
|
var defaultAccessor;
|
||||||
var builtinAccessor;
|
var builtinAccessor;
|
||||||
var customAccessor;
|
var customAccessor;
|
||||||
|
|
||||||
valueAccessors.forEach(v => {
|
valueAccessors.forEach(v => {
|
||||||
if (v instanceof DefaultValueAccessor) {
|
if (v instanceof DefaultValueAccessor) {
|
||||||
defaultAccessor = v;
|
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 {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ export function createChangeDetectorDefinitions(
|
|||||||
|
|
||||||
class ProtoViewVisitor implements TemplateAstVisitor {
|
class ProtoViewVisitor implements TemplateAstVisitor {
|
||||||
viewIndex: number;
|
viewIndex: number;
|
||||||
boundTextCount: number = 0;
|
nodeCount: number = 0;
|
||||||
boundElementCount: number = 0;
|
boundElementCount: number = 0;
|
||||||
variableNames: string[] = [];
|
variableNames: string[] = [];
|
||||||
bindingRecords: BindingRecord[] = [];
|
bindingRecords: BindingRecord[] = [];
|
||||||
@ -57,6 +57,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||||
|
this.nodeCount++;
|
||||||
this.boundElementCount++;
|
this.boundElementCount++;
|
||||||
templateVisitAll(this, ast.outputs);
|
templateVisitAll(this, ast.outputs);
|
||||||
for (var i = 0; i < ast.directives.length; i++) {
|
for (var i = 0; i < ast.directives.length; i++) {
|
||||||
@ -73,6 +74,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitElement(ast: ElementAst, context: any): any {
|
visitElement(ast: ElementAst, context: any): any {
|
||||||
|
this.nodeCount++;
|
||||||
if (ast.isBound()) {
|
if (ast.isBound()) {
|
||||||
this.boundElementCount++;
|
this.boundElementCount++;
|
||||||
}
|
}
|
||||||
@ -132,14 +134,20 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
}
|
}
|
||||||
visitAttr(ast: AttrAst, context: any): any { return null; }
|
visitAttr(ast: AttrAst, context: any): any { return null; }
|
||||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
visitBoundText(ast: BoundTextAst, context: any): any {
|
||||||
var boundTextIndex = this.boundTextCount++;
|
var nodeIndex = this.nodeCount++;
|
||||||
this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, boundTextIndex));
|
this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, nodeIndex));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitText(ast: TextAst, context: any): any {
|
||||||
|
this.nodeCount++;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
visitText(ast: TextAst, context: any): any { return null; }
|
|
||||||
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
||||||
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
||||||
var directiveMetadata = ast.directive;
|
var directiveMetadata = ast.directive;
|
||||||
|
var outputsArray = [];
|
||||||
|
StringMapWrapper.forEach(ast.directive.outputs, (eventName, dirProperty) => outputsArray.push(
|
||||||
|
[dirProperty, eventName]));
|
||||||
var directiveRecord = new DirectiveRecord({
|
var directiveRecord = new DirectiveRecord({
|
||||||
directiveIndex: directiveIndex,
|
directiveIndex: directiveIndex,
|
||||||
callAfterContentInit:
|
callAfterContentInit:
|
||||||
@ -153,7 +161,9 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
|||||||
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
|
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
|
||||||
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
|
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
|
||||||
callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -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);
|
this.directiveRecords.push(directiveRecord);
|
||||||
|
|
||||||
|
@ -3,6 +3,9 @@ import {SourceExpressions, moduleRef} from './source_module';
|
|||||||
import {
|
import {
|
||||||
ChangeDetectorJITGenerator
|
ChangeDetectorJITGenerator
|
||||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
} 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 {createChangeDetectorDefinitions} from './change_definition_factory';
|
||||||
import {IS_DART, isJsObject, CONST_EXPR} from 'angular2/src/facade/lang';
|
import {IS_DART, isJsObject, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
@ -23,6 +26,12 @@ const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
|||||||
const UTIL = "ChangeDetectionUtil";
|
const UTIL = "ChangeDetectionUtil";
|
||||||
const CHANGE_DETECTOR_STATE = "ChangeDetectorState";
|
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(
|
var ABSTRACT_CHANGE_DETECTOR_MODULE = moduleRef(
|
||||||
`package:angular2/src/core/change_detection/abstract_change_detector${MODULE_SUFFIX}`);
|
`package:angular2/src/core/change_detection/abstract_change_detector${MODULE_SUFFIX}`);
|
||||||
var UTIL_MODULE =
|
var UTIL_MODULE =
|
||||||
@ -45,14 +54,8 @@ export class ChangeDetectionCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
|
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
|
||||||
if (IS_DART || !this._genConfig.useJit) {
|
var proto = new DynamicProtoChangeDetector(definition);
|
||||||
var proto = new DynamicProtoChangeDetector(definition);
|
return () => proto.instantiate();
|
||||||
return (dispatcher) => proto.instantiate(dispatcher);
|
|
||||||
} else {
|
|
||||||
return new ChangeDetectorJITGenerator(definition, UTIL, ABSTRACT_CHANGE_DETECTOR,
|
|
||||||
CHANGE_DETECTOR_STATE)
|
|
||||||
.generate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
||||||
@ -81,7 +84,7 @@ export class ChangeDetectionCompiler {
|
|||||||
definition, `${UTIL_MODULE}${UTIL}`,
|
definition, `${UTIL_MODULE}${UTIL}`,
|
||||||
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`,
|
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`,
|
||||||
`${CONSTANTS_MODULE}${CHANGE_DETECTOR_STATE}`);
|
`${CONSTANTS_MODULE}${CHANGE_DETECTOR_STATE}`);
|
||||||
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
|
factories.push(`function() { return new ${codegen.typeName}(); }`);
|
||||||
sourcePart = codegen.generateSource();
|
sourcePart = codegen.generateSource();
|
||||||
}
|
}
|
||||||
index++;
|
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 {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||||
import {StyleCompiler} from 'angular2/src/compiler/style_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 {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
|
||||||
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||||
@ -44,7 +45,8 @@ export const COMPILER_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
|||||||
RuntimeMetadataResolver,
|
RuntimeMetadataResolver,
|
||||||
DEFAULT_PACKAGE_URL_PROVIDER,
|
DEFAULT_PACKAGE_URL_PROVIDER,
|
||||||
StyleCompiler,
|
StyleCompiler,
|
||||||
CommandCompiler,
|
ProtoViewCompiler,
|
||||||
|
ViewCompiler,
|
||||||
ChangeDetectionCompiler,
|
ChangeDetectionCompiler,
|
||||||
new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}),
|
new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}),
|
||||||
TemplateCompiler,
|
TemplateCompiler,
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
RegExpWrapper,
|
RegExpWrapper,
|
||||||
StringWrapper
|
StringWrapper
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
@ -21,6 +22,16 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i
|
|||||||
// group 2: "event" from "(event)"
|
// group 2: "event" from "(event)"
|
||||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
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.
|
* Metadata regarding compilation of a type.
|
||||||
*/
|
*/
|
||||||
@ -107,7 +118,7 @@ export class CompileTemplateMetadata {
|
|||||||
/**
|
/**
|
||||||
* Metadata regarding compilation of a directive.
|
* Metadata regarding compilation of a directive.
|
||||||
*/
|
*/
|
||||||
export class CompileDirectiveMetadata {
|
export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||||
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
|
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
|
||||||
outputs, host, lifecycleHooks, template}: {
|
outputs, host, lifecycleHooks, template}: {
|
||||||
type?: CompileTypeMetadata,
|
type?: CompileTypeMetadata,
|
||||||
@ -241,6 +252,7 @@ export class CompileDirectiveMetadata {
|
|||||||
|
|
||||||
toJson(): {[key: string]: any} {
|
toJson(): {[key: string]: any} {
|
||||||
return {
|
return {
|
||||||
|
'class': 'Directive',
|
||||||
'isComponent': this.isComponent,
|
'isComponent': this.isComponent,
|
||||||
'dynamicLoadable': this.dynamicLoadable,
|
'dynamicLoadable': this.dynamicLoadable,
|
||||||
'selector': this.selector,
|
'selector': this.selector,
|
||||||
@ -284,3 +296,38 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
|
|||||||
selector: '*'
|
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
|
||||||
|
};
|
||||||
|
397
modules/angular2/src/compiler/proto_view_compiler.ts
Normal file
397
modules/angular2/src/compiler/proto_view_compiler.ts
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
Type,
|
||||||
|
isString,
|
||||||
|
StringWrapper,
|
||||||
|
IS_DART,
|
||||||
|
CONST_EXPR
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {
|
||||||
|
SetWrapper,
|
||||||
|
StringMapWrapper,
|
||||||
|
ListWrapper,
|
||||||
|
MapWrapper
|
||||||
|
} from 'angular2/src/facade/collection';
|
||||||
|
import {
|
||||||
|
TemplateAst,
|
||||||
|
TemplateAstVisitor,
|
||||||
|
NgContentAst,
|
||||||
|
EmbeddedTemplateAst,
|
||||||
|
ElementAst,
|
||||||
|
VariableAst,
|
||||||
|
BoundEventAst,
|
||||||
|
BoundElementPropertyAst,
|
||||||
|
AttrAst,
|
||||||
|
BoundTextAst,
|
||||||
|
TextAst,
|
||||||
|
DirectiveAst,
|
||||||
|
BoundDirectivePropertyAst,
|
||||||
|
templateVisitAll
|
||||||
|
} from './template_ast';
|
||||||
|
import {
|
||||||
|
CompileTypeMetadata,
|
||||||
|
CompileDirectiveMetadata,
|
||||||
|
CompilePipeMetadata
|
||||||
|
} from './directive_metadata';
|
||||||
|
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
||||||
|
import {AppProtoView, AppView} from 'angular2/src/core/linker/view';
|
||||||
|
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||||
|
import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
|
||||||
|
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||||
|
import {
|
||||||
|
escapeSingleQuoteString,
|
||||||
|
codeGenConstConstructorCall,
|
||||||
|
codeGenValueFn,
|
||||||
|
codeGenFnHeader,
|
||||||
|
MODULE_SUFFIX,
|
||||||
|
codeGenStringMap,
|
||||||
|
Expression,
|
||||||
|
Statement
|
||||||
|
} from './util';
|
||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
|
export const PROTO_VIEW_JIT_IMPORTS = CONST_EXPR(
|
||||||
|
{'AppProtoView': AppProtoView, 'AppProtoElement': AppProtoElement, 'ViewType': ViewType});
|
||||||
|
|
||||||
|
// TODO: have a single file that reexports everything needed for
|
||||||
|
// codegen explicitly
|
||||||
|
// - helps understanding what codegen works against
|
||||||
|
// - less imports in codegen code
|
||||||
|
export var APP_VIEW_MODULE_REF = moduleRef('package:angular2/src/core/linker/view' + MODULE_SUFFIX);
|
||||||
|
export var VIEW_TYPE_MODULE_REF =
|
||||||
|
moduleRef('package:angular2/src/core/linker/view_type' + MODULE_SUFFIX);
|
||||||
|
export var APP_EL_MODULE_REF =
|
||||||
|
moduleRef('package:angular2/src/core/linker/element' + MODULE_SUFFIX);
|
||||||
|
export var METADATA_MODULE_REF =
|
||||||
|
moduleRef('package:angular2/src/core/metadata/view' + MODULE_SUFFIX);
|
||||||
|
|
||||||
|
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||||
|
const CLASS_ATTR = 'class';
|
||||||
|
const STYLE_ATTR = 'style';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ProtoViewCompiler {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
compileProtoViewRuntime(metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
|
||||||
|
template: TemplateAst[], pipes: CompilePipeMetadata[]):
|
||||||
|
CompileProtoViews<AppProtoView, AppProtoElement, any> {
|
||||||
|
var protoViewFactory = new RuntimeProtoViewFactory(metadataCache, component, pipes);
|
||||||
|
var allProtoViews = [];
|
||||||
|
protoViewFactory.createCompileProtoView(template, [], [], allProtoViews);
|
||||||
|
return new CompileProtoViews<AppProtoView, AppProtoElement, any>([], allProtoViews);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileProtoViewCodeGen(resolvedMetadataCacheExpr: Expression,
|
||||||
|
component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||||
|
pipes: CompilePipeMetadata[]):
|
||||||
|
CompileProtoViews<Expression, Expression, string> {
|
||||||
|
var protoViewFactory = new CodeGenProtoViewFactory(resolvedMetadataCacheExpr, component, pipes);
|
||||||
|
var allProtoViews = [];
|
||||||
|
var allStatements = [];
|
||||||
|
protoViewFactory.createCompileProtoView(template, [], allStatements, allProtoViews);
|
||||||
|
return new CompileProtoViews<Expression, Expression, string>(
|
||||||
|
allStatements.map(stmt => stmt.statement), allProtoViews);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompileProtoViews<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
|
||||||
|
constructor(public declarations: STATEMENT[],
|
||||||
|
public protoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
|
||||||
|
constructor(public embeddedTemplateIndex: number,
|
||||||
|
public protoElements: CompileProtoElement<APP_PROTO_EL>[],
|
||||||
|
public protoView: APP_PROTO_VIEW) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CompileProtoElement<APP_PROTO_EL> {
|
||||||
|
constructor(public boundElementIndex, public attrNameAndValues: string[][],
|
||||||
|
public variableNameAndValues: string[][], public renderEvents: BoundEventAst[],
|
||||||
|
public directives: CompileDirectiveMetadata[], public embeddedTemplateIndex: number,
|
||||||
|
public appProtoEl: APP_PROTO_EL) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
|
||||||
|
context: any): any {
|
||||||
|
templateVisitAll(visitor, asts, context);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ProtoViewFactory<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
|
||||||
|
constructor(public component: CompileDirectiveMetadata) {}
|
||||||
|
|
||||||
|
abstract createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
||||||
|
templateVariableBindings: string[][],
|
||||||
|
targetStatements: STATEMENT[]): APP_PROTO_VIEW;
|
||||||
|
|
||||||
|
abstract createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
||||||
|
variableNameAndValues: string[][],
|
||||||
|
directives: CompileDirectiveMetadata[],
|
||||||
|
targetStatements: STATEMENT[]): APP_PROTO_EL;
|
||||||
|
|
||||||
|
createCompileProtoView(template: TemplateAst[], templateVariableBindings: string[][],
|
||||||
|
targetStatements: STATEMENT[],
|
||||||
|
targetProtoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]):
|
||||||
|
CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
|
||||||
|
var embeddedTemplateIndex = targetProtoViews.length;
|
||||||
|
// Note: targetProtoViews needs to be in depth first order.
|
||||||
|
// So we "reserve" a space here that we fill after the recursion is done
|
||||||
|
targetProtoViews.push(null);
|
||||||
|
var builder = new ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, any>(
|
||||||
|
this, targetStatements, targetProtoViews);
|
||||||
|
templateVisitAll(builder, template);
|
||||||
|
var viewType = getViewType(this.component, embeddedTemplateIndex);
|
||||||
|
var appProtoView = this.createAppProtoView(embeddedTemplateIndex, viewType,
|
||||||
|
templateVariableBindings, targetStatements);
|
||||||
|
var cpv = new CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>(
|
||||||
|
embeddedTemplateIndex, builder.protoElements, appProtoView);
|
||||||
|
targetProtoViews[embeddedTemplateIndex] = cpv;
|
||||||
|
return cpv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CodeGenProtoViewFactory extends ProtoViewFactory<Expression, Expression, Statement> {
|
||||||
|
private _nextVarId: number = 0;
|
||||||
|
|
||||||
|
constructor(public resolvedMetadataCacheExpr: Expression, component: CompileDirectiveMetadata,
|
||||||
|
public pipes: CompilePipeMetadata[]) {
|
||||||
|
super(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _nextProtoViewVar(embeddedTemplateIndex: number): string {
|
||||||
|
return `appProtoView${this._nextVarId++}_${this.component.type.name}${embeddedTemplateIndex}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
||||||
|
templateVariableBindings: string[][],
|
||||||
|
targetStatements: Statement[]): Expression {
|
||||||
|
var protoViewVarName = this._nextProtoViewVar(embeddedTemplateIndex);
|
||||||
|
var viewTypeExpr = codeGenViewType(viewType);
|
||||||
|
var pipesExpr = embeddedTemplateIndex === 0 ?
|
||||||
|
codeGenTypesArray(this.pipes.map(pipeMeta => pipeMeta.type)) :
|
||||||
|
null;
|
||||||
|
var statement =
|
||||||
|
`var ${protoViewVarName} = ${APP_VIEW_MODULE_REF}AppProtoView.create(${this.resolvedMetadataCacheExpr.expression}, ${viewTypeExpr}, ${pipesExpr}, ${codeGenStringMap(templateVariableBindings)});`;
|
||||||
|
targetStatements.push(new Statement(statement));
|
||||||
|
return new Expression(protoViewVarName);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
||||||
|
variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
|
||||||
|
targetStatements: Statement[]): Expression {
|
||||||
|
var varName = `appProtoEl${this._nextVarId++}_${this.component.type.name}`;
|
||||||
|
var value = `${APP_EL_MODULE_REF}AppProtoElement.create(
|
||||||
|
${this.resolvedMetadataCacheExpr.expression},
|
||||||
|
${boundElementIndex},
|
||||||
|
${codeGenStringMap(attrNameAndValues)},
|
||||||
|
${codeGenDirectivesArray(directives)},
|
||||||
|
${codeGenStringMap(variableNameAndValues)}
|
||||||
|
)`;
|
||||||
|
var statement = `var ${varName} = ${value};`;
|
||||||
|
targetStatements.push(new Statement(statement));
|
||||||
|
return new Expression(varName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RuntimeProtoViewFactory extends ProtoViewFactory<AppProtoView, AppProtoElement, any> {
|
||||||
|
constructor(public metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
|
||||||
|
public pipes: CompilePipeMetadata[]) {
|
||||||
|
super(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
||||||
|
templateVariableBindings: string[][], targetStatements: any[]): AppProtoView {
|
||||||
|
var pipes =
|
||||||
|
embeddedTemplateIndex === 0 ? this.pipes.map(pipeMeta => pipeMeta.type.runtime) : [];
|
||||||
|
var templateVars = keyValueArrayToStringMap(templateVariableBindings);
|
||||||
|
return AppProtoView.create(this.metadataCache, viewType, pipes, templateVars);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
||||||
|
variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
|
||||||
|
targetStatements: any[]): AppProtoElement {
|
||||||
|
var attrs = keyValueArrayToStringMap(attrNameAndValues);
|
||||||
|
return AppProtoElement.create(this.metadataCache, boundElementIndex, attrs,
|
||||||
|
directives.map(dirMeta => dirMeta.type.runtime),
|
||||||
|
keyValueArrayToStringMap(variableNameAndValues));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> implements
|
||||||
|
TemplateAstVisitor {
|
||||||
|
protoElements: CompileProtoElement<APP_PROTO_EL>[] = [];
|
||||||
|
boundElementCount: number = 0;
|
||||||
|
|
||||||
|
constructor(public factory: ProtoViewFactory<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT>,
|
||||||
|
public allStatements: STATEMENT[],
|
||||||
|
public allProtoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
|
||||||
|
|
||||||
|
private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
|
||||||
|
attrAsts: TemplateAst[]): string[][] {
|
||||||
|
var attrs = visitAndReturnContext(this, attrAsts, {});
|
||||||
|
directives.forEach(directiveMeta => {
|
||||||
|
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
||||||
|
var prevValue = attrs[name];
|
||||||
|
attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return mapToKeyValueArray(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitBoundText(ast: BoundTextAst, context: any): any { return null; }
|
||||||
|
visitText(ast: TextAst, context: any): any { return null; }
|
||||||
|
|
||||||
|
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
||||||
|
|
||||||
|
visitElement(ast: ElementAst, context: any): any {
|
||||||
|
var boundElementIndex = null;
|
||||||
|
if (ast.isBound()) {
|
||||||
|
boundElementIndex = this.boundElementCount++;
|
||||||
|
}
|
||||||
|
var component = ast.getComponent();
|
||||||
|
|
||||||
|
var variableNameAndValues: string[][] = [];
|
||||||
|
if (isBlank(component)) {
|
||||||
|
ast.exportAsVars.forEach((varAst) => { variableNameAndValues.push([varAst.name, null]); });
|
||||||
|
}
|
||||||
|
var directives = [];
|
||||||
|
var renderEvents: Map<string, BoundEventAst> =
|
||||||
|
visitAndReturnContext(this, ast.outputs, new Map<string, BoundEventAst>());
|
||||||
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||||
|
directiveAst.visit(this, new DirectiveContext(index, boundElementIndex, renderEvents,
|
||||||
|
variableNameAndValues, directives));
|
||||||
|
});
|
||||||
|
var renderEventArray = [];
|
||||||
|
renderEvents.forEach((eventAst, _) => renderEventArray.push(eventAst));
|
||||||
|
|
||||||
|
var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
|
||||||
|
this._addProtoElement(ast.isBound(), boundElementIndex, attrNameAndValues,
|
||||||
|
variableNameAndValues, renderEventArray, directives, null);
|
||||||
|
templateVisitAll(this, ast.children);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||||
|
var boundElementIndex = this.boundElementCount++;
|
||||||
|
var directives: CompileDirectiveMetadata[] = [];
|
||||||
|
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||||
|
directiveAst.visit(
|
||||||
|
this, new DirectiveContext(index, boundElementIndex, new Map<string, BoundEventAst>(), [],
|
||||||
|
directives));
|
||||||
|
});
|
||||||
|
|
||||||
|
var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
|
||||||
|
var templateVariableBindings = ast.vars.map(
|
||||||
|
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
|
||||||
|
var nestedProtoView = this.factory.createCompileProtoView(
|
||||||
|
ast.children, templateVariableBindings, this.allStatements, this.allProtoViews);
|
||||||
|
this._addProtoElement(true, boundElementIndex, attrNameAndValues, [], [], directives,
|
||||||
|
nestedProtoView.embeddedTemplateIndex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addProtoElement(isBound: boolean, boundElementIndex, attrNameAndValues: string[][],
|
||||||
|
variableNameAndValues: string[][], renderEvents: BoundEventAst[],
|
||||||
|
directives: CompileDirectiveMetadata[], embeddedTemplateIndex: number) {
|
||||||
|
var appProtoEl = null;
|
||||||
|
if (isBound) {
|
||||||
|
appProtoEl =
|
||||||
|
this.factory.createAppProtoElement(boundElementIndex, attrNameAndValues,
|
||||||
|
variableNameAndValues, directives, this.allStatements);
|
||||||
|
}
|
||||||
|
var compileProtoEl = new CompileProtoElement<APP_PROTO_EL>(
|
||||||
|
boundElementIndex, attrNameAndValues, variableNameAndValues, renderEvents, directives,
|
||||||
|
embeddedTemplateIndex, appProtoEl);
|
||||||
|
this.protoElements.push(compileProtoEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||||
|
visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any {
|
||||||
|
attrNameAndValues[ast.name] = ast.value;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any {
|
||||||
|
ctx.targetDirectives.push(ast.directive);
|
||||||
|
templateVisitAll(this, ast.hostEvents, ctx.hostEventTargetAndNames);
|
||||||
|
ast.exportAsVars.forEach(
|
||||||
|
varAst => { ctx.targetVariableNameAndValues.push([varAst.name, ctx.index]); });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||||
|
eventTargetAndNames.set(ast.fullName, ast);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||||
|
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
||||||
|
var entryArray = [];
|
||||||
|
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
|
||||||
|
// We need to sort to get a defined output order
|
||||||
|
// for tests and for caching generated artifacts...
|
||||||
|
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
|
||||||
|
var keyValueArray = [];
|
||||||
|
entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
|
||||||
|
return keyValueArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
||||||
|
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
||||||
|
return `${attrValue1} ${attrValue2}`;
|
||||||
|
} else {
|
||||||
|
return attrValue2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DirectiveContext {
|
||||||
|
constructor(public index: number, public boundElementIndex: number,
|
||||||
|
public hostEventTargetAndNames: Map<string, BoundEventAst>,
|
||||||
|
public targetVariableNameAndValues: any[][],
|
||||||
|
public targetDirectives: CompileDirectiveMetadata[]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyValueArrayToStringMap(keyValueArray: any[][]): {[key: string]: any} {
|
||||||
|
var stringMap: {[key: string]: string} = {};
|
||||||
|
for (var i = 0; i < keyValueArray.length; i++) {
|
||||||
|
var entry = keyValueArray[i];
|
||||||
|
stringMap[entry[0]] = entry[1];
|
||||||
|
}
|
||||||
|
return stringMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
|
||||||
|
var expressions = directives.map(directiveType => typeRef(directiveType.type));
|
||||||
|
return `[${expressions.join(',')}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function codeGenTypesArray(types: CompileTypeMetadata[]): string {
|
||||||
|
var expressions = types.map(typeRef);
|
||||||
|
return `[${expressions.join(',')}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function codeGenViewType(value: ViewType): string {
|
||||||
|
if (IS_DART) {
|
||||||
|
return `${VIEW_TYPE_MODULE_REF}${value}`;
|
||||||
|
} else {
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeRef(type: CompileTypeMetadata): string {
|
||||||
|
return `${moduleRef(type.moduleUrl)}${type.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
|
||||||
|
if (embeddedTemplateIndex > 0) {
|
||||||
|
return ViewType.EMBEDDED;
|
||||||
|
} else if (component.type.isHost) {
|
||||||
|
return ViewType.HOST;
|
||||||
|
} else {
|
||||||
|
return ViewType.COMPONENT;
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,23 @@
|
|||||||
import {Compiler, Compiler_, internalCreateProtoView} from 'angular2/src/core/linker/compiler';
|
import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler';
|
||||||
import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
|
import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
|
||||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
|
||||||
import {TemplateCompiler} from './template_compiler';
|
import {TemplateCompiler} from './template_compiler';
|
||||||
|
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
import {Type} from 'angular2/src/facade/lang';
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
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()
|
@Injectable()
|
||||||
export class RuntimeCompiler_ extends Compiler_ implements RuntimeCompiler {
|
export class RuntimeCompiler_ extends Compiler_ implements RuntimeCompiler {
|
||||||
constructor(_protoViewFactory: ProtoViewFactory, private _templateCompiler: TemplateCompiler) {
|
constructor(private _templateCompiler: TemplateCompiler) { super(); }
|
||||||
super(_protoViewFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
compileInHost(componentType: Type): Promise<ProtoViewRef> {
|
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
||||||
return this._templateCompiler.compileHostComponentRuntime(componentType)
|
return this._templateCompiler.compileHostComponentRuntime(componentType)
|
||||||
.then(compiledHostTemplate => internalCreateProtoView(this, compiledHostTemplate));
|
.then(hostViewFactory => new HostViewFactoryRef_(hostViewFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
|
@ -11,25 +11,29 @@ import {BaseException} from 'angular2/src/facade/exceptions';
|
|||||||
import * as cpl from './directive_metadata';
|
import * as cpl from './directive_metadata';
|
||||||
import * as md from 'angular2/src/core/metadata/directives';
|
import * as md from 'angular2/src/core/metadata/directives';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
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 {ViewResolver} from 'angular2/src/core/linker/view_resolver';
|
||||||
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||||
import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector';
|
import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector';
|
||||||
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
|
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
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 {MODULE_SUFFIX} from './util';
|
||||||
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RuntimeMetadataResolver {
|
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,
|
constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
|
||||||
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[]) {}
|
private _viewResolver: ViewResolver,
|
||||||
|
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[],
|
||||||
|
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Type[]) {}
|
||||||
|
|
||||||
getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||||
var meta = this._cache.get(directiveType);
|
var meta = this._directiveCache.get(directiveType);
|
||||||
if (isBlank(meta)) {
|
if (isBlank(meta)) {
|
||||||
var dirMeta = this._directiveResolver.resolve(directiveType);
|
var dirMeta = this._directiveResolver.resolve(directiveType);
|
||||||
var moduleUrl = null;
|
var moduleUrl = null;
|
||||||
@ -63,7 +67,23 @@ export class RuntimeMetadataResolver {
|
|||||||
host: dirMeta.host,
|
host: dirMeta.host,
|
||||||
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType))
|
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;
|
return meta;
|
||||||
}
|
}
|
||||||
@ -72,13 +92,25 @@ export class RuntimeMetadataResolver {
|
|||||||
var view = this._viewResolver.resolve(component);
|
var view = this._viewResolver.resolve(component);
|
||||||
var directives = flattenDirectives(view, this._platformDirectives);
|
var directives = flattenDirectives(view, this._platformDirectives);
|
||||||
for (var i = 0; i < directives.length; i++) {
|
for (var i = 0; i < directives.length; i++) {
|
||||||
if (!isValidDirective(directives[i])) {
|
if (!isValidType(directives[i])) {
|
||||||
throw new BaseException(
|
throw new BaseException(
|
||||||
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
`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;
|
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 {
|
function flattenArray(tree: any[], out: Array<Type | any[]>): void {
|
||||||
for (var i = 0; i < tree.length; i++) {
|
for (var i = 0; i < tree.length; i++) {
|
||||||
var item = resolveForwardRef(tree[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);
|
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.
|
* Represents generated source code with module references. Internal to the Angular compiler.
|
||||||
*/
|
*/
|
||||||
export class SourceModule {
|
export class SourceModule {
|
||||||
|
static getSourceWithoutImports(sourceWithModuleRefs: string): string {
|
||||||
|
return StringWrapper.replaceAllMapped(sourceWithModuleRefs, MODULE_REGEXP, (match) => '');
|
||||||
|
}
|
||||||
|
|
||||||
constructor(public moduleUrl: string, public sourceWithModuleRefs: string) {}
|
constructor(public moduleUrl: string, public sourceWithModuleRefs: string) {}
|
||||||
|
|
||||||
getSourceWithImports(): SourceWithImports {
|
getSourceWithImports(): SourceWithImports {
|
||||||
|
@ -14,7 +14,10 @@ import {
|
|||||||
MODULE_SUFFIX
|
MODULE_SUFFIX
|
||||||
} from './util';
|
} from './util';
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
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()
|
@Injectable()
|
||||||
export class StyleCompiler {
|
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 {
|
import {
|
||||||
CompiledComponentTemplate,
|
IS_DART,
|
||||||
TemplateCmd,
|
Type,
|
||||||
CompiledHostTemplate,
|
Json,
|
||||||
BeginComponentCmd
|
isBlank,
|
||||||
} from 'angular2/src/core/linker/template_commands';
|
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 {
|
import {
|
||||||
createHostComponentMeta,
|
createHostComponentMeta,
|
||||||
CompileDirectiveMetadata,
|
CompileDirectiveMetadata,
|
||||||
CompileTypeMetadata,
|
CompileTypeMetadata,
|
||||||
CompileTemplateMetadata
|
CompileTemplateMetadata,
|
||||||
|
CompilePipeMetadata,
|
||||||
|
CompileMetadataWithType
|
||||||
} from './directive_metadata';
|
} 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 {Injectable} from 'angular2/src/core/di';
|
||||||
import {SourceModule, moduleRef} from './source_module';
|
import {SourceModule, moduleRef, SourceExpression} from './source_module';
|
||||||
import {ChangeDetectionCompiler} from './change_detector_compiler';
|
import {ChangeDetectionCompiler, CHANGE_DETECTION_JIT_IMPORTS} from './change_detector_compiler';
|
||||||
import {StyleCompiler} from './style_compiler';
|
import {StyleCompiler} from './style_compiler';
|
||||||
import {CommandCompiler} from './command_compiler';
|
import {ViewCompiler, VIEW_JIT_IMPORTS} from './view_compiler';
|
||||||
import {TemplateParser} from './template_parser';
|
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 {TemplateNormalizer} from './template_normalizer';
|
||||||
import {RuntimeMetadataResolver} from './runtime_metadata';
|
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 {
|
import {
|
||||||
codeGenExportVariable,
|
codeGenExportVariable,
|
||||||
escapeSingleQuoteString,
|
escapeSingleQuoteString,
|
||||||
codeGenValueFn,
|
codeGenValueFn,
|
||||||
MODULE_SUFFIX
|
MODULE_SUFFIX,
|
||||||
|
addAll,
|
||||||
|
Expression
|
||||||
} from './util';
|
} 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,
|
* An internal module of the Angular compiler that begins with component types,
|
||||||
* extracts templates, and eventually produces a compiled version of the component
|
* extracts templates, and eventually produces a compiled version of the component
|
||||||
@ -40,15 +77,16 @@ import {
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class TemplateCompiler {
|
export class TemplateCompiler {
|
||||||
private _hostCacheKeys = new Map<Type, any>();
|
private _hostCacheKeys = new Map<Type, any>();
|
||||||
private _compiledTemplateCache = new Map<any, CompiledComponentTemplate>();
|
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
|
||||||
private _compiledTemplateDone = new Map<any, Promise<CompiledComponentTemplate>>();
|
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
|
||||||
private _nextTemplateId: number = 0;
|
|
||||||
|
|
||||||
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
||||||
private _templateNormalizer: TemplateNormalizer,
|
private _templateNormalizer: TemplateNormalizer,
|
||||||
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
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):
|
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
||||||
Promise<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);
|
var hostCacheKey = this._hostCacheKeys.get(type);
|
||||||
if (isBlank(hostCacheKey)) {
|
if (isBlank(hostCacheKey)) {
|
||||||
hostCacheKey = new Object();
|
hostCacheKey = new Object();
|
||||||
this._hostCacheKeys.set(type, hostCacheKey);
|
this._hostCacheKeys.set(type, hostCacheKey);
|
||||||
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
var hostMeta: CompileDirectiveMetadata =
|
var hostMeta: CompileDirectiveMetadata =
|
||||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
|
|
||||||
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
|
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], [], new Set());
|
||||||
}
|
}
|
||||||
return this._compiledTemplateDone.get(hostCacheKey)
|
return this._compiledTemplateDone.get(hostCacheKey)
|
||||||
.then(compiledTemplate => new CompiledHostTemplate(compiledTemplate));
|
.then((compiledTemplate: CompiledTemplate) =>
|
||||||
|
new HostViewFactory(compMeta.selector, compiledTemplate.viewFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
this._hostCacheKeys.clear();
|
|
||||||
this._styleCompiler.clearCache();
|
this._styleCompiler.clearCache();
|
||||||
this._compiledTemplateCache.clear();
|
this._compiledTemplateCache.clear();
|
||||||
this._compiledTemplateDone.clear();
|
this._compiledTemplateDone.clear();
|
||||||
}
|
this._hostCacheKeys.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||||
@ -175,38 +143,22 @@ export class TemplateCompiler {
|
|||||||
throw new BaseException('No components given');
|
throw new BaseException('No components given');
|
||||||
}
|
}
|
||||||
var declarations = [];
|
var declarations = [];
|
||||||
var templateArguments = [];
|
|
||||||
var componentMetas: CompileDirectiveMetadata[] = [];
|
|
||||||
components.forEach(componentWithDirs => {
|
components.forEach(componentWithDirs => {
|
||||||
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||||
assertComponent(compMeta);
|
assertComponent(compMeta);
|
||||||
componentMetas.push(compMeta);
|
this._compileComponentCodeGen(compMeta, componentWithDirs.directives, componentWithDirs.pipes,
|
||||||
|
declarations);
|
||||||
this._processTemplateCodeGen(compMeta, componentWithDirs.directives, declarations,
|
|
||||||
templateArguments);
|
|
||||||
if (compMeta.dynamicLoadable) {
|
if (compMeta.dynamicLoadable) {
|
||||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||||
componentMetas.push(hostMeta);
|
var viewFactoryExpression =
|
||||||
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
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;
|
var moduleUrl = components[0].component.type.moduleUrl;
|
||||||
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
|
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
|
||||||
}
|
}
|
||||||
@ -215,31 +167,149 @@ export class TemplateCompiler {
|
|||||||
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
|
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
|
||||||
directives: CompileDirectiveMetadata[],
|
|
||||||
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata,
|
||||||
let uniqueDirectives = removeDuplicates(directives);
|
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 styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
|
||||||
var parsedTemplate = this._templateParser.parse(compMeta.template.template, uniqueDirectives,
|
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(
|
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
||||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||||
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
var protoViewExprs = this._protoViewCompiler.compileProtoViewCodeGen(
|
||||||
compMeta, parsedTemplate, changeDetectorsExprs.expressions,
|
new Expression(resolvedMetadataCacheExpr), compMeta, parsedTemplate, pipes);
|
||||||
codeGenComponentTemplateFactory);
|
var viewFactoryExpr = this._viewCompiler.compileComponentCodeGen(
|
||||||
|
compMeta, parsedTemplate, styleExpr, protoViewExprs.protoViews, changeDetectorsExprs,
|
||||||
|
codeGenComponentViewFactoryName);
|
||||||
|
|
||||||
addAll(styleExpr.declarations, targetDeclarations);
|
|
||||||
addAll(changeDetectorsExprs.declarations, targetDeclarations);
|
addAll(changeDetectorsExprs.declarations, targetDeclarations);
|
||||||
addAll(commandsExpr.declarations, targetDeclarations);
|
addAll(protoViewExprs.declarations, targetDeclarations);
|
||||||
|
addAll(viewFactoryExpr.declarations, targetDeclarations);
|
||||||
|
|
||||||
targetTemplateArguments.push(
|
return viewFactoryExpr.expression;
|
||||||
[changeDetectorsExprs.expressions[0], commandsExpr.expression, styleExpr.expression]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NormalizedComponentWithViewDirectives {
|
export class NormalizedComponentWithViewDirectives {
|
||||||
constructor(public component: CompileDirectiveMetadata,
|
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) {
|
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 {
|
function templateModuleUrl(moduleUrl: string): string {
|
||||||
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
|
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
|
||||||
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
|
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAll(source: any[], target: any[]) {
|
|
||||||
for (var i = 0; i < source.length; i++) {
|
function codeGenHostViewFactoryName(type: CompileTypeMetadata): string {
|
||||||
target.push(source[i]);
|
return `hostViewFactory_${type.name}`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
|
function codeGenComponentViewFactoryName(nestedCompType: CompileDirectiveMetadata): string {
|
||||||
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateGetterName(nestedCompType.type)}`;
|
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 = [];
|
let res = [];
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
let hasMatch =
|
let hasMatch =
|
||||||
@ -284,3 +352,100 @@ function removeDuplicates(items: CompileDirectiveMetadata[]): CompileDirectiveMe
|
|||||||
});
|
});
|
||||||
return res;
|
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 {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
||||||
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
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 {HtmlParser} from './html_parser';
|
||||||
import {splitNsName} from './html_tags';
|
import {splitNsName} from './html_tags';
|
||||||
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
||||||
|
import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
|
||||||
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -88,9 +89,10 @@ export class TemplateParser {
|
|||||||
private _htmlParser: HtmlParser,
|
private _htmlParser: HtmlParser,
|
||||||
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
|
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
|
||||||
|
|
||||||
parse(template: string, directives: CompileDirectiveMetadata[],
|
parse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||||
templateUrl: string): TemplateAst[] {
|
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 htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
|
||||||
var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
|
var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
|
||||||
var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors);
|
var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors);
|
||||||
@ -111,9 +113,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
errors: TemplateParseError[] = [];
|
errors: TemplateParseError[] = [];
|
||||||
directivesIndex = new Map<CompileDirectiveMetadata, number>();
|
directivesIndex = new Map<CompileDirectiveMetadata, number>();
|
||||||
ngContentCount: number = 0;
|
ngContentCount: number = 0;
|
||||||
|
pipesByName: Map<string, CompilePipeMetadata>;
|
||||||
|
|
||||||
constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser,
|
constructor(directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||||
private _schemaRegistry: ElementSchemaRegistry) {
|
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {
|
||||||
this.selectorMatcher = new SelectorMatcher();
|
this.selectorMatcher = new SelectorMatcher();
|
||||||
ListWrapper.forEachWithIndex(directives,
|
ListWrapper.forEachWithIndex(directives,
|
||||||
(directive: CompileDirectiveMetadata, index: number) => {
|
(directive: CompileDirectiveMetadata, index: number) => {
|
||||||
@ -121,6 +124,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
this.selectorMatcher.addSelectables(selector, directive);
|
this.selectorMatcher.addSelectables(selector, directive);
|
||||||
this.directivesIndex.set(directive, index);
|
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) {
|
private _reportError(message: string, sourceSpan: ParseSourceSpan) {
|
||||||
@ -130,7 +135,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||||
var sourceInfo = sourceSpan.start.toString();
|
var sourceInfo = sourceSpan.start.toString();
|
||||||
try {
|
try {
|
||||||
return this._exprParser.parseInterpolation(value, sourceInfo);
|
var ast = this._exprParser.parseInterpolation(value, sourceInfo);
|
||||||
|
this._checkPipes(ast, sourceSpan);
|
||||||
|
return ast;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._reportError(`${e}`, sourceSpan);
|
this._reportError(`${e}`, sourceSpan);
|
||||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||||
@ -140,7 +147,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||||
var sourceInfo = sourceSpan.start.toString();
|
var sourceInfo = sourceSpan.start.toString();
|
||||||
try {
|
try {
|
||||||
return this._exprParser.parseAction(value, sourceInfo);
|
var ast = this._exprParser.parseAction(value, sourceInfo);
|
||||||
|
this._checkPipes(ast, sourceSpan);
|
||||||
|
return ast;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._reportError(`${e}`, sourceSpan);
|
this._reportError(`${e}`, sourceSpan);
|
||||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||||
@ -150,7 +159,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||||
var sourceInfo = sourceSpan.start.toString();
|
var sourceInfo = sourceSpan.start.toString();
|
||||||
try {
|
try {
|
||||||
return this._exprParser.parseBinding(value, sourceInfo);
|
var ast = this._exprParser.parseBinding(value, sourceInfo);
|
||||||
|
this._checkPipes(ast, sourceSpan);
|
||||||
|
return ast;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._reportError(`${e}`, sourceSpan);
|
this._reportError(`${e}`, sourceSpan);
|
||||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||||
@ -160,13 +171,31 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||||||
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
|
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
|
||||||
var sourceInfo = sourceSpan.start.toString();
|
var sourceInfo = sourceSpan.start.toString();
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
this._reportError(`${e}`, sourceSpan);
|
this._reportError(`${e}`, sourceSpan);
|
||||||
return [];
|
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 {
|
visitText(ast: HtmlTextAst, component: Component): any {
|
||||||
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
|
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||||
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
|
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 EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
|
||||||
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
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 CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
var DASH_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 MODULE_SUFFIX = IS_DART ? '.dart' : '.js';
|
||||||
|
|
||||||
|
export var CONST_VAR = IS_DART ? 'const' : 'var';
|
||||||
|
|
||||||
export function camelCaseToDashCase(input: string): string {
|
export function camelCaseToDashCase(input: string): string {
|
||||||
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
||||||
(m) => { return '-' + m[1].toLowerCase(); });
|
(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 {
|
export function codeGenValueFn(params: string[], value: string, fnName: string = ''): string {
|
||||||
if (IS_DART) {
|
if (IS_DART) {
|
||||||
return `${fnName}(${params.join(',')}) => ${value}`;
|
return `${codeGenFnHeader(params, fnName)} => ${value}`;
|
||||||
} else {
|
} 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 {
|
export function codeGenToString(expr: string): string {
|
||||||
if (IS_DART) {
|
if (IS_DART) {
|
||||||
return `'\${${expr}}'`;
|
return `'\${${expr}}'`;
|
||||||
@ -86,3 +102,77 @@ export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
|||||||
return defaultValues;
|
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;
|
||||||
|
}
|
||||||
|
600
modules/angular2/src/compiler/view_compiler.ts
Normal file
600
modules/angular2/src/compiler/view_compiler.ts
Normal file
@ -0,0 +1,600 @@
|
|||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
Type,
|
||||||
|
isString,
|
||||||
|
StringWrapper,
|
||||||
|
IS_DART,
|
||||||
|
CONST_EXPR
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {
|
||||||
|
TemplateAst,
|
||||||
|
TemplateAstVisitor,
|
||||||
|
NgContentAst,
|
||||||
|
EmbeddedTemplateAst,
|
||||||
|
ElementAst,
|
||||||
|
VariableAst,
|
||||||
|
BoundEventAst,
|
||||||
|
BoundElementPropertyAst,
|
||||||
|
AttrAst,
|
||||||
|
BoundTextAst,
|
||||||
|
TextAst,
|
||||||
|
DirectiveAst,
|
||||||
|
BoundDirectivePropertyAst,
|
||||||
|
templateVisitAll
|
||||||
|
} from './template_ast';
|
||||||
|
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
||||||
|
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
||||||
|
import {
|
||||||
|
AppProtoView,
|
||||||
|
AppView,
|
||||||
|
flattenNestedViewRenderNodes,
|
||||||
|
checkSlotCount
|
||||||
|
} from 'angular2/src/core/linker/view';
|
||||||
|
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||||
|
import {AppViewManager_} from 'angular2/src/core/linker/view_manager';
|
||||||
|
import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
|
||||||
|
import {Renderer, ParentRenderer} from 'angular2/src/core/render/api';
|
||||||
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||||
|
import {
|
||||||
|
escapeSingleQuoteString,
|
||||||
|
codeGenConstConstructorCall,
|
||||||
|
codeGenValueFn,
|
||||||
|
codeGenFnHeader,
|
||||||
|
MODULE_SUFFIX,
|
||||||
|
Statement,
|
||||||
|
escapeValue,
|
||||||
|
codeGenArray,
|
||||||
|
codeGenFlatArray,
|
||||||
|
Expression,
|
||||||
|
flattenArray,
|
||||||
|
CONST_VAR
|
||||||
|
} from './util';
|
||||||
|
import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
|
||||||
|
|
||||||
|
import {
|
||||||
|
APP_VIEW_MODULE_REF,
|
||||||
|
APP_EL_MODULE_REF,
|
||||||
|
METADATA_MODULE_REF,
|
||||||
|
CompileProtoView,
|
||||||
|
CompileProtoElement
|
||||||
|
} from './proto_view_compiler';
|
||||||
|
|
||||||
|
export const VIEW_JIT_IMPORTS = CONST_EXPR({
|
||||||
|
'AppView': AppView,
|
||||||
|
'AppElement': AppElement,
|
||||||
|
'flattenNestedViewRenderNodes': flattenNestedViewRenderNodes,
|
||||||
|
'checkSlotCount': checkSlotCount
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ViewCompiler {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||||
|
styles: Array<string | any[]>,
|
||||||
|
protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
|
||||||
|
changeDetectorFactories: Function[],
|
||||||
|
componentViewFactory: Function): Function {
|
||||||
|
var viewFactory = new RuntimeViewFactory(component, styles, protoViews, changeDetectorFactories,
|
||||||
|
componentViewFactory);
|
||||||
|
return viewFactory.createViewFactory(template, 0, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||||
|
styles: SourceExpression,
|
||||||
|
protoViews: CompileProtoView<Expression, Expression>[],
|
||||||
|
changeDetectorFactoryExpressions: SourceExpressions,
|
||||||
|
componentViewFactory: Function): SourceExpression {
|
||||||
|
var viewFactory = new CodeGenViewFactory(
|
||||||
|
component, styles, protoViews, changeDetectorFactoryExpressions, componentViewFactory);
|
||||||
|
var targetStatements: Statement[] = [];
|
||||||
|
var viewFactoryExpression = viewFactory.createViewFactory(template, 0, targetStatements);
|
||||||
|
return new SourceExpression(targetStatements.map(stmt => stmt.statement),
|
||||||
|
viewFactoryExpression.expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ViewFactory<EXPRESSION, STATEMENT> {
|
||||||
|
createText(renderer: EXPRESSION, parent: EXPRESSION, text: string,
|
||||||
|
targetStatements: STATEMENT[]): EXPRESSION;
|
||||||
|
|
||||||
|
createElement(renderer: EXPRESSION, parent: EXPRESSION, name: string, rootSelector: EXPRESSION,
|
||||||
|
targetStatements: STATEMENT[]): EXPRESSION;
|
||||||
|
|
||||||
|
createTemplateAnchor(renderer: EXPRESSION, parent: EXPRESSION,
|
||||||
|
targetStatements: STATEMENT[]): EXPRESSION;
|
||||||
|
|
||||||
|
createGlobalEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
|
||||||
|
eventAst: BoundEventAst, targetStatements: STATEMENT[]): EXPRESSION;
|
||||||
|
|
||||||
|
createElementEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
|
||||||
|
renderNode: EXPRESSION, eventAst: BoundEventAst,
|
||||||
|
targetStatements: STATEMENT[]);
|
||||||
|
|
||||||
|
setElementAttribute(renderer: EXPRESSION, renderNode: EXPRESSION, attrName: string,
|
||||||
|
attrValue: string, targetStatements: STATEMENT[]);
|
||||||
|
|
||||||
|
createAppElement(appProtoEl: EXPRESSION, view: EXPRESSION, renderNode: EXPRESSION,
|
||||||
|
parentAppEl: EXPRESSION, embeddedViewFactory: EXPRESSION,
|
||||||
|
targetStatements: STATEMENT[]): EXPRESSION;
|
||||||
|
|
||||||
|
createAndSetComponentView(renderer: EXPRESSION, viewManager: EXPRESSION, view: EXPRESSION,
|
||||||
|
appEl: EXPRESSION, component: CompileDirectiveMetadata,
|
||||||
|
contentNodesByNgContentIndex: EXPRESSION[][],
|
||||||
|
targetStatements: STATEMENT[]);
|
||||||
|
|
||||||
|
getProjectedNodes(projectableNodes: EXPRESSION, ngContentIndex: number): EXPRESSION;
|
||||||
|
|
||||||
|
appendProjectedNodes(renderer: EXPRESSION, parent: EXPRESSION, nodes: EXPRESSION,
|
||||||
|
targetStatements: STATEMENT[]);
|
||||||
|
|
||||||
|
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
||||||
|
targetStatements: STATEMENT[]): EXPRESSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CodeGenViewFactory implements ViewFactory<Expression, Statement> {
|
||||||
|
private _nextVarId: number = 0;
|
||||||
|
constructor(public component: CompileDirectiveMetadata, public styles: SourceExpression,
|
||||||
|
public protoViews: CompileProtoView<Expression, Expression>[],
|
||||||
|
public changeDetectorExpressions: SourceExpressions,
|
||||||
|
public componentViewFactory: Function) {}
|
||||||
|
|
||||||
|
private _nextVar(prefix: string): string {
|
||||||
|
return `${prefix}${this._nextVarId++}_${this.component.type.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _nextRenderVar(): string { return this._nextVar('render'); }
|
||||||
|
|
||||||
|
private _nextAppVar(): string { return this._nextVar('app'); }
|
||||||
|
|
||||||
|
private _nextDisposableVar(): string {
|
||||||
|
return `disposable${this._nextVarId++}_${this.component.type.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
createText(renderer: Expression, parent: Expression, text: string,
|
||||||
|
targetStatements: Statement[]): Expression {
|
||||||
|
var varName = this._nextRenderVar();
|
||||||
|
var statement =
|
||||||
|
`var ${varName} = ${renderer.expression}.createText(${isPresent(parent) ? parent.expression : null}, ${escapeSingleQuoteString(text)});`;
|
||||||
|
targetStatements.push(new Statement(statement));
|
||||||
|
return new Expression(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
createElement(renderer: Expression, parentRenderNode: Expression, name: string,
|
||||||
|
rootSelector: Expression, targetStatements: Statement[]): Expression {
|
||||||
|
var varName = this._nextRenderVar();
|
||||||
|
var valueExpr;
|
||||||
|
if (isPresent(rootSelector)) {
|
||||||
|
valueExpr = `${rootSelector.expression} == null ?
|
||||||
|
${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)}) :
|
||||||
|
${renderer.expression}.selectRootElement(${rootSelector.expression});`;
|
||||||
|
} else {
|
||||||
|
valueExpr =
|
||||||
|
`${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)})`;
|
||||||
|
}
|
||||||
|
var statement = `var ${varName} = ${valueExpr};`;
|
||||||
|
targetStatements.push(new Statement(statement));
|
||||||
|
return new Expression(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
createTemplateAnchor(renderer: Expression, parentRenderNode: Expression,
|
||||||
|
targetStatements: Statement[]): Expression {
|
||||||
|
var varName = this._nextRenderVar();
|
||||||
|
var valueExpr =
|
||||||
|
`${renderer.expression}.createTemplateAnchor(${isPresent(parentRenderNode) ? parentRenderNode.expression : null});`;
|
||||||
|
targetStatements.push(new Statement(`var ${varName} = ${valueExpr}`));
|
||||||
|
return new Expression(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
createGlobalEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
|
||||||
|
eventAst: BoundEventAst, targetStatements: Statement[]): Expression {
|
||||||
|
var disposableVar = this._nextDisposableVar();
|
||||||
|
var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
|
||||||
|
targetStatements.push(new Statement(
|
||||||
|
`var ${disposableVar} = ${renderer.expression}.listenGlobal(${escapeValue(eventAst.target)}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
|
||||||
|
return new Expression(disposableVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
createElementEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
|
||||||
|
renderNode: Expression, eventAst: BoundEventAst,
|
||||||
|
targetStatements: Statement[]) {
|
||||||
|
var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
|
||||||
|
targetStatements.push(new Statement(
|
||||||
|
`${renderer.expression}.listen(${renderNode.expression}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
|
||||||
|
}
|
||||||
|
|
||||||
|
setElementAttribute(renderer: Expression, renderNode: Expression, attrName: string,
|
||||||
|
attrValue: string, targetStatements: Statement[]) {
|
||||||
|
targetStatements.push(new Statement(
|
||||||
|
`${renderer.expression}.setElementAttribute(${renderNode.expression}, ${escapeSingleQuoteString(attrName)}, ${escapeSingleQuoteString(attrValue)});`));
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppElement(appProtoEl: Expression, appView: Expression, renderNode: Expression,
|
||||||
|
parentAppEl: Expression, embeddedViewFactory: Expression,
|
||||||
|
targetStatements: Statement[]): Expression {
|
||||||
|
var appVar = this._nextAppVar();
|
||||||
|
var varValue =
|
||||||
|
`new ${APP_EL_MODULE_REF}AppElement(${appProtoEl.expression}, ${appView.expression},
|
||||||
|
${isPresent(parentAppEl) ? parentAppEl.expression : null}, ${renderNode.expression}, ${isPresent(embeddedViewFactory) ? embeddedViewFactory.expression : null})`;
|
||||||
|
targetStatements.push(new Statement(`var ${appVar} = ${varValue};`));
|
||||||
|
return new Expression(appVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAndSetComponentView(renderer: Expression, viewManager: Expression, view: Expression,
|
||||||
|
appEl: Expression, component: CompileDirectiveMetadata,
|
||||||
|
contentNodesByNgContentIndex: Expression[][],
|
||||||
|
targetStatements: Statement[]) {
|
||||||
|
var codeGenContentNodes;
|
||||||
|
if (this.component.type.isHost) {
|
||||||
|
codeGenContentNodes = `${view.expression}.projectableNodes`;
|
||||||
|
} else {
|
||||||
|
codeGenContentNodes =
|
||||||
|
`[${contentNodesByNgContentIndex.map( nodes => codeGenFlatArray(nodes) ).join(',')}]`;
|
||||||
|
}
|
||||||
|
targetStatements.push(new Statement(
|
||||||
|
`${this.componentViewFactory(component)}(${renderer.expression}, ${viewManager.expression}, ${appEl.expression}, ${codeGenContentNodes}, null, null, null);`));
|
||||||
|
}
|
||||||
|
|
||||||
|
getProjectedNodes(projectableNodes: Expression, ngContentIndex: number): Expression {
|
||||||
|
return new Expression(`${projectableNodes.expression}[${ngContentIndex}]`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendProjectedNodes(renderer: Expression, parent: Expression, nodes: Expression,
|
||||||
|
targetStatements: Statement[]) {
|
||||||
|
targetStatements.push(new Statement(
|
||||||
|
`${renderer.expression}.projectNodes(${parent.expression}, ${APP_VIEW_MODULE_REF}flattenNestedViewRenderNodes(${nodes.expression}));`));
|
||||||
|
}
|
||||||
|
|
||||||
|
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
||||||
|
targetStatements: Statement[]): Expression {
|
||||||
|
var compileProtoView = this.protoViews[embeddedTemplateIndex];
|
||||||
|
var isHostView = this.component.type.isHost;
|
||||||
|
var isComponentView = embeddedTemplateIndex === 0 && !isHostView;
|
||||||
|
var visitor = new ViewBuilderVisitor<Expression, Statement>(
|
||||||
|
new Expression('renderer'), new Expression('viewManager'),
|
||||||
|
new Expression('projectableNodes'), isHostView ? new Expression('rootSelector') : null,
|
||||||
|
new Expression('view'), compileProtoView, targetStatements, this);
|
||||||
|
|
||||||
|
templateVisitAll(
|
||||||
|
visitor, asts,
|
||||||
|
new ParentElement(isComponentView ? new Expression('parentRenderNode') : null, null, null));
|
||||||
|
|
||||||
|
var appProtoView = compileProtoView.protoView.expression;
|
||||||
|
var viewFactoryName = codeGenViewFactoryName(this.component, embeddedTemplateIndex);
|
||||||
|
var changeDetectorFactory = this.changeDetectorExpressions.expressions[embeddedTemplateIndex];
|
||||||
|
var factoryArgs = [
|
||||||
|
'parentRenderer',
|
||||||
|
'viewManager',
|
||||||
|
'containerEl',
|
||||||
|
'projectableNodes',
|
||||||
|
'rootSelector',
|
||||||
|
'dynamicallyCreatedProviders',
|
||||||
|
'rootInjector'
|
||||||
|
];
|
||||||
|
var initRendererStmts = [];
|
||||||
|
var rendererExpr = `parentRenderer`;
|
||||||
|
if (embeddedTemplateIndex === 0) {
|
||||||
|
var renderCompTypeVar = this._nextVar('renderType');
|
||||||
|
targetStatements.push(new Statement(`var ${renderCompTypeVar} = null;`));
|
||||||
|
var stylesVar = this._nextVar('styles');
|
||||||
|
targetStatements.push(
|
||||||
|
new Statement(`${CONST_VAR} ${stylesVar} = ${this.styles.expression};`));
|
||||||
|
var encapsulation = this.component.template.encapsulation;
|
||||||
|
initRendererStmts.push(`if (${renderCompTypeVar} == null) {
|
||||||
|
${renderCompTypeVar} = viewManager.createRenderComponentType(${codeGenViewEncapsulation(encapsulation)}, ${stylesVar});
|
||||||
|
}`);
|
||||||
|
rendererExpr = `parentRenderer.renderComponent(${renderCompTypeVar})`;
|
||||||
|
}
|
||||||
|
var statement = `
|
||||||
|
${codeGenFnHeader(factoryArgs, viewFactoryName)}{
|
||||||
|
${initRendererStmts.join('\n')}
|
||||||
|
var renderer = ${rendererExpr};
|
||||||
|
var view = new ${APP_VIEW_MODULE_REF}AppView(
|
||||||
|
${appProtoView}, renderer, viewManager,
|
||||||
|
projectableNodes,
|
||||||
|
containerEl,
|
||||||
|
dynamicallyCreatedProviders, rootInjector,
|
||||||
|
${changeDetectorFactory}()
|
||||||
|
);
|
||||||
|
${APP_VIEW_MODULE_REF}checkSlotCount(${escapeValue(this.component.type.name)}, ${this.component.template.ngContentSelectors.length}, projectableNodes);
|
||||||
|
${isComponentView ? 'var parentRenderNode = renderer.createViewRoot(view.containerAppElement.nativeElement);' : ''}
|
||||||
|
${visitor.renderStmts.map(stmt => stmt.statement).join('\n')}
|
||||||
|
${visitor.appStmts.map(stmt => stmt.statement).join('\n')}
|
||||||
|
|
||||||
|
view.init(${codeGenFlatArray(visitor.rootNodesOrAppElements)}, ${codeGenArray(visitor.renderNodes)}, ${codeGenArray(visitor.appDisposables)},
|
||||||
|
${codeGenArray(visitor.appElements)});
|
||||||
|
return view;
|
||||||
|
}`;
|
||||||
|
targetStatements.push(new Statement(statement));
|
||||||
|
return new Expression(viewFactoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RuntimeViewFactory implements ViewFactory<any, any> {
|
||||||
|
constructor(public component: CompileDirectiveMetadata, public styles: Array<string | any[]>,
|
||||||
|
public protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
|
||||||
|
public changeDetectorFactories: Function[], public componentViewFactory: Function) {}
|
||||||
|
|
||||||
|
createText(renderer: Renderer, parent: any, text: string, targetStatements: any[]): any {
|
||||||
|
return renderer.createText(parent, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
createElement(renderer: Renderer, parent: any, name: string, rootSelector: string,
|
||||||
|
targetStatements: any[]): any {
|
||||||
|
var el;
|
||||||
|
if (isPresent(rootSelector)) {
|
||||||
|
el = renderer.selectRootElement(rootSelector);
|
||||||
|
} else {
|
||||||
|
el = renderer.createElement(parent, name);
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
createTemplateAnchor(renderer: Renderer, parent: any, targetStatements: any[]): any {
|
||||||
|
return renderer.createTemplateAnchor(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
createGlobalEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
|
||||||
|
eventAst: BoundEventAst, targetStatements: any[]): any {
|
||||||
|
return renderer.listenGlobal(
|
||||||
|
eventAst.target, eventAst.name,
|
||||||
|
(event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
createElementEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
|
||||||
|
renderNode: any, eventAst: BoundEventAst, targetStatements: any[]) {
|
||||||
|
renderer.listen(renderNode, eventAst.name, (event) => appView.triggerEventHandlers(
|
||||||
|
eventAst.fullName, event, boundElementIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
setElementAttribute(renderer: Renderer, renderNode: any, attrName: string, attrValue: string,
|
||||||
|
targetStatements: any[]) {
|
||||||
|
renderer.setElementAttribute(renderNode, attrName, attrValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppElement(appProtoEl: AppProtoElement, appView: AppView, renderNode: any,
|
||||||
|
parentAppEl: AppElement, embeddedViewFactory: Function,
|
||||||
|
targetStatements: any[]): any {
|
||||||
|
return new AppElement(appProtoEl, appView, parentAppEl, renderNode, embeddedViewFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAndSetComponentView(renderer: Renderer, viewManager: AppViewManager_, appView: AppView,
|
||||||
|
appEl: AppElement, component: CompileDirectiveMetadata,
|
||||||
|
contentNodesByNgContentIndex: Array<Array<any | any[]>>,
|
||||||
|
targetStatements: any[]) {
|
||||||
|
var flattenedContentNodes;
|
||||||
|
if (this.component.type.isHost) {
|
||||||
|
flattenedContentNodes = appView.projectableNodes;
|
||||||
|
} else {
|
||||||
|
flattenedContentNodes = ListWrapper.createFixedSize(contentNodesByNgContentIndex.length);
|
||||||
|
for (var i = 0; i < contentNodesByNgContentIndex.length; i++) {
|
||||||
|
flattenedContentNodes[i] = flattenArray(contentNodesByNgContentIndex[i], []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.componentViewFactory(component)(renderer, viewManager, appEl, flattenedContentNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
getProjectedNodes(projectableNodes: any[][], ngContentIndex: number): any[] {
|
||||||
|
return projectableNodes[ngContentIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
appendProjectedNodes(renderer: Renderer, parent: any, nodes: any[], targetStatements: any[]) {
|
||||||
|
renderer.projectNodes(parent, flattenNestedViewRenderNodes(nodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
||||||
|
targetStatements: any[]): Function {
|
||||||
|
var compileProtoView = this.protoViews[embeddedTemplateIndex];
|
||||||
|
var isComponentView = compileProtoView.protoView.type === ViewType.COMPONENT;
|
||||||
|
var renderComponentType = null;
|
||||||
|
return (parentRenderer: ParentRenderer, viewManager: AppViewManager_, containerEl: AppElement,
|
||||||
|
projectableNodes: any[][], rootSelector: string = null,
|
||||||
|
dynamicallyCreatedProviders: ResolvedProvider[] = null,
|
||||||
|
rootInjector: Injector = null) => {
|
||||||
|
checkSlotCount(this.component.type.name, this.component.template.ngContentSelectors.length,
|
||||||
|
projectableNodes);
|
||||||
|
var renderer;
|
||||||
|
if (embeddedTemplateIndex === 0) {
|
||||||
|
if (isBlank(renderComponentType)) {
|
||||||
|
renderComponentType = viewManager.createRenderComponentType(
|
||||||
|
this.component.template.encapsulation, this.styles);
|
||||||
|
}
|
||||||
|
renderer = parentRenderer.renderComponent(renderComponentType);
|
||||||
|
} else {
|
||||||
|
renderer = <Renderer>parentRenderer;
|
||||||
|
}
|
||||||
|
var changeDetector = this.changeDetectorFactories[embeddedTemplateIndex]();
|
||||||
|
var view =
|
||||||
|
new AppView(compileProtoView.protoView, renderer, viewManager, projectableNodes,
|
||||||
|
containerEl, dynamicallyCreatedProviders, rootInjector, changeDetector);
|
||||||
|
var visitor = new ViewBuilderVisitor<any, any>(
|
||||||
|
renderer, viewManager, projectableNodes, rootSelector, view, compileProtoView, [], this);
|
||||||
|
var parentRenderNode =
|
||||||
|
isComponentView ? renderer.createViewRoot(containerEl.nativeElement) : null;
|
||||||
|
templateVisitAll(visitor, asts, new ParentElement(parentRenderNode, null, null));
|
||||||
|
view.init(flattenArray(visitor.rootNodesOrAppElements, []), visitor.renderNodes,
|
||||||
|
visitor.appDisposables, visitor.appElements);
|
||||||
|
return view;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParentElement<EXPRESSION> {
|
||||||
|
public contentNodesByNgContentIndex: Array<EXPRESSION>[];
|
||||||
|
|
||||||
|
constructor(public renderNode: EXPRESSION, public appEl: EXPRESSION,
|
||||||
|
public component: CompileDirectiveMetadata) {
|
||||||
|
if (isPresent(component)) {
|
||||||
|
this.contentNodesByNgContentIndex =
|
||||||
|
ListWrapper.createFixedSize(component.template.ngContentSelectors.length);
|
||||||
|
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
||||||
|
this.contentNodesByNgContentIndex[i] = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.contentNodesByNgContentIndex = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addContentNode(ngContentIndex: number, nodeExpr: EXPRESSION) {
|
||||||
|
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewBuilderVisitor<EXPRESSION, STATEMENT> implements TemplateAstVisitor {
|
||||||
|
renderStmts: Array<STATEMENT> = [];
|
||||||
|
renderNodes: EXPRESSION[] = [];
|
||||||
|
appStmts: Array<STATEMENT> = [];
|
||||||
|
appElements: EXPRESSION[] = [];
|
||||||
|
appDisposables: EXPRESSION[] = [];
|
||||||
|
|
||||||
|
rootNodesOrAppElements: EXPRESSION[] = [];
|
||||||
|
|
||||||
|
elementCount: number = 0;
|
||||||
|
|
||||||
|
constructor(public renderer: EXPRESSION, public viewManager: EXPRESSION,
|
||||||
|
public projectableNodes: EXPRESSION, public rootSelector: EXPRESSION,
|
||||||
|
public view: EXPRESSION, public protoView: CompileProtoView<EXPRESSION, EXPRESSION>,
|
||||||
|
public targetStatements: STATEMENT[],
|
||||||
|
public factory: ViewFactory<EXPRESSION, STATEMENT>) {}
|
||||||
|
|
||||||
|
private _addRenderNode(renderNode: EXPRESSION, appEl: EXPRESSION, ngContentIndex: number,
|
||||||
|
parent: ParentElement<EXPRESSION>) {
|
||||||
|
this.renderNodes.push(renderNode);
|
||||||
|
if (isPresent(parent.component)) {
|
||||||
|
if (isPresent(ngContentIndex)) {
|
||||||
|
parent.addContentNode(ngContentIndex, isPresent(appEl) ? appEl : renderNode);
|
||||||
|
}
|
||||||
|
} else if (isBlank(parent.renderNode)) {
|
||||||
|
this.rootNodesOrAppElements.push(isPresent(appEl) ? appEl : renderNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getParentRenderNode(ngContentIndex: number,
|
||||||
|
parent: ParentElement<EXPRESSION>): EXPRESSION {
|
||||||
|
return isPresent(parent.component) &&
|
||||||
|
parent.component.template.encapsulation !== ViewEncapsulation.Native ?
|
||||||
|
null :
|
||||||
|
parent.renderNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitBoundText(ast: BoundTextAst, parent: ParentElement<EXPRESSION>): any {
|
||||||
|
return this._visitText('', ast.ngContentIndex, parent);
|
||||||
|
}
|
||||||
|
visitText(ast: TextAst, parent: ParentElement<EXPRESSION>): any {
|
||||||
|
return this._visitText(ast.value, ast.ngContentIndex, parent);
|
||||||
|
}
|
||||||
|
private _visitText(value: string, ngContentIndex: number, parent: ParentElement<EXPRESSION>) {
|
||||||
|
var renderNode = this.factory.createText(
|
||||||
|
this.renderer, this._getParentRenderNode(ngContentIndex, parent), value, this.renderStmts);
|
||||||
|
this._addRenderNode(renderNode, null, ngContentIndex, parent);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitNgContent(ast: NgContentAst, parent: ParentElement<EXPRESSION>): any {
|
||||||
|
var nodesExpression = this.factory.getProjectedNodes(this.projectableNodes, ast.index);
|
||||||
|
if (isPresent(parent.component)) {
|
||||||
|
if (isPresent(ast.ngContentIndex)) {
|
||||||
|
parent.addContentNode(ast.ngContentIndex, nodesExpression);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isPresent(parent.renderNode)) {
|
||||||
|
this.factory.appendProjectedNodes(this.renderer, parent.renderNode, nodesExpression,
|
||||||
|
this.renderStmts);
|
||||||
|
} else {
|
||||||
|
this.rootNodesOrAppElements.push(nodesExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitElement(ast: ElementAst, parent: ParentElement<EXPRESSION>): any {
|
||||||
|
var renderNode = this.factory.createElement(
|
||||||
|
this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), ast.name,
|
||||||
|
this.rootSelector, this.renderStmts);
|
||||||
|
|
||||||
|
var component = ast.getComponent();
|
||||||
|
var elementIndex = this.elementCount++;
|
||||||
|
var protoEl = this.protoView.protoElements[elementIndex];
|
||||||
|
|
||||||
|
protoEl.renderEvents.forEach((eventAst) => {
|
||||||
|
if (isPresent(eventAst.target)) {
|
||||||
|
var disposable = this.factory.createGlobalEventListener(
|
||||||
|
this.renderer, this.view, protoEl.boundElementIndex, eventAst, this.renderStmts);
|
||||||
|
this.appDisposables.push(disposable);
|
||||||
|
} else {
|
||||||
|
this.factory.createElementEventListener(this.renderer, this.view, protoEl.boundElementIndex,
|
||||||
|
renderNode, eventAst, this.renderStmts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (var i = 0; i < protoEl.attrNameAndValues.length; i++) {
|
||||||
|
var attrName = protoEl.attrNameAndValues[i][0];
|
||||||
|
var attrValue = protoEl.attrNameAndValues[i][1];
|
||||||
|
this.factory.setElementAttribute(this.renderer, renderNode, attrName, attrValue,
|
||||||
|
this.renderStmts);
|
||||||
|
}
|
||||||
|
var appEl = null;
|
||||||
|
if (isPresent(protoEl.appProtoEl)) {
|
||||||
|
appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode, parent.appEl,
|
||||||
|
null, this.appStmts);
|
||||||
|
this.appElements.push(appEl);
|
||||||
|
}
|
||||||
|
this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
|
||||||
|
|
||||||
|
var newParent = new ParentElement<EXPRESSION>(
|
||||||
|
renderNode, isPresent(appEl) ? appEl : parent.appEl, component);
|
||||||
|
templateVisitAll(this, ast.children, newParent);
|
||||||
|
if (isPresent(appEl) && isPresent(component)) {
|
||||||
|
this.factory.createAndSetComponentView(this.renderer, this.viewManager, this.view, appEl,
|
||||||
|
component, newParent.contentNodesByNgContentIndex,
|
||||||
|
this.appStmts);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: ParentElement<EXPRESSION>): any {
|
||||||
|
var renderNode = this.factory.createTemplateAnchor(
|
||||||
|
this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), this.renderStmts);
|
||||||
|
|
||||||
|
var elementIndex = this.elementCount++;
|
||||||
|
var protoEl = this.protoView.protoElements[elementIndex];
|
||||||
|
var embeddedViewFactory = this.factory.createViewFactory(
|
||||||
|
ast.children, protoEl.embeddedTemplateIndex, this.targetStatements);
|
||||||
|
|
||||||
|
var appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode,
|
||||||
|
parent.appEl, embeddedViewFactory, this.appStmts);
|
||||||
|
this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
|
||||||
|
this.appElements.push(appEl);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||||
|
visitAttr(ast: AttrAst, ctx: any): any { return null; }
|
||||||
|
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
|
||||||
|
visitEvent(ast: BoundEventAst, ctx: any): any { return null; }
|
||||||
|
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||||
|
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function codeGenEventHandler(view: Expression, boundElementIndex: number,
|
||||||
|
eventName: string): string {
|
||||||
|
return codeGenValueFn(
|
||||||
|
['event'],
|
||||||
|
`${view.expression}.triggerEventHandlers(${escapeValue(eventName)}, event, ${boundElementIndex})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function codeGenViewFactoryName(component: CompileDirectiveMetadata,
|
||||||
|
embeddedTemplateIndex: number): string {
|
||||||
|
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function codeGenViewEncapsulation(value: ViewEncapsulation): string {
|
||||||
|
if (IS_DART) {
|
||||||
|
return `${METADATA_MODULE_REF}${value}`;
|
||||||
|
} else {
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
|
}
|
@ -11,13 +11,11 @@ import {
|
|||||||
KeyValueDiffers,
|
KeyValueDiffers,
|
||||||
defaultKeyValueDiffers
|
defaultKeyValueDiffers
|
||||||
} from './change_detection/change_detection';
|
} 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 {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 {ViewResolver} from './linker/view_resolver';
|
||||||
import {AppViewListener} from './linker/view_listener';
|
import {AppViewListener} from './linker/view_listener';
|
||||||
import {ProtoViewFactory} from './linker/proto_view_factory';
|
|
||||||
import {DirectiveResolver} from './linker/directive_resolver';
|
import {DirectiveResolver} from './linker/directive_resolver';
|
||||||
import {PipeResolver} from './linker/pipe_resolver';
|
import {PipeResolver} from './linker/pipe_resolver';
|
||||||
import {Compiler} from './linker/compiler';
|
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([
|
export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||||
new Provider(Compiler, {useClass: Compiler_}),
|
new Provider(Compiler, {useClass: Compiler_}),
|
||||||
APP_ID_RANDOM_PROVIDER,
|
APP_ID_RANDOM_PROVIDER,
|
||||||
AppViewPool,
|
ResolvedMetadataCache,
|
||||||
new Provider(APP_VIEW_POOL_CAPACITY, {useValue: 10000}),
|
|
||||||
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
||||||
AppViewManagerUtils,
|
|
||||||
AppViewListener,
|
AppViewListener,
|
||||||
ProtoViewFactory,
|
|
||||||
ViewResolver,
|
ViewResolver,
|
||||||
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
||||||
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
||||||
|
@ -33,11 +33,11 @@ import {
|
|||||||
ExceptionHandler,
|
ExceptionHandler,
|
||||||
unimplemented
|
unimplemented
|
||||||
} from 'angular2/src/facade/exceptions';
|
} from 'angular2/src/facade/exceptions';
|
||||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
|
||||||
import {Console} from 'angular2/src/core/console';
|
import {Console} from 'angular2/src/core/console';
|
||||||
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
||||||
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||||
import {lockMode} from 'angular2/src/facade/lang';
|
import {lockMode} from 'angular2/src/facade/lang';
|
||||||
|
import {ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct providers specific to an individual root component.
|
* Construct providers specific to an individual root component.
|
||||||
@ -56,10 +56,10 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
|
|||||||
() => { appRef._unloadComponent(ref); })
|
() => { appRef._unloadComponent(ref); })
|
||||||
.then((componentRef) => {
|
.then((componentRef) => {
|
||||||
ref = componentRef;
|
ref = componentRef;
|
||||||
if (isPresent(componentRef.location.nativeElement)) {
|
var testability = injector.getOptional(Testability);
|
||||||
|
if (isPresent(testability)) {
|
||||||
injector.get(TestabilityRegistry)
|
injector.get(TestabilityRegistry)
|
||||||
.registerApplication(componentRef.location.nativeElement,
|
.registerApplication(componentRef.location.nativeElement, testability);
|
||||||
injector.get(Testability));
|
|
||||||
}
|
}
|
||||||
return componentRef;
|
return componentRef;
|
||||||
});
|
});
|
||||||
@ -439,7 +439,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_loadComponent(ref): void {
|
_loadComponent(ref): void {
|
||||||
var appChangeDetector = internalView(ref.hostView).changeDetector;
|
var appChangeDetector = (<ElementRef_>ref.location).internalElement.parentView.changeDetector;
|
||||||
this._changeDetectorRefs.push(appChangeDetector.ref);
|
this._changeDetectorRefs.push(appChangeDetector.ref);
|
||||||
this.tick();
|
this.tick();
|
||||||
this._rootComponents.push(ref);
|
this._rootComponents.push(ref);
|
||||||
@ -451,7 +451,8 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||||||
if (!ListWrapper.contains(this._rootComponents, ref)) {
|
if (!ListWrapper.contains(this._rootComponents, ref)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.unregisterChangeDetector(internalView(ref.hostView).changeDetector.ref);
|
this.unregisterChangeDetector(
|
||||||
|
(<ElementRef_>ref.location).internalElement.parentView.changeDetector.ref);
|
||||||
ListWrapper.remove(this._rootComponents, ref);
|
ListWrapper.remove(this._rootComponents, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,9 @@ import {Pipes} from './pipes';
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionError,
|
ChangeDetectionError,
|
||||||
ExpressionChangedAfterItHasBeenCheckedException,
|
ExpressionChangedAfterItHasBeenCheckedException,
|
||||||
DehydratedException
|
DehydratedException,
|
||||||
|
EventEvaluationErrorContext,
|
||||||
|
EventEvaluationError
|
||||||
} from './exceptions';
|
} from './exceptions';
|
||||||
import {BindingTarget} from './binding_record';
|
import {BindingTarget} from './binding_record';
|
||||||
import {Locals} from './parser/locals';
|
import {Locals} from './parser/locals';
|
||||||
@ -43,9 +45,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||||||
subscriptions: any[];
|
subscriptions: any[];
|
||||||
streams: any[];
|
streams: any[];
|
||||||
|
|
||||||
constructor(public id: string, public dispatcher: ChangeDispatcher,
|
dispatcher: ChangeDispatcher;
|
||||||
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
|
|
||||||
public directiveIndices: DirectiveIndex[], public strategy: ChangeDetectionStrategy) {
|
|
||||||
|
constructor(public id: string, public numberOfPropertyProtoRecords: number,
|
||||||
|
public bindingTargets: BindingTarget[], public directiveIndices: DirectiveIndex[],
|
||||||
|
public strategy: ChangeDetectionStrategy) {
|
||||||
this.ref = new ChangeDetectorRef_(this);
|
this.ref = new ChangeDetectorRef_(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,10 +70,24 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||||||
|
|
||||||
remove(): void { this.parent.removeContentChild(this); }
|
remove(): void { this.parent.removeContentChild(this); }
|
||||||
|
|
||||||
handleEvent(eventName: string, elIndex: number, locals: Locals): boolean {
|
handleEvent(eventName: string, elIndex: number, event: any): boolean {
|
||||||
var res = this.handleEventInternal(eventName, elIndex, locals);
|
if (!this.hydrated()) {
|
||||||
this.markPathToRootAsCheckOnce();
|
return true;
|
||||||
return res;
|
}
|
||||||
|
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; }
|
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
|
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||||
// implementation of `hydrateDirectives`.
|
// 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.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
@ -143,12 +163,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||||||
|
|
||||||
this.locals = locals;
|
this.locals = locals;
|
||||||
this.pipes = pipes;
|
this.pipes = pipes;
|
||||||
this.hydrateDirectives(directives);
|
this.hydrateDirectives(dispatcher);
|
||||||
this.state = ChangeDetectorState.NeverChecked;
|
this.state = ChangeDetectorState.NeverChecked;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subclasses should override this method to hydrate any directives.
|
// 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
|
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||||
// implementation of `dehydrateDirectives`.
|
// implementation of `dehydrateDirectives`.
|
||||||
@ -160,6 +180,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||||||
this._unsubsribeFromObservables();
|
this._unsubsribeFromObservables();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.dispatcher = null;
|
||||||
this.context = null;
|
this.context = null;
|
||||||
this.locals = null;
|
this.locals = null;
|
||||||
this.pipes = null;
|
this.pipes = null;
|
||||||
@ -171,6 +192,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||||||
|
|
||||||
hydrated(): boolean { return isPresent(this.context); }
|
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 {
|
afterContentLifecycleCallbacks(): void {
|
||||||
this.dispatcher.notifyAfterContentChecked();
|
this.dispatcher.notifyAfterContentChecked();
|
||||||
this.afterContentLifecycleCallbacksInternal();
|
this.afterContentLifecycleCallbacksInternal();
|
||||||
@ -298,7 +332,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||||||
private _throwError(exception: any, stack: any): void {
|
private _throwError(exception: any, stack: any): void {
|
||||||
var error;
|
var error;
|
||||||
try {
|
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,
|
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
||||||
c.injector, this._currentBinding().debug) :
|
c.injector, this._currentBinding().debug) :
|
||||||
null;
|
null;
|
||||||
|
@ -66,8 +66,8 @@ export class ChangeDetectorJITGenerator {
|
|||||||
generate(): Function {
|
generate(): Function {
|
||||||
var factorySource = `
|
var factorySource = `
|
||||||
${this.generateSource()}
|
${this.generateSource()}
|
||||||
return function(dispatcher) {
|
return function() {
|
||||||
return new ${this.typeName}(dispatcher);
|
return new ${this.typeName}();
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
|
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
|
||||||
@ -77,9 +77,9 @@ export class ChangeDetectorJITGenerator {
|
|||||||
|
|
||||||
generateSource(): string {
|
generateSource(): string {
|
||||||
return `
|
return `
|
||||||
var ${this.typeName} = function ${this.typeName}(dispatcher) {
|
var ${this.typeName} = function ${this.typeName}() {
|
||||||
${this.abstractChangeDetectorVarName}.call(
|
${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,
|
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
|
||||||
${codify(this.changeDetectionStrategy)});
|
${codify(this.changeDetectionStrategy)});
|
||||||
this.dehydrateDirectives(false);
|
this.dehydrateDirectives(false);
|
||||||
@ -199,13 +199,14 @@ export class ChangeDetectorJITGenerator {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
_maybeGenDehydrateDirectives(): string {
|
_maybeGenDehydrateDirectives(): string {
|
||||||
var destroyPipesCode = this._names.genPipeOnDestroy();
|
var destroyPipesCode = this._names.genPipeOnDestroy();
|
||||||
if (destroyPipesCode) {
|
var destroyDirectivesCode = this._logic.genDirectivesOnDestroy(this.directiveRecords);
|
||||||
destroyPipesCode = `if (destroyPipes) { ${destroyPipesCode} }`;
|
|
||||||
}
|
|
||||||
var dehydrateFieldsCode = this._names.genDehydrateFields();
|
var dehydrateFieldsCode = this._names.genDehydrateFields();
|
||||||
if (!destroyPipesCode && !dehydrateFieldsCode) return '';
|
if (!destroyPipesCode && !destroyDirectivesCode && !dehydrateFieldsCode) return '';
|
||||||
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
|
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
|
||||||
${destroyPipesCode}
|
if (destroyPipes) {
|
||||||
|
${destroyPipesCode}
|
||||||
|
${destroyDirectivesCode}
|
||||||
|
}
|
||||||
${dehydrateFieldsCode}
|
${dehydrateFieldsCode}
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
@ -205,4 +205,4 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
|
|||||||
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
||||||
this.markForCheck();
|
this.markForCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -155,17 +155,49 @@ export class CodegenLogicUtil {
|
|||||||
var res = [];
|
var res = [];
|
||||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||||
var r = directiveRecords[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");
|
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) {
|
private _genReadDirective(index: number) {
|
||||||
|
var directiveExpr = `this.getDirectiveFor(directives, ${index})`;
|
||||||
// This is an experimental feature. Works only in Dart.
|
// This is an experimental feature. Works only in Dart.
|
||||||
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
|
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
|
||||||
return `this.observeDirective(this.getDirectiveFor(directives, ${index}), ${index})`;
|
return `this.observeDirective(${directiveExpr}, ${index})`;
|
||||||
} else {
|
} else {
|
||||||
return `this.getDirectiveFor(directives, ${index})`;
|
return directiveExpr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,10 +16,14 @@ export class DirectiveRecord {
|
|||||||
callOnChanges: boolean;
|
callOnChanges: boolean;
|
||||||
callDoCheck: boolean;
|
callDoCheck: boolean;
|
||||||
callOnInit: boolean;
|
callOnInit: boolean;
|
||||||
|
callOnDestroy: boolean;
|
||||||
changeDetection: ChangeDetectionStrategy;
|
changeDetection: ChangeDetectionStrategy;
|
||||||
|
// array of [emitter property name, eventName]
|
||||||
|
outputs: string[][];
|
||||||
|
|
||||||
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
|
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
|
||||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, changeDetection}: {
|
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, callOnDestroy,
|
||||||
|
changeDetection, outputs}: {
|
||||||
directiveIndex?: DirectiveIndex,
|
directiveIndex?: DirectiveIndex,
|
||||||
callAfterContentInit?: boolean,
|
callAfterContentInit?: boolean,
|
||||||
callAfterContentChecked?: boolean,
|
callAfterContentChecked?: boolean,
|
||||||
@ -28,7 +32,9 @@ export class DirectiveRecord {
|
|||||||
callOnChanges?: boolean,
|
callOnChanges?: boolean,
|
||||||
callDoCheck?: boolean,
|
callDoCheck?: boolean,
|
||||||
callOnInit?: boolean,
|
callOnInit?: boolean,
|
||||||
changeDetection?: ChangeDetectionStrategy
|
callOnDestroy?: boolean,
|
||||||
|
changeDetection?: ChangeDetectionStrategy,
|
||||||
|
outputs?: string[][]
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this.directiveIndex = directiveIndex;
|
this.directiveIndex = directiveIndex;
|
||||||
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
||||||
@ -38,7 +44,9 @@ export class DirectiveRecord {
|
|||||||
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
||||||
this.callDoCheck = normalizeBool(callDoCheck);
|
this.callDoCheck = normalizeBool(callDoCheck);
|
||||||
this.callOnInit = normalizeBool(callOnInit);
|
this.callOnInit = normalizeBool(callOnInit);
|
||||||
|
this.callOnDestroy = normalizeBool(callOnDestroy);
|
||||||
this.changeDetection = changeDetection;
|
this.changeDetection = changeDetection;
|
||||||
|
this.outputs = outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
isDefaultChangeDetection(): boolean {
|
isDefaultChangeDetection(): boolean {
|
||||||
|
@ -11,21 +11,21 @@ import {ChangeDispatcher, ChangeDetectorGenConfig} from './interfaces';
|
|||||||
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
||||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
||||||
import {ProtoRecord, RecordType} from './proto_record';
|
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> {
|
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||||
values: any[];
|
values: any[];
|
||||||
changes: any[];
|
changes: any[];
|
||||||
localPipes: any[];
|
localPipes: any[];
|
||||||
prevContexts: any[];
|
prevContexts: any[];
|
||||||
directives: any = null;
|
|
||||||
|
|
||||||
constructor(id: string, dispatcher: ChangeDispatcher, numberOfPropertyProtoRecords: number,
|
constructor(id: string, numberOfPropertyProtoRecords: number,
|
||||||
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
||||||
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
|
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
|
||||||
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
|
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
|
||||||
private _genConfig: ChangeDetectorGenConfig) {
|
private _genConfig: ChangeDetectorGenConfig) {
|
||||||
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
|
super(id, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, strategy);
|
||||||
strategy);
|
|
||||||
var len = _records.length + 1;
|
var len = _records.length + 1;
|
||||||
this.values = ListWrapper.createFixedSize(len);
|
this.values = ListWrapper.createFixedSize(len);
|
||||||
this.localPipes = 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);
|
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.values[0] = this.context;
|
||||||
this.directives = directives;
|
this.dispatcher = dispatcher;
|
||||||
|
|
||||||
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
|
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
|
||||||
for (var i = 0; i < this.directiveIndices.length; ++i) {
|
for (var i = 0; i < this.directiveIndices.length; ++i) {
|
||||||
var index = this.directiveIndices[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) {
|
dehydrateDirectives(destroyPipes: boolean) {
|
||||||
if (destroyPipes) {
|
if (destroyPipes) {
|
||||||
this._destroyPipes();
|
this._destroyPipes();
|
||||||
|
this._destroyDirectives();
|
||||||
}
|
}
|
||||||
this.values[0] = null;
|
this.values[0] = null;
|
||||||
this.directives = null;
|
|
||||||
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
|
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
|
||||||
ListWrapper.fill(this.changes, false);
|
ListWrapper.fill(this.changes, false);
|
||||||
ListWrapper.fill(this.localPipes, null);
|
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); }
|
checkNoChanges(): void { this.runDetectChanges(true); }
|
||||||
|
|
||||||
detectChangesInRecordsInternal(throwOnChange: boolean) {
|
detectChangesInRecordsInternal(throwOnChange: boolean) {
|
||||||
@ -241,12 +268,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
private _getDirectiveFor(directiveIndex) {
|
private _getDirectiveFor(directiveIndex: DirectiveIndex) {
|
||||||
return this.directives.getDirectiveFor(directiveIndex);
|
return this.dispatcher.getDirectiveFor(directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
private _getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
|
private _getDetectorFor(directiveIndex: DirectiveIndex) {
|
||||||
|
return this.dispatcher.getDetectorFor(directiveIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],
|
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],
|
||||||
|
@ -93,3 +93,20 @@ export class ChangeDetectionError extends WrappedException {
|
|||||||
export class DehydratedException extends BaseException {
|
export class DehydratedException extends BaseException {
|
||||||
constructor() { super('Attempt to detect changes on a dehydrated detector.'); }
|
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 {Locals} from './parser/locals';
|
||||||
import {BindingTarget, BindingRecord} from './binding_record';
|
import {BindingTarget, BindingRecord} from './binding_record';
|
||||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||||
import {ChangeDetectionStrategy} from './constants';
|
import {ChangeDetectionStrategy} from './constants';
|
||||||
import {ChangeDetectorRef} from './change_detector_ref';
|
import {ChangeDetectorRef} from './change_detector_ref';
|
||||||
|
|
||||||
@ -10,11 +10,14 @@ export class DebugContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ChangeDispatcher {
|
export interface ChangeDispatcher {
|
||||||
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
|
getDebugContext(appElement: any, elementIndex: number, directiveIndex: number): DebugContext;
|
||||||
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
|
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
|
||||||
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
|
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
|
||||||
notifyAfterContentChecked(): void;
|
notifyAfterContentChecked(): void;
|
||||||
notifyAfterViewChecked(): void;
|
notifyAfterViewChecked(): void;
|
||||||
|
notifyOnDestroy(): void;
|
||||||
|
getDetectorFor(directiveIndex: DirectiveIndex): ChangeDetector;
|
||||||
|
getDirectiveFor(directiveIndex: DirectiveIndex): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChangeDetector {
|
export interface ChangeDetector {
|
||||||
@ -27,16 +30,18 @@ export interface ChangeDetector {
|
|||||||
removeContentChild(cd: ChangeDetector): void;
|
removeContentChild(cd: ChangeDetector): void;
|
||||||
removeViewChild(cd: ChangeDetector): void;
|
removeViewChild(cd: ChangeDetector): void;
|
||||||
remove(): 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;
|
dehydrate(): void;
|
||||||
markPathToRootAsCheckOnce(): void;
|
markPathToRootAsCheckOnce(): void;
|
||||||
|
|
||||||
handleEvent(eventName: string, elIndex: number, locals: Locals);
|
handleEvent(eventName: string, elIndex: number, event: any);
|
||||||
detectChanges(): void;
|
detectChanges(): void;
|
||||||
checkNoChanges(): void;
|
checkNoChanges(): void;
|
||||||
|
destroyRecursive(): void;
|
||||||
|
markAsCheckOnce(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; }
|
export interface ProtoChangeDetector { instantiate(): ChangeDetector; }
|
||||||
|
|
||||||
export class ChangeDetectorGenConfig {
|
export class ChangeDetectorGenConfig {
|
||||||
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
|
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
|
||||||
|
@ -3,11 +3,11 @@ library change_detection.jit_proto_change_detector;
|
|||||||
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
|
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
|
||||||
|
|
||||||
class JitProtoChangeDetector implements ProtoChangeDetector {
|
class JitProtoChangeDetector implements ProtoChangeDetector {
|
||||||
JitProtoChangeDetector(definition) : super();
|
JitProtoChangeDetector(definition);
|
||||||
|
|
||||||
static bool isSupported() => false;
|
static bool isSupported() => false;
|
||||||
|
|
||||||
ChangeDetector instantiate(dispatcher) {
|
ChangeDetector instantiate() {
|
||||||
throw "Jit Change Detection not supported in Dart";
|
throw "Jit Change Detection not supported in Dart";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
|
|||||||
|
|
||||||
static isSupported(): boolean { return true; }
|
static isSupported(): boolean { return true; }
|
||||||
|
|
||||||
instantiate(dispatcher: any): ChangeDetector { return this._factory(dispatcher); }
|
instantiate(): ChangeDetector { return this._factory(); }
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_createFactory(definition: ChangeDetectorDefinition) {
|
_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;
|
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 'dart:core' show List;
|
||||||
export 'package:angular2/src/core/change_detection/abstract_change_detector.dart'
|
export 'package:angular2/src/core/change_detection/abstract_change_detector.dart'
|
||||||
show AbstractChangeDetector;
|
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'
|
export 'package:angular2/src/core/change_detection/change_detection_util.dart'
|
||||||
show ChangeDetectionUtil;
|
show ChangeDetectionUtil;
|
||||||
export 'package:angular2/src/facade/lang.dart' show assertionsEnabled, looseIdentical;
|
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';
|
// empty file as we only need the dart version
|
||||||
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
@ -54,12 +54,11 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
|
|||||||
this._directiveIndices = this._definition.directiveRecords.map(d => d.directiveIndex);
|
this._directiveIndices = this._definition.directiveRecords.map(d => d.directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(dispatcher: any): ChangeDetector {
|
instantiate(): ChangeDetector {
|
||||||
return new DynamicChangeDetector(
|
return new DynamicChangeDetector(
|
||||||
this._definition.id, dispatcher, this._propertyBindingRecords.length,
|
this._definition.id, this._propertyBindingRecords.length, this._propertyBindingTargets,
|
||||||
this._propertyBindingTargets, this._directiveIndices, this._definition.strategy,
|
this._directiveIndices, this._definition.strategy, this._propertyBindingRecords,
|
||||||
this._propertyBindingRecords, this._eventBindingRecords, this._definition.directiveRecords,
|
this._eventBindingRecords, this._definition.directiveRecords, this._definition.genConfig);
|
||||||
this._definition.genConfig);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection';
|
||||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
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 {AppElement} from 'angular2/src/core/linker/element';
|
||||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
import {AppView} from 'angular2/src/core/linker/view';
|
||||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,79 +103,68 @@ export abstract class DebugElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DebugElement_ extends DebugElement {
|
export class DebugElement_ extends DebugElement {
|
||||||
/** @internal */
|
constructor(private _appElement: AppElement) { super(); }
|
||||||
_elementInjector: ElementInjector;
|
|
||||||
|
|
||||||
constructor(private _parentView: AppView, private _boundElementIndex: number) {
|
|
||||||
super();
|
|
||||||
this._elementInjector = this._parentView.elementInjectors[this._boundElementIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
get componentInstance(): any {
|
get componentInstance(): any {
|
||||||
if (!isPresent(this._elementInjector)) {
|
if (!isPresent(this._appElement)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this._elementInjector.getComponent();
|
return this._appElement.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
get nativeElement(): any { return this.elementRef.nativeElement; }
|
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 {
|
getDirectiveInstance(directiveIndex: number): any {
|
||||||
return this._elementInjector.getDirectiveAtIndex(directiveIndex);
|
return this._appElement.getDirectiveAtIndex(directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
get children(): DebugElement[] {
|
get children(): DebugElement[] {
|
||||||
return this._getChildElements(this._parentView, this._boundElementIndex);
|
return this._getChildElements(this._appElement.parentView, this._appElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
get componentViewChildren(): DebugElement[] {
|
get componentViewChildren(): DebugElement[] {
|
||||||
var shadowView = this._parentView.getNestedView(this._boundElementIndex);
|
if (!isPresent(this._appElement.componentView)) {
|
||||||
|
|
||||||
if (!isPresent(shadowView) || shadowView.proto.type !== ViewType.COMPONENT) {
|
|
||||||
// The current element is not a component.
|
// The current element is not a component.
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._getChildElements(shadowView, null);
|
return this._getChildElements(this._appElement.componentView, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerEventHandler(eventName: string, eventObj: Event): void {
|
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 {
|
hasDirective(type: Type): boolean {
|
||||||
if (!isPresent(this._elementInjector)) {
|
if (!isPresent(this._appElement)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this._elementInjector.hasDirective(type);
|
return this._appElement.hasDirective(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
inject(type: Type): any {
|
inject(type: Type): any {
|
||||||
if (!isPresent(this._elementInjector)) {
|
if (!isPresent(this._appElement)) {
|
||||||
return null;
|
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 */
|
/** @internal */
|
||||||
_getChildElements(view: AppView, parentBoundElementIndex: number): DebugElement[] {
|
_getChildElements(view: AppView, parentAppElement: AppElement): DebugElement[] {
|
||||||
var els = [];
|
var els = [];
|
||||||
var parentElementBinder = null;
|
for (var i = 0; i < view.appElements.length; ++i) {
|
||||||
if (isPresent(parentBoundElementIndex)) {
|
var appEl = view.appElements[i];
|
||||||
parentElementBinder = view.proto.elementBinders[parentBoundElementIndex - view.elementOffset];
|
if (appEl.parent == parentAppElement) {
|
||||||
}
|
els.push(new DebugElement_(appEl));
|
||||||
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));
|
|
||||||
|
|
||||||
var views = view.viewContainers[view.elementOffset + i];
|
var views = appEl.nestedViews;
|
||||||
if (isPresent(views)) {
|
if (isPresent(views)) {
|
||||||
views.views.forEach(
|
views.forEach(
|
||||||
(nextView) => { els = els.concat(this._getChildElements(nextView, null)); });
|
(nextView) => { els = els.concat(this._getChildElements(nextView, null)); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,8 +180,7 @@ export class DebugElement_ extends DebugElement {
|
|||||||
* @return {DebugElement}
|
* @return {DebugElement}
|
||||||
*/
|
*/
|
||||||
export function inspectElement(elementRef: ElementRef): DebugElement {
|
export function inspectElement(elementRef: ElementRef): DebugElement {
|
||||||
return new DebugElement_(internalView((<ElementRef_>elementRef).parentView),
|
return new DebugElement_((<ElementRef_>elementRef).internalElement);
|
||||||
(<ElementRef_>elementRef).boundElementIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -194,6 +194,11 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ProtoInjector {
|
export class ProtoInjector {
|
||||||
|
static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
|
||||||
|
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||||
|
return new ProtoInjector(bd);
|
||||||
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_strategy: ProtoInjectorStrategy;
|
_strategy: ProtoInjectorStrategy;
|
||||||
numberOfProviders: number;
|
numberOfProviders: number;
|
||||||
@ -215,7 +220,6 @@ export interface InjectorStrategy {
|
|||||||
getObjAtIndex(index: number): any;
|
getObjAtIndex(index: number): any;
|
||||||
getMaxNumberOfObjects(): number;
|
getMaxNumberOfObjects(): number;
|
||||||
|
|
||||||
attach(parent: Injector, isHost: boolean): void;
|
|
||||||
resetConstructionCounter(): void;
|
resetConstructionCounter(): void;
|
||||||
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any;
|
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any;
|
||||||
}
|
}
|
||||||
@ -240,12 +244,6 @@ export class InjectorInlineStrategy implements InjectorStrategy {
|
|||||||
return this.injector._new(provider, visibility);
|
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 {
|
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||||
var p = this.protoStrategy;
|
var p = this.protoStrategy;
|
||||||
var inj = this.injector;
|
var inj = this.injector;
|
||||||
@ -346,12 +344,6 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
|
|||||||
return this.injector._new(provider, visibility);
|
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 {
|
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||||
var p = this.protoStrategy;
|
var p = this.protoStrategy;
|
||||||
|
|
||||||
@ -516,9 +508,7 @@ export class Injector {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
|
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
|
||||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
return new Injector(ProtoInjector.fromResolvedProviders(providers));
|
||||||
var proto = new ProtoInjector(bd);
|
|
||||||
return new Injector(proto, null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -531,8 +521,6 @@ export class Injector {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
_strategy: InjectorStrategy;
|
_strategy: InjectorStrategy;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_isHost: boolean = false;
|
|
||||||
/** @internal */
|
|
||||||
_constructionCounter: number = 0;
|
_constructionCounter: number = 0;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
public _proto: any /* ProtoInjector */;
|
public _proto: any /* ProtoInjector */;
|
||||||
@ -542,6 +530,7 @@ export class Injector {
|
|||||||
* Private
|
* Private
|
||||||
*/
|
*/
|
||||||
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
|
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
|
||||||
|
private _isHostBoundary: boolean = false,
|
||||||
private _depProvider: any /* DependencyProvider */ = null,
|
private _depProvider: any /* DependencyProvider */ = null,
|
||||||
private _debugContext: Function = null) {
|
private _debugContext: Function = null) {
|
||||||
this._proto = _proto;
|
this._proto = _proto;
|
||||||
@ -549,6 +538,12 @@ export class Injector {
|
|||||||
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this injector is a boundary to a host.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
get hostBoundary() { return this._isHostBoundary; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@ -692,7 +687,7 @@ export class Injector {
|
|||||||
createChildFromResolved(providers: ResolvedProvider[]): Injector {
|
createChildFromResolved(providers: ResolvedProvider[]): Injector {
|
||||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||||
var proto = new ProtoInjector(bd);
|
var proto = new ProtoInjector(bd);
|
||||||
var inj = new Injector(proto, null, null);
|
var inj = new Injector(proto);
|
||||||
inj._parent = this;
|
inj._parent = this;
|
||||||
return inj;
|
return inj;
|
||||||
}
|
}
|
||||||
@ -935,7 +930,7 @@ export class Injector {
|
|||||||
var inj: Injector = this;
|
var inj: Injector = this;
|
||||||
|
|
||||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||||
if (inj._isHost) {
|
if (inj._isHostBoundary) {
|
||||||
return this._getPrivateDependency(key, optional, inj);
|
return this._getPrivateDependency(key, optional, inj);
|
||||||
} else {
|
} else {
|
||||||
inj = inj._parent;
|
inj = inj._parent;
|
||||||
@ -946,7 +941,7 @@ export class Injector {
|
|||||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
||||||
if (obj !== UNDEFINED) return obj;
|
if (obj !== UNDEFINED) return obj;
|
||||||
|
|
||||||
if (isPresent(inj._parent) && inj._isHost) {
|
if (isPresent(inj._parent) && inj._isHostBoundary) {
|
||||||
return this._getPrivateDependency(key, optional, inj);
|
return this._getPrivateDependency(key, optional, inj);
|
||||||
} else {
|
} else {
|
||||||
inj = inj._parent;
|
inj = inj._parent;
|
||||||
@ -968,7 +963,7 @@ export class Injector {
|
|||||||
var inj: Injector = this;
|
var inj: Injector = this;
|
||||||
|
|
||||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||||
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||||
inj = inj._parent;
|
inj = inj._parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,7 +971,7 @@ export class Injector {
|
|||||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
||||||
if (obj !== UNDEFINED) return obj;
|
if (obj !== UNDEFINED) return obj;
|
||||||
|
|
||||||
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||||
inj = inj._parent;
|
inj = inj._parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,50 +538,62 @@ export function resolveFactory(provider: Provider): ResolvedFactory {
|
|||||||
* convenience provider syntax.
|
* convenience provider syntax.
|
||||||
*/
|
*/
|
||||||
export function resolveProvider(provider: Provider): ResolvedProvider {
|
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.
|
* Resolve a list of Providers.
|
||||||
*/
|
*/
|
||||||
export function resolveProviders(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
|
export function resolveProviders(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
|
||||||
var normalized = _createListOfProviders(_normalizeProviders(
|
var normalized = _normalizeProviders(providers, []);
|
||||||
providers, new Map<number, _NormalizedProvider | _NormalizedProvider[]>()));
|
var resolved = normalized.map(resolveProvider);
|
||||||
return normalized.map(b => {
|
return MapWrapper.values(mergeResolvedProviders(resolved, new Map<number, ResolvedProvider>()));
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The algorithm works as follows:
|
* Merges a list of ResolvedProviders into a list where
|
||||||
*
|
* each key is contained exactly once and multi providers
|
||||||
* [Provider] -> [_NormalizedProvider|[_NormalizedProvider]] -> [ResolvedProvider]
|
* have been merged.
|
||||||
*
|
|
||||||
* _NormalizedProvider is essentially a resolved provider before it was grouped by key.
|
|
||||||
*/
|
*/
|
||||||
class _NormalizedProvider {
|
export function mergeResolvedProviders(
|
||||||
constructor(public key: Key, public resolvedFactory: ResolvedFactory) {}
|
providers: ResolvedProvider[],
|
||||||
}
|
normalizedProvidersMap: Map<number, ResolvedProvider>): Map<number, ResolvedProvider> {
|
||||||
|
for (var i = 0; i < providers.length; i++) {
|
||||||
function _createListOfProviders(flattenedProviders: Map<number, any>): any[] {
|
var provider = providers[i];
|
||||||
return MapWrapper.values(flattenedProviders);
|
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[]>,
|
function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder | any[]>,
|
||||||
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>):
|
res: Provider[]): Provider[] {
|
||||||
Map<number, _NormalizedProvider | _NormalizedProvider[]> {
|
|
||||||
providers.forEach(b => {
|
providers.forEach(b => {
|
||||||
if (b instanceof Type) {
|
if (b instanceof Type) {
|
||||||
_normalizeProvider(provide(b, {useClass: b}), res);
|
res.push(provide(b, {useClass: b}));
|
||||||
|
|
||||||
} else if (b instanceof Provider) {
|
} else if (b instanceof Provider) {
|
||||||
_normalizeProvider(b, res);
|
res.push(b);
|
||||||
|
|
||||||
} else if (b instanceof Array) {
|
} else if (b instanceof Array) {
|
||||||
_normalizeProviders(b, res);
|
_normalizeProviders(b, res);
|
||||||
@ -597,36 +609,6 @@ function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder
|
|||||||
return res;
|
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[] {
|
function _constructDependencies(factoryFunction: Function, dependencies: any[]): Dependency[] {
|
||||||
if (isBlank(dependencies)) {
|
if (isBlank(dependencies)) {
|
||||||
return _dependenciesFor(factoryFunction);
|
return _dependenciesFor(factoryFunction);
|
||||||
|
@ -17,6 +17,6 @@ export {QueryList} from './linker/query_list';
|
|||||||
export {DynamicComponentLoader} from './linker/dynamic_component_loader';
|
export {DynamicComponentLoader} from './linker/dynamic_component_loader';
|
||||||
export {ElementRef} from './linker/element_ref';
|
export {ElementRef} from './linker/element_ref';
|
||||||
export {TemplateRef} from './linker/template_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 {ViewContainerRef} from './linker/view_container_ref';
|
||||||
export {ComponentRef} from './linker/dynamic_component_loader';
|
export {ComponentRef} from './linker/dynamic_component_loader';
|
@ -1,12 +1,12 @@
|
|||||||
import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
|
import {HostViewFactoryRef} from 'angular2/src/core/linker/view_ref';
|
||||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
|
||||||
|
|
||||||
import {Injectable} from 'angular2/src/core/di';
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
|
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
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
|
* 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.
|
* both compiles and instantiates a Component.
|
||||||
*/
|
*/
|
||||||
export abstract class Compiler {
|
export abstract class Compiler {
|
||||||
abstract compileInHost(componentType: Type): Promise<ProtoViewRef>;
|
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
|
||||||
abstract clearCache();
|
abstract clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _isCompiledHostTemplate(type: any): boolean {
|
function isHostViewFactory(type: any): boolean {
|
||||||
return type instanceof CompiledHostTemplate;
|
return type instanceof HostViewFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Compiler_ extends Compiler {
|
export class Compiler_ extends Compiler {
|
||||||
constructor(private _protoViewFactory: ProtoViewFactory) { super(); }
|
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
||||||
|
|
||||||
compileInHost(componentType: Type): Promise<ProtoViewRef> {
|
|
||||||
var metadatas = reflector.annotations(componentType);
|
var metadatas = reflector.annotations(componentType);
|
||||||
var compiledHostTemplate = metadatas.find(_isCompiledHostTemplate);
|
var hostViewFactory = metadatas.find(isHostViewFactory);
|
||||||
|
|
||||||
if (isBlank(compiledHostTemplate)) {
|
if (isBlank(hostViewFactory)) {
|
||||||
throw new BaseException(
|
throw new BaseException(`No precompiled component ${stringify(componentType)} found`);
|
||||||
`No precompiled template for component ${stringify(componentType)} found`);
|
|
||||||
}
|
}
|
||||||
return PromiseWrapper.resolve(this._createProtoView(compiledHostTemplate));
|
return PromiseWrapper.resolve(new HostViewFactoryRef_(hostViewFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createProtoView(compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
|
clearCache() {}
|
||||||
return this._protoViewFactory.createHost(compiledHostTemplate).ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCache() { this._protoViewFactory.clearCache(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function internalCreateProtoView(compiler: Compiler,
|
|
||||||
compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
|
|
||||||
return (<any>compiler)._createProtoView(compiledHostTemplate);
|
|
||||||
}
|
}
|
||||||
|
@ -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 {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {Promise} from 'angular2/src/facade/async';
|
import {Promise} from 'angular2/src/facade/async';
|
||||||
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
|
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
|
||||||
import {ElementRef} from './element_ref';
|
import {ElementRef, ElementRef_} from './element_ref';
|
||||||
import {ViewRef, HostViewRef} from './view_ref';
|
import {HostViewRef} from './view_ref';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an instance of a Component created via {@link DynamicComponentLoader}.
|
* 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.
|
* 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
|
* @internal
|
||||||
@ -140,7 +142,7 @@ export abstract class DynamicComponentLoader {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
abstract loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
|
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
|
* 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,
|
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
|
* 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>
|
* <child-component>Child</child-component>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
abstract loadNextToLocation(type: Type, location: ElementRef,
|
abstract loadNextToLocation(type: Type, location: ElementRef, providers?: ResolvedProvider[],
|
||||||
providers?: ResolvedProvider[]): Promise<ComponentRef>;
|
projectableNodes?: any[][]): Promise<ComponentRef>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
||||||
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); }
|
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); }
|
||||||
|
|
||||||
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
|
loadAsRoot(type: Type, overrideSelector: string, injector: Injector, onDispose?: () => void,
|
||||||
onDispose?: () => void): Promise<ComponentRef> {
|
projectableNodes?: any[][]): Promise<ComponentRef> {
|
||||||
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||||
var hostViewRef =
|
var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector,
|
||||||
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
injector, projectableNodes);
|
||||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
@ -259,24 +262,25 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
|
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
|
||||||
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
|
providers: ResolvedProvider[] = null,
|
||||||
|
projectableNodes: any[][] = null): Promise<ComponentRef> {
|
||||||
return this.loadNextToLocation(
|
return this.loadNextToLocation(
|
||||||
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
|
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), providers,
|
||||||
providers);
|
projectableNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadNextToLocation(type: Type, location: ElementRef,
|
loadNextToLocation(type: Type, location: ElementRef, providers: ResolvedProvider[] = null,
|
||||||
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
|
projectableNodes: any[][] = null): Promise<ComponentRef> {
|
||||||
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||||
var viewContainer = this._viewManager.getViewContainer(location);
|
var viewContainer = this._viewManager.getViewContainer(location);
|
||||||
var hostViewRef =
|
var hostViewRef = viewContainer.createHostView(hostProtoViewRef, viewContainer.length,
|
||||||
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, providers);
|
providers, projectableNodes);
|
||||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
var dispose = () => {
|
var dispose = () => {
|
||||||
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
|
var index = viewContainer.indexOf(hostViewRef);
|
||||||
if (index !== -1) {
|
if (!hostViewRef.destroyed && index !== -1) {
|
||||||
viewContainer.remove(index);
|
viewContainer.remove(index);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
867
modules/angular2/src/core/linker/element.ts
Normal file
867
modules/angular2/src/core/linker/element.ts
Normal file
@ -0,0 +1,867 @@
|
|||||||
|
import {
|
||||||
|
isPresent,
|
||||||
|
isBlank,
|
||||||
|
Type,
|
||||||
|
stringify,
|
||||||
|
CONST_EXPR,
|
||||||
|
StringWrapper
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
|
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {
|
||||||
|
Injector,
|
||||||
|
Key,
|
||||||
|
Dependency,
|
||||||
|
provide,
|
||||||
|
Provider,
|
||||||
|
ResolvedProvider,
|
||||||
|
NoProviderError,
|
||||||
|
AbstractProviderError,
|
||||||
|
CyclicDependencyError,
|
||||||
|
resolveForwardRef,
|
||||||
|
Injectable
|
||||||
|
} from 'angular2/src/core/di';
|
||||||
|
import {mergeResolvedProviders} from 'angular2/src/core/di/provider';
|
||||||
|
import {
|
||||||
|
UNDEFINED,
|
||||||
|
ProtoInjector,
|
||||||
|
Visibility,
|
||||||
|
InjectorInlineStrategy,
|
||||||
|
InjectorDynamicStrategy,
|
||||||
|
ProviderWithVisibility,
|
||||||
|
DependencyProvider
|
||||||
|
} from 'angular2/src/core/di/injector';
|
||||||
|
import {resolveProvider, ResolvedFactory, ResolvedProvider_} from 'angular2/src/core/di/provider';
|
||||||
|
|
||||||
|
import {AttributeMetadata, QueryMetadata} from '../metadata/di';
|
||||||
|
|
||||||
|
import {AppView} from './view';
|
||||||
|
import {ViewType} from './view_type';
|
||||||
|
import {ElementRef_} from './element_ref';
|
||||||
|
|
||||||
|
import {ViewContainerRef} from './view_container_ref';
|
||||||
|
import {ElementRef} from './element_ref';
|
||||||
|
import {Renderer} from 'angular2/src/core/render/api';
|
||||||
|
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||||
|
import {DirectiveMetadata, ComponentMetadata} from '../metadata/directives';
|
||||||
|
import {
|
||||||
|
ChangeDetector,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from 'angular2/src/core/change_detection/change_detection';
|
||||||
|
import {QueryList} from './query_list';
|
||||||
|
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
|
import {SetterFn} from 'angular2/src/core/reflection/types';
|
||||||
|
import {AfterViewChecked} from 'angular2/src/core/linker/interfaces';
|
||||||
|
import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider';
|
||||||
|
|
||||||
|
import {ViewContainerRef_} from "./view_container_ref";
|
||||||
|
import {ResolvedMetadataCache} from './resolved_metadata_cache';
|
||||||
|
|
||||||
|
var _staticKeys;
|
||||||
|
|
||||||
|
export class StaticKeys {
|
||||||
|
templateRefId: number;
|
||||||
|
viewContainerId: number;
|
||||||
|
changeDetectorRefId: number;
|
||||||
|
elementRefId: number;
|
||||||
|
rendererId: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.templateRefId = Key.get(TemplateRef).id;
|
||||||
|
this.viewContainerId = Key.get(ViewContainerRef).id;
|
||||||
|
this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
|
||||||
|
this.elementRefId = Key.get(ElementRef).id;
|
||||||
|
this.rendererId = Key.get(Renderer).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static instance(): StaticKeys {
|
||||||
|
if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
|
||||||
|
return _staticKeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DirectiveDependency extends Dependency {
|
||||||
|
constructor(key: Key, optional: boolean, lowerBoundVisibility: Object,
|
||||||
|
upperBoundVisibility: Object, properties: any[], public attributeName: string,
|
||||||
|
public queryDecorator: QueryMetadata) {
|
||||||
|
super(key, optional, lowerBoundVisibility, upperBoundVisibility, properties);
|
||||||
|
this._verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
_verify(): void {
|
||||||
|
var count = 0;
|
||||||
|
if (isPresent(this.queryDecorator)) count++;
|
||||||
|
if (isPresent(this.attributeName)) count++;
|
||||||
|
if (count > 1)
|
||||||
|
throw new BaseException(
|
||||||
|
'A directive injectable can contain only one of the following @Attribute or @Query.');
|
||||||
|
}
|
||||||
|
|
||||||
|
static createFrom(d: Dependency): DirectiveDependency {
|
||||||
|
return new DirectiveDependency(
|
||||||
|
d.key, d.optional, d.lowerBoundVisibility, d.upperBoundVisibility, d.properties,
|
||||||
|
DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
static _attributeName(properties: any[]): string {
|
||||||
|
var p = <AttributeMetadata>properties.find(p => p instanceof AttributeMetadata);
|
||||||
|
return isPresent(p) ? p.attributeName : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
static _query(properties: any[]): QueryMetadata {
|
||||||
|
return <QueryMetadata>properties.find(p => p instanceof QueryMetadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DirectiveProvider extends ResolvedProvider_ {
|
||||||
|
constructor(key: Key, factory: Function, deps: Dependency[], public isComponent: boolean,
|
||||||
|
public providers: ResolvedProvider[], public viewProviders: ResolvedProvider[],
|
||||||
|
public queries: QueryMetadataWithSetter[]) {
|
||||||
|
super(key, [new ResolvedFactory(factory, deps)], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
get displayName(): string { return this.key.displayName; }
|
||||||
|
|
||||||
|
static createFromType(type: Type, meta: DirectiveMetadata): DirectiveProvider {
|
||||||
|
var provider = new Provider(type, {useClass: type});
|
||||||
|
if (isBlank(meta)) {
|
||||||
|
meta = new DirectiveMetadata();
|
||||||
|
}
|
||||||
|
var rb = resolveProvider(provider);
|
||||||
|
var rf = rb.resolvedFactories[0];
|
||||||
|
var deps: DirectiveDependency[] = rf.dependencies.map(DirectiveDependency.createFrom);
|
||||||
|
var isComponent = meta instanceof ComponentMetadata;
|
||||||
|
var resolvedProviders = isPresent(meta.providers) ? Injector.resolve(meta.providers) : null;
|
||||||
|
var resolvedViewProviders = meta instanceof ComponentMetadata && isPresent(meta.viewProviders) ?
|
||||||
|
Injector.resolve(meta.viewProviders) :
|
||||||
|
null;
|
||||||
|
var queries = [];
|
||||||
|
if (isPresent(meta.queries)) {
|
||||||
|
StringMapWrapper.forEach(meta.queries, (meta, fieldName) => {
|
||||||
|
var setter = reflector.setter(fieldName);
|
||||||
|
queries.push(new QueryMetadataWithSetter(setter, meta));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// queries passed into the constructor.
|
||||||
|
// TODO: remove this after constructor queries are no longer supported
|
||||||
|
deps.forEach(d => {
|
||||||
|
if (isPresent(d.queryDecorator)) {
|
||||||
|
queries.push(new QueryMetadataWithSetter(null, d.queryDecorator));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return new DirectiveProvider(rb.key, rf.factory, deps, isComponent, resolvedProviders,
|
||||||
|
resolvedViewProviders, queries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryMetadataWithSetter {
|
||||||
|
constructor(public setter: SetterFn, public metadata: QueryMetadata) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setProvidersVisibility(providers: ResolvedProvider[], visibility: Visibility,
|
||||||
|
result: Map<number, Visibility>) {
|
||||||
|
for (var i = 0; i < providers.length; i++) {
|
||||||
|
result.set(providers[i].key.id, visibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppProtoElement {
|
||||||
|
protoInjector: ProtoInjector;
|
||||||
|
|
||||||
|
static create(metadataCache: ResolvedMetadataCache, index: number,
|
||||||
|
attributes: {[key: string]: string}, directiveTypes: Type[],
|
||||||
|
directiveVariableBindings: {[key: string]: number}): AppProtoElement {
|
||||||
|
var componentDirProvider = null;
|
||||||
|
var mergedProvidersMap: Map<number, ResolvedProvider> = new Map<number, ResolvedProvider>();
|
||||||
|
var providerVisibilityMap: Map<number, Visibility> = new Map<number, Visibility>();
|
||||||
|
var providers = ListWrapper.createGrowableSize(directiveTypes.length);
|
||||||
|
|
||||||
|
var protoQueryRefs = [];
|
||||||
|
for (var i = 0; i < directiveTypes.length; i++) {
|
||||||
|
var dirProvider = metadataCache.getResolvedDirectiveMetadata(directiveTypes[i]);
|
||||||
|
providers[i] = new ProviderWithVisibility(
|
||||||
|
dirProvider, dirProvider.isComponent ? Visibility.PublicAndPrivate : Visibility.Public);
|
||||||
|
|
||||||
|
if (dirProvider.isComponent) {
|
||||||
|
componentDirProvider = dirProvider;
|
||||||
|
} else {
|
||||||
|
if (isPresent(dirProvider.providers)) {
|
||||||
|
mergeResolvedProviders(dirProvider.providers, mergedProvidersMap);
|
||||||
|
setProvidersVisibility(dirProvider.providers, Visibility.Public, providerVisibilityMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isPresent(dirProvider.viewProviders)) {
|
||||||
|
mergeResolvedProviders(dirProvider.viewProviders, mergedProvidersMap);
|
||||||
|
setProvidersVisibility(dirProvider.viewProviders, Visibility.Private,
|
||||||
|
providerVisibilityMap);
|
||||||
|
}
|
||||||
|
for (var queryIdx = 0; queryIdx < dirProvider.queries.length; queryIdx++) {
|
||||||
|
var q = dirProvider.queries[queryIdx];
|
||||||
|
protoQueryRefs.push(new ProtoQueryRef(i, q.setter, q.metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isPresent(componentDirProvider) && isPresent(componentDirProvider.providers)) {
|
||||||
|
// directive providers need to be prioritized over component providers
|
||||||
|
mergeResolvedProviders(componentDirProvider.providers, mergedProvidersMap);
|
||||||
|
setProvidersVisibility(componentDirProvider.providers, Visibility.Public,
|
||||||
|
providerVisibilityMap);
|
||||||
|
}
|
||||||
|
mergedProvidersMap.forEach((provider, _) => {
|
||||||
|
providers.push(
|
||||||
|
new ProviderWithVisibility(provider, providerVisibilityMap.get(provider.key.id)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return new AppProtoElement(isPresent(componentDirProvider), index, attributes, providers,
|
||||||
|
protoQueryRefs, directiveVariableBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(public firstProviderIsComponent: boolean, public index: number,
|
||||||
|
public attributes: {[key: string]: string}, pwvs: ProviderWithVisibility[],
|
||||||
|
public protoQueryRefs: ProtoQueryRef[],
|
||||||
|
public directiveVariableBindings: {[key: string]: number}) {
|
||||||
|
var length = pwvs.length;
|
||||||
|
if (length > 0) {
|
||||||
|
this.protoInjector = new ProtoInjector(pwvs);
|
||||||
|
} else {
|
||||||
|
this.protoInjector = null;
|
||||||
|
this.protoQueryRefs = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getProviderAtIndex(index: number): any { return this.protoInjector.getProviderAtIndex(index); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Context {
|
||||||
|
constructor(public element: any, public componentElement: any, public injector: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InjectorWithHostBoundary {
|
||||||
|
constructor(public injector: Injector, public hostInjectorBoundary: boolean) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppElement implements DependencyProvider, ElementRef, AfterViewChecked {
|
||||||
|
static getViewParentInjector(parentViewType: ViewType, containerAppElement: AppElement,
|
||||||
|
imperativelyCreatedProviders: ResolvedProvider[],
|
||||||
|
rootInjector: Injector): InjectorWithHostBoundary {
|
||||||
|
var parentInjector;
|
||||||
|
var hostInjectorBoundary;
|
||||||
|
switch (parentViewType) {
|
||||||
|
case ViewType.COMPONENT:
|
||||||
|
parentInjector = containerAppElement._injector;
|
||||||
|
hostInjectorBoundary = true;
|
||||||
|
break;
|
||||||
|
case ViewType.EMBEDDED:
|
||||||
|
parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
|
||||||
|
containerAppElement._injector.parent :
|
||||||
|
containerAppElement._injector;
|
||||||
|
hostInjectorBoundary = containerAppElement._injector.hostBoundary;
|
||||||
|
break;
|
||||||
|
case ViewType.HOST:
|
||||||
|
if (isPresent(containerAppElement)) {
|
||||||
|
// host view is attached to a container
|
||||||
|
parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
|
||||||
|
containerAppElement._injector.parent :
|
||||||
|
containerAppElement._injector;
|
||||||
|
if (isPresent(imperativelyCreatedProviders)) {
|
||||||
|
var imperativeProvidersWithVisibility = imperativelyCreatedProviders.map(
|
||||||
|
p => new ProviderWithVisibility(p, Visibility.Public));
|
||||||
|
// The imperative injector is similar to having an element between
|
||||||
|
// the dynamic-loaded component and its parent => no boundary between
|
||||||
|
// the component and imperativelyCreatedInjector.
|
||||||
|
parentInjector = new Injector(new ProtoInjector(imperativeProvidersWithVisibility),
|
||||||
|
parentInjector, true, null, null);
|
||||||
|
hostInjectorBoundary = false;
|
||||||
|
} else {
|
||||||
|
hostInjectorBoundary = containerAppElement._injector.hostBoundary;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// bootstrap
|
||||||
|
parentInjector = rootInjector;
|
||||||
|
hostInjectorBoundary = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new InjectorWithHostBoundary(parentInjector, hostInjectorBoundary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public nestedViews: AppView[] = null;
|
||||||
|
public componentView: AppView = null;
|
||||||
|
|
||||||
|
private _queryStrategy: _QueryStrategy;
|
||||||
|
private _injector: Injector;
|
||||||
|
private _strategy: _ElementDirectiveStrategy;
|
||||||
|
public ref: ElementRef_;
|
||||||
|
|
||||||
|
constructor(public proto: AppProtoElement, public parentView: AppView, public parent: AppElement,
|
||||||
|
public nativeElement: any, public embeddedViewFactory: Function) {
|
||||||
|
this.ref = new ElementRef_(this);
|
||||||
|
var parentInjector = isPresent(parent) ? parent._injector : parentView.parentInjector;
|
||||||
|
if (isPresent(this.proto.protoInjector)) {
|
||||||
|
var isBoundary;
|
||||||
|
if (isPresent(parent) && isPresent(parent.proto.protoInjector)) {
|
||||||
|
isBoundary = false;
|
||||||
|
} else {
|
||||||
|
isBoundary = parentView.hostInjectorBoundary;
|
||||||
|
}
|
||||||
|
this._queryStrategy = this._buildQueryStrategy();
|
||||||
|
this._injector = new Injector(this.proto.protoInjector, parentInjector, isBoundary, this,
|
||||||
|
() => this._debugContext());
|
||||||
|
|
||||||
|
// we couple ourselves to the injector strategy to avoid polymorphic calls
|
||||||
|
var injectorStrategy = <any>this._injector.internalStrategy;
|
||||||
|
this._strategy = injectorStrategy instanceof InjectorInlineStrategy ?
|
||||||
|
new ElementDirectiveInlineStrategy(injectorStrategy, this) :
|
||||||
|
new ElementDirectiveDynamicStrategy(injectorStrategy, this);
|
||||||
|
this._strategy.init();
|
||||||
|
} else {
|
||||||
|
this._queryStrategy = null;
|
||||||
|
this._injector = parentInjector;
|
||||||
|
this._strategy = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachComponentView(componentView: AppView) { this.componentView = componentView; }
|
||||||
|
|
||||||
|
private _debugContext(): any {
|
||||||
|
var c = this.parentView.getDebugContext(this, null, null);
|
||||||
|
return isPresent(c) ? new _Context(c.element, c.componentElement, c.injector) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasVariableBinding(name: string): boolean {
|
||||||
|
var vb = this.proto.directiveVariableBindings;
|
||||||
|
return isPresent(vb) && StringMapWrapper.contains(vb, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVariableBinding(name: string): any {
|
||||||
|
var index = this.proto.directiveVariableBindings[name];
|
||||||
|
return isPresent(index) ? this.getDirectiveAtIndex(<number>index) : this.getElementRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
get(token: any): any { return this._injector.get(token); }
|
||||||
|
|
||||||
|
hasDirective(type: Type): boolean { return isPresent(this._injector.getOptional(type)); }
|
||||||
|
|
||||||
|
getComponent(): any { return isPresent(this._strategy) ? this._strategy.getComponent() : null; }
|
||||||
|
|
||||||
|
getInjector(): Injector { return this._injector; }
|
||||||
|
|
||||||
|
getElementRef(): ElementRef { return this.ref; }
|
||||||
|
|
||||||
|
getViewContainerRef(): ViewContainerRef { return new ViewContainerRef_(this); }
|
||||||
|
|
||||||
|
getTemplateRef(): TemplateRef {
|
||||||
|
if (isPresent(this.embeddedViewFactory)) {
|
||||||
|
return new TemplateRef_(this.ref);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependency(injector: Injector, provider: ResolvedProvider, dep: Dependency): any {
|
||||||
|
if (provider instanceof DirectiveProvider) {
|
||||||
|
var dirDep = <DirectiveDependency>dep;
|
||||||
|
|
||||||
|
if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep);
|
||||||
|
|
||||||
|
if (isPresent(dirDep.queryDecorator))
|
||||||
|
return this._queryStrategy.findQuery(dirDep.queryDecorator).list;
|
||||||
|
|
||||||
|
if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) {
|
||||||
|
// We provide the component's view change detector to components and
|
||||||
|
// the surrounding component's change detector to directives.
|
||||||
|
if (this.proto.firstProviderIsComponent) {
|
||||||
|
// Note: The component view is not yet created when
|
||||||
|
// this method is called!
|
||||||
|
return new _ComponentViewChangeDetectorRef(this);
|
||||||
|
} else {
|
||||||
|
return this.parentView.changeDetector.ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirDep.key.id === StaticKeys.instance().elementRefId) {
|
||||||
|
return this.getElementRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirDep.key.id === StaticKeys.instance().viewContainerId) {
|
||||||
|
return this.getViewContainerRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirDep.key.id === StaticKeys.instance().templateRefId) {
|
||||||
|
var tr = this.getTemplateRef();
|
||||||
|
if (isBlank(tr) && !dirDep.optional) {
|
||||||
|
throw new NoProviderError(null, dirDep.key);
|
||||||
|
}
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirDep.key.id === StaticKeys.instance().rendererId) {
|
||||||
|
return this.parentView.renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (provider instanceof PipeProvider) {
|
||||||
|
if (dep.key.id === StaticKeys.instance().changeDetectorRefId) {
|
||||||
|
// We provide the component's view change detector to components and
|
||||||
|
// the surrounding component's change detector to directives.
|
||||||
|
if (this.proto.firstProviderIsComponent) {
|
||||||
|
// Note: The component view is not yet created when
|
||||||
|
// this method is called!
|
||||||
|
return new _ComponentViewChangeDetectorRef(this);
|
||||||
|
} else {
|
||||||
|
return this.parentView.changeDetector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _buildAttribute(dep: DirectiveDependency): string {
|
||||||
|
var attributes = this.proto.attributes;
|
||||||
|
if (isPresent(attributes) && StringMapWrapper.contains(attributes, dep.attributeName)) {
|
||||||
|
return attributes[dep.attributeName];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
|
||||||
|
var templateRef = this.getTemplateRef();
|
||||||
|
if (query.selector === TemplateRef && isPresent(templateRef)) {
|
||||||
|
list.push(templateRef);
|
||||||
|
}
|
||||||
|
if (this._strategy != null) {
|
||||||
|
this._strategy.addDirectivesMatchingQuery(query, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _buildQueryStrategy(): _QueryStrategy {
|
||||||
|
if (this.proto.protoQueryRefs.length === 0) {
|
||||||
|
return _emptyQueryStrategy;
|
||||||
|
} else if (this.proto.protoQueryRefs.length <=
|
||||||
|
InlineQueryStrategy.NUMBER_OF_SUPPORTED_QUERIES) {
|
||||||
|
return new InlineQueryStrategy(this);
|
||||||
|
} else {
|
||||||
|
return new DynamicQueryStrategy(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getDirectiveAtIndex(index: number): any { return this._injector.getAt(index); }
|
||||||
|
|
||||||
|
ngAfterViewChecked(): void {
|
||||||
|
if (isPresent(this._queryStrategy)) this._queryStrategy.updateViewQueries();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterContentChecked(): void {
|
||||||
|
if (isPresent(this._queryStrategy)) this._queryStrategy.updateContentQueries();
|
||||||
|
}
|
||||||
|
|
||||||
|
traverseAndSetQueriesAsDirty(): void {
|
||||||
|
var inj: AppElement = this;
|
||||||
|
while (isPresent(inj)) {
|
||||||
|
inj._setQueriesAsDirty();
|
||||||
|
inj = inj.parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setQueriesAsDirty(): void {
|
||||||
|
if (isPresent(this._queryStrategy)) {
|
||||||
|
this._queryStrategy.setContentQueriesAsDirty();
|
||||||
|
}
|
||||||
|
if (this.parentView.proto.type === ViewType.COMPONENT) {
|
||||||
|
this.parentView.containerAppElement._queryStrategy.setViewQueriesAsDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface _QueryStrategy {
|
||||||
|
setContentQueriesAsDirty(): void;
|
||||||
|
setViewQueriesAsDirty(): void;
|
||||||
|
updateContentQueries(): void;
|
||||||
|
updateViewQueries(): void;
|
||||||
|
findQuery(query: QueryMetadata): QueryRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EmptyQueryStrategy implements _QueryStrategy {
|
||||||
|
setContentQueriesAsDirty(): void {}
|
||||||
|
setViewQueriesAsDirty(): void {}
|
||||||
|
updateContentQueries(): void {}
|
||||||
|
updateViewQueries(): void {}
|
||||||
|
findQuery(query: QueryMetadata): QueryRef {
|
||||||
|
throw new BaseException(`Cannot find query for directive ${query}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _emptyQueryStrategy = new _EmptyQueryStrategy();
|
||||||
|
|
||||||
|
class InlineQueryStrategy implements _QueryStrategy {
|
||||||
|
static NUMBER_OF_SUPPORTED_QUERIES = 3;
|
||||||
|
|
||||||
|
query0: QueryRef;
|
||||||
|
query1: QueryRef;
|
||||||
|
query2: QueryRef;
|
||||||
|
|
||||||
|
constructor(ei: AppElement) {
|
||||||
|
var protoRefs = ei.proto.protoQueryRefs;
|
||||||
|
if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], ei);
|
||||||
|
if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], ei);
|
||||||
|
if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], ei);
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentQueriesAsDirty(): void {
|
||||||
|
if (isPresent(this.query0) && !this.query0.isViewQuery) this.query0.dirty = true;
|
||||||
|
if (isPresent(this.query1) && !this.query1.isViewQuery) this.query1.dirty = true;
|
||||||
|
if (isPresent(this.query2) && !this.query2.isViewQuery) this.query2.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewQueriesAsDirty(): void {
|
||||||
|
if (isPresent(this.query0) && this.query0.isViewQuery) this.query0.dirty = true;
|
||||||
|
if (isPresent(this.query1) && this.query1.isViewQuery) this.query1.dirty = true;
|
||||||
|
if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContentQueries() {
|
||||||
|
if (isPresent(this.query0) && !this.query0.isViewQuery) {
|
||||||
|
this.query0.update();
|
||||||
|
}
|
||||||
|
if (isPresent(this.query1) && !this.query1.isViewQuery) {
|
||||||
|
this.query1.update();
|
||||||
|
}
|
||||||
|
if (isPresent(this.query2) && !this.query2.isViewQuery) {
|
||||||
|
this.query2.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateViewQueries() {
|
||||||
|
if (isPresent(this.query0) && this.query0.isViewQuery) {
|
||||||
|
this.query0.update();
|
||||||
|
}
|
||||||
|
if (isPresent(this.query1) && this.query1.isViewQuery) {
|
||||||
|
this.query1.update();
|
||||||
|
}
|
||||||
|
if (isPresent(this.query2) && this.query2.isViewQuery) {
|
||||||
|
this.query2.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findQuery(query: QueryMetadata): QueryRef {
|
||||||
|
if (isPresent(this.query0) && this.query0.protoQueryRef.query === query) {
|
||||||
|
return this.query0;
|
||||||
|
}
|
||||||
|
if (isPresent(this.query1) && this.query1.protoQueryRef.query === query) {
|
||||||
|
return this.query1;
|
||||||
|
}
|
||||||
|
if (isPresent(this.query2) && this.query2.protoQueryRef.query === query) {
|
||||||
|
return this.query2;
|
||||||
|
}
|
||||||
|
throw new BaseException(`Cannot find query for directive ${query}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DynamicQueryStrategy implements _QueryStrategy {
|
||||||
|
queries: QueryRef[];
|
||||||
|
|
||||||
|
constructor(ei: AppElement) {
|
||||||
|
this.queries = ei.proto.protoQueryRefs.map(p => new QueryRef(p, ei));
|
||||||
|
}
|
||||||
|
|
||||||
|
setContentQueriesAsDirty(): void {
|
||||||
|
for (var i = 0; i < this.queries.length; ++i) {
|
||||||
|
var q = this.queries[i];
|
||||||
|
if (!q.isViewQuery) q.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewQueriesAsDirty(): void {
|
||||||
|
for (var i = 0; i < this.queries.length; ++i) {
|
||||||
|
var q = this.queries[i];
|
||||||
|
if (q.isViewQuery) q.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateContentQueries() {
|
||||||
|
for (var i = 0; i < this.queries.length; ++i) {
|
||||||
|
var q = this.queries[i];
|
||||||
|
if (!q.isViewQuery) {
|
||||||
|
q.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateViewQueries() {
|
||||||
|
for (var i = 0; i < this.queries.length; ++i) {
|
||||||
|
var q = this.queries[i];
|
||||||
|
if (q.isViewQuery) {
|
||||||
|
q.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findQuery(query: QueryMetadata): QueryRef {
|
||||||
|
for (var i = 0; i < this.queries.length; ++i) {
|
||||||
|
var q = this.queries[i];
|
||||||
|
if (q.protoQueryRef.query === query) {
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new BaseException(`Cannot find query for directive ${query}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface _ElementDirectiveStrategy {
|
||||||
|
getComponent(): any;
|
||||||
|
isComponentKey(key: Key): boolean;
|
||||||
|
addDirectivesMatchingQuery(q: QueryMetadata, res: any[]): void;
|
||||||
|
init(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy used by the `ElementInjector` when the number of providers is 10 or less.
|
||||||
|
* In such a case, inlining fields is beneficial for performances.
|
||||||
|
*/
|
||||||
|
class ElementDirectiveInlineStrategy implements _ElementDirectiveStrategy {
|
||||||
|
constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: AppElement) {}
|
||||||
|
|
||||||
|
init(): void {
|
||||||
|
var i = this.injectorStrategy;
|
||||||
|
var p = i.protoStrategy;
|
||||||
|
i.resetConstructionCounter();
|
||||||
|
|
||||||
|
if (p.provider0 instanceof DirectiveProvider && isPresent(p.keyId0) && i.obj0 === UNDEFINED)
|
||||||
|
i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
|
||||||
|
if (p.provider1 instanceof DirectiveProvider && isPresent(p.keyId1) && i.obj1 === UNDEFINED)
|
||||||
|
i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
|
||||||
|
if (p.provider2 instanceof DirectiveProvider && isPresent(p.keyId2) && i.obj2 === UNDEFINED)
|
||||||
|
i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
|
||||||
|
if (p.provider3 instanceof DirectiveProvider && isPresent(p.keyId3) && i.obj3 === UNDEFINED)
|
||||||
|
i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
|
||||||
|
if (p.provider4 instanceof DirectiveProvider && isPresent(p.keyId4) && i.obj4 === UNDEFINED)
|
||||||
|
i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
|
||||||
|
if (p.provider5 instanceof DirectiveProvider && isPresent(p.keyId5) && i.obj5 === UNDEFINED)
|
||||||
|
i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
|
||||||
|
if (p.provider6 instanceof DirectiveProvider && isPresent(p.keyId6) && i.obj6 === UNDEFINED)
|
||||||
|
i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
|
||||||
|
if (p.provider7 instanceof DirectiveProvider && isPresent(p.keyId7) && i.obj7 === UNDEFINED)
|
||||||
|
i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
|
||||||
|
if (p.provider8 instanceof DirectiveProvider && isPresent(p.keyId8) && i.obj8 === UNDEFINED)
|
||||||
|
i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
|
||||||
|
if (p.provider9 instanceof DirectiveProvider && isPresent(p.keyId9) && i.obj9 === UNDEFINED)
|
||||||
|
i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponent(): any { return this.injectorStrategy.obj0; }
|
||||||
|
|
||||||
|
isComponentKey(key: Key): boolean {
|
||||||
|
return this._ei.proto.firstProviderIsComponent && isPresent(key) &&
|
||||||
|
key.id === this.injectorStrategy.protoStrategy.keyId0;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
|
||||||
|
var i = this.injectorStrategy;
|
||||||
|
var p = i.protoStrategy;
|
||||||
|
if (isPresent(p.provider0) && p.provider0.key.token === query.selector) {
|
||||||
|
if (i.obj0 === UNDEFINED) i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
|
||||||
|
list.push(i.obj0);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider1) && p.provider1.key.token === query.selector) {
|
||||||
|
if (i.obj1 === UNDEFINED) i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
|
||||||
|
list.push(i.obj1);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider2) && p.provider2.key.token === query.selector) {
|
||||||
|
if (i.obj2 === UNDEFINED) i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
|
||||||
|
list.push(i.obj2);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider3) && p.provider3.key.token === query.selector) {
|
||||||
|
if (i.obj3 === UNDEFINED) i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
|
||||||
|
list.push(i.obj3);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider4) && p.provider4.key.token === query.selector) {
|
||||||
|
if (i.obj4 === UNDEFINED) i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
|
||||||
|
list.push(i.obj4);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider5) && p.provider5.key.token === query.selector) {
|
||||||
|
if (i.obj5 === UNDEFINED) i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
|
||||||
|
list.push(i.obj5);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider6) && p.provider6.key.token === query.selector) {
|
||||||
|
if (i.obj6 === UNDEFINED) i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
|
||||||
|
list.push(i.obj6);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider7) && p.provider7.key.token === query.selector) {
|
||||||
|
if (i.obj7 === UNDEFINED) i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
|
||||||
|
list.push(i.obj7);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider8) && p.provider8.key.token === query.selector) {
|
||||||
|
if (i.obj8 === UNDEFINED) i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
|
||||||
|
list.push(i.obj8);
|
||||||
|
}
|
||||||
|
if (isPresent(p.provider9) && p.provider9.key.token === query.selector) {
|
||||||
|
if (i.obj9 === UNDEFINED) i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
|
||||||
|
list.push(i.obj9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy used by the `ElementInjector` when the number of bindings is 11 or more.
|
||||||
|
* In such a case, there are too many fields to inline (see ElementInjectorInlineStrategy).
|
||||||
|
*/
|
||||||
|
class ElementDirectiveDynamicStrategy implements _ElementDirectiveStrategy {
|
||||||
|
constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: AppElement) {}
|
||||||
|
|
||||||
|
init(): void {
|
||||||
|
var inj = this.injectorStrategy;
|
||||||
|
var p = inj.protoStrategy;
|
||||||
|
inj.resetConstructionCounter();
|
||||||
|
|
||||||
|
for (var i = 0; i < p.keyIds.length; i++) {
|
||||||
|
if (p.providers[i] instanceof DirectiveProvider && isPresent(p.keyIds[i]) &&
|
||||||
|
inj.objs[i] === UNDEFINED) {
|
||||||
|
inj.objs[i] = inj.instantiateProvider(p.providers[i], p.visibilities[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponent(): any { return this.injectorStrategy.objs[0]; }
|
||||||
|
|
||||||
|
isComponentKey(key: Key): boolean {
|
||||||
|
var p = this.injectorStrategy.protoStrategy;
|
||||||
|
return this._ei.proto.firstProviderIsComponent && isPresent(key) && key.id === p.keyIds[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
|
||||||
|
var ist = this.injectorStrategy;
|
||||||
|
var p = ist.protoStrategy;
|
||||||
|
|
||||||
|
for (var i = 0; i < p.providers.length; i++) {
|
||||||
|
if (p.providers[i].key.token === query.selector) {
|
||||||
|
if (ist.objs[i] === UNDEFINED) {
|
||||||
|
ist.objs[i] = ist.instantiateProvider(p.providers[i], p.visibilities[i]);
|
||||||
|
}
|
||||||
|
list.push(ist.objs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProtoQueryRef {
|
||||||
|
constructor(public dirIndex: number, public setter: SetterFn, public query: QueryMetadata) {}
|
||||||
|
|
||||||
|
get usesPropertySyntax(): boolean { return isPresent(this.setter); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryRef {
|
||||||
|
public list: QueryList<any>;
|
||||||
|
public dirty: boolean;
|
||||||
|
|
||||||
|
constructor(public protoQueryRef: ProtoQueryRef, private originator: AppElement) {
|
||||||
|
this.list = new QueryList<any>();
|
||||||
|
this.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; }
|
||||||
|
|
||||||
|
update(): void {
|
||||||
|
if (!this.dirty) return;
|
||||||
|
this._update();
|
||||||
|
this.dirty = false;
|
||||||
|
|
||||||
|
// TODO delete the check once only field queries are supported
|
||||||
|
if (this.protoQueryRef.usesPropertySyntax) {
|
||||||
|
var dir = this.originator.getDirectiveAtIndex(this.protoQueryRef.dirIndex);
|
||||||
|
if (this.protoQueryRef.query.first) {
|
||||||
|
this.protoQueryRef.setter(dir, this.list.length > 0 ? this.list.first : null);
|
||||||
|
} else {
|
||||||
|
this.protoQueryRef.setter(dir, this.list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.list.notifyOnChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _update(): void {
|
||||||
|
var aggregator = [];
|
||||||
|
if (this.protoQueryRef.query.isViewQuery) {
|
||||||
|
// intentionally skipping originator for view queries.
|
||||||
|
var nestedView = this.originator.componentView;
|
||||||
|
if (isPresent(nestedView)) this._visitView(nestedView, aggregator);
|
||||||
|
} else {
|
||||||
|
this._visit(this.originator, aggregator);
|
||||||
|
}
|
||||||
|
this.list.reset(aggregator);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _visit(inj: AppElement, aggregator: any[]): void {
|
||||||
|
var view = inj.parentView;
|
||||||
|
var startIdx = inj.proto.index;
|
||||||
|
for (var i = startIdx; i < view.appElements.length; i++) {
|
||||||
|
var curInj = view.appElements[i];
|
||||||
|
// The first injector after inj, that is outside the subtree rooted at
|
||||||
|
// inj has to have a null parent or a parent that is an ancestor of inj.
|
||||||
|
if (i > startIdx && (isBlank(curInj.parent) || curInj.parent.proto.index < startIdx)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.protoQueryRef.query.descendants &&
|
||||||
|
!(curInj.parent == this.originator || curInj == this.originator))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// We visit the view container(VC) views right after the injector that contains
|
||||||
|
// the VC. Theoretically, that might not be the right order if there are
|
||||||
|
// child injectors of said injector. Not clear whether if such case can
|
||||||
|
// even be constructed with the current apis.
|
||||||
|
this._visitInjector(curInj, aggregator);
|
||||||
|
this._visitViewContainerViews(curInj.nestedViews, aggregator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitInjector(inj: AppElement, aggregator: any[]) {
|
||||||
|
if (this.protoQueryRef.query.isVarBindingQuery) {
|
||||||
|
this._aggregateVariableBinding(inj, aggregator);
|
||||||
|
} else {
|
||||||
|
this._aggregateDirective(inj, aggregator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitViewContainerViews(views: AppView[], aggregator: any[]) {
|
||||||
|
if (isPresent(views)) {
|
||||||
|
for (var j = 0; j < views.length; j++) {
|
||||||
|
this._visitView(views[j], aggregator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _visitView(view: AppView, aggregator: any[]) {
|
||||||
|
for (var i = 0; i < view.appElements.length; i++) {
|
||||||
|
var inj = view.appElements[i];
|
||||||
|
this._visitInjector(inj, aggregator);
|
||||||
|
this._visitViewContainerViews(inj.nestedViews, aggregator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _aggregateVariableBinding(inj: AppElement, aggregator: any[]): void {
|
||||||
|
var vb = this.protoQueryRef.query.varBindings;
|
||||||
|
for (var i = 0; i < vb.length; ++i) {
|
||||||
|
if (inj.hasVariableBinding(vb[i])) {
|
||||||
|
aggregator.push(inj.getVariableBinding(vb[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _aggregateDirective(inj: AppElement, aggregator: any[]): void {
|
||||||
|
inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ComponentViewChangeDetectorRef extends ChangeDetectorRef {
|
||||||
|
constructor(private _appElement: AppElement) { super(); }
|
||||||
|
|
||||||
|
markForCheck(): void { this._appElement.componentView.changeDetector.ref.markForCheck(); }
|
||||||
|
detach(): void { this._appElement.componentView.changeDetector.ref.detach(); }
|
||||||
|
detectChanges(): void { this._appElement.componentView.changeDetector.ref.detectChanges(); }
|
||||||
|
checkNoChanges(): void { this._appElement.componentView.changeDetector.ref.checkNoChanges(); }
|
||||||
|
reattach(): void { this._appElement.componentView.changeDetector.ref.reattach(); }
|
||||||
|
}
|
@ -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 {unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
import {ViewRef, ViewRef_} from './view_ref';
|
import {AppElement} from './element';
|
||||||
import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/render/api';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a location in a View that has an injection, change-detection and render context
|
* 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
|
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
||||||
* element.
|
* element.
|
||||||
*/
|
*/
|
||||||
export abstract class ElementRef implements RenderElementRef {
|
export abstract class ElementRef {
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The underlying native element or `null` if direct access to native elements is not supported
|
* 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).
|
* (e.g. when the application runs in a web worker).
|
||||||
@ -48,24 +31,13 @@ export abstract class ElementRef implements RenderElementRef {
|
|||||||
* </p>
|
* </p>
|
||||||
* </div>
|
* </div>
|
||||||
*/
|
*/
|
||||||
get nativeElement(): any { return unimplemented(); };
|
get nativeElement(): any { return unimplemented(); }
|
||||||
|
|
||||||
get renderView(): RenderViewRef { return unimplemented(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ElementRef_ extends ElementRef {
|
export class ElementRef_ implements ElementRef {
|
||||||
constructor(public parentView: ViewRef,
|
constructor(private _appElement: AppElement) {}
|
||||||
|
|
||||||
/**
|
get internalElement(): AppElement { return this._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 renderView(): RenderViewRef { return (<ViewRef_>this.parentView).render; }
|
get nativeElement() { return this._appElement.nativeElement; }
|
||||||
set renderView(value) { unimplemented(); }
|
|
||||||
get nativeElement(): any { return this._renderer.getNativeElementSync(this); }
|
|
||||||
}
|
}
|
||||||
|
@ -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)}`);
|
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;
|
|
||||||
}
|
|
35
modules/angular2/src/core/linker/resolved_metadata_cache.ts
Normal file
35
modules/angular2/src/core/linker/resolved_metadata_cache.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import {Injectable} from '../di';
|
||||||
|
import {Type, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {DirectiveProvider} from './element';
|
||||||
|
import {DirectiveResolver, CODEGEN_DIRECTIVE_RESOLVER} from './directive_resolver';
|
||||||
|
import {PipeProvider} from '../pipes/pipe_provider';
|
||||||
|
import {PipeResolver, CODEGEN_PIPE_RESOLVER} from './pipe_resolver';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ResolvedMetadataCache {
|
||||||
|
private _directiveCache: Map<Type, DirectiveProvider> = new Map<Type, DirectiveProvider>();
|
||||||
|
private _pipeCache: Map<Type, PipeProvider> = new Map<Type, PipeProvider>();
|
||||||
|
|
||||||
|
constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver) {}
|
||||||
|
|
||||||
|
getResolvedDirectiveMetadata(type: Type): DirectiveProvider {
|
||||||
|
var result = this._directiveCache.get(type);
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = DirectiveProvider.createFromType(type, this._directiveResolver.resolve(type));
|
||||||
|
this._directiveCache.set(type, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
getResolvedPipeMetadata(type: Type): PipeProvider {
|
||||||
|
var result = this._pipeCache.get(type);
|
||||||
|
if (isBlank(result)) {
|
||||||
|
result = PipeProvider.createFromType(type, this._pipeResolver.resolve(type));
|
||||||
|
this._pipeCache.set(type, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export var CODEGEN_RESOLVED_METADATA_CACHE =
|
||||||
|
new ResolvedMetadataCache(CODEGEN_DIRECTIVE_RESOLVER, CODEGEN_PIPE_RESOLVER);
|
@ -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 {ElementRef, ElementRef_} from './element_ref';
|
||||||
import * as viewModule from './view';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an Embedded Template that can be used to instantiate Embedded Views.
|
* 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
|
// TODO(i): rename to anchor or location
|
||||||
elementRef: ElementRef;
|
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 {
|
export class TemplateRef_ extends TemplateRef {
|
||||||
constructor(elementRef: ElementRef) {
|
constructor(private _elementRef: ElementRef_) { super(); }
|
||||||
super();
|
|
||||||
this.elementRef = elementRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getProtoView(): viewModule.AppProtoView {
|
get elementRef(): ElementRef_ { return this._elementRef; }
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,86 +10,53 @@ import {
|
|||||||
DirectiveIndex,
|
DirectiveIndex,
|
||||||
BindingTarget,
|
BindingTarget,
|
||||||
Locals,
|
Locals,
|
||||||
ProtoChangeDetector
|
ProtoChangeDetector,
|
||||||
|
ChangeDetectorRef
|
||||||
} from 'angular2/src/core/change_detection/change_detection';
|
} 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 {DebugContext} from 'angular2/src/core/change_detection/interfaces';
|
||||||
|
|
||||||
|
import {AppProtoElement, AppElement, DirectiveProvider} from './element';
|
||||||
import {
|
import {
|
||||||
ProtoElementInjector,
|
isPresent,
|
||||||
ElementInjector,
|
isBlank,
|
||||||
PreBuiltObjects,
|
Type,
|
||||||
DirectiveProvider
|
isArray,
|
||||||
} from './element_injector';
|
isNumber,
|
||||||
import {ElementBinder} from './element_binder';
|
CONST,
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
CONST_EXPR
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||||
import * as renderApi from 'angular2/src/core/render/api';
|
import {Renderer, RootRenderer} from 'angular2/src/core/render/api';
|
||||||
import {RenderEventDispatcher} from 'angular2/src/core/render/api';
|
import {ViewRef_, HostViewFactoryRef} from './view_ref';
|
||||||
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
|
||||||
import {ElementRef} from './element_ref';
|
|
||||||
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
|
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
|
||||||
import {camelCaseToDashCase} from 'angular2/src/core/render/util';
|
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';
|
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-';
|
const REFLECT_PREFIX: string = 'ng-reflect-';
|
||||||
|
|
||||||
export enum ViewType {
|
const EMPTY_CONTEXT = CONST_EXPR(new Object());
|
||||||
// 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[] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
|
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
export class AppView implements ChangeDispatcher {
|
||||||
// AppViews that have been merged in depth first order.
|
ref: ViewRef_;
|
||||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
rootNodesOrAppElements: any[];
|
||||||
// entries.
|
allNodes: any[];
|
||||||
views: AppView[] = null;
|
disposables: Function[];
|
||||||
// root elementInjectors of this AppView
|
appElements: AppElement[];
|
||||||
// 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The context against which data-binding expressions in this view are evaluated against.
|
* The context against which data-binding expressions in this view are evaluated against.
|
||||||
* This is always a component instance.
|
* This is always a component instance.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
context: any = null;
|
context: any = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,70 +66,133 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
|||||||
*/
|
*/
|
||||||
locals: Locals;
|
locals: Locals;
|
||||||
|
|
||||||
constructor(public renderer: renderApi.Renderer, public proto: AppProtoView,
|
pipes: Pipes;
|
||||||
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);
|
|
||||||
|
|
||||||
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[],
|
init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[],
|
||||||
rootElementInjectors: ElementInjector[], preBuiltObjects: PreBuiltObjects[],
|
appElements: AppElement[]) {
|
||||||
views: AppView[], elementRefs: ElementRef[], viewContainers: AppViewContainer[]) {
|
this.rootNodesOrAppElements = rootNodesOrAppElements;
|
||||||
this.changeDetector = changeDetector;
|
this.allNodes = allNodes;
|
||||||
this.elementInjectors = elementInjectors;
|
this.disposables = disposables;
|
||||||
this.rootElementInjectors = rootElementInjectors;
|
this.appElements = appElements;
|
||||||
this.preBuiltObjects = preBuiltObjects;
|
var localsMap = new Map<string, any>();
|
||||||
this.views = views;
|
StringMapWrapper.forEach(this.proto.templateVariableBindings,
|
||||||
this.elementRefs = elementRefs;
|
(templateName, _) => { localsMap.set(templateName, null); });
|
||||||
this.viewContainers = viewContainers;
|
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 {
|
setLocal(contextName: string, value: any): void {
|
||||||
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
|
if (!this.hasLocal(contextName)) {
|
||||||
if (!this.proto.templateVariableBindings.has(contextName)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var templateName = this.proto.templateVariableBindings.get(contextName);
|
var templateName = this.proto.templateVariableBindings[contextName];
|
||||||
this.locals.set(templateName, value);
|
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
|
// dispatch to element injector or text nodes based on context
|
||||||
notifyOnBinding(b: BindingTarget, currentValue: any): void {
|
notifyOnBinding(b: BindingTarget, currentValue: any): void {
|
||||||
if (b.isTextNode()) {
|
if (b.isTextNode()) {
|
||||||
this.renderer.setText(this.render, b.elementIndex + this.textOffset, currentValue);
|
this.renderer.setText(this.allNodes[b.elementIndex], currentValue);
|
||||||
} else {
|
} else {
|
||||||
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
|
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
||||||
if (b.isElementProperty()) {
|
if (b.isElementProperty()) {
|
||||||
this.renderer.setElementProperty(elementRef, b.name, currentValue);
|
this.renderer.setElementProperty(nativeElement, b.name, currentValue);
|
||||||
} else if (b.isElementAttribute()) {
|
} else if (b.isElementAttribute()) {
|
||||||
this.renderer.setElementAttribute(elementRef, b.name,
|
this.renderer.setElementAttribute(nativeElement, b.name,
|
||||||
isPresent(currentValue) ? `${currentValue}` : null);
|
isPresent(currentValue) ? `${currentValue}` : null);
|
||||||
} else if (b.isElementClass()) {
|
} else if (b.isElementClass()) {
|
||||||
this.renderer.setElementClass(elementRef, b.name, currentValue);
|
this.renderer.setElementClass(nativeElement, b.name, currentValue);
|
||||||
} else if (b.isElementStyle()) {
|
} else if (b.isElementStyle()) {
|
||||||
var unit = isPresent(b.unit) ? b.unit : '';
|
var unit = isPresent(b.unit) ? b.unit : '';
|
||||||
this.renderer.setElementStyle(elementRef, b.name,
|
this.renderer.setElementStyle(nativeElement, b.name,
|
||||||
isPresent(currentValue) ? `${currentValue}${unit}` : null);
|
isPresent(currentValue) ? `${currentValue}${unit}` : null);
|
||||||
} else {
|
} else {
|
||||||
throw new BaseException('Unsupported directive record');
|
throw new BaseException('Unsupported directive record');
|
||||||
@ -172,57 +202,39 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
|||||||
|
|
||||||
logBindingUpdate(b: BindingTarget, value: any): void {
|
logBindingUpdate(b: BindingTarget, value: any): void {
|
||||||
if (b.isDirective() || b.isElementProperty()) {
|
if (b.isDirective() || b.isElementProperty()) {
|
||||||
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
|
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
||||||
this.renderer.setBindingDebugInfo(
|
this.renderer.setBindingDebugInfo(
|
||||||
elementRef, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
|
nativeElement, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyAfterContentChecked(): void {
|
notifyAfterContentChecked(): void {
|
||||||
var eiCount = this.proto.elementBinders.length;
|
var count = this.appElements.length;
|
||||||
var ei = this.elementInjectors;
|
for (var i = count - 1; i >= 0; i--) {
|
||||||
for (var i = eiCount - 1; i >= 0; i--) {
|
this.appElements[i].ngAfterContentChecked();
|
||||||
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterContentChecked();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyAfterViewChecked(): void {
|
notifyAfterViewChecked(): void {
|
||||||
var eiCount = this.proto.elementBinders.length;
|
var count = this.appElements.length;
|
||||||
var ei = this.elementInjectors;
|
for (var i = count - 1; i >= 0; i--) {
|
||||||
for (var i = eiCount - 1; i >= 0; i--) {
|
this.appElements[i].ngAfterViewChecked();
|
||||||
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterViewChecked();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveFor(directive: DirectiveIndex): any {
|
getDebugContext(appElement: AppElement, elementIndex: number,
|
||||||
var elementInjector = this.elementInjectors[this.elementOffset + directive.elementIndex];
|
directiveIndex: number): DebugContext {
|
||||||
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 {
|
|
||||||
try {
|
try {
|
||||||
var offsettedIndex = this.elementOffset + elementIndex;
|
if (isBlank(appElement) && elementIndex < this.appElements.length) {
|
||||||
var hasRefForIndex = offsettedIndex < this.elementRefs.length;
|
appElement = this.appElements[elementIndex];
|
||||||
|
}
|
||||||
|
var container = this.containerAppElement;
|
||||||
|
|
||||||
var elementRef = hasRefForIndex ? this.elementRefs[this.elementOffset + elementIndex] : null;
|
var element = isPresent(appElement) ? appElement.nativeElement : null;
|
||||||
var container = this.getContainerElement();
|
|
||||||
var ei = hasRefForIndex ? this.elementInjectors[this.elementOffset + elementIndex] : null;
|
|
||||||
|
|
||||||
var element = isPresent(elementRef) ? elementRef.nativeElement : null;
|
|
||||||
var componentElement = isPresent(container) ? container.nativeElement : null;
|
var componentElement = isPresent(container) ? container.nativeElement : null;
|
||||||
var directive = isPresent(directiveIndex) ? this.getDirectiveFor(directiveIndex) : null;
|
var directive =
|
||||||
var injector = isPresent(ei) ? ei.getInjector() : null;
|
isPresent(directiveIndex) ? appElement.getDirectiveAtIndex(directiveIndex) : null;
|
||||||
|
var injector = isPresent(appElement) ? appElement.getInjector() : null;
|
||||||
|
|
||||||
return new DebugContext(element, componentElement, directive, this.context,
|
return new DebugContext(element, componentElement, directive, this.context,
|
||||||
_localsToStringMap(this.locals), injector);
|
_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 {
|
getDetectorFor(directive: DirectiveIndex): any {
|
||||||
var childView = this.getNestedView(this.elementOffset + directive.elementIndex);
|
var componentView = this.appElements[directive.elementIndex].componentView;
|
||||||
return isPresent(childView) ? childView.changeDetector : null;
|
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} {
|
function _localsToStringMap(locals: Locals): {[key: string]: any} {
|
||||||
@ -283,69 +280,61 @@ function _localsToStringMap(locals: Locals): {[key: string]: any} {
|
|||||||
return res;
|
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 {
|
export class AppProtoView {
|
||||||
ref: ProtoViewRef;
|
static create(metadataCache: ResolvedMetadataCache, type: ViewType, pipes: Type[],
|
||||||
protoLocals: Map<string, any>;
|
templateVariableBindings: {[key: string]: string}): AppProtoView {
|
||||||
|
var protoPipes = null;
|
||||||
elementBinders: ElementBinder[] = null;
|
if (isPresent(pipes) && pipes.length > 0) {
|
||||||
mergeInfo: AppProtoViewMergeInfo = null;
|
var boundPipes = ListWrapper.createFixedSize(pipes.length);
|
||||||
variableLocations: Map<string, number> = null;
|
for (var i = 0; i < pipes.length; i++) {
|
||||||
textBindingCount = null;
|
boundPipes[i] = metadataCache.getResolvedPipeMetadata(pipes[i]);
|
||||||
render: renderApi.RenderProtoViewRef = null;
|
}
|
||||||
|
protoPipes = ProtoPipes.fromProviders(boundPipes);
|
||||||
constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
|
}
|
||||||
public isMergable: boolean, public changeDetectorFactory: Function,
|
return new AppProtoView(type, protoPipes, templateVariableBindings);
|
||||||
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
|
|
||||||
this.ref = new ProtoViewRef_(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(render: renderApi.RenderProtoViewRef, elementBinders: ElementBinder[],
|
constructor(public type: ViewType, public protoPipes: ProtoPipes,
|
||||||
textBindingCount: number, mergeInfo: AppProtoViewMergeInfo,
|
public templateVariableBindings: {[key: string]: string}) {}
|
||||||
variableLocations: Map<string, number>) {
|
}
|
||||||
this.render = render;
|
|
||||||
this.elementBinders = elementBinders;
|
|
||||||
this.textBindingCount = textBindingCount;
|
@CONST()
|
||||||
this.mergeInfo = mergeInfo;
|
export class HostViewFactory {
|
||||||
this.variableLocations = variableLocations;
|
constructor(public selector: string, public viewFactory: Function) {}
|
||||||
this.protoLocals = new Map<string, any>();
|
}
|
||||||
if (isPresent(this.templateVariableBindings)) {
|
|
||||||
this.templateVariableBindings.forEach(
|
export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
|
||||||
(templateName, _) => { this.protoLocals.set(templateName, null); });
|
return _flattenNestedViewRenderNodes(nodes, []);
|
||||||
}
|
}
|
||||||
if (isPresent(variableLocations)) {
|
|
||||||
// The view's locals needs to have a full set of variable names at construction time
|
function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
|
||||||
// in order to prevent new variables from being set later in the lifecycle. Since we don't
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
// want
|
var node = nodes[i];
|
||||||
// to actually create variable bindings for the $implicit bindings, add to the
|
if (node instanceof AppElement) {
|
||||||
// protoLocals manually.
|
var appEl = <AppElement>node;
|
||||||
variableLocations.forEach((_, templateName) => { this.protoLocals.set(templateName, null); });
|
renderNodes.push(appEl.nativeElement);
|
||||||
}
|
if (isPresent(appEl.nestedViews)) {
|
||||||
}
|
for (var k = 0; k < appEl.nestedViews.length; k++) {
|
||||||
|
_flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
|
||||||
isInitialized(): boolean { return isPresent(this.elementBinders); }
|
}
|
||||||
|
}
|
||||||
|
} 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 {ResolvedProvider} from 'angular2/src/core/di';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import * as avmModule from './view_manager';
|
import {AppElement} from './element';
|
||||||
import * as viewModule from './view';
|
|
||||||
|
|
||||||
import {ElementRef, ElementRef_} from './element_ref';
|
import {ElementRef, ElementRef_} from './element_ref';
|
||||||
import {TemplateRef} from './template_ref';
|
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||||
import {ViewRef, HostViewRef, ProtoViewRef, internalView} from './view_ref';
|
import {
|
||||||
|
EmbeddedViewRef,
|
||||||
|
HostViewRef,
|
||||||
|
HostViewFactoryRef,
|
||||||
|
HostViewFactoryRef_,
|
||||||
|
ViewRef,
|
||||||
|
ViewRef_
|
||||||
|
} from './view_ref';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a container where one or more Views can be attached.
|
* 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.
|
* Anchor element that specifies the location of this container in the containing View.
|
||||||
* <!-- TODO: rename to anchorElement -->
|
* <!-- TODO: rename to anchorElement -->
|
||||||
*/
|
*/
|
||||||
public element: ElementRef;
|
get element(): ElementRef { return unimplemented(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys all Views in this container.
|
* Destroys all Views in this container.
|
||||||
@ -64,7 +70,7 @@ export abstract class ViewContainerRef {
|
|||||||
*
|
*
|
||||||
* Returns the {@link ViewRef} for the newly created View.
|
* 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
|
* 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.
|
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
|
||||||
*/
|
*/
|
||||||
abstract createHostView(protoViewRef?: ProtoViewRef, index?: number,
|
abstract createHostView(hostViewFactoryRef: HostViewFactoryRef, index?: number,
|
||||||
dynamicallyCreatedProviders?: ResolvedProvider[]): HostViewRef;
|
dynamicallyCreatedProviders?: ResolvedProvider[],
|
||||||
|
projectableNodes?: any[][]): HostViewRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
|
* 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}.
|
* 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
|
* 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.
|
* 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 {
|
export class ViewContainerRef_ extends ViewContainerRef {
|
||||||
constructor(public viewManager: avmModule.AppViewManager, element: ElementRef) {
|
constructor(private _element: AppElement) { super(); }
|
||||||
super();
|
|
||||||
this.element = element;
|
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> {
|
get element(): ElementRef_ { return this._element.ref; }
|
||||||
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; }
|
|
||||||
|
|
||||||
// TODO(rado): profile and decide whether bounds checks should be added
|
// TODO(rado): profile and decide whether bounds checks should be added
|
||||||
// to the methods below.
|
// to the methods below.
|
||||||
createEmbeddedView(templateRef: TemplateRef, index: number = -1): ViewRef {
|
createEmbeddedView(templateRef: TemplateRef_, index: number = -1): EmbeddedViewRef {
|
||||||
if (index == -1) index = this.length;
|
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,
|
createHostView(hostViewFactoryRef: HostViewFactoryRef_, index: number = -1,
|
||||||
dynamicallyCreatedProviders: ResolvedProvider[] = null): HostViewRef {
|
dynamicallyCreatedProviders: ResolvedProvider[] = null,
|
||||||
|
projectableNodes: any[][] = null): HostViewRef {
|
||||||
if (index == -1) index = this.length;
|
if (index == -1) index = this.length;
|
||||||
return this.viewManager.createHostViewInContainer(this.element, index, protoViewRef,
|
var vm = this._element.parentView.viewManager;
|
||||||
dynamicallyCreatedProviders);
|
return vm.createHostViewInContainer(this._element.ref, index, hostViewFactoryRef,
|
||||||
|
dynamicallyCreatedProviders, projectableNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(i): refactor insert+remove into move
|
// 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;
|
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 {
|
indexOf(viewRef: ViewRef_): number {
|
||||||
return ListWrapper.indexOf(this._getViews(), internalView(viewRef));
|
return ListWrapper.indexOf(this._element.nestedViews, viewRef.internalView);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(i): rename to destroy
|
// TODO(i): rename to destroy
|
||||||
remove(index: number = -1): void {
|
remove(index: number = -1): void {
|
||||||
if (index == -1) index = this.length - 1;
|
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.
|
// view is intentionally not returned to the client.
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(i): refactor insert+remove into move
|
// TODO(i): refactor insert+remove into move
|
||||||
detach(index: number = -1): ViewRef {
|
detach(index: number = -1): EmbeddedViewRef {
|
||||||
if (index == -1) index = this.length - 1;
|
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,
|
ResolvedProvider,
|
||||||
forwardRef
|
forwardRef
|
||||||
} from 'angular2/src/core/di';
|
} 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 {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 {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 {ViewContainerRef} from './view_container_ref';
|
||||||
import {TemplateRef, TemplateRef_} from './template_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 {AppViewListener} from './view_listener';
|
||||||
|
import {RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
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.
|
* 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.
|
* Returns the {@link ElementRef} that makes up the specified Host View.
|
||||||
*/
|
*/
|
||||||
getHostElement(hostViewRef: HostViewRef): ElementRef {
|
abstract 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches the Component View of the Component specified via `hostLocation` and returns the
|
* 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
|
* 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.
|
* 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}.
|
* by compiling the component with {@link Compiler#compileInHost}.
|
||||||
*
|
*
|
||||||
* Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host
|
* Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host
|
||||||
@ -101,7 +99,7 @@ export abstract class AppViewManager {
|
|||||||
* viewRef: ng.ViewRef;
|
* viewRef: ng.ViewRef;
|
||||||
*
|
*
|
||||||
* constructor(public appViewManager: ng.AppViewManager, compiler: ng.Compiler) {
|
* 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);
|
* this.viewRef = appViewManager.createRootHostView(protoView, 'some-component', null);
|
||||||
* })
|
* })
|
||||||
* }
|
* }
|
||||||
@ -115,8 +113,8 @@ export abstract class AppViewManager {
|
|||||||
* ng.bootstrap(MyApp);
|
* ng.bootstrap(MyApp);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
abstract createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
abstract createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string,
|
||||||
injector: Injector): HostViewRef;
|
injector: Injector, projectableNodes?: any[][]): HostViewRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys the Host View created via {@link AppViewManager#createRootHostView}.
|
* 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
|
// 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.
|
// we should make it private, otherwise we have two apis to do the same thing.
|
||||||
abstract createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
|
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
|
* 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
|
* The component is instantiated using its {@link ProtoViewRef `protoViewRef`} which can be
|
||||||
* obtained via {@link Compiler#compileInHost}.
|
* 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.
|
* that will be created for the Host View.
|
||||||
*
|
*
|
||||||
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
|
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
|
||||||
*
|
*
|
||||||
* Use {@link AppViewManager#destroyViewInContainer} to destroy the created Host View.
|
* Use {@link AppViewManager#destroyViewInContainer} to destroy the created Host View.
|
||||||
*/
|
*/
|
||||||
abstract createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
|
abstract createHostViewInContainer(
|
||||||
protoViewRef: ProtoViewRef,
|
viewContainerLocation: ElementRef, index: number, hostViewFactoryRef: HostViewFactoryRef,
|
||||||
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef;
|
dynamicallyCreatedProviders: ResolvedProvider[], projectableNodes: any[][]): HostViewRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroys an Embedded or Host View attached to a View Container at the specified `index`.
|
* 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
|
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||||
abstract attachViewInContainer(viewContainerLocation: ElementRef, index: number,
|
abstract attachViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||||
viewRef: ViewRef): ViewRef;
|
viewRef: EmbeddedViewRef): EmbeddedViewRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See {@link AppViewManager#attachViewInContainer}.
|
* See {@link AppViewManager#attachViewInContainer}.
|
||||||
*/
|
*/
|
||||||
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef;
|
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): EmbeddedViewRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppViewManager_ extends AppViewManager {
|
export class AppViewManager_ extends AppViewManager {
|
||||||
private _protoViewFactory: ProtoViewFactory;
|
private _nextCompTypeId: number = 0;
|
||||||
|
|
||||||
constructor(private _viewPool: AppViewPool, private _viewListener: AppViewListener,
|
constructor(private _renderer: RootRenderer, private _viewListener: AppViewListener,
|
||||||
private _utils: AppViewManagerUtils, private _renderer: Renderer,
|
@Inject(APP_ID) private _appId: string) {
|
||||||
@Inject(forwardRef(() => ProtoViewFactory)) _protoViewFactory) {
|
|
||||||
super();
|
super();
|
||||||
this._protoViewFactory = _protoViewFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewContainer(location: ElementRef): ViewContainerRef {
|
getViewContainer(location: ElementRef_): ViewContainerRef {
|
||||||
var hostView = internalView((<ElementRef_>location).parentView);
|
return location.internalElement.getViewContainerRef();
|
||||||
return hostView.elementInjectors[(<ElementRef_>location).boundElementIndex]
|
|
||||||
.getViewContainerRef();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
|
getHostElement(hostViewRef: ViewRef_): ElementRef {
|
||||||
var hostView = internalView((<ElementRef_>hostLocation).parentView);
|
var hostView = hostViewRef.internalView;
|
||||||
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
|
if (hostView.proto.type !== ViewType.HOST) {
|
||||||
var componentView = hostView.getNestedView(boundElementIndex);
|
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)) {
|
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);
|
for (var i = 0; i < componentView.appElements.length; i++) {
|
||||||
if (isBlank(binderIdx)) {
|
var compAppEl = componentView.appElements[i];
|
||||||
throw new BaseException(`Could not find variable ${variableName}`);
|
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 {
|
getComponent(hostLocation: ElementRef_): any {
|
||||||
var hostView = internalView((<ElementRef_>hostLocation).parentView);
|
return hostLocation.internalElement.getComponent();
|
||||||
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
|
|
||||||
return this._utils.getComponentInstance(hostView, boundElementIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()');
|
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()');
|
||||||
|
|
||||||
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
createRootHostView(hostViewFactoryRef: HostViewFactoryRef_, overrideSelector: string,
|
||||||
injector: Injector): HostViewRef {
|
injector: Injector, projectableNodes: any[][] = null): HostViewRef {
|
||||||
var s = this._createRootHostViewScope();
|
var s = this._createRootHostViewScope();
|
||||||
var hostProtoView: viewModule.AppProtoView = internalProtoView(hostProtoViewRef);
|
var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
|
||||||
this._protoViewFactory.initializeProtoViewIfNeeded(hostProtoView);
|
var selector = isPresent(overrideSelector) ? overrideSelector : hostViewFactory.selector;
|
||||||
var hostElementSelector = overrideSelector;
|
var view = hostViewFactory.viewFactory(this._renderer, this, null, projectableNodes, selector,
|
||||||
if (isBlank(hostElementSelector)) {
|
null, injector);
|
||||||
hostElementSelector = hostProtoView.elementBinders[0].componentDirective.metadata.selector;
|
return wtfLeave(s, view.ref);
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()');
|
_destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()');
|
||||||
|
|
||||||
destroyRootHostView(hostViewRef: HostViewRef) {
|
destroyRootHostView(hostViewRef: ViewRef_) {
|
||||||
// Note: Don't put the hostView into the view pool
|
|
||||||
// as it is depending on the element for which it was created.
|
|
||||||
var s = this._destroyRootHostViewScope();
|
var s = this._destroyRootHostViewScope();
|
||||||
var hostView = internalView(<ViewRef>hostViewRef);
|
var hostView = hostViewRef.internalView;
|
||||||
this._renderer.detachFragment(hostView.renderFragment);
|
hostView.renderer.detachView(flattenNestedViewRenderNodes(hostView.rootNodesOrAppElements));
|
||||||
this._renderer.dehydrateView(hostView.render);
|
hostView.destroy();
|
||||||
this._viewDehydrateRecurse(hostView);
|
|
||||||
this._viewListener.onViewDestroyed(hostView);
|
|
||||||
this._renderer.destroyView(hostView.render);
|
|
||||||
wtfLeave(s);
|
wtfLeave(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,97 +248,43 @@ export class AppViewManager_ extends AppViewManager {
|
|||||||
_createEmbeddedViewInContainerScope: WtfScopeFn =
|
_createEmbeddedViewInContainerScope: WtfScopeFn =
|
||||||
wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()');
|
wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()');
|
||||||
|
|
||||||
createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
|
createEmbeddedViewInContainer(viewContainerLocation: ElementRef_, index: number,
|
||||||
templateRef: TemplateRef): ViewRef {
|
templateRef: TemplateRef_): EmbeddedViewRef {
|
||||||
var s = this._createEmbeddedViewInContainerScope();
|
var s = this._createEmbeddedViewInContainerScope();
|
||||||
var protoView = internalProtoView((<TemplateRef_>templateRef).protoViewRef);
|
var contextEl = templateRef.elementRef.internalElement;
|
||||||
if (protoView.type !== viewModule.ViewType.EMBEDDED) {
|
var view: AppView =
|
||||||
throw new BaseException('This method can only be called with embedded ProtoViews!');
|
contextEl.embeddedViewFactory(contextEl.parentView.renderer, this, contextEl,
|
||||||
}
|
contextEl.parentView.projectableNodes, null, null, null);
|
||||||
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
|
this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
|
||||||
return wtfLeave(s, this._createViewInContainer(viewContainerLocation, index, protoView,
|
return wtfLeave(s, view.ref);
|
||||||
templateRef.elementRef, null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_createHostViewInContainerScope: WtfScopeFn =
|
_createHostViewInContainerScope: WtfScopeFn =
|
||||||
wtfCreateScope('AppViewManager#createHostViewInContainer()');
|
wtfCreateScope('AppViewManager#createHostViewInContainer()');
|
||||||
|
|
||||||
createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
|
createHostViewInContainer(viewContainerLocation: ElementRef_, index: number,
|
||||||
protoViewRef: ProtoViewRef,
|
hostViewFactoryRef: HostViewFactoryRef_,
|
||||||
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef {
|
dynamicallyCreatedProviders: ResolvedProvider[],
|
||||||
|
projectableNodes: any[][]): HostViewRef {
|
||||||
var s = this._createHostViewInContainerScope();
|
var s = this._createHostViewInContainerScope();
|
||||||
var protoView = internalProtoView(protoViewRef);
|
// TODO(tbosch): This should be specifiable via an additional argument!
|
||||||
if (protoView.type !== viewModule.ViewType.HOST) {
|
var contextEl = viewContainerLocation.internalElement;
|
||||||
throw new BaseException('This method can only be called with host ProtoViews!');
|
var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
|
||||||
}
|
var view = hostViewFactory.viewFactory(
|
||||||
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
|
contextEl.parentView.renderer, contextEl.parentView.viewManager, contextEl,
|
||||||
return wtfLeave(
|
projectableNodes, null, dynamicallyCreatedProviders, null);
|
||||||
s, this._createViewInContainer(viewContainerLocation, index, protoView,
|
this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
|
||||||
viewContainerLocation, imperativelyCreatedInjector));
|
return wtfLeave(s, view.ref);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_destroyViewInContainerScope = wtfCreateScope('AppViewMananger#destroyViewInContainer()');
|
_destroyViewInContainerScope = wtfCreateScope('AppViewMananger#destroyViewInContainer()');
|
||||||
|
|
||||||
destroyViewInContainer(viewContainerLocation: ElementRef, index: number) {
|
destroyViewInContainer(viewContainerLocation: ElementRef_, index: number) {
|
||||||
var s = this._destroyViewInContainerScope();
|
var s = this._destroyViewInContainerScope();
|
||||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
|
||||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
view.destroy();
|
||||||
this._destroyViewInContainer(parentView, boundElementIndex, index);
|
|
||||||
wtfLeave(s);
|
wtfLeave(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,20 +292,10 @@ export class AppViewManager_ extends AppViewManager {
|
|||||||
_attachViewInContainerScope = wtfCreateScope('AppViewMananger#attachViewInContainer()');
|
_attachViewInContainerScope = wtfCreateScope('AppViewMananger#attachViewInContainer()');
|
||||||
|
|
||||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||||
attachViewInContainer(viewContainerLocation: ElementRef, index: number,
|
attachViewInContainer(viewContainerLocation: ElementRef_, index: number,
|
||||||
viewRef: ViewRef): ViewRef {
|
viewRef: ViewRef_): EmbeddedViewRef {
|
||||||
var s = this._attachViewInContainerScope();
|
var s = this._attachViewInContainerScope();
|
||||||
var view = internalView(viewRef);
|
this._attachViewToContainer(viewRef.internalView, viewContainerLocation.internalElement, index);
|
||||||
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);
|
|
||||||
return wtfLeave(s, viewRef);
|
return wtfLeave(s, viewRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,88 +303,72 @@ export class AppViewManager_ extends AppViewManager {
|
|||||||
_detachViewInContainerScope = wtfCreateScope('AppViewMananger#detachViewInContainer()');
|
_detachViewInContainerScope = wtfCreateScope('AppViewMananger#detachViewInContainer()');
|
||||||
|
|
||||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||||
detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef {
|
detachViewInContainer(viewContainerLocation: ElementRef_, index: number): EmbeddedViewRef {
|
||||||
var s = this._detachViewInContainerScope();
|
var s = this._detachViewInContainerScope();
|
||||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
|
||||||
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);
|
|
||||||
return wtfLeave(s, view.ref);
|
return wtfLeave(s, view.ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_createMainView(protoView: viewModule.AppProtoView,
|
onViewCreated(view: AppView) { this._viewListener.onViewCreated(view); }
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_createPooledView(protoView: viewModule.AppProtoView): viewModule.AppView {
|
onViewDestroyed(view: AppView) { this._viewListener.onViewDestroyed(view); }
|
||||||
var view = this._viewPool.getView(protoView);
|
|
||||||
if (isBlank(view)) {
|
/** @internal */
|
||||||
view = this._createMainView(
|
createRenderComponentType(encapsulation: ViewEncapsulation,
|
||||||
protoView,
|
styles: Array<string | any[]>): RenderComponentType {
|
||||||
this._renderer.createView(protoView.render, protoView.mergeInfo.embeddedViewCount + 1));
|
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;
|
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 {unimplemented} from 'angular2/src/facade/exceptions';
|
||||||
import * as viewModule from './view';
|
|
||||||
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
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 abstract class ViewRef {
|
||||||
export function internalView(viewRef: ViewRef): viewModule.AppView {
|
/**
|
||||||
return (<ViewRef_>viewRef)._view;
|
* @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}
|
* Represents a View containing a single Element that is the Host Element of a {@link Component}
|
||||||
* instance.
|
* instance.
|
||||||
@ -24,11 +20,8 @@ export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppPro
|
|||||||
* of the higher-level APIs: {@link AppViewManager#createRootHostView},
|
* of the higher-level APIs: {@link AppViewManager#createRootHostView},
|
||||||
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
|
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
|
||||||
*/
|
*/
|
||||||
export interface HostViewRef {
|
export abstract class HostViewRef extends ViewRef {
|
||||||
/**
|
get rootNodes(): any[] { return unimplemented(); };
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
changeDetectorRef: ChangeDetectorRef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,96 +77,43 @@ export interface HostViewRef {
|
|||||||
* <!-- /ViewRef: outer-0 -->
|
* <!-- /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.
|
* Sets `value` of local variable called `variableName` in this View.
|
||||||
*/
|
*/
|
||||||
abstract setLocal(variableName: string, value: any): void;
|
abstract setLocal(variableName: string, value: any): void;
|
||||||
|
|
||||||
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }
|
/**
|
||||||
set changeDetectorRef(value: ChangeDetectorRef) {
|
* Checks whether this view has a local variable called `variableName`.
|
||||||
unimplemented(); // TODO: https://github.com/Microsoft/TypeScript/issues/12
|
*/
|
||||||
}
|
abstract hasLocal(variableName: string): boolean;
|
||||||
|
|
||||||
|
get rootNodes(): any[] { return unimplemented(); };
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ViewRef_ extends ViewRef {
|
export class ViewRef_ implements EmbeddedViewRef, HostViewRef {
|
||||||
private _changeDetectorRef: ChangeDetectorRef = null;
|
constructor(private _view: AppView) { this._view = _view; }
|
||||||
/** @internal */
|
|
||||||
public _view: viewModule.AppView;
|
|
||||||
constructor(_view: viewModule.AppView) {
|
|
||||||
super();
|
|
||||||
this._view = _view;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
get internalView(): AppView { return this._view; }
|
||||||
* Return `RenderViewRef`
|
|
||||||
*/
|
|
||||||
get render(): RenderViewRef { return this._view.render; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return `RenderFragmentRef`
|
|
||||||
*/
|
|
||||||
get renderFragment(): RenderFragmentRef { return this._view.renderFragment; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return `ChangeDetectorRef`
|
* Return `ChangeDetectorRef`
|
||||||
*/
|
*/
|
||||||
get changeDetectorRef(): ChangeDetectorRef {
|
get changeDetectorRef(): ChangeDetectorRef { return this._view.changeDetector.ref; }
|
||||||
if (this._changeDetectorRef === null) {
|
|
||||||
this._changeDetectorRef = this._view.changeDetector.ref;
|
get rootNodes(): any[] { return this._view.flatRootNodes; }
|
||||||
}
|
|
||||||
return this._changeDetectorRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLocal(variableName: string, value: any): void { this._view.setLocal(variableName, value); }
|
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; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export abstract class HostViewFactoryRef {}
|
||||||
* 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 class ProtoViewRef_ extends ProtoViewRef {
|
export class HostViewFactoryRef_ implements HostViewFactoryRef {
|
||||||
/** @internal */
|
constructor(private _hostViewFactory: HostViewFactory) {}
|
||||||
public _protoView: viewModule.AppProtoView;
|
|
||||||
constructor(_protoView: viewModule.AppProtoView) {
|
get internalHostViewFactory(): HostViewFactory { return this._hostViewFactory; }
|
||||||
super();
|
}
|
||||||
this._protoView = _protoView;
|
|
||||||
}
|
|
||||||
}
|
|
11
modules/angular2/src/core/linker/view_type.ts
Normal file
11
modules/angular2/src/core/linker/view_type.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export enum ViewType {
|
||||||
|
// A view that contains the host element with bound component directive.
|
||||||
|
// Contains a COMPONENT view
|
||||||
|
HOST,
|
||||||
|
// The view of the component
|
||||||
|
// Can contain 0 to n EMBEDDED views
|
||||||
|
COMPONENT,
|
||||||
|
// A view that is embedded into another View via a <template> element
|
||||||
|
// inside of a COMPONENT view
|
||||||
|
EMBEDDED
|
||||||
|
}
|
@ -45,7 +45,6 @@ export class Pipes implements cd.Pipes {
|
|||||||
get(name: string): cd.SelectedPipe {
|
get(name: string): cd.SelectedPipe {
|
||||||
var cached = StringMapWrapper.get(this._config, name);
|
var cached = StringMapWrapper.get(this._config, name);
|
||||||
if (isPresent(cached)) return cached;
|
if (isPresent(cached)) return cached;
|
||||||
|
|
||||||
var p = this.proto.get(name);
|
var p = this.proto.get(name);
|
||||||
var transform = this.injector.instantiateResolved(p);
|
var transform = this.injector.instantiateResolved(p);
|
||||||
var res = new cd.SelectedPipe(transform, p.pure);
|
var res = new cd.SelectedPipe(transform, p.pure);
|
||||||
|
@ -1,19 +1,2 @@
|
|||||||
// Public API for render
|
// Public API for render
|
||||||
export {
|
export {RootRenderer, Renderer, RenderComponentType} from './render/api';
|
||||||
RenderEventDispatcher,
|
|
||||||
Renderer,
|
|
||||||
RenderElementRef,
|
|
||||||
RenderViewRef,
|
|
||||||
RenderProtoViewRef,
|
|
||||||
RenderFragmentRef,
|
|
||||||
RenderViewWithFragments,
|
|
||||||
RenderTemplateCmd,
|
|
||||||
RenderCommandVisitor,
|
|
||||||
RenderTextCmd,
|
|
||||||
RenderNgContentCmd,
|
|
||||||
RenderBeginElementCmd,
|
|
||||||
RenderBeginComponentCmd,
|
|
||||||
RenderEmbeddedTemplateCmd,
|
|
||||||
RenderBeginCmd,
|
|
||||||
RenderComponentTemplate
|
|
||||||
} from './render/api';
|
|
||||||
|
@ -1,196 +1,54 @@
|
|||||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||||
import {Map} from 'angular2/src/facade/collection';
|
|
||||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
|
||||||
|
|
||||||
/**
|
export class RenderComponentType {
|
||||||
* Represents an Angular ProtoView in the Rendering Context.
|
constructor(public id: string, public encapsulation: ViewEncapsulation,
|
||||||
*
|
public styles: Array<string | any[]>) {}
|
||||||
* 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 interface ParentRenderer { renderComponent(componentType: RenderComponentType): Renderer; }
|
||||||
* Command to begin rendering.
|
|
||||||
*/
|
|
||||||
export abstract class RenderBeginCmd extends RenderTemplateCmd {
|
|
||||||
get ngContentIndex(): number { return unimplemented(); };
|
|
||||||
get isBound(): boolean { return unimplemented(); };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
export abstract class Renderer implements ParentRenderer {
|
||||||
* Command to render text.
|
abstract renderComponent(componentType: RenderComponentType): Renderer;
|
||||||
*/
|
|
||||||
export abstract class RenderTextCmd extends RenderBeginCmd {
|
|
||||||
get value(): string { return unimplemented(); };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
abstract selectRootElement(selector: string): any;
|
||||||
* 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 createElement(parentElement: any, name: 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 createViewRoot(hostElement: any): any;
|
||||||
* Command to begin rendering a component.
|
|
||||||
*/
|
|
||||||
export abstract class RenderBeginComponentCmd extends RenderBeginElementCmd {
|
|
||||||
get templateId(): string { return unimplemented(); };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
abstract createTemplateAnchor(parentElement: 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 createText(parentElement: any, value: string): 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 projectNodes(parentElement: any, nodes: any[]);
|
||||||
|
|
||||||
/**
|
abstract attachViewAfter(node: any, viewRootNodes: 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 detachView(viewRootNodes: any[]);
|
||||||
* Represents an Element that is part of a {@link RenderViewRef Render View}.
|
|
||||||
*
|
abstract destroyView(hostElement: any, viewAllNodes: any[]);
|
||||||
* `RenderElementRef` is a counterpart to {@link ElementRef} available in the Application Context.
|
|
||||||
*
|
abstract listen(renderElement: any, name: string, callback: Function);
|
||||||
* When using `Renderer` from the Application Context, `ElementRef` can be used instead of
|
|
||||||
* `RenderElementRef`.
|
abstract listenGlobal(target: string, name: string, callback: Function): Function;
|
||||||
*/
|
|
||||||
export interface RenderElementRef {
|
abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: any);
|
||||||
/**
|
|
||||||
* Reference to the Render View that contains this Element.
|
abstract setElementAttribute(renderElement: any, attributeName: string, attributeValue: string);
|
||||||
*/
|
|
||||||
renderView: RenderViewRef;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* Used only in debug mode to serialize property changes to comment nodes,
|
||||||
*
|
* such as <template> placeholders.
|
||||||
* Index of the Element (in the depth-first order) inside the Render View.
|
|
||||||
*
|
|
||||||
* This index is used internally by Angular to locate elements.
|
|
||||||
*/
|
*/
|
||||||
boundElementIndex: number;
|
abstract setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
abstract setElementClass(renderElement: any, className: string, isAdd: boolean);
|
||||||
* Template for rendering a component, including commands and styles.
|
|
||||||
*/
|
abstract setElementStyle(renderElement: any, styleName: string, styleValue: string);
|
||||||
export class RenderComponentTemplate {
|
|
||||||
constructor(public id: string, public shortId: string, public encapsulation: ViewEncapsulation,
|
abstract invokeElementMethod(renderElement: any, methodName: string, args: any[]);
|
||||||
public commands: RenderTemplateCmd[], public styles: string[]) {}
|
|
||||||
|
abstract setText(renderNode: any, text: string);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,184 +63,7 @@ export class RenderComponentTemplate {
|
|||||||
*
|
*
|
||||||
* The default Renderer implementation is `DomRenderer`. Also available is `WebWorkerRenderer`.
|
* 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);
|
|
||||||
|
|
||||||
/**
|
export abstract class RootRenderer implements ParentRenderer {
|
||||||
* Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s.
|
abstract renderComponent(componentType: RenderComponentType): Renderer;
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -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';
|
import {ConcreteType, global, Type, isFunction, stringify} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
var _nextClassId = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declares the interface to be used with {@link Class}.
|
* 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);
|
Reflect.defineMetadata('annotations', this.annotations, constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!constructor['name']) {
|
||||||
|
constructor['overriddenName'] = `class${_nextClassId++}`;
|
||||||
|
}
|
||||||
|
|
||||||
return <ConcreteType>constructor;
|
return <ConcreteType>constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class ObservableWrapper {
|
|||||||
static void callNext(EventEmitter emitter, value) {
|
static void callNext(EventEmitter emitter, value) {
|
||||||
emitter.add(value);
|
emitter.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void callEmit(EventEmitter emitter, value) {
|
static void callEmit(EventEmitter emitter, value) {
|
||||||
emitter.add(value);
|
emitter.add(value);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ class EventEmitter<T> extends Stream<T> {
|
|||||||
void add(value) {
|
void add(value) {
|
||||||
_controller.add(value);
|
_controller.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void emit(value) {
|
void emit(value) {
|
||||||
_controller.add(value);
|
_controller.add(value);
|
||||||
}
|
}
|
||||||
|
@ -331,3 +331,7 @@ class DateWrapper {
|
|||||||
|
|
||||||
// needed to match the exports from lang.js
|
// needed to match the exports from lang.js
|
||||||
var global = null;
|
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) {
|
if (token.name) {
|
||||||
return token.name;
|
return token.name;
|
||||||
}
|
}
|
||||||
|
if (token.overriddenName) {
|
||||||
|
return token.overriddenName;
|
||||||
|
}
|
||||||
|
|
||||||
var res = token.toString();
|
var res = token.toString();
|
||||||
var newLineIndex = res.indexOf("\n");
|
var newLineIndex = res.indexOf("\n");
|
||||||
@ -412,3 +415,15 @@ export function getSymbolIterator(): string | symbol {
|
|||||||
}
|
}
|
||||||
return _symbolIterator;
|
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,
|
platform,
|
||||||
ExceptionHandler,
|
ExceptionHandler,
|
||||||
Reflector,
|
Reflector,
|
||||||
Renderer,
|
RootRenderer,
|
||||||
reflector,
|
reflector,
|
||||||
APPLICATION_COMMON_PROVIDERS,
|
APPLICATION_COMMON_PROVIDERS,
|
||||||
PLATFORM_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 {KeyEventsPlugin} from 'angular2/src/platform/dom/events/key_events';
|
||||||
import {HammerGesturesPlugin} from 'angular2/src/platform/dom/events/hammer_gestures';
|
import {HammerGesturesPlugin} from 'angular2/src/platform/dom/events/hammer_gestures';
|
||||||
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
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 {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
|
||||||
import {SharedStylesHost} 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";
|
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: DomEventsPlugin, multi: true}),
|
||||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
|
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
|
||||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
|
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
|
||||||
new Provider(DomRenderer, {useClass: DomRenderer_}),
|
new Provider(DomRootRenderer, {useClass: DomRootRenderer_}),
|
||||||
new Provider(Renderer, {useExisting: DomRenderer}),
|
new Provider(RootRenderer, {useExisting: DomRootRenderer}),
|
||||||
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
Testability,
|
Testability,
|
||||||
|
@ -4,7 +4,6 @@ import {Injectable, provide, Provider} from 'angular2/src/core/di';
|
|||||||
import {AppViewListener} from 'angular2/src/core/linker/view_listener';
|
import {AppViewListener} from 'angular2/src/core/linker/view_listener';
|
||||||
import {AppView} from 'angular2/src/core/linker/view';
|
import {AppView} from 'angular2/src/core/linker/view';
|
||||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
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';
|
import {DebugElement, DebugElement_} from 'angular2/src/core/debug/debug_element';
|
||||||
|
|
||||||
const NG_ID_PROPERTY = 'ngid';
|
const NG_ID_PROPERTY = 'ngid';
|
||||||
@ -43,7 +42,7 @@ export function inspectNativeElement(element): DebugElement {
|
|||||||
if (isPresent(elId)) {
|
if (isPresent(elId)) {
|
||||||
var view = _allViewsById.get(elId[0]);
|
var view = _allViewsById.get(elId[0]);
|
||||||
if (isPresent(view)) {
|
if (isPresent(view)) {
|
||||||
return new DebugElement_(view, elId[1]);
|
return new DebugElement_(view.appElements[elId[1]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -51,17 +50,15 @@ export function inspectNativeElement(element): DebugElement {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DebugElementViewListener implements AppViewListener {
|
export class DebugElementViewListener implements AppViewListener {
|
||||||
constructor(private _renderer: Renderer) {
|
constructor() { DOM.setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement); }
|
||||||
DOM.setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
onViewCreated(view: AppView) {
|
onViewCreated(view: AppView) {
|
||||||
var viewId = _nextId++;
|
var viewId = _nextId++;
|
||||||
_allViewsById.set(viewId, view);
|
_allViewsById.set(viewId, view);
|
||||||
_allIdsByView.set(view, viewId);
|
_allIdsByView.set(view, viewId);
|
||||||
for (var i = 0; i < view.elementRefs.length; i++) {
|
for (var i = 0; i < view.appElements.length; i++) {
|
||||||
var el = view.elementRefs[i];
|
var el = view.appElements[i];
|
||||||
_setElementId(this._renderer.getNativeElementSync(el), [viewId, i]);
|
_setElementId(el.nativeElement, [viewId, i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,38 +7,18 @@ import {
|
|||||||
RegExpWrapper,
|
RegExpWrapper,
|
||||||
CONST_EXPR,
|
CONST_EXPR,
|
||||||
stringify,
|
stringify,
|
||||||
StringWrapper
|
StringWrapper,
|
||||||
|
isArray
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||||
import {DomSharedStylesHost} from './shared_styles_host';
|
import {DomSharedStylesHost} from './shared_styles_host';
|
||||||
import {WtfScopeFn, wtfLeave, wtfCreateScope} from 'angular2/src/core/profile/profile';
|
|
||||||
|
|
||||||
import {
|
import {Renderer, RootRenderer, RenderComponentType} from 'angular2/core';
|
||||||
Renderer,
|
|
||||||
RenderProtoViewRef,
|
|
||||||
RenderViewRef,
|
|
||||||
RenderElementRef,
|
|
||||||
RenderFragmentRef,
|
|
||||||
RenderViewWithFragments,
|
|
||||||
RenderTemplateCmd,
|
|
||||||
RenderEventDispatcher,
|
|
||||||
RenderComponentTemplate
|
|
||||||
} from 'angular2/core';
|
|
||||||
|
|
||||||
import {EventManager} from './events/event_manager';
|
import {EventManager} from './events/event_manager';
|
||||||
|
|
||||||
import {DOCUMENT} from './dom_tokens';
|
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 {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||||
import {camelCaseToDashCase} from './util';
|
import {camelCaseToDashCase} from './util';
|
||||||
@ -48,243 +28,225 @@ const NAMESPACE_URIS =
|
|||||||
const TEMPLATE_COMMENT_TEXT = 'template bindings={}';
|
const TEMPLATE_COMMENT_TEXT = 'template bindings={}';
|
||||||
var TEMPLATE_BINDINGS_EXP = /^template bindings=(.*)$/g;
|
var TEMPLATE_BINDINGS_EXP = /^template bindings=(.*)$/g;
|
||||||
|
|
||||||
export abstract class DomRenderer extends Renderer implements NodeFactory<Node> {
|
export abstract class DomRootRenderer implements RootRenderer {
|
||||||
abstract registerComponentTemplate(template: RenderComponentTemplate);
|
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,
|
renderComponent(componentProto: RenderComponentType): Renderer {
|
||||||
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
|
var renderer = this._registeredComponents.get(componentProto.id);
|
||||||
|
if (isBlank(renderer)) {
|
||||||
abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
|
renderer = new DomRenderer(this, componentProto);
|
||||||
hostElementSelector: string): RenderViewWithFragments;
|
this._registeredComponents.set(componentProto.id, renderer);
|
||||||
|
}
|
||||||
abstract createView(protoViewRef: RenderProtoViewRef,
|
return renderer;
|
||||||
fragmentCount: number): RenderViewWithFragments;
|
|
||||||
|
|
||||||
abstract destroyView(viewRef: RenderViewRef);
|
|
||||||
|
|
||||||
abstract createRootContentInsertionPoint();
|
|
||||||
|
|
||||||
getNativeElementSync(location: RenderElementRef): any {
|
|
||||||
return resolveInternalDomView(location.renderView).boundElements[location.boundElementIndex];
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
export class DomRenderer implements Renderer {
|
||||||
fragmentRef: RenderFragmentRef) {
|
private _contentAttr: string;
|
||||||
var previousFragmentNodes = resolveInternalDomFragment(previousFragmentRef);
|
private _hostAttr: string;
|
||||||
if (previousFragmentNodes.length > 0) {
|
private _styles: string[];
|
||||||
var sibling = previousFragmentNodes[previousFragmentNodes.length - 1];
|
|
||||||
let nodes = resolveInternalDomFragment(fragmentRef);
|
constructor(private _rootRenderer: DomRootRenderer, private componentProto: RenderComponentType) {
|
||||||
moveNodesAfterSibling(sibling, nodes);
|
this._styles = _flattenStyles(componentProto.id, componentProto.styles, []);
|
||||||
this.animateNodesEnter(nodes);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
renderComponent(componentProto: RenderComponentType): Renderer {
|
||||||
* Iterates through all nodes being added to the DOM and animates them if necessary
|
return this._rootRenderer.renderComponent(componentProto);
|
||||||
* @param nodes
|
|
||||||
*/
|
|
||||||
animateNodesEnter(nodes: Node[]) {
|
|
||||||
for (let i = 0; i < nodes.length; i++) this.animateNodeEnter(nodes[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
* Performs animations if necessary
|
||||||
* @param node
|
* @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) {
|
animateNodeEnter(node: Node) {
|
||||||
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
||||||
DOM.addClass(node, 'ng-enter');
|
DOM.addClass(node, 'ng-enter');
|
||||||
this._animate.css()
|
this._rootRenderer.animate.css()
|
||||||
.addAnimationClass('ng-enter-active')
|
.addAnimationClass('ng-enter-active')
|
||||||
.start(<HTMLElement>node)
|
.start(<HTMLElement>node)
|
||||||
.onComplete(() => { DOM.removeClass(node, 'ng-enter'); });
|
.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) {
|
animateNodeLeave(node: Node) {
|
||||||
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
||||||
DOM.addClass(node, 'ng-leave');
|
DOM.addClass(node, 'ng-leave');
|
||||||
this._animate.css()
|
this._rootRenderer.animate.css()
|
||||||
.addAnimationClass('ng-leave-active')
|
.addAnimationClass('ng-leave-active')
|
||||||
.start(<HTMLElement>node)
|
.start(<HTMLElement>node)
|
||||||
.onComplete(() => {
|
.onComplete(() => {
|
||||||
@ -295,73 +257,6 @@ export class DomRenderer_ extends DomRenderer {
|
|||||||
DOM.remove(node);
|
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) {
|
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 {
|
function decoratePreventDefault(eventHandler: Function): Function {
|
||||||
return (event) => {
|
return (event) => {
|
||||||
var allowDefaultBehavior = eventHandler(event);
|
var allowDefaultBehavior = eventHandler(event);
|
||||||
if (!allowDefaultBehavior) {
|
if (allowDefaultBehavior === false) {
|
||||||
// TODO(tbosch): move preventDefault into event plugins...
|
// TODO(tbosch): move preventDefault into event plugins...
|
||||||
DOM.preventDefault(event);
|
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;
|
var NS_PREFIX_RE = /^@([^:]+):(.+)/g;
|
||||||
|
|
||||||
function splitNamespace(name: string): string[] {
|
function splitNamespace(name: string): string[] {
|
||||||
@ -398,4 +325,4 @@ function splitNamespace(name: string): string[] {
|
|||||||
}
|
}
|
||||||
let match = RegExpWrapper.firstMatch(NS_PREFIX_RE, name);
|
let match = RegExpWrapper.firstMatch(NS_PREFIX_RE, name);
|
||||||
return [match[1], match[2]];
|
return [match[1], match[2]];
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import {XHR} from 'angular2/src/compiler/xhr';
|
import {XHR} from 'angular2/src/compiler/xhr';
|
||||||
import {WebWorkerXHRImpl} from 'angular2/src/web_workers/worker/xhr_impl';
|
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 {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 {
|
import {
|
||||||
PLATFORM_DIRECTIVES,
|
PLATFORM_DIRECTIVES,
|
||||||
PLATFORM_PIPES,
|
PLATFORM_PIPES,
|
||||||
@ -23,11 +23,7 @@ import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler';
|
|||||||
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
import {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||||
import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
|
import {ON_WEB_WORKER} from "angular2/src/web_workers/shared/api";
|
||||||
import {Provider} from 'angular2/src/core/di';
|
import {Provider} from 'angular2/src/core/di';
|
||||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||||
import {
|
|
||||||
RenderViewWithFragmentsStore
|
|
||||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
|
||||||
import {WebWorkerEventDispatcher} from 'angular2/src/web_workers/worker/event_dispatcher';
|
|
||||||
|
|
||||||
class PrintLogger {
|
class PrintLogger {
|
||||||
log = print;
|
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(PLATFORM_DIRECTIVES, {useValue: COMMON_DIRECTIVES, multi: true}),
|
||||||
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
|
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
|
||||||
new Provider(ServiceMessageBrokerFactory, {useClass: ServiceMessageBrokerFactory_}),
|
new Provider(ServiceMessageBrokerFactory, {useClass: ServiceMessageBrokerFactory_}),
|
||||||
WebWorkerRenderer,
|
WebWorkerRootRenderer,
|
||||||
new Provider(Renderer, {useExisting: WebWorkerRenderer}),
|
new Provider(RootRenderer, {useExisting: WebWorkerRootRenderer}),
|
||||||
new Provider(ON_WEB_WORKER, {useValue: true}),
|
new Provider(ON_WEB_WORKER, {useValue: true}),
|
||||||
RenderViewWithFragmentsStore,
|
RenderStore,
|
||||||
RenderProtoViewRefStore,
|
|
||||||
new Provider(ExceptionHandler, {useFactory: _exceptionHandler, deps: []}),
|
new Provider(ExceptionHandler, {useFactory: _exceptionHandler, deps: []}),
|
||||||
WebWorkerXHRImpl,
|
WebWorkerXHRImpl,
|
||||||
new Provider(XHR, {useExisting: WebWorkerXHRImpl}),
|
new Provider(XHR, {useExisting: WebWorkerXHRImpl})
|
||||||
WebWorkerEventDispatcher
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function _exceptionHandler(): ExceptionHandler {
|
function _exceptionHandler(): ExceptionHandler {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
reflector,
|
reflector,
|
||||||
APPLICATION_COMMON_PROVIDERS,
|
APPLICATION_COMMON_PROVIDERS,
|
||||||
PLATFORM_COMMON_PROVIDERS,
|
PLATFORM_COMMON_PROVIDERS,
|
||||||
Renderer,
|
RootRenderer,
|
||||||
PLATFORM_INITIALIZER,
|
PLATFORM_INITIALIZER,
|
||||||
APP_INITIALIZER
|
APP_INITIALIZER
|
||||||
} from 'angular2/core';
|
} 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 {KeyEventsPlugin} from 'angular2/src/platform/dom/events/key_events';
|
||||||
import {HammerGesturesPlugin} from 'angular2/src/platform/dom/events/hammer_gestures';
|
import {HammerGesturesPlugin} from 'angular2/src/platform/dom/events/hammer_gestures';
|
||||||
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
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 {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
|
||||||
import {SharedStylesHost} 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';
|
import {BrowserDetails} from 'angular2/src/animate/browser_details';
|
||||||
@ -46,10 +46,7 @@ import {
|
|||||||
} from 'angular2/src/web_workers/shared/client_message_broker';
|
} from 'angular2/src/web_workers/shared/client_message_broker';
|
||||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
||||||
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
|
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 {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||||
import {
|
|
||||||
RenderViewWithFragmentsStore
|
|
||||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
|
||||||
|
|
||||||
export const WORKER_SCRIPT: OpaqueToken = CONST_EXPR(new OpaqueToken("WebWorkerScript"));
|
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: DomEventsPlugin, multi: true}),
|
||||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
|
new Provider(EVENT_MANAGER_PLUGINS, {useClass: KeyEventsPlugin, multi: true}),
|
||||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
|
new Provider(EVENT_MANAGER_PLUGINS, {useClass: HammerGesturesPlugin, multi: true}),
|
||||||
new Provider(DomRenderer, {useClass: DomRenderer_}),
|
new Provider(DomRootRenderer, {useClass: DomRootRenderer_}),
|
||||||
new Provider(Renderer, {useExisting: DomRenderer}),
|
new Provider(RootRenderer, {useExisting: DomRootRenderer}),
|
||||||
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
new Provider(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
||||||
new Provider(XHR, {useClass: XHRImpl}),
|
new Provider(XHR, {useClass: XHRImpl}),
|
||||||
MessageBasedXHRImpl,
|
MessageBasedXHRImpl,
|
||||||
@ -81,8 +78,7 @@ export const WORKER_RENDER_APP_COMMON: Array<any /*Type | Provider | any[]*/> =
|
|||||||
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
|
new Provider(ClientMessageBrokerFactory, {useClass: ClientMessageBrokerFactory_}),
|
||||||
Serializer,
|
Serializer,
|
||||||
new Provider(ON_WEB_WORKER, {useValue: false}),
|
new Provider(ON_WEB_WORKER, {useValue: false}),
|
||||||
RenderViewWithFragmentsStore,
|
RenderStore,
|
||||||
RenderProtoViewRefStore,
|
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
Testability,
|
Testability,
|
||||||
BrowserDetails,
|
BrowserDetails,
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
Injector,
|
Injector,
|
||||||
Injectable,
|
Injectable,
|
||||||
ViewMetadata,
|
ViewMetadata,
|
||||||
ViewRef,
|
EmbeddedViewRef,
|
||||||
ViewResolver,
|
ViewResolver,
|
||||||
provide
|
provide
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
@ -15,8 +15,8 @@ import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
|||||||
import {Promise} from 'angular2/src/facade/async';
|
import {Promise} from 'angular2/src/facade/async';
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
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 {AppView} from 'angular2/src/core/linker/view';
|
||||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
|
||||||
|
|
||||||
import {el} from './utils';
|
import {el} from './utils';
|
||||||
|
|
||||||
@ -65,10 +65,10 @@ export class ComponentFixture_ extends ComponentFixture {
|
|||||||
|
|
||||||
constructor(componentRef: ComponentRef) {
|
constructor(componentRef: ComponentRef) {
|
||||||
super();
|
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.componentInstance = this.debugElement.componentInstance;
|
||||||
this.nativeElement = this.debugElement.nativeElement;
|
this.nativeElement = this.debugElement.nativeElement;
|
||||||
this._componentParentView = internalView(<ViewRef>componentRef.hostView);
|
|
||||||
this._componentRef = componentRef;
|
this._componentRef = componentRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
|
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 {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {
|
import {
|
||||||
IterableDiffers,
|
IterableDiffers,
|
||||||
@ -46,11 +46,10 @@ import {
|
|||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {FunctionWrapper, Type} from 'angular2/src/facade/lang';
|
import {FunctionWrapper, Type} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/linker/view_pool';
|
import {RootRenderer} from 'angular2/src/core/render/api';
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/linker/view_manager_utils';
|
|
||||||
|
|
||||||
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
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 {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
|
||||||
import {SharedStylesHost} 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';
|
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 {Serializer} from "angular2/src/web_workers/shared/serializer";
|
||||||
import {Log} from './utils';
|
import {Log} from './utils';
|
||||||
import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler';
|
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 {DynamicComponentLoader_} from "angular2/src/core/linker/dynamic_component_loader";
|
||||||
import {AppViewManager_} from "angular2/src/core/linker/view_manager";
|
import {AppViewManager_} from "angular2/src/core/linker/view_manager";
|
||||||
|
|
||||||
@ -92,20 +90,17 @@ function _getAppBindings() {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
APPLICATION_COMMON_PROVIDERS,
|
APPLICATION_COMMON_PROVIDERS,
|
||||||
provide(ChangeDetectorGenConfig, {useValue: new ChangeDetectorGenConfig(true, false, true)}),
|
provide(ChangeDetectorGenConfig, {useValue: new ChangeDetectorGenConfig(true, false, false)}),
|
||||||
provide(DOCUMENT, {useValue: appDoc}),
|
provide(DOCUMENT, {useValue: appDoc}),
|
||||||
provide(DomRenderer, {useClass: DomRenderer_}),
|
provide(DomRootRenderer, {useClass: DomRootRenderer_}),
|
||||||
provide(Renderer, {useExisting: DomRenderer}),
|
provide(RootRenderer, {useExisting: DomRootRenderer}),
|
||||||
provide(APP_ID, {useValue: 'a'}),
|
provide(APP_ID, {useValue: 'a'}),
|
||||||
DomSharedStylesHost,
|
DomSharedStylesHost,
|
||||||
provide(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
provide(SharedStylesHost, {useExisting: DomSharedStylesHost}),
|
||||||
AppViewPool,
|
|
||||||
provide(AppViewManager, {useClass: AppViewManager_}),
|
provide(AppViewManager, {useClass: AppViewManager_}),
|
||||||
AppViewManagerUtils,
|
|
||||||
Serializer,
|
Serializer,
|
||||||
ELEMENT_PROBE_PROVIDERS,
|
ELEMENT_PROBE_PROVIDERS,
|
||||||
provide(APP_VIEW_POOL_CAPACITY, {useValue: 500}),
|
ResolvedMetadataCache,
|
||||||
ProtoViewFactory,
|
|
||||||
provide(DirectiveResolver, {useClass: MockDirectiveResolver}),
|
provide(DirectiveResolver, {useClass: MockDirectiveResolver}),
|
||||||
provide(ViewResolver, {useClass: MockViewResolver}),
|
provide(ViewResolver, {useClass: MockViewResolver}),
|
||||||
provide(IterableDiffers, {useValue: defaultIterableDiffers}),
|
provide(IterableDiffers, {useValue: defaultIterableDiffers}),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export const NG2_APP_VIEW_MANAGER = 'ng2.AppViewManager';
|
export const NG2_APP_VIEW_MANAGER = 'ng2.AppViewManager';
|
||||||
export const NG2_COMPILER = 'ng2.Compiler';
|
export const NG2_COMPILER = 'ng2.Compiler';
|
||||||
export const NG2_INJECTOR = 'ng2.Injector';
|
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 NG2_ZONE = 'ng2.NgZone';
|
||||||
|
|
||||||
export const NG1_CONTROLLER = '$controller';
|
export const NG1_CONTROLLER = '$controller';
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
HostViewRef,
|
HostViewRef,
|
||||||
Injector,
|
Injector,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
ProtoViewRef,
|
HostViewFactoryRef,
|
||||||
SimpleChange
|
SimpleChange
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
import {NG1_SCOPE} from './constants';
|
import {NG1_SCOPE} from './constants';
|
||||||
@ -25,13 +25,13 @@ export class DowngradeNg2ComponentAdapter {
|
|||||||
changeDetector: ChangeDetectorRef = null;
|
changeDetector: ChangeDetectorRef = null;
|
||||||
componentScope: angular.IScope;
|
componentScope: angular.IScope;
|
||||||
childNodes: Node[];
|
childNodes: Node[];
|
||||||
contentInserctionPoint: Node = null;
|
contentInsertionPoint: Node = null;
|
||||||
|
|
||||||
constructor(private id: string, private info: ComponentInfo,
|
constructor(private id: string, private info: ComponentInfo,
|
||||||
private element: angular.IAugmentedJQuery, private attrs: angular.IAttributes,
|
private element: angular.IAugmentedJQuery, private attrs: angular.IAttributes,
|
||||||
private scope: angular.IScope, private parentInjector: Injector,
|
private scope: angular.IScope, private parentInjector: Injector,
|
||||||
private parse: angular.IParseService, private viewManager: AppViewManager,
|
private parse: angular.IParseService, private viewManager: AppViewManager,
|
||||||
private protoView: ProtoViewRef) {
|
private hostViewFactory: HostViewFactoryRef) {
|
||||||
(<any>this.element[0]).id = id;
|
(<any>this.element[0]).id = id;
|
||||||
this.componentScope = scope.$new();
|
this.componentScope = scope.$new();
|
||||||
this.childNodes = <Node[]><any>element.contents();
|
this.childNodes = <Node[]><any>element.contents();
|
||||||
@ -40,13 +40,13 @@ export class DowngradeNg2ComponentAdapter {
|
|||||||
bootstrapNg2() {
|
bootstrapNg2() {
|
||||||
var childInjector = this.parentInjector.resolveAndCreateChild(
|
var childInjector = this.parentInjector.resolveAndCreateChild(
|
||||||
[provide(NG1_SCOPE, {useValue: this.componentScope})]);
|
[provide(NG1_SCOPE, {useValue: this.componentScope})]);
|
||||||
this.hostViewRef =
|
this.contentInsertionPoint = document.createComment('ng1 insertion point');
|
||||||
this.viewManager.createRootHostView(this.protoView, '#' + this.id, childInjector);
|
|
||||||
var renderer: any = (<any>this.hostViewRef).render;
|
this.hostViewRef = this.viewManager.createRootHostView(
|
||||||
|
this.hostViewFactory, '#' + this.id, childInjector, [[this.contentInsertionPoint]]);
|
||||||
var hostElement = this.viewManager.getHostElement(this.hostViewRef);
|
var hostElement = this.viewManager.getHostElement(this.hostViewRef);
|
||||||
this.changeDetector = this.hostViewRef.changeDetectorRef;
|
this.changeDetector = this.hostViewRef.changeDetectorRef;
|
||||||
this.component = this.viewManager.getComponent(hostElement);
|
this.component = this.viewManager.getComponent(hostElement);
|
||||||
this.contentInserctionPoint = renderer.rootContentInsertionPoints[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupInputs(): void {
|
setupInputs(): void {
|
||||||
@ -105,10 +105,10 @@ export class DowngradeNg2ComponentAdapter {
|
|||||||
|
|
||||||
projectContent() {
|
projectContent() {
|
||||||
var childNodes = this.childNodes;
|
var childNodes = this.childNodes;
|
||||||
if (this.contentInserctionPoint) {
|
var parent = this.contentInsertionPoint.parentNode;
|
||||||
var parent = this.contentInserctionPoint.parentNode;
|
if (parent) {
|
||||||
for (var i = 0, ii = childNodes.length; i < ii; i++) {
|
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,
|
Injector,
|
||||||
NgZone,
|
NgZone,
|
||||||
PlatformRef,
|
PlatformRef,
|
||||||
ProtoViewRef,
|
HostViewFactoryRef,
|
||||||
Provider,
|
Provider,
|
||||||
Type,
|
Type,
|
||||||
APPLICATION_COMMON_PROVIDERS
|
APPLICATION_COMMON_PROVIDERS
|
||||||
@ -26,7 +26,7 @@ import {
|
|||||||
NG2_APP_VIEW_MANAGER,
|
NG2_APP_VIEW_MANAGER,
|
||||||
NG2_COMPILER,
|
NG2_COMPILER,
|
||||||
NG2_INJECTOR,
|
NG2_INJECTOR,
|
||||||
NG2_PROTO_VIEW_REF_MAP,
|
NG2_HOST_VIEW_FACTORY_REF_MAP,
|
||||||
NG2_ZONE,
|
NG2_ZONE,
|
||||||
REQUIRE_INJECTOR
|
REQUIRE_INJECTOR
|
||||||
} from './constants';
|
} from './constants';
|
||||||
@ -307,13 +307,13 @@ export class UpgradeAdapter {
|
|||||||
var original$applyFn: Function;
|
var original$applyFn: Function;
|
||||||
var rootScopePrototype: any;
|
var rootScopePrototype: any;
|
||||||
var rootScope: angular.IRootScopeService;
|
var rootScope: angular.IRootScopeService;
|
||||||
var protoViewRefMap: ProtoViewRefMap = {};
|
var hostViewFactoryRefMap: HostViewFactoryRefMap = {};
|
||||||
var ng1Module = angular.module(this.idPrefix, modules);
|
var ng1Module = angular.module(this.idPrefix, modules);
|
||||||
var ng1compilePromise: Promise<any> = null;
|
var ng1compilePromise: Promise<any> = null;
|
||||||
ng1Module.value(NG2_INJECTOR, injector)
|
ng1Module.value(NG2_INJECTOR, injector)
|
||||||
.value(NG2_ZONE, ngZone)
|
.value(NG2_ZONE, ngZone)
|
||||||
.value(NG2_COMPILER, compiler)
|
.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))
|
.value(NG2_APP_VIEW_MANAGER, injector.get(AppViewManager))
|
||||||
.config([
|
.config([
|
||||||
'$provide',
|
'$provide',
|
||||||
@ -347,7 +347,7 @@ export class UpgradeAdapter {
|
|||||||
|
|
||||||
angular.element(element).data(controllerKey(NG2_INJECTOR), injector);
|
angular.element(element).data(controllerKey(NG2_INJECTOR), injector);
|
||||||
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
|
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
|
||||||
Promise.all([this.compileNg2Components(compiler, protoViewRefMap), ng1compilePromise])
|
Promise.all([this.compileNg2Components(compiler, hostViewFactoryRefMap), ng1compilePromise])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ngZone.run(() => {
|
ngZone.run(() => {
|
||||||
if (rootScopePrototype) {
|
if (rootScopePrototype) {
|
||||||
@ -470,33 +470,35 @@ export class UpgradeAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
private compileNg2Components(compiler: Compiler,
|
private compileNg2Components(compiler: Compiler, hostViewFactoryRefMap: HostViewFactoryRefMap):
|
||||||
protoViewRefMap: ProtoViewRefMap): Promise<ProtoViewRefMap> {
|
Promise<HostViewFactoryRefMap> {
|
||||||
var promises: Array<Promise<ProtoViewRef>> = [];
|
var promises: Array<Promise<HostViewFactoryRef>> = [];
|
||||||
var types = this.upgradedComponents;
|
var types = this.upgradedComponents;
|
||||||
for (var i = 0; i < types.length; i++) {
|
for (var i = 0; i < types.length; i++) {
|
||||||
promises.push(compiler.compileInHost(types[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;
|
var types = this.upgradedComponents;
|
||||||
for (var i = 0; i < protoViews.length; i++) {
|
for (var i = 0; i < hostViewFactories.length; i++) {
|
||||||
protoViewRefMap[getComponentInfo(types[i]).selector] = protoViews[i];
|
hostViewFactoryRefMap[getComponentInfo(types[i]).selector] = hostViewFactories[i];
|
||||||
}
|
}
|
||||||
return protoViewRefMap;
|
return hostViewFactoryRefMap;
|
||||||
}, onError);
|
}, onError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ProtoViewRefMap {
|
interface HostViewFactoryRefMap {
|
||||||
[selector: string]: ProtoViewRef;
|
[selector: string]: HostViewFactoryRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function {
|
function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function {
|
||||||
(<any>directiveFactory).$inject = [NG2_PROTO_VIEW_REF_MAP, NG2_APP_VIEW_MANAGER, NG1_PARSE];
|
(<any>directiveFactory).$inject =
|
||||||
function directiveFactory(protoViewRefMap: ProtoViewRefMap, viewManager: AppViewManager,
|
[NG2_HOST_VIEW_FACTORY_REF_MAP, NG2_APP_VIEW_MANAGER, NG1_PARSE];
|
||||||
|
function directiveFactory(hostViewFactoryRefMap: HostViewFactoryRefMap,
|
||||||
|
viewManager: AppViewManager,
|
||||||
parse: angular.IParseService): angular.IDirective {
|
parse: angular.IParseService): angular.IDirective {
|
||||||
var protoView: ProtoViewRef = protoViewRefMap[info.selector];
|
var hostViewFactory: HostViewFactoryRef = hostViewFactoryRefMap[info.selector];
|
||||||
if (!protoView) throw new Error('Expecting ProtoViewRef for: ' + info.selector);
|
if (!hostViewFactory) throw new Error('Expecting HostViewFactoryRef for: ' + info.selector);
|
||||||
var idCount = 0;
|
var idCount = 0;
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
@ -507,7 +509,7 @@ function ng1ComponentDirective(info: ComponentInfo, idPrefix: string): Function
|
|||||||
var domElement = <any>element[0];
|
var domElement = <any>element[0];
|
||||||
var facade = new DowngradeNg2ComponentAdapter(idPrefix + (idCount++), info, element,
|
var facade = new DowngradeNg2ComponentAdapter(idPrefix + (idCount++), info, element,
|
||||||
attrs, scope, <Injector>parentInjector,
|
attrs, scope, <Injector>parentInjector,
|
||||||
parse, viewManager, protoView);
|
parse, viewManager, hostViewFactory);
|
||||||
facade.setupInputs();
|
facade.setupInputs();
|
||||||
facade.bootstrapNg2();
|
facade.bootstrapNg2();
|
||||||
facade.projectContent();
|
facade.projectContent();
|
||||||
|
@ -1,75 +1,4 @@
|
|||||||
import {CONST_EXPR} from "angular2/src/facade/lang";
|
import {CONST_EXPR} from "angular2/src/facade/lang";
|
||||||
import {OpaqueToken} from "angular2/src/core/di";
|
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 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(); }
|
|
||||||
}
|
|
45
modules/angular2/src/web_workers/shared/render_store.ts
Normal file
45
modules/angular2/src/web_workers/shared/render_store.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import {Injectable} from "angular2/src/core/di";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RenderStore {
|
||||||
|
private _nextIndex: number = 0;
|
||||||
|
private _lookupById: Map<number, any>;
|
||||||
|
private _lookupByObject: Map<any, number>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._lookupById = new Map<number, any>();
|
||||||
|
this._lookupByObject = new Map<any, number>();
|
||||||
|
}
|
||||||
|
|
||||||
|
allocateId(): number { return this._nextIndex++; }
|
||||||
|
|
||||||
|
store(obj: any, id: number): void {
|
||||||
|
this._lookupById.set(id, obj);
|
||||||
|
this._lookupByObject.set(obj, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(obj: any): void {
|
||||||
|
var index = this._lookupByObject.get(obj);
|
||||||
|
this._lookupByObject.delete(obj);
|
||||||
|
this._lookupById.delete(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
deserialize(id: number): any {
|
||||||
|
if (id == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._lookupById.has(id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._lookupById.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(obj: any): number {
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this._lookupByObject.get(obj);
|
||||||
|
}
|
||||||
|
}
|
@ -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 {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||||
|
|
||||||
import {Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
|
import {Map, StringMapWrapper, MapWrapper} from "angular2/src/facade/collection";
|
||||||
import {
|
import {RenderComponentType} from "angular2/src/core/render/api";
|
||||||
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 {Injectable} from "angular2/src/core/di";
|
import {Injectable} from "angular2/src/core/di";
|
||||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||||
import {
|
|
||||||
RenderViewWithFragmentsStore
|
|
||||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
|
||||||
import {ViewEncapsulation, VIEW_ENCAPSULATION_VALUES} from 'angular2/src/core/metadata/view';
|
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)
|
// PRIMITIVE is any type that does not need to be serialized (string, number, boolean)
|
||||||
@ -40,8 +13,7 @@ export const PRIMITIVE: Type = String;
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Serializer {
|
export class Serializer {
|
||||||
constructor(private _protoViewStore: RenderProtoViewRefStore,
|
constructor(private _renderStore: RenderStore) {}
|
||||||
private _renderViewStore: RenderViewWithFragmentsStore) {}
|
|
||||||
|
|
||||||
serialize(obj: any, type: any): Object {
|
serialize(obj: any, type: any): Object {
|
||||||
if (!isPresent(obj)) {
|
if (!isPresent(obj)) {
|
||||||
@ -53,18 +25,10 @@ export class Serializer {
|
|||||||
if (type == PRIMITIVE) {
|
if (type == PRIMITIVE) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
if (type == RenderProtoViewRef) {
|
if (type == RenderStoreObject) {
|
||||||
return this._protoViewStore.serialize(obj);
|
return this._renderStore.serialize(obj);
|
||||||
} else if (type == RenderViewRef) {
|
} else if (type === RenderComponentType) {
|
||||||
return this._renderViewStore.serializeRenderViewRef(obj);
|
return this._serializeRenderComponentType(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);
|
|
||||||
} else if (type === ViewEncapsulation) {
|
} else if (type === ViewEncapsulation) {
|
||||||
return serializeEnum(obj);
|
return serializeEnum(obj);
|
||||||
} else {
|
} else {
|
||||||
@ -85,18 +49,10 @@ export class Serializer {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == RenderProtoViewRef) {
|
if (type == RenderStoreObject) {
|
||||||
return this._protoViewStore.deserialize(map);
|
return this._renderStore.deserialize(map);
|
||||||
} else if (type == RenderViewRef) {
|
} else if (type === RenderComponentType) {
|
||||||
return this._renderViewStore.deserializeRenderViewRef(map);
|
return this._deserializeRenderComponentType(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);
|
|
||||||
} else if (type === ViewEncapsulation) {
|
} else if (type === ViewEncapsulation) {
|
||||||
return VIEW_ENCAPSULATION_VALUES[map];
|
return VIEW_ENCAPSULATION_VALUES[map];
|
||||||
} else {
|
} else {
|
||||||
@ -134,114 +90,20 @@ export class Serializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allocateRenderViews(fragmentCount: number) { this._renderViewStore.allocate(fragmentCount); }
|
private _serializeRenderComponentType(obj: RenderComponentType): Object {
|
||||||
|
|
||||||
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 {
|
|
||||||
return {
|
return {
|
||||||
'id': obj.id,
|
'id': obj.id,
|
||||||
'shortId': obj.shortId,
|
|
||||||
'encapsulation': this.serialize(obj.encapsulation, ViewEncapsulation),
|
'encapsulation': this.serialize(obj.encapsulation, ViewEncapsulation),
|
||||||
'commands': this.serialize(obj.commands, WebWorkerTemplateCmd),
|
|
||||||
'styles': this.serialize(obj.styles, PRIMITIVE)
|
'styles': this.serialize(obj.styles, PRIMITIVE)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _deserializeRenderTemplate(map: {[key: string]: any}): RenderComponentTemplate {
|
private _deserializeRenderComponentType(map: {[key: string]: any}): RenderComponentType {
|
||||||
return new RenderComponentTemplate(map['id'], map['shortId'],
|
return new RenderComponentType(map['id'],
|
||||||
this.deserialize(map['encapsulation'], ViewEncapsulation),
|
this.deserialize(map['encapsulation'], ViewEncapsulation),
|
||||||
this.deserialize(map['commands'], WebWorkerTemplateCmd),
|
this.deserialize(map['styles'], PRIMITIVE));
|
||||||
this.deserialize(map['styles'], PRIMITIVE));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function serializeTemplateCmd(cmd: RenderTemplateCmd): Object {
|
export class RenderStoreObject {}
|
||||||
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))),
|
|
||||||
];
|
|
@ -1,8 +1,4 @@
|
|||||||
import {
|
import {Serializer, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
|
||||||
RenderViewRef,
|
|
||||||
RenderEventDispatcher,
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
import {Serializer} from 'angular2/src/web_workers/shared/serializer';
|
|
||||||
import {
|
import {
|
||||||
serializeMouseEvent,
|
serializeMouseEvent,
|
||||||
serializeKeyboardEvent,
|
serializeKeyboardEvent,
|
||||||
@ -13,15 +9,13 @@ import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
|||||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
export class EventDispatcher implements RenderEventDispatcher {
|
export class EventDispatcher {
|
||||||
constructor(private _viewRef: RenderViewRef, private _sink: EventEmitter<any>,
|
constructor(private _sink: EventEmitter<any>, private _serializer: Serializer) {}
|
||||||
private _serializer: Serializer) {}
|
|
||||||
|
|
||||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>): boolean {
|
dispatchRenderEvent(element: any, eventTarget: string, eventName: string, event: any): boolean {
|
||||||
var e = locals.get('$event');
|
|
||||||
var serializedEvent;
|
var serializedEvent;
|
||||||
// TODO (jteplitz602): support custom events #3350
|
// TODO (jteplitz602): support custom events #3350
|
||||||
switch (e.type) {
|
switch (event.type) {
|
||||||
case "click":
|
case "click":
|
||||||
case "mouseup":
|
case "mouseup":
|
||||||
case "mousedown":
|
case "mousedown":
|
||||||
@ -33,17 +27,17 @@ export class EventDispatcher implements RenderEventDispatcher {
|
|||||||
case "mouseout":
|
case "mouseout":
|
||||||
case "mouseover":
|
case "mouseover":
|
||||||
case "show":
|
case "show":
|
||||||
serializedEvent = serializeMouseEvent(e);
|
serializedEvent = serializeMouseEvent(event);
|
||||||
break;
|
break;
|
||||||
case "keydown":
|
case "keydown":
|
||||||
case "keypress":
|
case "keypress":
|
||||||
case "keyup":
|
case "keyup":
|
||||||
serializedEvent = serializeKeyboardEvent(e);
|
serializedEvent = serializeKeyboardEvent(event);
|
||||||
break;
|
break;
|
||||||
case "input":
|
case "input":
|
||||||
case "change":
|
case "change":
|
||||||
case "blur":
|
case "blur":
|
||||||
serializedEvent = serializeEventWithTarget(e);
|
serializedEvent = serializeEventWithTarget(event);
|
||||||
break;
|
break;
|
||||||
case "abort":
|
case "abort":
|
||||||
case "afterprint":
|
case "afterprint":
|
||||||
@ -93,19 +87,16 @@ export class EventDispatcher implements RenderEventDispatcher {
|
|||||||
case "visibilitychange":
|
case "visibilitychange":
|
||||||
case "volumechange":
|
case "volumechange":
|
||||||
case "waiting":
|
case "waiting":
|
||||||
serializedEvent = serializeGenericEvent(e);
|
serializedEvent = serializeGenericEvent(event);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new BaseException(eventName + " not supported on WebWorkers");
|
throw new BaseException(eventName + " not supported on WebWorkers");
|
||||||
}
|
}
|
||||||
var serializedLocals = StringMapWrapper.create();
|
|
||||||
StringMapWrapper.set(serializedLocals, '$event', serializedEvent);
|
|
||||||
|
|
||||||
ObservableWrapper.callEmit(this._sink, {
|
ObservableWrapper.callEmit(this._sink, {
|
||||||
"viewRef": this._serializer.serialize(this._viewRef, RenderViewRef),
|
"element": this._serializer.serialize(element, RenderStoreObject),
|
||||||
"elementIndex": elementIndex,
|
|
||||||
"eventName": eventName,
|
"eventName": eventName,
|
||||||
"locals": serializedLocals
|
"eventTarget": eventTarget,
|
||||||
|
"event": serializedEvent
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(kegluneq): Eventually, we want the user to indicate from the UI side whether the event
|
// 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 {Injectable} from 'angular2/src/core/di';
|
||||||
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
import {MessageBus} from 'angular2/src/web_workers/shared/message_bus';
|
||||||
import {Serializer, PRIMITIVE} from 'angular2/src/web_workers/shared/serializer';
|
import {Serializer, PRIMITIVE, RenderStoreObject} from 'angular2/src/web_workers/shared/serializer';
|
||||||
import {
|
import {RootRenderer, Renderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||||
RenderViewRef,
|
|
||||||
RenderFragmentRef,
|
|
||||||
RenderProtoViewRef,
|
|
||||||
Renderer,
|
|
||||||
RenderTemplateCmd,
|
|
||||||
RenderComponentTemplate
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
import {WebWorkerElementRef, WebWorkerTemplateCmd} from 'angular2/src/web_workers/shared/api';
|
|
||||||
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
import {EVENT_CHANNEL, RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
||||||
import {Type} from 'angular2/src/facade/lang';
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
import {bind} from './bind';
|
import {bind} from './bind';
|
||||||
import {EventDispatcher} from 'angular2/src/web_workers/ui/event_dispatcher';
|
import {EventDispatcher} from 'angular2/src/web_workers/ui/event_dispatcher';
|
||||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
import {RenderStore} from 'angular2/src/web_workers/shared/render_store';
|
||||||
import {
|
|
||||||
RenderViewWithFragmentsStore
|
|
||||||
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
|
|
||||||
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
|
import {ServiceMessageBrokerFactory} from 'angular2/src/web_workers/shared/service_message_broker';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MessageBasedRenderer {
|
export class MessageBasedRenderer {
|
||||||
|
private _eventDispatcher: EventDispatcher;
|
||||||
|
|
||||||
constructor(private _brokerFactory: ServiceMessageBrokerFactory, private _bus: MessageBus,
|
constructor(private _brokerFactory: ServiceMessageBrokerFactory, private _bus: MessageBus,
|
||||||
private _serializer: Serializer,
|
private _serializer: Serializer, private _renderStore: RenderStore,
|
||||||
private _renderProtoViewRefStore: RenderProtoViewRefStore,
|
private _rootRenderer: RootRenderer) {}
|
||||||
private _renderViewWithFragmentsStore: RenderViewWithFragmentsStore,
|
|
||||||
private _renderer: Renderer) {}
|
|
||||||
|
|
||||||
start(): void {
|
start(): void {
|
||||||
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
var broker = this._brokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
||||||
this._bus.initChannel(EVENT_CHANNEL);
|
this._bus.initChannel(EVENT_CHANNEL);
|
||||||
|
this._eventDispatcher = new EventDispatcher(this._bus.to(EVENT_CHANNEL), this._serializer);
|
||||||
|
|
||||||
broker.registerMethod("registerComponentTemplate", [RenderComponentTemplate],
|
broker.registerMethod("renderComponent", [RenderComponentType, PRIMITIVE],
|
||||||
bind(this._renderer.registerComponentTemplate, this._renderer));
|
bind(this._renderComponent, this));
|
||||||
broker.registerMethod("createProtoView", [PRIMITIVE, WebWorkerTemplateCmd, PRIMITIVE],
|
|
||||||
bind(this._createProtoView, this));
|
broker.registerMethod("selectRootElement", [RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||||
broker.registerMethod("createRootHostView",
|
bind(this._selectRootElement, this));
|
||||||
[RenderProtoViewRef, PRIMITIVE, PRIMITIVE, PRIMITIVE],
|
broker.registerMethod("createElement",
|
||||||
bind(this._createRootHostView, this));
|
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||||
broker.registerMethod("createView", [RenderProtoViewRef, PRIMITIVE, PRIMITIVE],
|
bind(this._createElement, this));
|
||||||
bind(this._createView, this));
|
broker.registerMethod("createViewRoot", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
|
||||||
broker.registerMethod("destroyView", [RenderViewRef], bind(this._destroyView, this));
|
bind(this._createViewRoot, this));
|
||||||
broker.registerMethod("attachFragmentAfterFragment", [RenderFragmentRef, RenderFragmentRef],
|
broker.registerMethod("createTemplateAnchor", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
|
||||||
bind(this._renderer.attachFragmentAfterFragment, this._renderer));
|
bind(this._createTemplateAnchor, this));
|
||||||
broker.registerMethod("attachFragmentAfterElement", [WebWorkerElementRef, RenderFragmentRef],
|
broker.registerMethod("createText",
|
||||||
bind(this._renderer.attachFragmentAfterElement, this._renderer));
|
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||||
broker.registerMethod("detachFragment", [RenderFragmentRef],
|
bind(this._createText, this));
|
||||||
bind(this._renderer.detachFragment, this._renderer));
|
broker.registerMethod("projectNodes", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
|
||||||
broker.registerMethod("hydrateView", [RenderViewRef],
|
bind(this._projectNodes, this));
|
||||||
bind(this._renderer.hydrateView, this._renderer));
|
broker.registerMethod("attachViewAfter",
|
||||||
broker.registerMethod("dehydrateView", [RenderViewRef],
|
[RenderStoreObject, RenderStoreObject, RenderStoreObject],
|
||||||
bind(this._renderer.dehydrateView, this._renderer));
|
bind(this._attachViewAfter, this));
|
||||||
broker.registerMethod("setText", [RenderViewRef, PRIMITIVE, PRIMITIVE],
|
broker.registerMethod("detachView", [RenderStoreObject, RenderStoreObject],
|
||||||
bind(this._renderer.setText, this._renderer));
|
bind(this._detachView, this));
|
||||||
broker.registerMethod("setElementProperty", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
|
broker.registerMethod("destroyView", [RenderStoreObject, RenderStoreObject, RenderStoreObject],
|
||||||
bind(this._renderer.setElementProperty, this._renderer));
|
bind(this._destroyView, this));
|
||||||
broker.registerMethod("setElementAttribute", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
|
broker.registerMethod("setElementProperty",
|
||||||
bind(this._renderer.setElementAttribute, this._renderer));
|
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||||
broker.registerMethod("setBindingDebugInfo", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
|
bind(this._setElementProperty, this));
|
||||||
bind(this._renderer.setBindingDebugInfo, this._renderer));
|
broker.registerMethod("setElementAttribute",
|
||||||
broker.registerMethod("setElementClass", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
|
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||||
bind(this._renderer.setElementClass, this._renderer));
|
bind(this._setElementAttribute, this));
|
||||||
broker.registerMethod("setElementStyle", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
|
broker.registerMethod("setBindingDebugInfo",
|
||||||
bind(this._renderer.setElementStyle, this._renderer));
|
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||||
broker.registerMethod("invokeElementMethod", [WebWorkerElementRef, PRIMITIVE, PRIMITIVE],
|
bind(this._setBindingDebugInfo, this));
|
||||||
bind(this._renderer.invokeElementMethod, this._renderer));
|
broker.registerMethod("setElementClass",
|
||||||
broker.registerMethod("setEventDispatcher", [RenderViewRef],
|
[RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
|
||||||
bind(this._setEventDispatcher, this));
|
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 {
|
private _renderComponent(renderComponentType: RenderComponentType, rendererId: number) {
|
||||||
this._renderer.destroyView(viewRef);
|
var renderer = this._rootRenderer.renderComponent(renderComponentType);
|
||||||
this._renderViewWithFragmentsStore.remove(viewRef);
|
this._renderStore.store(renderer, rendererId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[],
|
private _selectRootElement(renderer: Renderer, selector: string, elId: number) {
|
||||||
refIndex: number) {
|
this._renderStore.store(renderer.selectRootElement(selector), elId);
|
||||||
var protoViewRef = this._renderer.createProtoView(componentTemplateId, cmds);
|
|
||||||
this._renderProtoViewRefStore.store(protoViewRef, refIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createRootHostView(ref: RenderProtoViewRef, fragmentCount: number, selector: string,
|
private _createElement(renderer: Renderer, parentElement: any, name: string, elId: number) {
|
||||||
startIndex: number) {
|
this._renderStore.store(renderer.createElement(parentElement, name), elId);
|
||||||
var renderViewWithFragments = this._renderer.createRootHostView(ref, fragmentCount, selector);
|
|
||||||
this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createView(ref: RenderProtoViewRef, fragmentCount: number, startIndex: number) {
|
private _createViewRoot(renderer: Renderer, hostElement: any, elId: number) {
|
||||||
var renderViewWithFragments = this._renderer.createView(ref, fragmentCount);
|
var viewRoot = renderer.createViewRoot(hostElement);
|
||||||
this._renderViewWithFragmentsStore.store(renderViewWithFragments, startIndex);
|
if (this._renderStore.serialize(hostElement) !== elId) {
|
||||||
|
this._renderStore.store(viewRoot, elId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setEventDispatcher(viewRef: RenderViewRef) {
|
private _createTemplateAnchor(renderer: Renderer, parentElement: any, elId: number) {
|
||||||
var dispatcher = new EventDispatcher(viewRef, this._bus.to(EVENT_CHANNEL), this._serializer);
|
this._renderStore.store(renderer.createTemplateAnchor(parentElement), elId);
|
||||||
this._renderer.setEventDispatcher(viewRef, dispatcher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
import {Renderer, RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||||
Renderer,
|
|
||||||
RenderProtoViewRef,
|
|
||||||
RenderViewRef,
|
|
||||||
RenderElementRef,
|
|
||||||
RenderEventDispatcher,
|
|
||||||
RenderViewWithFragments,
|
|
||||||
RenderFragmentRef,
|
|
||||||
RenderTemplateCmd,
|
|
||||||
RenderComponentTemplate
|
|
||||||
} from 'angular2/src/core/render/api';
|
|
||||||
import {
|
import {
|
||||||
ClientMessageBroker,
|
ClientMessageBroker,
|
||||||
ClientMessageBrokerFactory,
|
ClientMessageBrokerFactory,
|
||||||
FnArg,
|
FnArg,
|
||||||
UiArguments
|
UiArguments
|
||||||
} from "angular2/src/web_workers/shared/client_message_broker";
|
} 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 {Injectable} from "angular2/src/core/di";
|
||||||
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
|
import {RenderStore} from 'angular2/src/web_workers/shared/render_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 {RENDERER_CHANNEL} from 'angular2/src/web_workers/shared/messaging_api';
|
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()
|
@Injectable()
|
||||||
export class WebWorkerRenderer implements Renderer {
|
export class WebWorkerRootRenderer implements RootRenderer {
|
||||||
private _messageBroker;
|
private _messageBroker;
|
||||||
constructor(messageBrokerFactory: ClientMessageBrokerFactory,
|
public globalEvents: NamedEventEmitter = new NamedEventEmitter();
|
||||||
private _renderProtoViewRefStore: RenderProtoViewRefStore,
|
private _componentRenderers: Map<string, WebWorkerRenderer> =
|
||||||
private _renderViewStore: RenderViewWithFragmentsStore,
|
new Map<string, WebWorkerRenderer>();
|
||||||
private _eventDispatcher: WebWorkerEventDispatcher) {
|
|
||||||
|
constructor(messageBrokerFactory: ClientMessageBrokerFactory, bus: MessageBus,
|
||||||
|
private _serializer: Serializer, private _renderStore: RenderStore) {
|
||||||
this._messageBroker = messageBrokerFactory.createMessageBroker(RENDERER_CHANNEL);
|
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) {
|
private _dispatchEvent(message: {[key: string]: any}): void {
|
||||||
var fnArgs = [new FnArg(template, RenderComponentTemplate)];
|
var eventName = message['eventName'];
|
||||||
var args = new UiArguments("registerComponentTemplate", fnArgs);
|
var target = message['eventTarget'];
|
||||||
this._messageBroker.runOnService(args, null);
|
var event = deserializeGenericEvent(message['event']);
|
||||||
}
|
if (isPresent(target)) {
|
||||||
|
this.globalEvents.dispatchEvent(eventNameWithTarget(target, eventName), event);
|
||||||
createProtoView(componentTemplateId: string, cmds: RenderTemplateCmd[]): RenderProtoViewRef {
|
} else {
|
||||||
var renderProtoViewRef = this._renderProtoViewRefStore.allocate();
|
var element =
|
||||||
|
<WebWorkerRenderNode>this._serializer.deserialize(message['element'], RenderStoreObject);
|
||||||
var fnArgs: FnArg[] = [
|
element.events.dispatchEvent(eventName, event);
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
fnArgs.push(new FnArg(startIndex, null));
|
|
||||||
|
|
||||||
var args = new UiArguments(method, fnArgs);
|
|
||||||
this._messageBroker.runOnService(args, null);
|
|
||||||
|
|
||||||
return renderViewWithFragments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
renderComponent(componentType: RenderComponentType): Renderer {
|
||||||
* Destroys the given view after it has been dehydrated and detached
|
var result = this._componentRenderers.get(componentType.id);
|
||||||
*/
|
if (isBlank(result)) {
|
||||||
destroyView(viewRef: RenderViewRef) {
|
result = new WebWorkerRenderer(this, componentType);
|
||||||
var fnArgs = [new FnArg(viewRef, RenderViewRef)];
|
this._componentRenderers.set(componentType.id, result);
|
||||||
var args = new UiArguments("destroyView", fnArgs);
|
var id = this._renderStore.allocateId();
|
||||||
this._messageBroker.runOnService(args, null);
|
this._renderStore.store(result, id);
|
||||||
this._renderViewStore.remove(viewRef);
|
this.runOnService('renderComponent', [
|
||||||
|
new FnArg(componentType, RenderComponentType),
|
||||||
|
new FnArg(result, RenderStoreObject),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
runOnService(fnName: string, fnArgs: FnArg[]) {
|
||||||
* Attaches a fragment after another fragment.
|
var args = new UiArguments(fnName, fnArgs);
|
||||||
*/
|
|
||||||
attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
|
|
||||||
fragmentRef: RenderFragmentRef) {
|
|
||||||
var fnArgs = [
|
|
||||||
new FnArg(previousFragmentRef, RenderFragmentRef),
|
|
||||||
new FnArg(fragmentRef, RenderFragmentRef)
|
|
||||||
];
|
|
||||||
var args = new UiArguments("attachFragmentAfterFragment", fnArgs);
|
|
||||||
this._messageBroker.runOnService(args, null);
|
this._messageBroker.runOnService(args, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
allocateNode(): WebWorkerRenderNode {
|
||||||
* Attaches a fragment after an element.
|
var result = new WebWorkerRenderNode();
|
||||||
*/
|
var id = this._renderStore.allocateId();
|
||||||
attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef) {
|
this._renderStore.store(result, id);
|
||||||
var fnArgs =
|
return result;
|
||||||
[new FnArg(elementRef, WebWorkerElementRef), new FnArg(fragmentRef, RenderFragmentRef)];
|
|
||||||
var args = new UiArguments("attachFragmentAfterElement", fnArgs);
|
|
||||||
this._messageBroker.runOnService(args, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
allocateId(): number { return this._renderStore.allocateId(); }
|
||||||
* Detaches a fragment.
|
|
||||||
*/
|
|
||||||
detachFragment(fragmentRef: RenderFragmentRef) {
|
|
||||||
var fnArgs = [new FnArg(fragmentRef, RenderFragmentRef)];
|
|
||||||
var args = new UiArguments("detachFragment", fnArgs);
|
|
||||||
this._messageBroker.runOnService(args, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
destroyNodes(nodes: any[]) {
|
||||||
* Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
* inside of the view pool.
|
this._renderStore.remove(nodes[i]);
|
||||||
*/
|
}
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {Component, View, provide} from 'angular2/core';
|
||||||
import {NgFor} from 'angular2/common';
|
import {NgFor} from 'angular2/common';
|
||||||
import {NgClass} from 'angular2/src/common/directives/ng_class';
|
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) {
|
function detectChangesAndCheck(fixture: ComponentFixture, classes: string, elIndex: number = 0) {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -30,8 +29,6 @@ export function main() {
|
|||||||
describe('binding to CSS class list', () => {
|
describe('binding to CSS class list', () => {
|
||||||
|
|
||||||
describe('viewpool support', () => {
|
describe('viewpool support', () => {
|
||||||
beforeEachProviders(() => { return [provide(APP_VIEW_POOL_CAPACITY, {useValue: 100})]; });
|
|
||||||
|
|
||||||
it('should clean up when the directive is destroyed',
|
it('should clean up when the directive is destroyed',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
var template = '<div *ngFor="var item of items" [ngClass]="item"></div>';
|
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';
|
import {SpyObject, proxy} from 'angular2/testing_internal';
|
||||||
|
|
||||||
export class SpyChangeDetectorRef extends SpyObject {
|
export class SpyChangeDetectorRef extends SpyObject {
|
||||||
constructor() { super(ChangeDetectorRef_); }
|
constructor() {
|
||||||
|
super(ChangeDetectorRef);
|
||||||
|
this.spy('markForCheck');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SpyNgControl extends SpyObject {}
|
export class SpyNgControl extends SpyObject {}
|
||||||
|
@ -47,7 +47,6 @@ export function main() {
|
|||||||
var directive: TestDirective;
|
var directive: TestDirective;
|
||||||
var locals: Locals;
|
var locals: Locals;
|
||||||
var pipes: Pipes;
|
var pipes: Pipes;
|
||||||
var eventLocals: Locals;
|
|
||||||
|
|
||||||
beforeEach(inject([TemplateParser], (_templateParser) => {
|
beforeEach(inject([TemplateParser], (_templateParser) => {
|
||||||
parser = _templateParser;
|
parser = _templateParser;
|
||||||
@ -55,7 +54,6 @@ export function main() {
|
|||||||
directive = new TestDirective();
|
directive = new TestDirective();
|
||||||
dispatcher = new TestDispatcher([directive], []);
|
dispatcher = new TestDispatcher([directive], []);
|
||||||
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||||
eventLocals = new Locals(null, MapWrapper.createFromStringMap({'$event': null}));
|
|
||||||
pipes = new TestPipes();
|
pipes = new TestPipes();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -65,9 +63,9 @@ export function main() {
|
|||||||
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
|
createChangeDetectorDefinitions(new CompileTypeMetadata({name: 'SomeComp'}),
|
||||||
ChangeDetectionStrategy.Default,
|
ChangeDetectionStrategy.Default,
|
||||||
new ChangeDetectorGenConfig(true, false, false),
|
new ChangeDetectorGenConfig(true, false, false),
|
||||||
parser.parse(template, directives, 'TestComp'))
|
parser.parse(template, directives, [], 'TestComp'))
|
||||||
.map(definition => new DynamicProtoChangeDetector(definition));
|
.map(definition => new DynamicProtoChangeDetector(definition));
|
||||||
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
|
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate();
|
||||||
changeDetector.hydrate(context, locals, dispatcher, pipes);
|
changeDetector.hydrate(context, locals, dispatcher, pipes);
|
||||||
return changeDetector;
|
return changeDetector;
|
||||||
}
|
}
|
||||||
@ -91,8 +89,7 @@ export function main() {
|
|||||||
it('should handle events on regular elements', () => {
|
it('should handle events on regular elements', () => {
|
||||||
var changeDetector = createChangeDetector('<div on-click="onEvent($event)">', [], 0);
|
var changeDetector = createChangeDetector('<div on-click="onEvent($event)">', [], 0);
|
||||||
|
|
||||||
eventLocals.set('$event', 'click');
|
changeDetector.handleEvent('click', 0, 'click');
|
||||||
changeDetector.handleEvent('click', 0, eventLocals);
|
|
||||||
expect(context.eventLog).toEqual(['click']);
|
expect(context.eventLog).toEqual(['click']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -105,16 +102,14 @@ export function main() {
|
|||||||
var changeDetector =
|
var changeDetector =
|
||||||
createChangeDetector('<template on-click="onEvent($event)">', [dirMeta], 0);
|
createChangeDetector('<template on-click="onEvent($event)">', [dirMeta], 0);
|
||||||
|
|
||||||
eventLocals.set('$event', 'click');
|
changeDetector.handleEvent('click', 0, 'click');
|
||||||
changeDetector.handleEvent('click', 0, eventLocals);
|
|
||||||
expect(context.eventLog).toEqual(['click']);
|
expect(context.eventLog).toEqual(['click']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle events with targets', () => {
|
it('should handle events with targets', () => {
|
||||||
var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0);
|
var changeDetector = createChangeDetector('<div (window:click)="onEvent($event)">', [], 0);
|
||||||
|
|
||||||
eventLocals.set('$event', 'click');
|
changeDetector.handleEvent('window:click', 0, 'click');
|
||||||
changeDetector.handleEvent('window:click', 0, eventLocals);
|
|
||||||
expect(context.eventLog).toEqual(['click']);
|
expect(context.eventLog).toEqual(['click']);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -177,8 +172,7 @@ export function main() {
|
|||||||
|
|
||||||
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
var changeDetector = createChangeDetector('<div>', [dirMeta], 0);
|
||||||
|
|
||||||
eventLocals.set('$event', 'click');
|
changeDetector.handleEvent('click', 0, 'click');
|
||||||
changeDetector.handleEvent('click', 0, eventLocals);
|
|
||||||
expect(directive.eventLog).toEqual(['click']);
|
expect(directive.eventLog).toEqual(['click']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -56,10 +56,18 @@ var THIS_MODULE_REF = moduleRef(THIS_MODULE_URL);
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('ChangeDetectorCompiler', () => {
|
describe('ChangeDetectorCompiler', () => {
|
||||||
beforeEachProviders(() => TEST_PROVIDERS);
|
beforeEachProviders(() => [
|
||||||
|
TEST_PROVIDERS,
|
||||||
|
provide(ChangeDetectorGenConfig,
|
||||||
|
{useValue: new ChangeDetectorGenConfig(true, false, false)})
|
||||||
|
]);
|
||||||
|
|
||||||
var parser: TemplateParser;
|
var parser: TemplateParser;
|
||||||
var compiler: ChangeDetectionCompiler;
|
var compiler: ChangeDetectionCompiler;
|
||||||
|
beforeEachProviders(() => [
|
||||||
|
provide(ChangeDetectorGenConfig,
|
||||||
|
{useValue: new ChangeDetectorGenConfig(true, false, false)})
|
||||||
|
]);
|
||||||
|
|
||||||
beforeEach(inject([TemplateParser, ChangeDetectionCompiler], (_parser, _compiler) => {
|
beforeEach(inject([TemplateParser, ChangeDetectionCompiler], (_parser, _compiler) => {
|
||||||
parser = _parser;
|
parser = _parser;
|
||||||
@ -71,35 +79,16 @@ export function main() {
|
|||||||
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): string[] {
|
||||||
var type =
|
var type =
|
||||||
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
|
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 =
|
var factories =
|
||||||
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||||
return testChangeDetector(factories[0]);
|
return testChangeDetector(factories[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('no jit', () => {
|
it('should watch element properties', () => {
|
||||||
beforeEachProviders(() => [
|
expect(detectChanges(compiler, '<div [elProp]="someProp">'))
|
||||||
provide(ChangeDetectorGenConfig,
|
.toEqual(['elementProperty(elProp)=someValue']);
|
||||||
{useValue: new ChangeDetectorGenConfig(true, false, false)})
|
|
||||||
]);
|
|
||||||
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', () => {
|
describe('compileComponentCodeGen', () => {
|
||||||
@ -108,7 +97,7 @@ export function main() {
|
|||||||
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): Promise<string[]> {
|
directives: CompileDirectiveMetadata[] = CONST_EXPR([])): Promise<string[]> {
|
||||||
var type =
|
var type =
|
||||||
new CompileTypeMetadata({name: stringify(SomeComponent), moduleUrl: THIS_MODULE_URL});
|
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 =
|
var sourceExpressions =
|
||||||
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||||
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
|
var testableModule = createTestableModule(sourceExpressions, 0).getSourceWithImports();
|
||||||
@ -140,7 +129,7 @@ function createTestableModule(source: SourceExpressions,
|
|||||||
|
|
||||||
export function testChangeDetector(changeDetectorFactory: Function): string[] {
|
export function testChangeDetector(changeDetectorFactory: Function): string[] {
|
||||||
var dispatcher = new TestDispatcher([], []);
|
var dispatcher = new TestDispatcher([], []);
|
||||||
var cd = changeDetectorFactory(dispatcher);
|
var cd = changeDetectorFactory();
|
||||||
var ctx = new SomeComponent();
|
var ctx = new SomeComponent();
|
||||||
ctx.someProp = 'someValue';
|
ctx.someProp = 'someValue';
|
||||||
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import {isBlank} from 'angular2/src/facade/lang';
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
import {Pipes} from 'angular2/src/core/change_detection/pipes';
|
import {Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||||
|
import {EventEmitter} from 'angular2/src/facade/async';
|
||||||
import {
|
import {
|
||||||
ProtoChangeDetector,
|
ChangeDetector,
|
||||||
ChangeDispatcher,
|
ChangeDispatcher,
|
||||||
DirectiveIndex,
|
DirectiveIndex,
|
||||||
BindingTarget
|
BindingTarget
|
||||||
@ -10,6 +11,7 @@ import {
|
|||||||
export class TestDirective {
|
export class TestDirective {
|
||||||
eventLog: string[] = [];
|
eventLog: string[] = [];
|
||||||
dirProp: string;
|
dirProp: string;
|
||||||
|
click: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
onEvent(value: string) { this.eventLog.push(value); }
|
onEvent(value: string) { this.eventLog.push(value); }
|
||||||
}
|
}
|
||||||
@ -17,7 +19,7 @@ export class TestDirective {
|
|||||||
export class TestDispatcher implements ChangeDispatcher {
|
export class TestDispatcher implements ChangeDispatcher {
|
||||||
log: string[];
|
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]; }
|
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
|
||||||
|
|
||||||
@ -34,7 +36,9 @@ export class TestDispatcher implements ChangeDispatcher {
|
|||||||
notifyAfterContentChecked() {}
|
notifyAfterContentChecked() {}
|
||||||
notifyAfterViewChecked() {}
|
notifyAfterViewChecked() {}
|
||||||
|
|
||||||
getDebugContext(a, b) { return null; }
|
notifyOnDestroy() {}
|
||||||
|
|
||||||
|
getDebugContext(a, b, c) { return null; }
|
||||||
|
|
||||||
_asString(value) { return (isBlank(value) ? 'null' : value.toString()); }
|
_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 {Component, View, provide} from 'angular2/core';
|
||||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
import {SpyProtoViewFactory} from '../core/spies';
|
import {SpyTemplateCompiler} from './spies';
|
||||||
import {
|
import {TemplateCompiler} from 'angular2/src/compiler/compiler';
|
||||||
CompiledHostTemplate,
|
import {RuntimeCompiler, RuntimeCompiler_} from 'angular2/src/compiler/runtime_compiler';
|
||||||
CompiledComponentTemplate,
|
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||||
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';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('RuntimeCompiler', () => {
|
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; }));
|
beforeEach(inject([RuntimeCompiler], (_compiler) => { compiler = _compiler; }));
|
||||||
|
|
||||||
describe('compileInHost', () => {
|
it('compileInHost should compile the template via TemplateCompiler',
|
||||||
var protoViewFactorySpy;
|
inject([AsyncTestCompleter], (async) => {
|
||||||
var someProtoView;
|
compiler.compileInHost(SomeComponent)
|
||||||
|
.then((hostViewFactoryRef) => {
|
||||||
beforeEachProviders(() => {
|
expect(hostViewFactoryRef.internalHostViewFactory).toBe(someHostViewFactory);
|
||||||
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]);
|
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should clear the cache',
|
it('should clear the cache', () => {
|
||||||
inject([AsyncTestCompleter], (async) => {compiler.compileInHost(SomeComponent)
|
compiler.clearCache();
|
||||||
.then((protoViewRef1) => {
|
expect(templateCompilerSpy.spy('clearCache')).toHaveBeenCalled();
|
||||||
compiler.clearCache();
|
});
|
||||||
compiler.compileInHost(SomeComponent)
|
|
||||||
.then((protoViewRef2) => {
|
|
||||||
expect(protoViewRef1)
|
|
||||||
.not.toBe(protoViewRef2);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
})}));
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export function main() {
|
|||||||
describe('getMetadata', () => {
|
describe('getMetadata', () => {
|
||||||
it('should read metadata',
|
it('should read metadata',
|
||||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
var meta = resolver.getMetadata(ComponentWithEverything);
|
var meta = resolver.getDirectiveMetadata(ComponentWithEverything);
|
||||||
expect(meta.selector).toEqual('someSelector');
|
expect(meta.selector).toEqual('someSelector');
|
||||||
expect(meta.exportAs).toEqual('someExportAs');
|
expect(meta.exportAs).toEqual('someExportAs');
|
||||||
expect(meta.isComponent).toBe(true);
|
expect(meta.isComponent).toBe(true);
|
||||||
@ -70,7 +70,8 @@ export function main() {
|
|||||||
|
|
||||||
it('should use the moduleUrl from the reflector if none is given',
|
it('should use the moduleUrl from the reflector if none is given',
|
||||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
var value: string = resolver.getMetadata(ComponentWithoutModuleId).type.moduleUrl;
|
var value: string =
|
||||||
|
resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl;
|
||||||
var expectedEndValue =
|
var expectedEndValue =
|
||||||
IS_DART ? 'base/dist/dart/angular2/test/compiler/runtime_metadata_spec.dart' : './';
|
IS_DART ? 'base/dist/dart/angular2/test/compiler/runtime_metadata_spec.dart' : './';
|
||||||
expect(value.endsWith(expectedEndValue)).toBe(true);
|
expect(value.endsWith(expectedEndValue)).toBe(true);
|
||||||
@ -82,7 +83,7 @@ export function main() {
|
|||||||
it('should return the directive metadatas',
|
it('should return the directive metadatas',
|
||||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
|
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
|
||||||
.toEqual([resolver.getMetadata(SomeDirective)]);
|
.toEqual([resolver.getDirectiveMetadata(SomeDirective)]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("platform directives", () => {
|
describe("platform directives", () => {
|
||||||
@ -91,7 +92,10 @@ export function main() {
|
|||||||
it('should include platform directives when available',
|
it('should include platform directives when available',
|
||||||
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
inject([RuntimeMetadataResolver], (resolver: RuntimeMetadataResolver) => {
|
||||||
expect(resolver.getViewDirectivesMetadata(ComponentWithEverything))
|
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/src/compiler/xhr.dart';
|
||||||
import 'package:angular2/testing_internal.dart';
|
import 'package:angular2/testing_internal.dart';
|
||||||
|
import 'package:angular2/src/compiler/template_compiler.dart';
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
class SpyXHR extends SpyObject implements XHR {
|
class SpyXHR extends SpyObject implements XHR {
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
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 {XHR} from 'angular2/src/compiler/xhr';
|
||||||
|
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
|
||||||
|
|
||||||
import {SpyObject, proxy} from 'angular2/testing_internal';
|
import {SpyObject, proxy} from 'angular2/testing_internal';
|
||||||
|
|
||||||
export class SpyXHR extends SpyObject {
|
export class SpyXHR extends SpyObject {
|
||||||
constructor() { super(XHR); }
|
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
Reference in New Issue
Block a user