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:
@ -11,13 +11,11 @@ import {
|
||||
KeyValueDiffers,
|
||||
defaultKeyValueDiffers
|
||||
} from './change_detection/change_detection';
|
||||
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from './linker/view_pool';
|
||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||
import {AppViewManager} from './linker/view_manager';
|
||||
import {AppViewManager_} from "./linker/view_manager";
|
||||
import {AppViewManagerUtils} from './linker/view_manager_utils';
|
||||
import {ViewResolver} from './linker/view_resolver';
|
||||
import {AppViewListener} from './linker/view_listener';
|
||||
import {ProtoViewFactory} from './linker/proto_view_factory';
|
||||
import {DirectiveResolver} from './linker/directive_resolver';
|
||||
import {PipeResolver} from './linker/pipe_resolver';
|
||||
import {Compiler} from './linker/compiler';
|
||||
@ -32,12 +30,9 @@ import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
|
||||
export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||
new Provider(Compiler, {useClass: Compiler_}),
|
||||
APP_ID_RANDOM_PROVIDER,
|
||||
AppViewPool,
|
||||
new Provider(APP_VIEW_POOL_CAPACITY, {useValue: 10000}),
|
||||
ResolvedMetadataCache,
|
||||
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
||||
AppViewManagerUtils,
|
||||
AppViewListener,
|
||||
ProtoViewFactory,
|
||||
ViewResolver,
|
||||
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
||||
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
||||
|
@ -33,11 +33,11 @@ import {
|
||||
ExceptionHandler,
|
||||
unimplemented
|
||||
} from 'angular2/src/facade/exceptions';
|
||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
||||
import {Console} from 'angular2/src/core/console';
|
||||
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
||||
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
import {lockMode} from 'angular2/src/facade/lang';
|
||||
import {ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
|
||||
/**
|
||||
* Construct providers specific to an individual root component.
|
||||
@ -56,10 +56,10 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
|
||||
() => { appRef._unloadComponent(ref); })
|
||||
.then((componentRef) => {
|
||||
ref = componentRef;
|
||||
if (isPresent(componentRef.location.nativeElement)) {
|
||||
var testability = injector.getOptional(Testability);
|
||||
if (isPresent(testability)) {
|
||||
injector.get(TestabilityRegistry)
|
||||
.registerApplication(componentRef.location.nativeElement,
|
||||
injector.get(Testability));
|
||||
.registerApplication(componentRef.location.nativeElement, testability);
|
||||
}
|
||||
return componentRef;
|
||||
});
|
||||
@ -439,7 +439,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
|
||||
/** @internal */
|
||||
_loadComponent(ref): void {
|
||||
var appChangeDetector = internalView(ref.hostView).changeDetector;
|
||||
var appChangeDetector = (<ElementRef_>ref.location).internalElement.parentView.changeDetector;
|
||||
this._changeDetectorRefs.push(appChangeDetector.ref);
|
||||
this.tick();
|
||||
this._rootComponents.push(ref);
|
||||
@ -451,7 +451,8 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
if (!ListWrapper.contains(this._rootComponents, ref)) {
|
||||
return;
|
||||
}
|
||||
this.unregisterChangeDetector(internalView(ref.hostView).changeDetector.ref);
|
||||
this.unregisterChangeDetector(
|
||||
(<ElementRef_>ref.location).internalElement.parentView.changeDetector.ref);
|
||||
ListWrapper.remove(this._rootComponents, ref);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,9 @@ import {Pipes} from './pipes';
|
||||
import {
|
||||
ChangeDetectionError,
|
||||
ExpressionChangedAfterItHasBeenCheckedException,
|
||||
DehydratedException
|
||||
DehydratedException,
|
||||
EventEvaluationErrorContext,
|
||||
EventEvaluationError
|
||||
} from './exceptions';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {Locals} from './parser/locals';
|
||||
@ -43,9 +45,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
subscriptions: any[];
|
||||
streams: any[];
|
||||
|
||||
constructor(public id: string, public dispatcher: ChangeDispatcher,
|
||||
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
|
||||
public directiveIndices: DirectiveIndex[], public strategy: ChangeDetectionStrategy) {
|
||||
dispatcher: ChangeDispatcher;
|
||||
|
||||
|
||||
constructor(public id: string, public numberOfPropertyProtoRecords: number,
|
||||
public bindingTargets: BindingTarget[], public directiveIndices: DirectiveIndex[],
|
||||
public strategy: ChangeDetectionStrategy) {
|
||||
this.ref = new ChangeDetectorRef_(this);
|
||||
}
|
||||
|
||||
@ -65,10 +70,24 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
remove(): void { this.parent.removeContentChild(this); }
|
||||
|
||||
handleEvent(eventName: string, elIndex: number, locals: Locals): boolean {
|
||||
var res = this.handleEventInternal(eventName, elIndex, locals);
|
||||
this.markPathToRootAsCheckOnce();
|
||||
return res;
|
||||
handleEvent(eventName: string, elIndex: number, event: any): boolean {
|
||||
if (!this.hydrated()) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
var locals = new Map<string, any>();
|
||||
locals.set('$event', event);
|
||||
var res = !this.handleEventInternal(eventName, elIndex, new Locals(this.locals, locals));
|
||||
this.markPathToRootAsCheckOnce();
|
||||
return res;
|
||||
} catch (e) {
|
||||
var c = this.dispatcher.getDebugContext(null, elIndex, null);
|
||||
var context = isPresent(c) ?
|
||||
new EventEvaluationErrorContext(c.element, c.componentElement, c.context,
|
||||
c.locals, c.injector) :
|
||||
null;
|
||||
throw new EventEvaluationError(eventName, e, e.stack, context);
|
||||
}
|
||||
}
|
||||
|
||||
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean { return false; }
|
||||
@ -133,7 +152,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||
// implementation of `hydrateDirectives`.
|
||||
hydrate(context: T, locals: Locals, directives: any, pipes: Pipes): void {
|
||||
hydrate(context: T, locals: Locals, dispatcher: ChangeDispatcher, pipes: Pipes): void {
|
||||
this.dispatcher = dispatcher;
|
||||
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
|
||||
this.context = context;
|
||||
|
||||
@ -143,12 +163,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
this.locals = locals;
|
||||
this.pipes = pipes;
|
||||
this.hydrateDirectives(directives);
|
||||
this.hydrateDirectives(dispatcher);
|
||||
this.state = ChangeDetectorState.NeverChecked;
|
||||
}
|
||||
|
||||
// Subclasses should override this method to hydrate any directives.
|
||||
hydrateDirectives(directives: any): void {}
|
||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {}
|
||||
|
||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||
// implementation of `dehydrateDirectives`.
|
||||
@ -160,6 +180,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
this._unsubsribeFromObservables();
|
||||
}
|
||||
|
||||
this.dispatcher = null;
|
||||
this.context = null;
|
||||
this.locals = null;
|
||||
this.pipes = null;
|
||||
@ -171,6 +192,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
hydrated(): boolean { return isPresent(this.context); }
|
||||
|
||||
destroyRecursive(): void {
|
||||
this.dispatcher.notifyOnDestroy();
|
||||
this.dehydrate();
|
||||
var children = this.contentChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].destroyRecursive();
|
||||
}
|
||||
children = this.viewChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].destroyRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
afterContentLifecycleCallbacks(): void {
|
||||
this.dispatcher.notifyAfterContentChecked();
|
||||
this.afterContentLifecycleCallbacksInternal();
|
||||
@ -298,7 +332,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
private _throwError(exception: any, stack: any): void {
|
||||
var error;
|
||||
try {
|
||||
var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
|
||||
var c = this.dispatcher.getDebugContext(null, this._currentBinding().elementIndex, null);
|
||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
||||
c.injector, this._currentBinding().debug) :
|
||||
null;
|
||||
|
@ -66,8 +66,8 @@ export class ChangeDetectorJITGenerator {
|
||||
generate(): Function {
|
||||
var factorySource = `
|
||||
${this.generateSource()}
|
||||
return function(dispatcher) {
|
||||
return new ${this.typeName}(dispatcher);
|
||||
return function() {
|
||||
return new ${this.typeName}();
|
||||
}
|
||||
`;
|
||||
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
|
||||
@ -77,9 +77,9 @@ export class ChangeDetectorJITGenerator {
|
||||
|
||||
generateSource(): string {
|
||||
return `
|
||||
var ${this.typeName} = function ${this.typeName}(dispatcher) {
|
||||
var ${this.typeName} = function ${this.typeName}() {
|
||||
${this.abstractChangeDetectorVarName}.call(
|
||||
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
|
||||
this, ${JSON.stringify(this.id)}, ${this.records.length},
|
||||
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
|
||||
${codify(this.changeDetectionStrategy)});
|
||||
this.dehydrateDirectives(false);
|
||||
@ -199,13 +199,14 @@ export class ChangeDetectorJITGenerator {
|
||||
/** @internal */
|
||||
_maybeGenDehydrateDirectives(): string {
|
||||
var destroyPipesCode = this._names.genPipeOnDestroy();
|
||||
if (destroyPipesCode) {
|
||||
destroyPipesCode = `if (destroyPipes) { ${destroyPipesCode} }`;
|
||||
}
|
||||
var destroyDirectivesCode = this._logic.genDirectivesOnDestroy(this.directiveRecords);
|
||||
var dehydrateFieldsCode = this._names.genDehydrateFields();
|
||||
if (!destroyPipesCode && !dehydrateFieldsCode) return '';
|
||||
if (!destroyPipesCode && !destroyDirectivesCode && !dehydrateFieldsCode) return '';
|
||||
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
|
||||
${destroyPipesCode}
|
||||
if (destroyPipes) {
|
||||
${destroyPipesCode}
|
||||
${destroyDirectivesCode}
|
||||
}
|
||||
${dehydrateFieldsCode}
|
||||
}`;
|
||||
}
|
||||
|
@ -205,4 +205,4 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
|
||||
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
||||
this.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
@ -155,17 +155,49 @@ export class CodegenLogicUtil {
|
||||
var res = [];
|
||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||
var r = directiveRecords[i];
|
||||
res.push(`${this._names.getDirectiveName(r.directiveIndex)} = ${this._genReadDirective(i)};`);
|
||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
||||
res.push(`${dirVarName} = ${this._genReadDirective(i)};`);
|
||||
if (isPresent(r.outputs)) {
|
||||
r.outputs.forEach(output => {
|
||||
var eventHandlerExpr = this._genEventHandler(r.directiveIndex.elementIndex, output[1]);
|
||||
if (IS_DART) {
|
||||
res.push(`${dirVarName}.${output[0]}.listen(${eventHandlerExpr});`);
|
||||
} else {
|
||||
res.push(`${dirVarName}.${output[0]}.subscribe({next: ${eventHandlerExpr}});`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return res.join("\n");
|
||||
}
|
||||
|
||||
genDirectivesOnDestroy(directiveRecords: DirectiveRecord[]): string {
|
||||
var res = [];
|
||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||
var r = directiveRecords[i];
|
||||
if (r.callOnDestroy) {
|
||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
||||
res.push(`${dirVarName}.ngOnDestroy();`);
|
||||
}
|
||||
}
|
||||
return res.join("\n");
|
||||
}
|
||||
|
||||
private _genEventHandler(boundElementIndex: number, eventName: string): string {
|
||||
if (IS_DART) {
|
||||
return `(event) => this.handleEvent('${eventName}', ${boundElementIndex}, event)`;
|
||||
} else {
|
||||
return `(function(event) { return this.handleEvent('${eventName}', ${boundElementIndex}, event); }).bind(this)`;
|
||||
}
|
||||
}
|
||||
|
||||
private _genReadDirective(index: number) {
|
||||
var directiveExpr = `this.getDirectiveFor(directives, ${index})`;
|
||||
// This is an experimental feature. Works only in Dart.
|
||||
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
|
||||
return `this.observeDirective(this.getDirectiveFor(directives, ${index}), ${index})`;
|
||||
return `this.observeDirective(${directiveExpr}, ${index})`;
|
||||
} else {
|
||||
return `this.getDirectiveFor(directives, ${index})`;
|
||||
return directiveExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,14 @@ export class DirectiveRecord {
|
||||
callOnChanges: boolean;
|
||||
callDoCheck: boolean;
|
||||
callOnInit: boolean;
|
||||
callOnDestroy: boolean;
|
||||
changeDetection: ChangeDetectionStrategy;
|
||||
// array of [emitter property name, eventName]
|
||||
outputs: string[][];
|
||||
|
||||
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
|
||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, changeDetection}: {
|
||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, callOnDestroy,
|
||||
changeDetection, outputs}: {
|
||||
directiveIndex?: DirectiveIndex,
|
||||
callAfterContentInit?: boolean,
|
||||
callAfterContentChecked?: boolean,
|
||||
@ -28,7 +32,9 @@ export class DirectiveRecord {
|
||||
callOnChanges?: boolean,
|
||||
callDoCheck?: boolean,
|
||||
callOnInit?: boolean,
|
||||
changeDetection?: ChangeDetectionStrategy
|
||||
callOnDestroy?: boolean,
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
outputs?: string[][]
|
||||
} = {}) {
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
||||
@ -38,7 +44,9 @@ export class DirectiveRecord {
|
||||
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
||||
this.callDoCheck = normalizeBool(callDoCheck);
|
||||
this.callOnInit = normalizeBool(callOnInit);
|
||||
this.callOnDestroy = normalizeBool(callOnDestroy);
|
||||
this.changeDetection = changeDetection;
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
isDefaultChangeDetection(): boolean {
|
||||
|
@ -11,21 +11,21 @@ import {ChangeDispatcher, ChangeDetectorGenConfig} from './interfaces';
|
||||
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
values: any[];
|
||||
changes: any[];
|
||||
localPipes: any[];
|
||||
prevContexts: any[];
|
||||
directives: any = null;
|
||||
|
||||
constructor(id: string, dispatcher: ChangeDispatcher, numberOfPropertyProtoRecords: number,
|
||||
constructor(id: string, numberOfPropertyProtoRecords: number,
|
||||
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
||||
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
|
||||
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
|
||||
private _genConfig: ChangeDetectorGenConfig) {
|
||||
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
|
||||
strategy);
|
||||
super(id, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, strategy);
|
||||
var len = _records.length + 1;
|
||||
this.values = ListWrapper.createFixedSize(len);
|
||||
this.localPipes = ListWrapper.createFixedSize(len);
|
||||
@ -104,24 +104,41 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
return this._eventBindings.filter(eb => eb.eventName == eventName && eb.elIndex === elIndex);
|
||||
}
|
||||
|
||||
hydrateDirectives(directives: any): void {
|
||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {
|
||||
this.values[0] = this.context;
|
||||
this.directives = directives;
|
||||
this.dispatcher = dispatcher;
|
||||
|
||||
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
|
||||
for (var i = 0; i < this.directiveIndices.length; ++i) {
|
||||
var index = this.directiveIndices[i];
|
||||
super.observeDirective(directives.getDirectiveFor(index), i);
|
||||
super.observeDirective(this._getDirectiveFor(index), i);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
||||
var r = this._directiveRecords[i];
|
||||
if (isPresent(r.outputs)) {
|
||||
r.outputs.forEach(output => {
|
||||
var eventHandler =
|
||||
<any>this._createEventHandler(r.directiveIndex.elementIndex, output[1]);
|
||||
var directive = this._getDirectiveFor(r.directiveIndex);
|
||||
var getter = reflector.getter(output[0]);
|
||||
ObservableWrapper.subscribe(getter(directive), eventHandler);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _createEventHandler(boundElementIndex: number, eventName: string): Function {
|
||||
return (event) => this.handleEvent(eventName, boundElementIndex, event);
|
||||
}
|
||||
|
||||
|
||||
dehydrateDirectives(destroyPipes: boolean) {
|
||||
if (destroyPipes) {
|
||||
this._destroyPipes();
|
||||
this._destroyDirectives();
|
||||
}
|
||||
this.values[0] = null;
|
||||
this.directives = null;
|
||||
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
|
||||
ListWrapper.fill(this.changes, false);
|
||||
ListWrapper.fill(this.localPipes, null);
|
||||
@ -137,6 +154,16 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyDirectives() {
|
||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
||||
var record = this._directiveRecords[i];
|
||||
if (record.callOnDestroy) {
|
||||
this._getDirectiveFor(record.directiveIndex).ngOnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkNoChanges(): void { this.runDetectChanges(true); }
|
||||
|
||||
detectChangesInRecordsInternal(throwOnChange: boolean) {
|
||||
@ -241,12 +268,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _getDirectiveFor(directiveIndex) {
|
||||
return this.directives.getDirectiveFor(directiveIndex);
|
||||
private _getDirectiveFor(directiveIndex: DirectiveIndex) {
|
||||
return this.dispatcher.getDirectiveFor(directiveIndex);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
|
||||
private _getDetectorFor(directiveIndex: DirectiveIndex) {
|
||||
return this.dispatcher.getDetectorFor(directiveIndex);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],
|
||||
|
@ -93,3 +93,20 @@ export class ChangeDetectionError extends WrappedException {
|
||||
export class DehydratedException extends BaseException {
|
||||
constructor() { super('Attempt to detect changes on a dehydrated detector.'); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an exception thrown by an event handler.
|
||||
*/
|
||||
export class EventEvaluationError extends WrappedException {
|
||||
constructor(eventName: string, originalException: any, originalStack: any, context: any) {
|
||||
super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error context included when an event handler throws an exception.
|
||||
*/
|
||||
export class EventEvaluationErrorContext {
|
||||
constructor(public element: any, public componentElement: any, public context: any,
|
||||
public locals: any, public injector: any) {}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Locals} from './parser/locals';
|
||||
import {BindingTarget, BindingRecord} from './binding_record';
|
||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||
import {ChangeDetectionStrategy} from './constants';
|
||||
import {ChangeDetectorRef} from './change_detector_ref';
|
||||
|
||||
@ -10,11 +10,14 @@ export class DebugContext {
|
||||
}
|
||||
|
||||
export interface ChangeDispatcher {
|
||||
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
|
||||
getDebugContext(appElement: any, elementIndex: number, directiveIndex: number): DebugContext;
|
||||
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
|
||||
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
|
||||
notifyAfterContentChecked(): void;
|
||||
notifyAfterViewChecked(): void;
|
||||
notifyOnDestroy(): void;
|
||||
getDetectorFor(directiveIndex: DirectiveIndex): ChangeDetector;
|
||||
getDirectiveFor(directiveIndex: DirectiveIndex): any;
|
||||
}
|
||||
|
||||
export interface ChangeDetector {
|
||||
@ -27,16 +30,18 @@ export interface ChangeDetector {
|
||||
removeContentChild(cd: ChangeDetector): void;
|
||||
removeViewChild(cd: ChangeDetector): void;
|
||||
remove(): void;
|
||||
hydrate(context: any, locals: Locals, directives: any, pipes: any): void;
|
||||
hydrate(context: any, locals: Locals, dispatcher: ChangeDispatcher, pipes: any): void;
|
||||
dehydrate(): void;
|
||||
markPathToRootAsCheckOnce(): void;
|
||||
|
||||
handleEvent(eventName: string, elIndex: number, locals: Locals);
|
||||
handleEvent(eventName: string, elIndex: number, event: any);
|
||||
detectChanges(): void;
|
||||
checkNoChanges(): void;
|
||||
destroyRecursive(): void;
|
||||
markAsCheckOnce(): void;
|
||||
}
|
||||
|
||||
export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; }
|
||||
export interface ProtoChangeDetector { instantiate(): ChangeDetector; }
|
||||
|
||||
export class ChangeDetectorGenConfig {
|
||||
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
|
||||
|
@ -3,11 +3,11 @@ library change_detection.jit_proto_change_detector;
|
||||
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
|
||||
|
||||
class JitProtoChangeDetector implements ProtoChangeDetector {
|
||||
JitProtoChangeDetector(definition) : super();
|
||||
JitProtoChangeDetector(definition);
|
||||
|
||||
static bool isSupported() => false;
|
||||
|
||||
ChangeDetector instantiate(dispatcher) {
|
||||
ChangeDetector instantiate() {
|
||||
throw "Jit Change Detection not supported in Dart";
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
|
||||
|
||||
static isSupported(): boolean { return true; }
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector { return this._factory(dispatcher); }
|
||||
instantiate(): ChangeDetector { return this._factory(); }
|
||||
|
||||
/** @internal */
|
||||
_createFactory(definition: ChangeDetectorDefinition) {
|
||||
|
@ -41,5 +41,5 @@ export class Locals {
|
||||
}
|
||||
}
|
||||
|
||||
clearValues(): void { MapWrapper.clearValues(this.current); }
|
||||
clearLocalValues(): void { MapWrapper.clearValues(this.current); }
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
library angular2.src.change_detection.pregen_proto_change_detector;
|
||||
|
||||
import 'package:angular2/src/core/change_detection/interfaces.dart';
|
||||
import 'package:angular2/src/facade/lang.dart' show looseIdentical;
|
||||
|
||||
export 'dart:core' show List;
|
||||
export 'package:angular2/src/core/change_detection/abstract_change_detector.dart'
|
||||
show AbstractChangeDetector;
|
||||
@ -20,34 +17,3 @@ export 'package:angular2/src/core/change_detection/proto_record.dart'
|
||||
export 'package:angular2/src/core/change_detection/change_detection_util.dart'
|
||||
show ChangeDetectionUtil;
|
||||
export 'package:angular2/src/facade/lang.dart' show assertionsEnabled, looseIdentical;
|
||||
|
||||
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
|
||||
ChangeDetectorDefinition definition);
|
||||
|
||||
typedef ChangeDetector InstantiateMethod(dynamic dispatcher);
|
||||
|
||||
/// Implementation of [ProtoChangeDetector] for use by pre-generated change
|
||||
/// detectors in Angular 2 Dart.
|
||||
/// Classes generated by the `TemplateCompiler` use this. The `export`s above
|
||||
/// allow the generated code to `import` a single library and get all
|
||||
/// dependencies.
|
||||
class PregenProtoChangeDetector extends ProtoChangeDetector {
|
||||
/// The [ChangeDetectorDefinition#id]. Strictly informational.
|
||||
final String id;
|
||||
|
||||
/// Closure used to generate an actual [ChangeDetector].
|
||||
final InstantiateMethod _instantiateMethod;
|
||||
|
||||
/// Internal ctor.
|
||||
PregenProtoChangeDetector._(this.id, this._instantiateMethod);
|
||||
|
||||
static bool isSupported() => true;
|
||||
|
||||
factory PregenProtoChangeDetector(
|
||||
InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) {
|
||||
return new PregenProtoChangeDetector._(def.id, instantiateMethod);
|
||||
}
|
||||
|
||||
@override
|
||||
instantiate(dynamic dispatcher) => _instantiateMethod(dispatcher);
|
||||
}
|
||||
|
@ -1,14 +1 @@
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {ProtoChangeDetector, ChangeDetector} from './interfaces';
|
||||
import {coalesce} from './coalesce';
|
||||
|
||||
export {Function as PregenProtoChangeDetectorFactory};
|
||||
|
||||
export class PregenProtoChangeDetector implements ProtoChangeDetector {
|
||||
static isSupported(): boolean { return false; }
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
throw new BaseException('Pregen change detection not supported in Js');
|
||||
}
|
||||
}
|
||||
// empty file as we only need the dart version
|
@ -54,12 +54,11 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
|
||||
this._directiveIndices = this._definition.directiveRecords.map(d => d.directiveIndex);
|
||||
}
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
instantiate(): ChangeDetector {
|
||||
return new DynamicChangeDetector(
|
||||
this._definition.id, dispatcher, this._propertyBindingRecords.length,
|
||||
this._propertyBindingTargets, this._directiveIndices, this._definition.strategy,
|
||||
this._propertyBindingRecords, this._eventBindingRecords, this._definition.directiveRecords,
|
||||
this._definition.genConfig);
|
||||
this._definition.id, this._propertyBindingRecords.length, this._propertyBindingTargets,
|
||||
this._directiveIndices, this._definition.strategy, this._propertyBindingRecords,
|
||||
this._eventBindingRecords, this._definition.directiveRecords, this._definition.genConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ElementInjector} from 'angular2/src/core/linker/element_injector';
|
||||
import {AppView, ViewType} from 'angular2/src/core/linker/view';
|
||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
import {AppElement} from 'angular2/src/core/linker/element';
|
||||
import {AppView} from 'angular2/src/core/linker/view';
|
||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
|
||||
/**
|
||||
@ -103,79 +103,68 @@ export abstract class DebugElement {
|
||||
}
|
||||
|
||||
export class DebugElement_ extends DebugElement {
|
||||
/** @internal */
|
||||
_elementInjector: ElementInjector;
|
||||
|
||||
constructor(private _parentView: AppView, private _boundElementIndex: number) {
|
||||
super();
|
||||
this._elementInjector = this._parentView.elementInjectors[this._boundElementIndex];
|
||||
}
|
||||
constructor(private _appElement: AppElement) { super(); }
|
||||
|
||||
get componentInstance(): any {
|
||||
if (!isPresent(this._elementInjector)) {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return null;
|
||||
}
|
||||
return this._elementInjector.getComponent();
|
||||
return this._appElement.getComponent();
|
||||
}
|
||||
|
||||
get nativeElement(): any { return this.elementRef.nativeElement; }
|
||||
|
||||
get elementRef(): ElementRef { return this._parentView.elementRefs[this._boundElementIndex]; }
|
||||
get elementRef(): ElementRef { return this._appElement.ref; }
|
||||
|
||||
getDirectiveInstance(directiveIndex: number): any {
|
||||
return this._elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||
return this._appElement.getDirectiveAtIndex(directiveIndex);
|
||||
}
|
||||
|
||||
get children(): DebugElement[] {
|
||||
return this._getChildElements(this._parentView, this._boundElementIndex);
|
||||
return this._getChildElements(this._appElement.parentView, this._appElement);
|
||||
}
|
||||
|
||||
get componentViewChildren(): DebugElement[] {
|
||||
var shadowView = this._parentView.getNestedView(this._boundElementIndex);
|
||||
|
||||
if (!isPresent(shadowView) || shadowView.proto.type !== ViewType.COMPONENT) {
|
||||
if (!isPresent(this._appElement.componentView)) {
|
||||
// The current element is not a component.
|
||||
return [];
|
||||
}
|
||||
|
||||
return this._getChildElements(shadowView, null);
|
||||
return this._getChildElements(this._appElement.componentView, null);
|
||||
}
|
||||
|
||||
triggerEventHandler(eventName: string, eventObj: Event): void {
|
||||
this._parentView.triggerEventHandlers(eventName, eventObj, this._boundElementIndex);
|
||||
this._appElement.parentView.triggerEventHandlers(eventName, eventObj,
|
||||
this._appElement.proto.index);
|
||||
}
|
||||
|
||||
hasDirective(type: Type): boolean {
|
||||
if (!isPresent(this._elementInjector)) {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return false;
|
||||
}
|
||||
return this._elementInjector.hasDirective(type);
|
||||
return this._appElement.hasDirective(type);
|
||||
}
|
||||
|
||||
inject(type: Type): any {
|
||||
if (!isPresent(this._elementInjector)) {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return null;
|
||||
}
|
||||
return this._elementInjector.get(type);
|
||||
return this._appElement.get(type);
|
||||
}
|
||||
|
||||
getLocal(name: string): any { return this._parentView.locals.get(name); }
|
||||
getLocal(name: string): any { return this._appElement.parentView.locals.get(name); }
|
||||
|
||||
/** @internal */
|
||||
_getChildElements(view: AppView, parentBoundElementIndex: number): DebugElement[] {
|
||||
_getChildElements(view: AppView, parentAppElement: AppElement): DebugElement[] {
|
||||
var els = [];
|
||||
var parentElementBinder = null;
|
||||
if (isPresent(parentBoundElementIndex)) {
|
||||
parentElementBinder = view.proto.elementBinders[parentBoundElementIndex - view.elementOffset];
|
||||
}
|
||||
for (var i = 0; i < view.proto.elementBinders.length; ++i) {
|
||||
var binder = view.proto.elementBinders[i];
|
||||
if (binder.parent == parentElementBinder) {
|
||||
els.push(new DebugElement_(view, view.elementOffset + i));
|
||||
for (var i = 0; i < view.appElements.length; ++i) {
|
||||
var appEl = view.appElements[i];
|
||||
if (appEl.parent == parentAppElement) {
|
||||
els.push(new DebugElement_(appEl));
|
||||
|
||||
var views = view.viewContainers[view.elementOffset + i];
|
||||
var views = appEl.nestedViews;
|
||||
if (isPresent(views)) {
|
||||
views.views.forEach(
|
||||
views.forEach(
|
||||
(nextView) => { els = els.concat(this._getChildElements(nextView, null)); });
|
||||
}
|
||||
}
|
||||
@ -191,8 +180,7 @@ export class DebugElement_ extends DebugElement {
|
||||
* @return {DebugElement}
|
||||
*/
|
||||
export function inspectElement(elementRef: ElementRef): DebugElement {
|
||||
return new DebugElement_(internalView((<ElementRef_>elementRef).parentView),
|
||||
(<ElementRef_>elementRef).boundElementIndex);
|
||||
return new DebugElement_((<ElementRef_>elementRef).internalElement);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,6 +194,11 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
||||
}
|
||||
|
||||
export class ProtoInjector {
|
||||
static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
|
||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||
return new ProtoInjector(bd);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_strategy: ProtoInjectorStrategy;
|
||||
numberOfProviders: number;
|
||||
@ -215,7 +220,6 @@ export interface InjectorStrategy {
|
||||
getObjAtIndex(index: number): any;
|
||||
getMaxNumberOfObjects(): number;
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void;
|
||||
resetConstructionCounter(): void;
|
||||
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any;
|
||||
}
|
||||
@ -240,12 +244,6 @@ export class InjectorInlineStrategy implements InjectorStrategy {
|
||||
return this.injector._new(provider, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void {
|
||||
var inj = this.injector;
|
||||
inj._parent = parent;
|
||||
inj._isHost = isHost;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
@ -346,12 +344,6 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
|
||||
return this.injector._new(provider, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void {
|
||||
var inj = this.injector;
|
||||
inj._parent = parent;
|
||||
inj._isHost = isHost;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||
var p = this.protoStrategy;
|
||||
|
||||
@ -516,9 +508,7 @@ export class Injector {
|
||||
* ```
|
||||
*/
|
||||
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
|
||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||
var proto = new ProtoInjector(bd);
|
||||
return new Injector(proto, null, null);
|
||||
return new Injector(ProtoInjector.fromResolvedProviders(providers));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -531,8 +521,6 @@ export class Injector {
|
||||
/** @internal */
|
||||
_strategy: InjectorStrategy;
|
||||
/** @internal */
|
||||
_isHost: boolean = false;
|
||||
/** @internal */
|
||||
_constructionCounter: number = 0;
|
||||
/** @internal */
|
||||
public _proto: any /* ProtoInjector */;
|
||||
@ -542,6 +530,7 @@ export class Injector {
|
||||
* Private
|
||||
*/
|
||||
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
|
||||
private _isHostBoundary: boolean = false,
|
||||
private _depProvider: any /* DependencyProvider */ = null,
|
||||
private _debugContext: Function = null) {
|
||||
this._proto = _proto;
|
||||
@ -549,6 +538,12 @@ export class Injector {
|
||||
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this injector is a boundary to a host.
|
||||
* @internal
|
||||
*/
|
||||
get hostBoundary() { return this._isHostBoundary; }
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -692,7 +687,7 @@ export class Injector {
|
||||
createChildFromResolved(providers: ResolvedProvider[]): Injector {
|
||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||
var proto = new ProtoInjector(bd);
|
||||
var inj = new Injector(proto, null, null);
|
||||
var inj = new Injector(proto);
|
||||
inj._parent = this;
|
||||
return inj;
|
||||
}
|
||||
@ -935,7 +930,7 @@ export class Injector {
|
||||
var inj: Injector = this;
|
||||
|
||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||
if (inj._isHost) {
|
||||
if (inj._isHostBoundary) {
|
||||
return this._getPrivateDependency(key, optional, inj);
|
||||
} else {
|
||||
inj = inj._parent;
|
||||
@ -946,7 +941,7 @@ export class Injector {
|
||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
|
||||
if (isPresent(inj._parent) && inj._isHost) {
|
||||
if (isPresent(inj._parent) && inj._isHostBoundary) {
|
||||
return this._getPrivateDependency(key, optional, inj);
|
||||
} else {
|
||||
inj = inj._parent;
|
||||
@ -968,7 +963,7 @@ export class Injector {
|
||||
var inj: Injector = this;
|
||||
|
||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
inj = inj._parent;
|
||||
}
|
||||
|
||||
@ -976,7 +971,7 @@ export class Injector {
|
||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
|
||||
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
inj = inj._parent;
|
||||
}
|
||||
|
||||
|
@ -538,50 +538,62 @@ export function resolveFactory(provider: Provider): ResolvedFactory {
|
||||
* convenience provider syntax.
|
||||
*/
|
||||
export function resolveProvider(provider: Provider): ResolvedProvider {
|
||||
return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], false);
|
||||
return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], provider.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of Providers.
|
||||
*/
|
||||
export function resolveProviders(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
|
||||
var normalized = _createListOfProviders(_normalizeProviders(
|
||||
providers, new Map<number, _NormalizedProvider | _NormalizedProvider[]>()));
|
||||
return normalized.map(b => {
|
||||
if (b instanceof _NormalizedProvider) {
|
||||
return new ResolvedProvider_(b.key, [b.resolvedFactory], false);
|
||||
|
||||
} else {
|
||||
var arr = <_NormalizedProvider[]>b;
|
||||
return new ResolvedProvider_(arr[0].key, arr.map(_ => _.resolvedFactory), true);
|
||||
}
|
||||
});
|
||||
var normalized = _normalizeProviders(providers, []);
|
||||
var resolved = normalized.map(resolveProvider);
|
||||
return MapWrapper.values(mergeResolvedProviders(resolved, new Map<number, ResolvedProvider>()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The algorithm works as follows:
|
||||
*
|
||||
* [Provider] -> [_NormalizedProvider|[_NormalizedProvider]] -> [ResolvedProvider]
|
||||
*
|
||||
* _NormalizedProvider is essentially a resolved provider before it was grouped by key.
|
||||
* Merges a list of ResolvedProviders into a list where
|
||||
* each key is contained exactly once and multi providers
|
||||
* have been merged.
|
||||
*/
|
||||
class _NormalizedProvider {
|
||||
constructor(public key: Key, public resolvedFactory: ResolvedFactory) {}
|
||||
}
|
||||
|
||||
function _createListOfProviders(flattenedProviders: Map<number, any>): any[] {
|
||||
return MapWrapper.values(flattenedProviders);
|
||||
export function mergeResolvedProviders(
|
||||
providers: ResolvedProvider[],
|
||||
normalizedProvidersMap: Map<number, ResolvedProvider>): Map<number, ResolvedProvider> {
|
||||
for (var i = 0; i < providers.length; i++) {
|
||||
var provider = providers[i];
|
||||
var existing = normalizedProvidersMap.get(provider.key.id);
|
||||
if (isPresent(existing)) {
|
||||
if (provider.multiProvider !== existing.multiProvider) {
|
||||
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
|
||||
}
|
||||
if (provider.multiProvider) {
|
||||
for (var j = 0; j < provider.resolvedFactories.length; j++) {
|
||||
existing.resolvedFactories.push(provider.resolvedFactories[j]);
|
||||
}
|
||||
} else {
|
||||
normalizedProvidersMap.set(provider.key.id, provider);
|
||||
}
|
||||
} else {
|
||||
var resolvedProvider;
|
||||
if (provider.multiProvider) {
|
||||
resolvedProvider = new ResolvedProvider_(
|
||||
provider.key, ListWrapper.clone(provider.resolvedFactories), provider.multiProvider);
|
||||
} else {
|
||||
resolvedProvider = provider;
|
||||
}
|
||||
normalizedProvidersMap.set(provider.key.id, resolvedProvider);
|
||||
}
|
||||
}
|
||||
return normalizedProvidersMap;
|
||||
}
|
||||
|
||||
function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder | any[]>,
|
||||
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>):
|
||||
Map<number, _NormalizedProvider | _NormalizedProvider[]> {
|
||||
res: Provider[]): Provider[] {
|
||||
providers.forEach(b => {
|
||||
if (b instanceof Type) {
|
||||
_normalizeProvider(provide(b, {useClass: b}), res);
|
||||
res.push(provide(b, {useClass: b}));
|
||||
|
||||
} else if (b instanceof Provider) {
|
||||
_normalizeProvider(b, res);
|
||||
res.push(b);
|
||||
|
||||
} else if (b instanceof Array) {
|
||||
_normalizeProviders(b, res);
|
||||
@ -597,36 +609,6 @@ function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder
|
||||
return res;
|
||||
}
|
||||
|
||||
function _normalizeProvider(b: Provider,
|
||||
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>): void {
|
||||
var key = Key.get(b.token);
|
||||
var factory = resolveFactory(b);
|
||||
var normalized = new _NormalizedProvider(key, factory);
|
||||
|
||||
if (b.multi) {
|
||||
var existingProvider = res.get(key.id);
|
||||
|
||||
if (existingProvider instanceof Array) {
|
||||
existingProvider.push(normalized);
|
||||
|
||||
} else if (isBlank(existingProvider)) {
|
||||
res.set(key.id, [normalized]);
|
||||
|
||||
} else {
|
||||
throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
|
||||
}
|
||||
|
||||
} else {
|
||||
var existingProvider = res.get(key.id);
|
||||
|
||||
if (existingProvider instanceof Array) {
|
||||
throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
|
||||
}
|
||||
|
||||
res.set(key.id, normalized);
|
||||
}
|
||||
}
|
||||
|
||||
function _constructDependencies(factoryFunction: Function, dependencies: any[]): Dependency[] {
|
||||
if (isBlank(dependencies)) {
|
||||
return _dependenciesFor(factoryFunction);
|
||||
|
@ -17,6 +17,6 @@ export {QueryList} from './linker/query_list';
|
||||
export {DynamicComponentLoader} from './linker/dynamic_component_loader';
|
||||
export {ElementRef} from './linker/element_ref';
|
||||
export {TemplateRef} from './linker/template_ref';
|
||||
export {ViewRef, HostViewRef, ProtoViewRef} from './linker/view_ref';
|
||||
export {EmbeddedViewRef, HostViewRef, ViewRef, HostViewFactoryRef} from './linker/view_ref';
|
||||
export {ViewContainerRef} from './linker/view_container_ref';
|
||||
export {ComponentRef} from './linker/dynamic_component_loader';
|
@ -1,12 +1,12 @@
|
||||
import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {HostViewFactoryRef} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
|
||||
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||
import {HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
/**
|
||||
* Low-level service for compiling {@link Component}s into {@link ProtoViewRef ProtoViews}s, which
|
||||
@ -16,37 +16,25 @@ import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
|
||||
* both compiles and instantiates a Component.
|
||||
*/
|
||||
export abstract class Compiler {
|
||||
abstract compileInHost(componentType: Type): Promise<ProtoViewRef>;
|
||||
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
|
||||
abstract clearCache();
|
||||
}
|
||||
|
||||
function _isCompiledHostTemplate(type: any): boolean {
|
||||
return type instanceof CompiledHostTemplate;
|
||||
function isHostViewFactory(type: any): boolean {
|
||||
return type instanceof HostViewFactory;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class Compiler_ extends Compiler {
|
||||
constructor(private _protoViewFactory: ProtoViewFactory) { super(); }
|
||||
|
||||
compileInHost(componentType: Type): Promise<ProtoViewRef> {
|
||||
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
||||
var metadatas = reflector.annotations(componentType);
|
||||
var compiledHostTemplate = metadatas.find(_isCompiledHostTemplate);
|
||||
var hostViewFactory = metadatas.find(isHostViewFactory);
|
||||
|
||||
if (isBlank(compiledHostTemplate)) {
|
||||
throw new BaseException(
|
||||
`No precompiled template for component ${stringify(componentType)} found`);
|
||||
if (isBlank(hostViewFactory)) {
|
||||
throw new BaseException(`No precompiled component ${stringify(componentType)} found`);
|
||||
}
|
||||
return PromiseWrapper.resolve(this._createProtoView(compiledHostTemplate));
|
||||
return PromiseWrapper.resolve(new HostViewFactoryRef_(hostViewFactory));
|
||||
}
|
||||
|
||||
private _createProtoView(compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
|
||||
return this._protoViewFactory.createHost(compiledHostTemplate).ref;
|
||||
}
|
||||
|
||||
clearCache() { this._protoViewFactory.clearCache(); }
|
||||
}
|
||||
|
||||
export function internalCreateProtoView(compiler: Compiler,
|
||||
compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
|
||||
return (<any>compiler)._createProtoView(compiledHostTemplate);
|
||||
clearCache() {}
|
||||
}
|
||||
|
@ -138,3 +138,5 @@ export class DirectiveResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export var CODEGEN_DIRECTIVE_RESOLVER = new DirectiveResolver();
|
||||
|
@ -3,8 +3,8 @@ import {Compiler} from './compiler';
|
||||
import {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {ViewRef, HostViewRef} from './view_ref';
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {HostViewRef} from './view_ref';
|
||||
|
||||
/**
|
||||
* Represents an instance of a Component created via {@link DynamicComponentLoader}.
|
||||
@ -42,7 +42,9 @@ export abstract class ComponentRef {
|
||||
/**
|
||||
* The {@link ViewRef} of the Host View of this Component instance.
|
||||
*/
|
||||
get hostView(): HostViewRef { return this.location.parentView; }
|
||||
get hostView(): HostViewRef {
|
||||
return (<ElementRef_>this.location).internalElement.parentView.ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -140,7 +142,7 @@ export abstract class DynamicComponentLoader {
|
||||
* ```
|
||||
*/
|
||||
abstract loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
|
||||
onDispose?: () => void): Promise<ComponentRef>;
|
||||
onDispose?: () => void, projectableNodes?: any[][]): Promise<ComponentRef>;
|
||||
|
||||
/**
|
||||
* Creates an instance of a Component and attaches it to a View Container located inside of the
|
||||
@ -190,7 +192,8 @@ export abstract class DynamicComponentLoader {
|
||||
* ```
|
||||
*/
|
||||
abstract loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
|
||||
providers?: ResolvedProvider[]): Promise<ComponentRef>;
|
||||
providers?: ResolvedProvider[],
|
||||
projectableNodes?: any[][]): Promise<ComponentRef>;
|
||||
|
||||
/**
|
||||
* Creates an instance of a Component and attaches it to the View Container found at the
|
||||
@ -232,19 +235,19 @@ export abstract class DynamicComponentLoader {
|
||||
* <child-component>Child</child-component>
|
||||
* ```
|
||||
*/
|
||||
abstract loadNextToLocation(type: Type, location: ElementRef,
|
||||
providers?: ResolvedProvider[]): Promise<ComponentRef>;
|
||||
abstract loadNextToLocation(type: Type, location: ElementRef, providers?: ResolvedProvider[],
|
||||
projectableNodes?: any[][]): Promise<ComponentRef>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
||||
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); }
|
||||
|
||||
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
|
||||
onDispose?: () => void): Promise<ComponentRef> {
|
||||
loadAsRoot(type: Type, overrideSelector: string, injector: Injector, onDispose?: () => void,
|
||||
projectableNodes?: any[][]): Promise<ComponentRef> {
|
||||
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||
var hostViewRef =
|
||||
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
||||
var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector,
|
||||
injector, projectableNodes);
|
||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
@ -259,24 +262,25 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
||||
}
|
||||
|
||||
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
|
||||
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
|
||||
providers: ResolvedProvider[] = null,
|
||||
projectableNodes: any[][] = null): Promise<ComponentRef> {
|
||||
return this.loadNextToLocation(
|
||||
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
|
||||
providers);
|
||||
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), providers,
|
||||
projectableNodes);
|
||||
}
|
||||
|
||||
loadNextToLocation(type: Type, location: ElementRef,
|
||||
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
|
||||
loadNextToLocation(type: Type, location: ElementRef, providers: ResolvedProvider[] = null,
|
||||
projectableNodes: any[][] = null): Promise<ComponentRef> {
|
||||
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||
var viewContainer = this._viewManager.getViewContainer(location);
|
||||
var hostViewRef =
|
||||
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, providers);
|
||||
var hostViewRef = viewContainer.createHostView(hostProtoViewRef, viewContainer.length,
|
||||
providers, projectableNodes);
|
||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
var dispose = () => {
|
||||
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
|
||||
if (index !== -1) {
|
||||
var index = viewContainer.indexOf(hostViewRef);
|
||||
if (!hostViewRef.destroyed && index !== -1) {
|
||||
viewContainer.remove(index);
|
||||
}
|
||||
};
|
||||
|
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 {ViewRef, ViewRef_} from './view_ref';
|
||||
import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/render/api';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {AppElement} from './element';
|
||||
|
||||
/**
|
||||
* Represents a location in a View that has an injection, change-detection and render context
|
||||
@ -12,23 +11,7 @@ import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/rende
|
||||
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
||||
* element.
|
||||
*/
|
||||
export abstract class ElementRef implements RenderElementRef {
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Reference to the {@link ViewRef} that this `ElementRef` is part of.
|
||||
*/
|
||||
parentView: ViewRef;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Index of the element inside the {@link ViewRef}.
|
||||
*
|
||||
* This is used internally by the Angular framework to locate elements.
|
||||
*/
|
||||
boundElementIndex: number;
|
||||
|
||||
export abstract class ElementRef {
|
||||
/**
|
||||
* The underlying native element or `null` if direct access to native elements is not supported
|
||||
* (e.g. when the application runs in a web worker).
|
||||
@ -48,24 +31,13 @@ export abstract class ElementRef implements RenderElementRef {
|
||||
* </p>
|
||||
* </div>
|
||||
*/
|
||||
get nativeElement(): any { return unimplemented(); };
|
||||
|
||||
get renderView(): RenderViewRef { return unimplemented(); }
|
||||
get nativeElement(): any { return unimplemented(); }
|
||||
}
|
||||
|
||||
export class ElementRef_ extends ElementRef {
|
||||
constructor(public parentView: ViewRef,
|
||||
export class ElementRef_ implements ElementRef {
|
||||
constructor(private _appElement: AppElement) {}
|
||||
|
||||
/**
|
||||
* Index of the element inside the {@link ViewRef}.
|
||||
*
|
||||
* This is used internally by the Angular framework to locate elements.
|
||||
*/
|
||||
public boundElementIndex: number, private _renderer: Renderer) {
|
||||
super();
|
||||
}
|
||||
get internalElement(): AppElement { return this._appElement; }
|
||||
|
||||
get renderView(): RenderViewRef { return (<ViewRef_>this.parentView).render; }
|
||||
set renderView(value) { unimplemented(); }
|
||||
get nativeElement(): any { return this._renderer.getNativeElementSync(this); }
|
||||
get nativeElement() { return this._appElement.nativeElement; }
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
export const EVENT_TARGET_SEPARATOR = ':';
|
||||
|
||||
export class EventConfig {
|
||||
constructor(public fieldName: string, public eventName: string, public isLongForm: boolean) {}
|
||||
|
||||
static parse(eventConfig: string): EventConfig {
|
||||
var fieldName = eventConfig, eventName = eventConfig, isLongForm = false;
|
||||
var separatorIdx = eventConfig.indexOf(EVENT_TARGET_SEPARATOR);
|
||||
if (separatorIdx > -1) {
|
||||
// long format: 'fieldName: eventName'
|
||||
fieldName = eventConfig.substring(0, separatorIdx).trim();
|
||||
eventName = eventConfig.substring(separatorIdx + 1).trim();
|
||||
isLongForm = true;
|
||||
}
|
||||
return new EventConfig(fieldName, eventName, isLongForm);
|
||||
}
|
||||
|
||||
getFullName(): string {
|
||||
return this.isLongForm ? `${this.fieldName}${EVENT_TARGET_SEPARATOR}${this.eventName}` :
|
||||
this.eventName;
|
||||
}
|
||||
}
|
@ -31,3 +31,5 @@ export class PipeResolver {
|
||||
throw new BaseException(`No Pipe decorator found on ${stringify(type)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export var CODEGEN_PIPE_RESOLVER = new PipeResolver();
|
||||
|
@ -1,341 +0,0 @@
|
||||
import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/facade/lang';
|
||||
|
||||
import {RenderProtoViewRef, RenderComponentTemplate} from 'angular2/src/core/render/api';
|
||||
|
||||
import {Optional, Injectable, Provider, resolveForwardRef, Inject} from 'angular2/src/core/di';
|
||||
|
||||
import {PipeProvider} from '../pipes/pipe_provider';
|
||||
import {ProtoPipes} from '../pipes/pipes';
|
||||
|
||||
import {AppProtoView, AppProtoViewMergeInfo, ViewType} from './view';
|
||||
import {ElementBinder} from './element_binder';
|
||||
import {ProtoElementInjector, DirectiveProvider} from './element_injector';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {ViewResolver} from './view_resolver';
|
||||
import {PipeResolver} from './pipe_resolver';
|
||||
import {ViewMetadata, ViewEncapsulation} from '../metadata/view';
|
||||
import {PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||
|
||||
import {
|
||||
visitAllCommands,
|
||||
CompiledComponentTemplate,
|
||||
CompiledHostTemplate,
|
||||
TemplateCmd,
|
||||
CommandVisitor,
|
||||
EmbeddedTemplateCmd,
|
||||
BeginComponentCmd,
|
||||
BeginElementCmd,
|
||||
IBeginElementCmd,
|
||||
TextCmd,
|
||||
NgContentCmd
|
||||
} from './template_commands';
|
||||
|
||||
import {Renderer} from 'angular2/src/core/render/api';
|
||||
import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ProtoViewFactory {
|
||||
private _cache: Map<string, AppProtoView> = new Map<string, AppProtoView>();
|
||||
private _nextTemplateId: number = 0;
|
||||
|
||||
constructor(private _renderer: Renderer,
|
||||
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Array<Type | any[]>,
|
||||
private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
|
||||
private _pipeResolver: PipeResolver, @Inject(APP_ID) private _appId: string) {}
|
||||
|
||||
clearCache() { this._cache.clear(); }
|
||||
|
||||
createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
|
||||
var compiledTemplate = compiledHostTemplate.template;
|
||||
var result = this._cache.get(compiledTemplate.id);
|
||||
if (isBlank(result)) {
|
||||
var emptyMap: {[key: string]: PipeProvider} = {};
|
||||
var shortId = `${this._appId}-${this._nextTemplateId++}`;
|
||||
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
|
||||
compiledTemplate.id, shortId, ViewEncapsulation.None, compiledTemplate.commands, []));
|
||||
result =
|
||||
new AppProtoView(compiledTemplate.id, compiledTemplate.commands, ViewType.HOST, true,
|
||||
compiledTemplate.changeDetectorFactory, null, new ProtoPipes(emptyMap));
|
||||
this._cache.set(compiledTemplate.id, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _createComponent(cmd: BeginComponentCmd): AppProtoView {
|
||||
var nestedProtoView = this._cache.get(cmd.templateId);
|
||||
if (isBlank(nestedProtoView)) {
|
||||
var component = cmd.directives[0];
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var compiledTemplate = cmd.templateGetter();
|
||||
var styles = _flattenStyleArr(compiledTemplate.styles, []);
|
||||
var shortId = `${this._appId}-${this._nextTemplateId++}`;
|
||||
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
|
||||
compiledTemplate.id, shortId, cmd.encapsulation, compiledTemplate.commands, styles));
|
||||
var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
|
||||
|
||||
nestedProtoView = new AppProtoView(
|
||||
compiledTemplate.id, compiledTemplate.commands, ViewType.COMPONENT, true,
|
||||
compiledTemplate.changeDetectorFactory, null, ProtoPipes.fromProviders(boundPipes));
|
||||
// Note: The cache is updated before recursing
|
||||
// to be able to resolve cycles
|
||||
this._cache.set(compiledTemplate.id, nestedProtoView);
|
||||
this._initializeProtoView(nestedProtoView, null);
|
||||
}
|
||||
return nestedProtoView;
|
||||
}
|
||||
|
||||
private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView {
|
||||
var nestedProtoView = new AppProtoView(
|
||||
parent.templateId, cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory,
|
||||
arrayToMap(cmd.variableNameAndValues, true), new ProtoPipes(parent.pipes.config));
|
||||
if (cmd.isMerged) {
|
||||
this.initializeProtoViewIfNeeded(nestedProtoView);
|
||||
}
|
||||
return nestedProtoView;
|
||||
}
|
||||
|
||||
initializeProtoViewIfNeeded(protoView: AppProtoView) {
|
||||
if (!protoView.isInitialized()) {
|
||||
var render = this._renderer.createProtoView(protoView.templateId, protoView.templateCmds);
|
||||
this._initializeProtoView(protoView, render);
|
||||
}
|
||||
}
|
||||
|
||||
private _initializeProtoView(protoView: AppProtoView, render: RenderProtoViewRef) {
|
||||
var initializer = new _ProtoViewInitializer(protoView, this._directiveResolver, this);
|
||||
visitAllCommands(initializer, protoView.templateCmds);
|
||||
var mergeInfo =
|
||||
new AppProtoViewMergeInfo(initializer.mergeEmbeddedViewCount, initializer.mergeElementCount,
|
||||
initializer.mergeViewCount);
|
||||
protoView.init(render, initializer.elementBinders, initializer.boundTextCount, mergeInfo,
|
||||
initializer.variableLocations);
|
||||
}
|
||||
|
||||
private _bindPipe(typeOrProvider): PipeProvider {
|
||||
let meta = this._pipeResolver.resolve(typeOrProvider);
|
||||
return PipeProvider.createFromType(typeOrProvider, meta);
|
||||
}
|
||||
|
||||
private _flattenPipes(view: ViewMetadata): any[] {
|
||||
let pipes = [];
|
||||
if (isPresent(this._platformPipes)) {
|
||||
_flattenArray(this._platformPipes, pipes);
|
||||
}
|
||||
if (isPresent(view.pipes)) {
|
||||
_flattenArray(view.pipes, pipes);
|
||||
}
|
||||
return pipes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createComponent(protoViewFactory: ProtoViewFactory, cmd: BeginComponentCmd): AppProtoView {
|
||||
return (<any>protoViewFactory)._createComponent(cmd);
|
||||
}
|
||||
|
||||
function createEmbeddedTemplate(protoViewFactory: ProtoViewFactory, cmd: EmbeddedTemplateCmd,
|
||||
parent: AppProtoView): AppProtoView {
|
||||
return (<any>protoViewFactory)._createEmbeddedTemplate(cmd, parent);
|
||||
}
|
||||
|
||||
class _ProtoViewInitializer implements CommandVisitor {
|
||||
variableLocations: Map<string, number> = new Map<string, number>();
|
||||
boundTextCount: number = 0;
|
||||
boundElementIndex: number = 0;
|
||||
elementBinderStack: ElementBinder[] = [];
|
||||
distanceToParentElementBinder: number = 0;
|
||||
distanceToParentProtoElementInjector: number = 0;
|
||||
elementBinders: ElementBinder[] = [];
|
||||
mergeEmbeddedViewCount: number = 0;
|
||||
mergeElementCount: number = 0;
|
||||
mergeViewCount: number = 1;
|
||||
|
||||
constructor(private _protoView: AppProtoView, private _directiveResolver: DirectiveResolver,
|
||||
private _protoViewFactory: ProtoViewFactory) {}
|
||||
|
||||
visitText(cmd: TextCmd, context: any): any {
|
||||
if (cmd.isBound) {
|
||||
this.boundTextCount++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
|
||||
visitBeginElement(cmd: BeginElementCmd, context: any): any {
|
||||
if (cmd.isBound) {
|
||||
this._visitBeginBoundElement(cmd, null);
|
||||
} else {
|
||||
this._visitBeginElement(cmd, null, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitEndElement(context: any): any { return this._visitEndElement(); }
|
||||
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
|
||||
var nestedProtoView = createComponent(this._protoViewFactory, cmd);
|
||||
return this._visitBeginBoundElement(cmd, nestedProtoView);
|
||||
}
|
||||
visitEndComponent(context: any): any { return this._visitEndElement(); }
|
||||
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
|
||||
var nestedProtoView = createEmbeddedTemplate(this._protoViewFactory, cmd, this._protoView);
|
||||
if (cmd.isMerged) {
|
||||
this.mergeEmbeddedViewCount++;
|
||||
}
|
||||
this._visitBeginBoundElement(cmd, nestedProtoView);
|
||||
return this._visitEndElement();
|
||||
}
|
||||
|
||||
private _visitBeginBoundElement(cmd: IBeginElementCmd, nestedProtoView: AppProtoView): any {
|
||||
if (isPresent(nestedProtoView) && nestedProtoView.isMergable) {
|
||||
this.mergeElementCount += nestedProtoView.mergeInfo.elementCount;
|
||||
this.mergeViewCount += nestedProtoView.mergeInfo.viewCount;
|
||||
this.mergeEmbeddedViewCount += nestedProtoView.mergeInfo.embeddedViewCount;
|
||||
}
|
||||
var elementBinder = _createElementBinder(
|
||||
this._directiveResolver, nestedProtoView, this.elementBinderStack, this.boundElementIndex,
|
||||
this.distanceToParentElementBinder, this.distanceToParentProtoElementInjector, cmd);
|
||||
this.elementBinders.push(elementBinder);
|
||||
var protoElementInjector = elementBinder.protoElementInjector;
|
||||
for (var i = 0; i < cmd.variableNameAndValues.length; i += 2) {
|
||||
this.variableLocations.set(<string>cmd.variableNameAndValues[i], this.boundElementIndex);
|
||||
}
|
||||
this.boundElementIndex++;
|
||||
this.mergeElementCount++;
|
||||
return this._visitBeginElement(cmd, elementBinder, protoElementInjector);
|
||||
}
|
||||
|
||||
private _visitBeginElement(cmd: IBeginElementCmd, elementBinder: ElementBinder,
|
||||
protoElementInjector: ProtoElementInjector): any {
|
||||
this.distanceToParentElementBinder =
|
||||
isPresent(elementBinder) ? 1 : this.distanceToParentElementBinder + 1;
|
||||
this.distanceToParentProtoElementInjector =
|
||||
isPresent(protoElementInjector) ? 1 : this.distanceToParentProtoElementInjector + 1;
|
||||
this.elementBinderStack.push(elementBinder);
|
||||
return null;
|
||||
}
|
||||
|
||||
private _visitEndElement(): any {
|
||||
var parentElementBinder = this.elementBinderStack.pop();
|
||||
var parentProtoElementInjector =
|
||||
isPresent(parentElementBinder) ? parentElementBinder.protoElementInjector : null;
|
||||
this.distanceToParentElementBinder = isPresent(parentElementBinder) ?
|
||||
parentElementBinder.distanceToParent :
|
||||
this.distanceToParentElementBinder - 1;
|
||||
this.distanceToParentProtoElementInjector = isPresent(parentProtoElementInjector) ?
|
||||
parentProtoElementInjector.distanceToParent :
|
||||
this.distanceToParentProtoElementInjector - 1;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _createElementBinder(directiveResolver: DirectiveResolver, nestedProtoView: AppProtoView,
|
||||
elementBinderStack: ElementBinder[], boundElementIndex: number,
|
||||
distanceToParentBinder: number, distanceToParentPei: number,
|
||||
beginElementCmd: IBeginElementCmd): ElementBinder {
|
||||
var parentElementBinder: ElementBinder = null;
|
||||
var parentProtoElementInjector: ProtoElementInjector = null;
|
||||
if (distanceToParentBinder > 0) {
|
||||
parentElementBinder = elementBinderStack[elementBinderStack.length - distanceToParentBinder];
|
||||
}
|
||||
if (isBlank(parentElementBinder)) {
|
||||
distanceToParentBinder = -1;
|
||||
}
|
||||
if (distanceToParentPei > 0) {
|
||||
var peiBinder = elementBinderStack[elementBinderStack.length - distanceToParentPei];
|
||||
if (isPresent(peiBinder)) {
|
||||
parentProtoElementInjector = peiBinder.protoElementInjector;
|
||||
}
|
||||
}
|
||||
if (isBlank(parentProtoElementInjector)) {
|
||||
distanceToParentPei = -1;
|
||||
}
|
||||
var componentDirectiveProvider: DirectiveProvider = null;
|
||||
var isEmbeddedTemplate = false;
|
||||
var directiveProviders: DirectiveProvider[] =
|
||||
beginElementCmd.directives.map(type => provideDirective(directiveResolver, type));
|
||||
if (beginElementCmd instanceof BeginComponentCmd) {
|
||||
componentDirectiveProvider = directiveProviders[0];
|
||||
} else if (beginElementCmd instanceof EmbeddedTemplateCmd) {
|
||||
isEmbeddedTemplate = true;
|
||||
}
|
||||
|
||||
var protoElementInjector = null;
|
||||
// Create a protoElementInjector for any element that either has bindings *or* has one
|
||||
// or more var- defined *or* for <template> elements:
|
||||
// - Elements with a var- defined need a their own element injector
|
||||
// so that, when hydrating, $implicit can be set to the element.
|
||||
// - <template> elements need their own ElementInjector so that we can query their TemplateRef
|
||||
var hasVariables = beginElementCmd.variableNameAndValues.length > 0;
|
||||
if (directiveProviders.length > 0 || hasVariables || isEmbeddedTemplate) {
|
||||
var directiveVariableBindings = new Map<string, number>();
|
||||
if (!isEmbeddedTemplate) {
|
||||
directiveVariableBindings = createDirectiveVariableBindings(
|
||||
beginElementCmd.variableNameAndValues, directiveProviders);
|
||||
}
|
||||
protoElementInjector = ProtoElementInjector.create(
|
||||
parentProtoElementInjector, boundElementIndex, directiveProviders,
|
||||
isPresent(componentDirectiveProvider), distanceToParentPei, directiveVariableBindings);
|
||||
protoElementInjector.attributes = arrayToMap(beginElementCmd.attrNameAndValues, false);
|
||||
}
|
||||
|
||||
return new ElementBinder(boundElementIndex, parentElementBinder, distanceToParentBinder,
|
||||
protoElementInjector, componentDirectiveProvider, nestedProtoView);
|
||||
}
|
||||
|
||||
function provideDirective(directiveResolver: DirectiveResolver, type: Type): DirectiveProvider {
|
||||
let annotation = directiveResolver.resolve(type);
|
||||
return DirectiveProvider.createFromType(type, annotation);
|
||||
}
|
||||
|
||||
export function createDirectiveVariableBindings(
|
||||
variableNameAndValues: Array<string | number>,
|
||||
directiveProviders: DirectiveProvider[]): Map<string, number> {
|
||||
var directiveVariableBindings = new Map<string, number>();
|
||||
for (var i = 0; i < variableNameAndValues.length; i += 2) {
|
||||
var templateName = <string>variableNameAndValues[i];
|
||||
var dirIndex = <number>variableNameAndValues[i + 1];
|
||||
if (isNumber(dirIndex)) {
|
||||
directiveVariableBindings.set(templateName, dirIndex);
|
||||
} else {
|
||||
// a variable without a directive index -> reference the element
|
||||
directiveVariableBindings.set(templateName, null);
|
||||
}
|
||||
}
|
||||
return directiveVariableBindings;
|
||||
}
|
||||
|
||||
|
||||
function arrayToMap(arr: string[], inverse: boolean): Map<string, string> {
|
||||
var result = new Map<string, string>();
|
||||
for (var i = 0; i < arr.length; i += 2) {
|
||||
if (inverse) {
|
||||
result.set(arr[i + 1], arr[i]);
|
||||
} else {
|
||||
result.set(arr[i], arr[i + 1]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _flattenArray(tree: any[], out: Array<Type | Provider | any[]>): void {
|
||||
for (var i = 0; i < tree.length; i++) {
|
||||
var item = resolveForwardRef(tree[i]);
|
||||
if (isArray(item)) {
|
||||
_flattenArray(item, out);
|
||||
} else {
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _flattenStyleArr(arr: Array<string | any[]>, out: string[]): string[] {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var entry = arr[i];
|
||||
if (isArray(entry)) {
|
||||
_flattenStyleArr(<any[]>entry, out);
|
||||
} else {
|
||||
out.push(<string>entry);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
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 * as viewModule from './view';
|
||||
|
||||
/**
|
||||
* Represents an Embedded Template that can be used to instantiate Embedded Views.
|
||||
@ -28,33 +26,10 @@ export abstract class TemplateRef {
|
||||
*/
|
||||
// TODO(i): rename to anchor or location
|
||||
elementRef: ElementRef;
|
||||
|
||||
/**
|
||||
* Allows you to check if this Embedded Template defines Local Variable with name matching `name`.
|
||||
*/
|
||||
abstract hasLocal(name: string): boolean;
|
||||
}
|
||||
|
||||
export class TemplateRef_ extends TemplateRef {
|
||||
constructor(elementRef: ElementRef) {
|
||||
super();
|
||||
this.elementRef = elementRef;
|
||||
}
|
||||
constructor(private _elementRef: ElementRef_) { super(); }
|
||||
|
||||
private _getProtoView(): viewModule.AppProtoView {
|
||||
let elementRef = <ElementRef_>this.elementRef;
|
||||
var parentView = internalView(elementRef.parentView);
|
||||
return parentView.proto.elementBinders[elementRef.boundElementIndex - parentView.elementOffset]
|
||||
.nestedProtoView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference to the ProtoView used for creating Embedded Views that are based on the compiled
|
||||
* Embedded Template.
|
||||
*/
|
||||
get protoViewRef(): ProtoViewRef { return this._getProtoView().ref; }
|
||||
|
||||
hasLocal(name: string): boolean {
|
||||
return this._getProtoView().templateVariableBindings.has(name);
|
||||
}
|
||||
get elementRef(): ElementRef_ { return this._elementRef; }
|
||||
}
|
||||
|
@ -10,86 +10,53 @@ import {
|
||||
DirectiveIndex,
|
||||
BindingTarget,
|
||||
Locals,
|
||||
ProtoChangeDetector
|
||||
ProtoChangeDetector,
|
||||
ChangeDetectorRef
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
|
||||
import {DebugContext} from 'angular2/src/core/change_detection/interfaces';
|
||||
|
||||
import {AppProtoElement, AppElement, DirectiveProvider} from './element';
|
||||
import {
|
||||
ProtoElementInjector,
|
||||
ElementInjector,
|
||||
PreBuiltObjects,
|
||||
DirectiveProvider
|
||||
} from './element_injector';
|
||||
import {ElementBinder} from './element_binder';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
isPresent,
|
||||
isBlank,
|
||||
Type,
|
||||
isArray,
|
||||
isNumber,
|
||||
CONST,
|
||||
CONST_EXPR
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import * as renderApi from 'angular2/src/core/render/api';
|
||||
import {RenderEventDispatcher} from 'angular2/src/core/render/api';
|
||||
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {Renderer, RootRenderer} from 'angular2/src/core/render/api';
|
||||
import {ViewRef_, HostViewFactoryRef} from './view_ref';
|
||||
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
|
||||
import {camelCaseToDashCase} from 'angular2/src/core/render/util';
|
||||
import {TemplateCmd} from './template_commands';
|
||||
import {ViewRef_, ProtoViewRef_} from "./view_ref";
|
||||
|
||||
export {DebugContext} from 'angular2/src/core/change_detection/interfaces';
|
||||
import {Pipes} from 'angular2/src/core/pipes/pipes';
|
||||
import {AppViewManager_, AppViewManager} from './view_manager';
|
||||
import {ResolvedMetadataCache} from './resolved_metadata_cache';
|
||||
import {ViewType} from './view_type';
|
||||
|
||||
const REFLECT_PREFIX: string = 'ng-reflect-';
|
||||
|
||||
export enum ViewType {
|
||||
// A view that contains the host element with bound component directive.
|
||||
// Contains a COMPONENT view
|
||||
HOST,
|
||||
// The view of the component
|
||||
// Can contain 0 to n EMBEDDED views
|
||||
COMPONENT,
|
||||
// A view that is embedded into another View via a <template> element
|
||||
// inside of a COMPONENT view
|
||||
EMBEDDED
|
||||
}
|
||||
|
||||
export class AppViewContainer {
|
||||
// The order in this list matches the DOM order.
|
||||
views: AppView[] = [];
|
||||
}
|
||||
const EMPTY_CONTEXT = CONST_EXPR(new Object());
|
||||
|
||||
/**
|
||||
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
|
||||
*
|
||||
*/
|
||||
export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
// AppViews that have been merged in depth first order.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
views: AppView[] = null;
|
||||
// root elementInjectors of this AppView
|
||||
// This list is local to this AppView and not shared with other Views.
|
||||
rootElementInjectors: ElementInjector[];
|
||||
// ElementInjectors of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
elementInjectors: ElementInjector[] = null;
|
||||
// ViewContainers of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
viewContainers: AppViewContainer[] = null;
|
||||
// PreBuiltObjects of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
preBuiltObjects: PreBuiltObjects[] = null;
|
||||
// ElementRef of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
elementRefs: ElementRef[];
|
||||
|
||||
ref: ViewRef;
|
||||
changeDetector: ChangeDetector = null;
|
||||
export class AppView implements ChangeDispatcher {
|
||||
ref: ViewRef_;
|
||||
rootNodesOrAppElements: any[];
|
||||
allNodes: any[];
|
||||
disposables: Function[];
|
||||
appElements: AppElement[];
|
||||
|
||||
/**
|
||||
* The context against which data-binding expressions in this view are evaluated against.
|
||||
* This is always a component instance.
|
||||
*/
|
||||
|
||||
context: any = null;
|
||||
|
||||
/**
|
||||
@ -99,70 +66,133 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
*/
|
||||
locals: Locals;
|
||||
|
||||
constructor(public renderer: renderApi.Renderer, public proto: AppProtoView,
|
||||
public viewOffset: number, public elementOffset: number, public textOffset: number,
|
||||
protoLocals: Map<string, any>, public render: renderApi.RenderViewRef,
|
||||
public renderFragment: renderApi.RenderFragmentRef,
|
||||
public containerElementInjector: ElementInjector) {
|
||||
this.ref = new ViewRef_(this);
|
||||
pipes: Pipes;
|
||||
|
||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); // TODO optimize this
|
||||
parentInjector: Injector;
|
||||
|
||||
/**
|
||||
* Whether root injectors of this view
|
||||
* have a hostBoundary.
|
||||
*/
|
||||
hostInjectorBoundary: boolean;
|
||||
|
||||
destroyed: boolean = false;
|
||||
|
||||
constructor(public proto: AppProtoView, public renderer: Renderer,
|
||||
public viewManager: AppViewManager_, public projectableNodes: Array<any | any[]>,
|
||||
public containerAppElement: AppElement,
|
||||
imperativelyCreatedProviders: ResolvedProvider[], rootInjector: Injector,
|
||||
public changeDetector: ChangeDetector) {
|
||||
this.ref = new ViewRef_(this);
|
||||
var injectorWithHostBoundary = AppElement.getViewParentInjector(
|
||||
this.proto.type, containerAppElement, imperativelyCreatedProviders, rootInjector);
|
||||
this.parentInjector = injectorWithHostBoundary.injector;
|
||||
this.hostInjectorBoundary = injectorWithHostBoundary.hostInjectorBoundary;
|
||||
var pipes;
|
||||
var context;
|
||||
switch (proto.type) {
|
||||
case ViewType.COMPONENT:
|
||||
pipes = new Pipes(proto.protoPipes, containerAppElement.getInjector());
|
||||
context = containerAppElement.getComponent();
|
||||
break;
|
||||
case ViewType.EMBEDDED:
|
||||
pipes = containerAppElement.parentView.pipes;
|
||||
context = containerAppElement.parentView.context;
|
||||
break;
|
||||
case ViewType.HOST:
|
||||
pipes = null;
|
||||
context = EMPTY_CONTEXT;
|
||||
break;
|
||||
}
|
||||
this.pipes = pipes;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
init(changeDetector: ChangeDetector, elementInjectors: ElementInjector[],
|
||||
rootElementInjectors: ElementInjector[], preBuiltObjects: PreBuiltObjects[],
|
||||
views: AppView[], elementRefs: ElementRef[], viewContainers: AppViewContainer[]) {
|
||||
this.changeDetector = changeDetector;
|
||||
this.elementInjectors = elementInjectors;
|
||||
this.rootElementInjectors = rootElementInjectors;
|
||||
this.preBuiltObjects = preBuiltObjects;
|
||||
this.views = views;
|
||||
this.elementRefs = elementRefs;
|
||||
this.viewContainers = viewContainers;
|
||||
init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[],
|
||||
appElements: AppElement[]) {
|
||||
this.rootNodesOrAppElements = rootNodesOrAppElements;
|
||||
this.allNodes = allNodes;
|
||||
this.disposables = disposables;
|
||||
this.appElements = appElements;
|
||||
var localsMap = new Map<string, any>();
|
||||
StringMapWrapper.forEach(this.proto.templateVariableBindings,
|
||||
(templateName, _) => { localsMap.set(templateName, null); });
|
||||
for (var i = 0; i < appElements.length; i++) {
|
||||
var appEl = appElements[i];
|
||||
StringMapWrapper.forEach(appEl.proto.directiveVariableBindings, (directiveIndex, name) => {
|
||||
if (isBlank(directiveIndex)) {
|
||||
localsMap.set(name, appEl.nativeElement);
|
||||
} else {
|
||||
localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
|
||||
}
|
||||
});
|
||||
}
|
||||
var parentLocals = null;
|
||||
if (this.proto.type !== ViewType.COMPONENT) {
|
||||
parentLocals =
|
||||
isPresent(this.containerAppElement) ? this.containerAppElement.parentView.locals : null;
|
||||
}
|
||||
if (this.proto.type === ViewType.COMPONENT) {
|
||||
// Note: the render nodes have been attached to their host element
|
||||
// in the ViewFactory already.
|
||||
this.containerAppElement.attachComponentView(this);
|
||||
this.containerAppElement.parentView.changeDetector.addViewChild(this.changeDetector);
|
||||
}
|
||||
this.locals = new Locals(parentLocals, localsMap);
|
||||
this.changeDetector.hydrate(this.context, this.locals, this, this.pipes);
|
||||
this.viewManager.onViewCreated(this);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.destroyed) {
|
||||
throw new BaseException('This view has already been destroyed!');
|
||||
}
|
||||
this.changeDetector.destroyRecursive();
|
||||
}
|
||||
|
||||
notifyOnDestroy() {
|
||||
this.destroyed = true;
|
||||
var hostElement =
|
||||
this.proto.type === ViewType.COMPONENT ? this.containerAppElement.nativeElement : null;
|
||||
this.renderer.destroyView(hostElement, this.allNodes);
|
||||
for (var i = 0; i < this.disposables.length; i++) {
|
||||
this.disposables[i]();
|
||||
}
|
||||
this.viewManager.onViewDestroyed(this);
|
||||
}
|
||||
|
||||
get changeDetectorRef(): ChangeDetectorRef { return this.changeDetector.ref; }
|
||||
|
||||
get flatRootNodes(): any[] { return flattenNestedViewRenderNodes(this.rootNodesOrAppElements); }
|
||||
|
||||
hasLocal(contextName: string): boolean {
|
||||
return StringMapWrapper.contains(this.proto.templateVariableBindings, contextName);
|
||||
}
|
||||
|
||||
setLocal(contextName: string, value: any): void {
|
||||
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
|
||||
if (!this.proto.templateVariableBindings.has(contextName)) {
|
||||
if (!this.hasLocal(contextName)) {
|
||||
return;
|
||||
}
|
||||
var templateName = this.proto.templateVariableBindings.get(contextName);
|
||||
var templateName = this.proto.templateVariableBindings[contextName];
|
||||
this.locals.set(templateName, value);
|
||||
}
|
||||
|
||||
hydrated(): boolean { return isPresent(this.context); }
|
||||
|
||||
/**
|
||||
* Triggers the event handlers for the element and the directives.
|
||||
*
|
||||
* This method is intended to be called from directive EventEmitters.
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {*} eventObj
|
||||
* @param {number} boundElementIndex
|
||||
*/
|
||||
triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): void {
|
||||
var locals = new Map<string, any>();
|
||||
locals.set('$event', eventObj);
|
||||
this.dispatchEvent(boundElementIndex, eventName, locals);
|
||||
}
|
||||
|
||||
// dispatch to element injector or text nodes based on context
|
||||
notifyOnBinding(b: BindingTarget, currentValue: any): void {
|
||||
if (b.isTextNode()) {
|
||||
this.renderer.setText(this.render, b.elementIndex + this.textOffset, currentValue);
|
||||
this.renderer.setText(this.allNodes[b.elementIndex], currentValue);
|
||||
} else {
|
||||
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
|
||||
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
||||
if (b.isElementProperty()) {
|
||||
this.renderer.setElementProperty(elementRef, b.name, currentValue);
|
||||
this.renderer.setElementProperty(nativeElement, b.name, currentValue);
|
||||
} else if (b.isElementAttribute()) {
|
||||
this.renderer.setElementAttribute(elementRef, b.name,
|
||||
this.renderer.setElementAttribute(nativeElement, b.name,
|
||||
isPresent(currentValue) ? `${currentValue}` : null);
|
||||
} else if (b.isElementClass()) {
|
||||
this.renderer.setElementClass(elementRef, b.name, currentValue);
|
||||
this.renderer.setElementClass(nativeElement, b.name, currentValue);
|
||||
} else if (b.isElementStyle()) {
|
||||
var unit = isPresent(b.unit) ? b.unit : '';
|
||||
this.renderer.setElementStyle(elementRef, b.name,
|
||||
this.renderer.setElementStyle(nativeElement, b.name,
|
||||
isPresent(currentValue) ? `${currentValue}${unit}` : null);
|
||||
} else {
|
||||
throw new BaseException('Unsupported directive record');
|
||||
@ -172,57 +202,39 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
|
||||
logBindingUpdate(b: BindingTarget, value: any): void {
|
||||
if (b.isDirective() || b.isElementProperty()) {
|
||||
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
|
||||
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
||||
this.renderer.setBindingDebugInfo(
|
||||
elementRef, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
|
||||
nativeElement, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
notifyAfterContentChecked(): void {
|
||||
var eiCount = this.proto.elementBinders.length;
|
||||
var ei = this.elementInjectors;
|
||||
for (var i = eiCount - 1; i >= 0; i--) {
|
||||
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterContentChecked();
|
||||
var count = this.appElements.length;
|
||||
for (var i = count - 1; i >= 0; i--) {
|
||||
this.appElements[i].ngAfterContentChecked();
|
||||
}
|
||||
}
|
||||
|
||||
notifyAfterViewChecked(): void {
|
||||
var eiCount = this.proto.elementBinders.length;
|
||||
var ei = this.elementInjectors;
|
||||
for (var i = eiCount - 1; i >= 0; i--) {
|
||||
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterViewChecked();
|
||||
var count = this.appElements.length;
|
||||
for (var i = count - 1; i >= 0; i--) {
|
||||
this.appElements[i].ngAfterViewChecked();
|
||||
}
|
||||
}
|
||||
|
||||
getDirectiveFor(directive: DirectiveIndex): any {
|
||||
var elementInjector = this.elementInjectors[this.elementOffset + directive.elementIndex];
|
||||
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
|
||||
}
|
||||
|
||||
getNestedView(boundElementIndex: number): AppView {
|
||||
var eli = this.elementInjectors[boundElementIndex];
|
||||
return isPresent(eli) ? eli.getNestedView() : null;
|
||||
}
|
||||
|
||||
getContainerElement(): ElementRef {
|
||||
return isPresent(this.containerElementInjector) ?
|
||||
this.containerElementInjector.getElementRef() :
|
||||
null;
|
||||
}
|
||||
|
||||
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext {
|
||||
getDebugContext(appElement: AppElement, elementIndex: number,
|
||||
directiveIndex: number): DebugContext {
|
||||
try {
|
||||
var offsettedIndex = this.elementOffset + elementIndex;
|
||||
var hasRefForIndex = offsettedIndex < this.elementRefs.length;
|
||||
if (isBlank(appElement) && elementIndex < this.appElements.length) {
|
||||
appElement = this.appElements[elementIndex];
|
||||
}
|
||||
var container = this.containerAppElement;
|
||||
|
||||
var elementRef = hasRefForIndex ? this.elementRefs[this.elementOffset + elementIndex] : null;
|
||||
var container = this.getContainerElement();
|
||||
var ei = hasRefForIndex ? this.elementInjectors[this.elementOffset + elementIndex] : null;
|
||||
|
||||
var element = isPresent(elementRef) ? elementRef.nativeElement : null;
|
||||
var element = isPresent(appElement) ? appElement.nativeElement : null;
|
||||
var componentElement = isPresent(container) ? container.nativeElement : null;
|
||||
var directive = isPresent(directiveIndex) ? this.getDirectiveFor(directiveIndex) : null;
|
||||
var injector = isPresent(ei) ? ei.getInjector() : null;
|
||||
var directive =
|
||||
isPresent(directiveIndex) ? appElement.getDirectiveAtIndex(directiveIndex) : null;
|
||||
var injector = isPresent(appElement) ? appElement.getInjector() : null;
|
||||
|
||||
return new DebugContext(element, componentElement, directive, this.context,
|
||||
_localsToStringMap(this.locals), injector);
|
||||
@ -234,43 +246,28 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
getDirectiveFor(directive: DirectiveIndex): any {
|
||||
return this.appElements[directive.elementIndex].getDirectiveAtIndex(directive.directiveIndex);
|
||||
}
|
||||
|
||||
getDetectorFor(directive: DirectiveIndex): any {
|
||||
var childView = this.getNestedView(this.elementOffset + directive.elementIndex);
|
||||
return isPresent(childView) ? childView.changeDetector : null;
|
||||
var componentView = this.appElements[directive.elementIndex].componentView;
|
||||
return isPresent(componentView) ? componentView.changeDetector : null;
|
||||
}
|
||||
|
||||
invokeElementMethod(elementIndex: number, methodName: string, args: any[]) {
|
||||
this.renderer.invokeElementMethod(this.elementRefs[elementIndex], methodName, args);
|
||||
/**
|
||||
* Triggers the event handlers for the element and the directives.
|
||||
*
|
||||
* This method is intended to be called from directive EventEmitters.
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {*} eventObj
|
||||
* @param {number} boundElementIndex
|
||||
* @return false if preventDefault must be applied to the DOM event
|
||||
*/
|
||||
triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): boolean {
|
||||
return this.changeDetector.handleEvent(eventName, boundElementIndex, eventObj);
|
||||
}
|
||||
|
||||
// implementation of RenderEventDispatcher#dispatchRenderEvent
|
||||
dispatchRenderEvent(boundElementIndex: number, eventName: string,
|
||||
locals: Map<string, any>): boolean {
|
||||
var elementRef = this.elementRefs[boundElementIndex];
|
||||
var view = internalView(elementRef.parentView);
|
||||
return view.dispatchEvent(elementRef.boundElementIndex, eventName, locals);
|
||||
}
|
||||
|
||||
|
||||
// returns false if preventDefault must be applied to the DOM event
|
||||
dispatchEvent(boundElementIndex: number, eventName: string, locals: Map<string, any>): boolean {
|
||||
try {
|
||||
if (this.hydrated()) {
|
||||
return !this.changeDetector.handleEvent(eventName, boundElementIndex - this.elementOffset,
|
||||
new Locals(this.locals, locals));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
var c = this.getDebugContext(boundElementIndex - this.elementOffset, null);
|
||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
||||
c.injector) :
|
||||
null;
|
||||
throw new EventEvaluationError(eventName, e, e.stack, context);
|
||||
}
|
||||
}
|
||||
|
||||
get ownBindersCount(): number { return this.proto.elementBinders.length; }
|
||||
}
|
||||
|
||||
function _localsToStringMap(locals: Locals): {[key: string]: any} {
|
||||
@ -283,69 +280,61 @@ function _localsToStringMap(locals: Locals): {[key: string]: any} {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error context included when an event handler throws an exception.
|
||||
*/
|
||||
class _Context {
|
||||
constructor(public element: any, public componentElement: any, public context: any,
|
||||
public locals: any, public injector: any) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an exception thrown by an event handler.
|
||||
*/
|
||||
class EventEvaluationError extends WrappedException {
|
||||
constructor(eventName: string, originalException: any, originalStack: any, context: any) {
|
||||
super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AppProtoViewMergeInfo {
|
||||
constructor(public embeddedViewCount: number, public elementCount: number,
|
||||
public viewCount: number) {}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class AppProtoView {
|
||||
ref: ProtoViewRef;
|
||||
protoLocals: Map<string, any>;
|
||||
|
||||
elementBinders: ElementBinder[] = null;
|
||||
mergeInfo: AppProtoViewMergeInfo = null;
|
||||
variableLocations: Map<string, number> = null;
|
||||
textBindingCount = null;
|
||||
render: renderApi.RenderProtoViewRef = null;
|
||||
|
||||
constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
|
||||
public isMergable: boolean, public changeDetectorFactory: Function,
|
||||
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
|
||||
this.ref = new ProtoViewRef_(this);
|
||||
static create(metadataCache: ResolvedMetadataCache, type: ViewType, pipes: Type[],
|
||||
templateVariableBindings: {[key: string]: string}): AppProtoView {
|
||||
var protoPipes = null;
|
||||
if (isPresent(pipes) && pipes.length > 0) {
|
||||
var boundPipes = ListWrapper.createFixedSize(pipes.length);
|
||||
for (var i = 0; i < pipes.length; i++) {
|
||||
boundPipes[i] = metadataCache.getResolvedPipeMetadata(pipes[i]);
|
||||
}
|
||||
protoPipes = ProtoPipes.fromProviders(boundPipes);
|
||||
}
|
||||
return new AppProtoView(type, protoPipes, templateVariableBindings);
|
||||
}
|
||||
|
||||
init(render: renderApi.RenderProtoViewRef, elementBinders: ElementBinder[],
|
||||
textBindingCount: number, mergeInfo: AppProtoViewMergeInfo,
|
||||
variableLocations: Map<string, number>) {
|
||||
this.render = render;
|
||||
this.elementBinders = elementBinders;
|
||||
this.textBindingCount = textBindingCount;
|
||||
this.mergeInfo = mergeInfo;
|
||||
this.variableLocations = variableLocations;
|
||||
this.protoLocals = new Map<string, any>();
|
||||
if (isPresent(this.templateVariableBindings)) {
|
||||
this.templateVariableBindings.forEach(
|
||||
(templateName, _) => { this.protoLocals.set(templateName, null); });
|
||||
}
|
||||
if (isPresent(variableLocations)) {
|
||||
// The view's locals needs to have a full set of variable names at construction time
|
||||
// in order to prevent new variables from being set later in the lifecycle. Since we don't
|
||||
// want
|
||||
// to actually create variable bindings for the $implicit bindings, add to the
|
||||
// protoLocals manually.
|
||||
variableLocations.forEach((_, templateName) => { this.protoLocals.set(templateName, null); });
|
||||
}
|
||||
}
|
||||
|
||||
isInitialized(): boolean { return isPresent(this.elementBinders); }
|
||||
constructor(public type: ViewType, public protoPipes: ProtoPipes,
|
||||
public templateVariableBindings: {[key: string]: string}) {}
|
||||
}
|
||||
|
||||
|
||||
@CONST()
|
||||
export class HostViewFactory {
|
||||
constructor(public selector: string, public viewFactory: Function) {}
|
||||
}
|
||||
|
||||
export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
|
||||
return _flattenNestedViewRenderNodes(nodes, []);
|
||||
}
|
||||
|
||||
function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
if (node instanceof AppElement) {
|
||||
var appEl = <AppElement>node;
|
||||
renderNodes.push(appEl.nativeElement);
|
||||
if (isPresent(appEl.nestedViews)) {
|
||||
for (var k = 0; k < appEl.nestedViews.length; k++) {
|
||||
_flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
renderNodes.push(node);
|
||||
}
|
||||
}
|
||||
return renderNodes;
|
||||
}
|
||||
|
||||
export function checkSlotCount(componentName: string, expectedSlotCount: number,
|
||||
projectableNodes: any[][]): void {
|
||||
var givenSlotCount = isPresent(projectableNodes) ? projectableNodes.length : 0;
|
||||
if (givenSlotCount < expectedSlotCount) {
|
||||
throw new BaseException(
|
||||
`The component ${componentName} has ${expectedSlotCount} <ng-content> elements,` +
|
||||
` but only ${givenSlotCount} slots were provided.`);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,18 @@ import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ResolvedProvider} from 'angular2/src/core/di';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
import * as avmModule from './view_manager';
|
||||
import * as viewModule from './view';
|
||||
import {AppElement} from './element';
|
||||
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {TemplateRef} from './template_ref';
|
||||
import {ViewRef, HostViewRef, ProtoViewRef, internalView} from './view_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {
|
||||
EmbeddedViewRef,
|
||||
HostViewRef,
|
||||
HostViewFactoryRef,
|
||||
HostViewFactoryRef_,
|
||||
ViewRef,
|
||||
ViewRef_
|
||||
} from './view_ref';
|
||||
|
||||
/**
|
||||
* Represents a container where one or more Views can be attached.
|
||||
@ -35,7 +41,7 @@ export abstract class ViewContainerRef {
|
||||
* Anchor element that specifies the location of this container in the containing View.
|
||||
* <!-- TODO: rename to anchorElement -->
|
||||
*/
|
||||
public element: ElementRef;
|
||||
get element(): ElementRef { return unimplemented(); }
|
||||
|
||||
/**
|
||||
* Destroys all Views in this container.
|
||||
@ -64,7 +70,7 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* Returns the {@link ViewRef} for the newly created View.
|
||||
*/
|
||||
abstract createEmbeddedView(templateRef: TemplateRef, index?: number): ViewRef;
|
||||
abstract createEmbeddedView(templateRef: TemplateRef, index?: number): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* Instantiates a single {@link Component} and inserts its Host View into this container at the
|
||||
@ -80,8 +86,9 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
|
||||
*/
|
||||
abstract createHostView(protoViewRef?: ProtoViewRef, index?: number,
|
||||
dynamicallyCreatedProviders?: ResolvedProvider[]): HostViewRef;
|
||||
abstract createHostView(hostViewFactoryRef: HostViewFactoryRef, index?: number,
|
||||
dynamicallyCreatedProviders?: ResolvedProvider[],
|
||||
projectableNodes?: any[][]): HostViewRef;
|
||||
|
||||
/**
|
||||
* Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
|
||||
@ -90,7 +97,7 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* Returns the inserted {@link ViewRef}.
|
||||
*/
|
||||
abstract insert(viewRef: ViewRef, index?: number): ViewRef;
|
||||
abstract insert(viewRef: EmbeddedViewRef, index?: number): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* Returns the index of the View, specified via {@link ViewRef}, within the current container or
|
||||
@ -110,58 +117,60 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* If the `index` param is omitted, the last {@link ViewRef} is detached.
|
||||
*/
|
||||
abstract detach(index?: number): ViewRef;
|
||||
abstract detach(index?: number): EmbeddedViewRef;
|
||||
}
|
||||
|
||||
export class ViewContainerRef_ extends ViewContainerRef {
|
||||
constructor(public viewManager: avmModule.AppViewManager, element: ElementRef) {
|
||||
super();
|
||||
this.element = element;
|
||||
constructor(private _element: AppElement) { super(); }
|
||||
|
||||
get(index: number): EmbeddedViewRef { return this._element.nestedViews[index].ref; }
|
||||
get length(): number {
|
||||
var views = this._element.nestedViews;
|
||||
return isPresent(views) ? views.length : 0;
|
||||
}
|
||||
|
||||
private _getViews(): Array<viewModule.AppView> {
|
||||
let element = <ElementRef_>this.element;
|
||||
var vc = internalView(element.parentView).viewContainers[element.boundElementIndex];
|
||||
return isPresent(vc) ? vc.views : [];
|
||||
}
|
||||
|
||||
get(index: number): ViewRef { return this._getViews()[index].ref; }
|
||||
get length(): number { return this._getViews().length; }
|
||||
get element(): ElementRef_ { return this._element.ref; }
|
||||
|
||||
// TODO(rado): profile and decide whether bounds checks should be added
|
||||
// to the methods below.
|
||||
createEmbeddedView(templateRef: TemplateRef, index: number = -1): ViewRef {
|
||||
createEmbeddedView(templateRef: TemplateRef_, index: number = -1): EmbeddedViewRef {
|
||||
if (index == -1) index = this.length;
|
||||
return this.viewManager.createEmbeddedViewInContainer(this.element, index, templateRef);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.createEmbeddedViewInContainer(this._element.ref, index, templateRef);
|
||||
}
|
||||
|
||||
createHostView(protoViewRef: ProtoViewRef = null, index: number = -1,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[] = null): HostViewRef {
|
||||
createHostView(hostViewFactoryRef: HostViewFactoryRef_, index: number = -1,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[] = null,
|
||||
projectableNodes: any[][] = null): HostViewRef {
|
||||
if (index == -1) index = this.length;
|
||||
return this.viewManager.createHostViewInContainer(this.element, index, protoViewRef,
|
||||
dynamicallyCreatedProviders);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.createHostViewInContainer(this._element.ref, index, hostViewFactoryRef,
|
||||
dynamicallyCreatedProviders, projectableNodes);
|
||||
}
|
||||
|
||||
// TODO(i): refactor insert+remove into move
|
||||
insert(viewRef: ViewRef, index: number = -1): ViewRef {
|
||||
insert(viewRef: ViewRef_, index: number = -1): EmbeddedViewRef {
|
||||
if (index == -1) index = this.length;
|
||||
return this.viewManager.attachViewInContainer(this.element, index, viewRef);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.attachViewInContainer(this._element.ref, index, viewRef);
|
||||
}
|
||||
|
||||
indexOf(viewRef: ViewRef): number {
|
||||
return ListWrapper.indexOf(this._getViews(), internalView(viewRef));
|
||||
indexOf(viewRef: ViewRef_): number {
|
||||
return ListWrapper.indexOf(this._element.nestedViews, viewRef.internalView);
|
||||
}
|
||||
|
||||
// TODO(i): rename to destroy
|
||||
remove(index: number = -1): void {
|
||||
if (index == -1) index = this.length - 1;
|
||||
this.viewManager.destroyViewInContainer(this.element, index);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.destroyViewInContainer(this._element.ref, index);
|
||||
// view is intentionally not returned to the client.
|
||||
}
|
||||
|
||||
// TODO(i): refactor insert+remove into move
|
||||
detach(index: number = -1): ViewRef {
|
||||
detach(index: number = -1): EmbeddedViewRef {
|
||||
if (index == -1) index = this.length - 1;
|
||||
return this.viewManager.detachViewInContainer(this.element, index);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.detachViewInContainer(this._element.ref, index);
|
||||
}
|
||||
}
|
||||
|
@ -6,24 +6,27 @@ import {
|
||||
ResolvedProvider,
|
||||
forwardRef
|
||||
} from 'angular2/src/core/di';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {isPresent, isBlank, isArray} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import * as viewModule from './view';
|
||||
import {AppView, HostViewFactory, flattenNestedViewRenderNodes} from './view';
|
||||
import {AppElement} from './element';
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {ProtoViewRef, ViewRef, HostViewRef, internalView, internalProtoView} from './view_ref';
|
||||
import {
|
||||
HostViewFactoryRef,
|
||||
HostViewFactoryRef_,
|
||||
EmbeddedViewRef,
|
||||
HostViewRef,
|
||||
ViewRef_
|
||||
} from './view_ref';
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {
|
||||
Renderer,
|
||||
RenderViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderViewWithFragments
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {AppViewManagerUtils} from './view_manager_utils';
|
||||
import {AppViewPool} from './view_pool';
|
||||
import {AppViewListener} from './view_listener';
|
||||
import {RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
import {ProtoViewFactory} from './proto_view_factory';
|
||||
import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {ViewType} from './view_type';
|
||||
|
||||
/**
|
||||
* Service exposing low level API for creating, moving and destroying Views.
|
||||
@ -40,13 +43,7 @@ export abstract class AppViewManager {
|
||||
/**
|
||||
* Returns the {@link ElementRef} that makes up the specified Host View.
|
||||
*/
|
||||
getHostElement(hostViewRef: HostViewRef): ElementRef {
|
||||
var hostView = internalView(<ViewRef>hostViewRef);
|
||||
if (hostView.proto.type !== viewModule.ViewType.HOST) {
|
||||
throw new BaseException('This operation is only allowed on host views');
|
||||
}
|
||||
return hostView.elementRefs[hostView.elementOffset];
|
||||
}
|
||||
abstract getHostElement(hostViewRef: HostViewRef): ElementRef;
|
||||
|
||||
/**
|
||||
* Searches the Component View of the Component specified via `hostLocation` and returns the
|
||||
@ -70,7 +67,8 @@ export abstract class AppViewManager {
|
||||
* This as a low-level way to bootstrap an application and upgrade an existing Element to a
|
||||
* Host Element. Most applications should use {@link DynamicComponentLoader#loadAsRoot} instead.
|
||||
*
|
||||
* The Component and its View are created based on the `hostProtoViewRef` which can be obtained
|
||||
* The Component and its View are created based on the `hostProtoComponentRef` which can be
|
||||
* obtained
|
||||
* by compiling the component with {@link Compiler#compileInHost}.
|
||||
*
|
||||
* Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host
|
||||
@ -101,7 +99,7 @@ export abstract class AppViewManager {
|
||||
* viewRef: ng.ViewRef;
|
||||
*
|
||||
* constructor(public appViewManager: ng.AppViewManager, compiler: ng.Compiler) {
|
||||
* compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoViewRef) => {
|
||||
* compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoComponentRef) => {
|
||||
* this.viewRef = appViewManager.createRootHostView(protoView, 'some-component', null);
|
||||
* })
|
||||
* }
|
||||
@ -115,8 +113,8 @@ export abstract class AppViewManager {
|
||||
* ng.bootstrap(MyApp);
|
||||
* ```
|
||||
*/
|
||||
abstract createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
||||
injector: Injector): HostViewRef;
|
||||
abstract createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string,
|
||||
injector: Injector, projectableNodes?: any[][]): HostViewRef;
|
||||
|
||||
/**
|
||||
* Destroys the Host View created via {@link AppViewManager#createRootHostView}.
|
||||
@ -140,7 +138,7 @@ export abstract class AppViewManager {
|
||||
// TODO(i): this low-level version of ViewContainerRef#createEmbeddedView doesn't add anything new
|
||||
// we should make it private, otherwise we have two apis to do the same thing.
|
||||
abstract createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
templateRef: TemplateRef): ViewRef;
|
||||
templateRef: TemplateRef): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* Instantiates a single {@link Component} and inserts its Host View into the View Container
|
||||
@ -150,16 +148,16 @@ export abstract class AppViewManager {
|
||||
* The component is instantiated using its {@link ProtoViewRef `protoViewRef`} which can be
|
||||
* obtained via {@link Compiler#compileInHost}.
|
||||
*
|
||||
* You can optionally specify `imperativelyCreatedInjector`, which configure the {@link Injector}
|
||||
* You can optionally specify `dynamicallyCreatedProviders`, which configure the {@link Injector}
|
||||
* that will be created for the Host View.
|
||||
*
|
||||
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
|
||||
*
|
||||
* Use {@link AppViewManager#destroyViewInContainer} to destroy the created Host View.
|
||||
*/
|
||||
abstract createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
protoViewRef: ProtoViewRef,
|
||||
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef;
|
||||
abstract createHostViewInContainer(
|
||||
viewContainerLocation: ElementRef, index: number, hostViewFactoryRef: HostViewFactoryRef,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[], projectableNodes: any[][]): HostViewRef;
|
||||
|
||||
/**
|
||||
* Destroys an Embedded or Host View attached to a View Container at the specified `index`.
|
||||
@ -174,85 +172,75 @@ export abstract class AppViewManager {
|
||||
*/
|
||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||
abstract attachViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
viewRef: ViewRef): ViewRef;
|
||||
viewRef: EmbeddedViewRef): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* See {@link AppViewManager#attachViewInContainer}.
|
||||
*/
|
||||
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef;
|
||||
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): EmbeddedViewRef;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManager_ extends AppViewManager {
|
||||
private _protoViewFactory: ProtoViewFactory;
|
||||
private _nextCompTypeId: number = 0;
|
||||
|
||||
constructor(private _viewPool: AppViewPool, private _viewListener: AppViewListener,
|
||||
private _utils: AppViewManagerUtils, private _renderer: Renderer,
|
||||
@Inject(forwardRef(() => ProtoViewFactory)) _protoViewFactory) {
|
||||
constructor(private _renderer: RootRenderer, private _viewListener: AppViewListener,
|
||||
@Inject(APP_ID) private _appId: string) {
|
||||
super();
|
||||
this._protoViewFactory = _protoViewFactory;
|
||||
}
|
||||
|
||||
getViewContainer(location: ElementRef): ViewContainerRef {
|
||||
var hostView = internalView((<ElementRef_>location).parentView);
|
||||
return hostView.elementInjectors[(<ElementRef_>location).boundElementIndex]
|
||||
.getViewContainerRef();
|
||||
getViewContainer(location: ElementRef_): ViewContainerRef {
|
||||
return location.internalElement.getViewContainerRef();
|
||||
}
|
||||
|
||||
getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
|
||||
var hostView = internalView((<ElementRef_>hostLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
|
||||
var componentView = hostView.getNestedView(boundElementIndex);
|
||||
getHostElement(hostViewRef: ViewRef_): ElementRef {
|
||||
var hostView = hostViewRef.internalView;
|
||||
if (hostView.proto.type !== ViewType.HOST) {
|
||||
throw new BaseException('This operation is only allowed on host views');
|
||||
}
|
||||
return hostView.appElements[0].ref;
|
||||
}
|
||||
|
||||
getNamedElementInComponentView(hostLocation: ElementRef_, variableName: string): ElementRef {
|
||||
var appEl = hostLocation.internalElement;
|
||||
var componentView = appEl.componentView;
|
||||
if (isBlank(componentView)) {
|
||||
throw new BaseException(`There is no component directive at element ${boundElementIndex}`);
|
||||
throw new BaseException(`There is no component directive at element ${hostLocation}`);
|
||||
}
|
||||
var binderIdx = componentView.proto.variableLocations.get(variableName);
|
||||
if (isBlank(binderIdx)) {
|
||||
throw new BaseException(`Could not find variable ${variableName}`);
|
||||
for (var i = 0; i < componentView.appElements.length; i++) {
|
||||
var compAppEl = componentView.appElements[i];
|
||||
if (StringMapWrapper.contains(compAppEl.proto.directiveVariableBindings, variableName)) {
|
||||
return compAppEl.ref;
|
||||
}
|
||||
}
|
||||
return componentView.elementRefs[componentView.elementOffset + binderIdx];
|
||||
throw new BaseException(`Could not find variable ${variableName}`);
|
||||
}
|
||||
|
||||
getComponent(hostLocation: ElementRef): any {
|
||||
var hostView = internalView((<ElementRef_>hostLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
|
||||
return this._utils.getComponentInstance(hostView, boundElementIndex);
|
||||
getComponent(hostLocation: ElementRef_): any {
|
||||
return hostLocation.internalElement.getComponent();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()');
|
||||
|
||||
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
||||
injector: Injector): HostViewRef {
|
||||
createRootHostView(hostViewFactoryRef: HostViewFactoryRef_, overrideSelector: string,
|
||||
injector: Injector, projectableNodes: any[][] = null): HostViewRef {
|
||||
var s = this._createRootHostViewScope();
|
||||
var hostProtoView: viewModule.AppProtoView = internalProtoView(hostProtoViewRef);
|
||||
this._protoViewFactory.initializeProtoViewIfNeeded(hostProtoView);
|
||||
var hostElementSelector = overrideSelector;
|
||||
if (isBlank(hostElementSelector)) {
|
||||
hostElementSelector = hostProtoView.elementBinders[0].componentDirective.metadata.selector;
|
||||
}
|
||||
var renderViewWithFragments = this._renderer.createRootHostView(
|
||||
hostProtoView.render, hostProtoView.mergeInfo.embeddedViewCount + 1, hostElementSelector);
|
||||
var hostView = this._createMainView(hostProtoView, renderViewWithFragments);
|
||||
|
||||
this._renderer.hydrateView(hostView.render);
|
||||
this._utils.hydrateRootHostView(hostView, injector);
|
||||
return wtfLeave(s, hostView.ref);
|
||||
var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
|
||||
var selector = isPresent(overrideSelector) ? overrideSelector : hostViewFactory.selector;
|
||||
var view = hostViewFactory.viewFactory(this._renderer, this, null, projectableNodes, selector,
|
||||
null, injector);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()');
|
||||
|
||||
destroyRootHostView(hostViewRef: HostViewRef) {
|
||||
// Note: Don't put the hostView into the view pool
|
||||
// as it is depending on the element for which it was created.
|
||||
destroyRootHostView(hostViewRef: ViewRef_) {
|
||||
var s = this._destroyRootHostViewScope();
|
||||
var hostView = internalView(<ViewRef>hostViewRef);
|
||||
this._renderer.detachFragment(hostView.renderFragment);
|
||||
this._renderer.dehydrateView(hostView.render);
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
this._viewListener.onViewDestroyed(hostView);
|
||||
this._renderer.destroyView(hostView.render);
|
||||
var hostView = hostViewRef.internalView;
|
||||
hostView.renderer.detachView(flattenNestedViewRenderNodes(hostView.rootNodesOrAppElements));
|
||||
hostView.destroy();
|
||||
wtfLeave(s);
|
||||
}
|
||||
|
||||
@ -260,97 +248,43 @@ export class AppViewManager_ extends AppViewManager {
|
||||
_createEmbeddedViewInContainerScope: WtfScopeFn =
|
||||
wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()');
|
||||
|
||||
createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
templateRef: TemplateRef): ViewRef {
|
||||
createEmbeddedViewInContainer(viewContainerLocation: ElementRef_, index: number,
|
||||
templateRef: TemplateRef_): EmbeddedViewRef {
|
||||
var s = this._createEmbeddedViewInContainerScope();
|
||||
var protoView = internalProtoView((<TemplateRef_>templateRef).protoViewRef);
|
||||
if (protoView.type !== viewModule.ViewType.EMBEDDED) {
|
||||
throw new BaseException('This method can only be called with embedded ProtoViews!');
|
||||
}
|
||||
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
|
||||
return wtfLeave(s, this._createViewInContainer(viewContainerLocation, index, protoView,
|
||||
templateRef.elementRef, null));
|
||||
var contextEl = templateRef.elementRef.internalElement;
|
||||
var view: AppView =
|
||||
contextEl.embeddedViewFactory(contextEl.parentView.renderer, this, contextEl,
|
||||
contextEl.parentView.projectableNodes, null, null, null);
|
||||
this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createHostViewInContainerScope: WtfScopeFn =
|
||||
wtfCreateScope('AppViewManager#createHostViewInContainer()');
|
||||
|
||||
createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
protoViewRef: ProtoViewRef,
|
||||
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef {
|
||||
createHostViewInContainer(viewContainerLocation: ElementRef_, index: number,
|
||||
hostViewFactoryRef: HostViewFactoryRef_,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[],
|
||||
projectableNodes: any[][]): HostViewRef {
|
||||
var s = this._createHostViewInContainerScope();
|
||||
var protoView = internalProtoView(protoViewRef);
|
||||
if (protoView.type !== viewModule.ViewType.HOST) {
|
||||
throw new BaseException('This method can only be called with host ProtoViews!');
|
||||
}
|
||||
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
|
||||
return wtfLeave(
|
||||
s, this._createViewInContainer(viewContainerLocation, index, protoView,
|
||||
viewContainerLocation, imperativelyCreatedInjector));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* See {@link AppViewManager#destroyViewInContainer}.
|
||||
* @internal
|
||||
*/
|
||||
_createViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
protoView: viewModule.AppProtoView, context: ElementRef,
|
||||
imperativelyCreatedInjector: ResolvedProvider[]): ViewRef {
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
var contextView = internalView((<ElementRef_>context).parentView);
|
||||
var contextBoundElementIndex = (<ElementRef_>context).boundElementIndex;
|
||||
var embeddedFragmentView = contextView.getNestedView(contextBoundElementIndex);
|
||||
var view;
|
||||
if (protoView.type === viewModule.ViewType.EMBEDDED && isPresent(embeddedFragmentView) &&
|
||||
!embeddedFragmentView.hydrated()) {
|
||||
// Case 1: instantiate the first view of a template that has been merged into a parent
|
||||
view = embeddedFragmentView;
|
||||
this._attachRenderView(parentView, boundElementIndex, index, view);
|
||||
} else {
|
||||
// Case 2: instantiate another copy of the template or a host ProtoView.
|
||||
// This is a separate case
|
||||
// as we only inline one copy of the template into the parent view.
|
||||
view = this._createPooledView(protoView);
|
||||
this._attachRenderView(parentView, boundElementIndex, index, view);
|
||||
this._renderer.hydrateView(view.render);
|
||||
}
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
|
||||
contextBoundElementIndex, index, view);
|
||||
|
||||
try {
|
||||
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
|
||||
contextBoundElementIndex, index,
|
||||
imperativelyCreatedInjector);
|
||||
} catch (e) {
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
|
||||
throw e;
|
||||
}
|
||||
return view.ref;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_attachRenderView(parentView: viewModule.AppView, boundElementIndex: number, index: number,
|
||||
view: viewModule.AppView) {
|
||||
var elementRef = parentView.elementRefs[boundElementIndex];
|
||||
if (index === 0) {
|
||||
this._renderer.attachFragmentAfterElement(elementRef, view.renderFragment);
|
||||
} else {
|
||||
var prevView = parentView.viewContainers[boundElementIndex].views[index - 1];
|
||||
this._renderer.attachFragmentAfterFragment(prevView.renderFragment, view.renderFragment);
|
||||
}
|
||||
// TODO(tbosch): This should be specifiable via an additional argument!
|
||||
var contextEl = viewContainerLocation.internalElement;
|
||||
var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
|
||||
var view = hostViewFactory.viewFactory(
|
||||
contextEl.parentView.renderer, contextEl.parentView.viewManager, contextEl,
|
||||
projectableNodes, null, dynamicallyCreatedProviders, null);
|
||||
this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyViewInContainerScope = wtfCreateScope('AppViewMananger#destroyViewInContainer()');
|
||||
|
||||
destroyViewInContainer(viewContainerLocation: ElementRef, index: number) {
|
||||
destroyViewInContainer(viewContainerLocation: ElementRef_, index: number) {
|
||||
var s = this._destroyViewInContainerScope();
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
this._destroyViewInContainer(parentView, boundElementIndex, index);
|
||||
var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
|
||||
view.destroy();
|
||||
wtfLeave(s);
|
||||
}
|
||||
|
||||
@ -358,20 +292,10 @@ export class AppViewManager_ extends AppViewManager {
|
||||
_attachViewInContainerScope = wtfCreateScope('AppViewMananger#attachViewInContainer()');
|
||||
|
||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||
attachViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
viewRef: ViewRef): ViewRef {
|
||||
attachViewInContainer(viewContainerLocation: ElementRef_, index: number,
|
||||
viewRef: ViewRef_): EmbeddedViewRef {
|
||||
var s = this._attachViewInContainerScope();
|
||||
var view = internalView(viewRef);
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
// TODO(tbosch): the public methods attachViewInContainer/detachViewInContainer
|
||||
// are used for moving elements without the same container.
|
||||
// We will change this into an atomic `move` operation, which should preserve the
|
||||
// previous parent injector (see https://github.com/angular/angular/issues/1377).
|
||||
// Right now we are destroying any special
|
||||
// context view that might have been used.
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, index, view);
|
||||
this._attachRenderView(parentView, boundElementIndex, index, view);
|
||||
this._attachViewToContainer(viewRef.internalView, viewContainerLocation.internalElement, index);
|
||||
return wtfLeave(s, viewRef);
|
||||
}
|
||||
|
||||
@ -379,88 +303,72 @@ export class AppViewManager_ extends AppViewManager {
|
||||
_detachViewInContainerScope = wtfCreateScope('AppViewMananger#detachViewInContainer()');
|
||||
|
||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||
detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef {
|
||||
detachViewInContainer(viewContainerLocation: ElementRef_, index: number): EmbeddedViewRef {
|
||||
var s = this._detachViewInContainerScope();
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
|
||||
this._renderer.detachFragment(view.renderFragment);
|
||||
var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createMainView(protoView: viewModule.AppProtoView,
|
||||
renderViewWithFragments: RenderViewWithFragments): viewModule.AppView {
|
||||
var mergedParentView =
|
||||
this._utils.createView(protoView, renderViewWithFragments, this, this._renderer);
|
||||
this._renderer.setEventDispatcher(mergedParentView.render, mergedParentView);
|
||||
this._viewListener.onViewCreated(mergedParentView);
|
||||
return mergedParentView;
|
||||
}
|
||||
onViewCreated(view: AppView) { this._viewListener.onViewCreated(view); }
|
||||
|
||||
/** @internal */
|
||||
_createPooledView(protoView: viewModule.AppProtoView): viewModule.AppView {
|
||||
var view = this._viewPool.getView(protoView);
|
||||
if (isBlank(view)) {
|
||||
view = this._createMainView(
|
||||
protoView,
|
||||
this._renderer.createView(protoView.render, protoView.mergeInfo.embeddedViewCount + 1));
|
||||
onViewDestroyed(view: AppView) { this._viewListener.onViewDestroyed(view); }
|
||||
|
||||
/** @internal */
|
||||
createRenderComponentType(encapsulation: ViewEncapsulation,
|
||||
styles: Array<string | any[]>): RenderComponentType {
|
||||
return new RenderComponentType(`${this._appId}-${this._nextCompTypeId++}`, encapsulation,
|
||||
styles);
|
||||
}
|
||||
|
||||
private _attachViewToContainer(view: AppView, vcAppElement: AppElement, viewIndex: number) {
|
||||
if (view.proto.type === ViewType.COMPONENT) {
|
||||
throw new BaseException(`Component views can't be moved!`);
|
||||
}
|
||||
var nestedViews = vcAppElement.nestedViews;
|
||||
if (nestedViews == null) {
|
||||
nestedViews = [];
|
||||
vcAppElement.nestedViews = nestedViews;
|
||||
}
|
||||
ListWrapper.insert(nestedViews, viewIndex, view);
|
||||
var refNode;
|
||||
if (viewIndex > 0) {
|
||||
var prevView = nestedViews[viewIndex - 1];
|
||||
refNode = prevView.rootNodesOrAppElements.length > 0 ?
|
||||
prevView.rootNodesOrAppElements[prevView.rootNodesOrAppElements.length - 1] :
|
||||
null;
|
||||
} else {
|
||||
refNode = vcAppElement.nativeElement;
|
||||
}
|
||||
if (isPresent(refNode)) {
|
||||
var refRenderNode;
|
||||
if (refNode instanceof AppElement) {
|
||||
refRenderNode = (<AppElement>refNode).nativeElement;
|
||||
} else {
|
||||
refRenderNode = refNode;
|
||||
}
|
||||
view.renderer.attachViewAfter(refRenderNode,
|
||||
flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
|
||||
}
|
||||
// TODO: This is only needed when a view is destroyed,
|
||||
// not when it is detached for reordering with ng-for...
|
||||
vcAppElement.parentView.changeDetector.addContentChild(view.changeDetector);
|
||||
vcAppElement.traverseAndSetQueriesAsDirty();
|
||||
}
|
||||
|
||||
private _detachViewInContainer(vcAppElement: AppElement, viewIndex: number): AppView {
|
||||
var view = ListWrapper.removeAt(vcAppElement.nestedViews, viewIndex);
|
||||
if (view.proto.type === ViewType.COMPONENT) {
|
||||
throw new BaseException(`Component views can't be moved!`);
|
||||
}
|
||||
vcAppElement.traverseAndSetQueriesAsDirty();
|
||||
|
||||
view.renderer.detachView(flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
|
||||
|
||||
// TODO: This is only needed when a view is destroyed,
|
||||
// not when it is detached for reordering with ng-for...
|
||||
view.changeDetector.remove();
|
||||
return view;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyPooledView(view: viewModule.AppView) {
|
||||
var wasReturned = this._viewPool.returnView(view);
|
||||
if (!wasReturned) {
|
||||
this._viewListener.onViewDestroyed(view);
|
||||
this._renderer.destroyView(view.render);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||
index: number) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
|
||||
this._viewDehydrateRecurse(view);
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
|
||||
if (view.viewOffset > 0) {
|
||||
// Case 1: a view that is part of another view.
|
||||
// Just detach the fragment
|
||||
this._renderer.detachFragment(view.renderFragment);
|
||||
} else {
|
||||
// Case 2: a view that is not part of another view.
|
||||
// dehydrate and destroy it.
|
||||
this._renderer.dehydrateView(view.render);
|
||||
this._renderer.detachFragment(view.renderFragment);
|
||||
this._destroyPooledView(view);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_viewDehydrateRecurse(view: viewModule.AppView) {
|
||||
if (view.hydrated()) {
|
||||
this._utils.dehydrateView(view);
|
||||
}
|
||||
var viewContainers = view.viewContainers;
|
||||
var startViewOffset = view.viewOffset;
|
||||
var endViewOffset = view.viewOffset + view.proto.mergeInfo.viewCount - 1;
|
||||
var elementOffset = view.elementOffset;
|
||||
for (var viewIdx = startViewOffset; viewIdx <= endViewOffset; viewIdx++) {
|
||||
var currView = view.views[viewIdx];
|
||||
for (var binderIdx = 0; binderIdx < currView.proto.elementBinders.length;
|
||||
binderIdx++, elementOffset++) {
|
||||
var vc = viewContainers[elementOffset];
|
||||
if (isPresent(vc)) {
|
||||
for (var j = vc.views.length - 1; j >= 0; j--) {
|
||||
this._destroyViewInContainer(currView, elementOffset, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,266 +0,0 @@
|
||||
import {Injector, Provider, Injectable, ResolvedProvider} from 'angular2/src/core/di';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import * as eli from './element_injector';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import * as viewModule from './view';
|
||||
import * as avmModule from './view_manager';
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {Renderer, RenderViewWithFragments} from 'angular2/src/core/render/api';
|
||||
import {Locals} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {Pipes} from 'angular2/src/core/pipes/pipes';
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManagerUtils {
|
||||
constructor() {}
|
||||
|
||||
getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
|
||||
var eli = parentView.elementInjectors[boundElementIndex];
|
||||
return eli.getComponent();
|
||||
}
|
||||
|
||||
createView(mergedParentViewProto: viewModule.AppProtoView,
|
||||
renderViewWithFragments: RenderViewWithFragments,
|
||||
viewManager: avmModule.AppViewManager, renderer: Renderer): viewModule.AppView {
|
||||
var renderFragments = renderViewWithFragments.fragmentRefs;
|
||||
var renderView = renderViewWithFragments.viewRef;
|
||||
|
||||
var elementCount = mergedParentViewProto.mergeInfo.elementCount;
|
||||
var viewCount = mergedParentViewProto.mergeInfo.viewCount;
|
||||
var elementRefs: ElementRef[] = ListWrapper.createFixedSize(elementCount);
|
||||
var viewContainers = ListWrapper.createFixedSize(elementCount);
|
||||
var preBuiltObjects: eli.PreBuiltObjects[] = ListWrapper.createFixedSize(elementCount);
|
||||
var elementInjectors: eli.ElementInjector[] = ListWrapper.createFixedSize(elementCount);
|
||||
var views = ListWrapper.createFixedSize(viewCount);
|
||||
|
||||
var elementOffset = 0;
|
||||
var textOffset = 0;
|
||||
var fragmentIdx = 0;
|
||||
var containerElementIndicesByViewIndex: number[] = ListWrapper.createFixedSize(viewCount);
|
||||
for (var viewOffset = 0; viewOffset < viewCount; viewOffset++) {
|
||||
var containerElementIndex = containerElementIndicesByViewIndex[viewOffset];
|
||||
var containerElementInjector =
|
||||
isPresent(containerElementIndex) ? elementInjectors[containerElementIndex] : null;
|
||||
var parentView =
|
||||
isPresent(containerElementInjector) ? preBuiltObjects[containerElementIndex].view : null;
|
||||
var protoView =
|
||||
isPresent(containerElementIndex) ?
|
||||
parentView.proto.elementBinders[containerElementIndex - parentView.elementOffset]
|
||||
.nestedProtoView :
|
||||
mergedParentViewProto;
|
||||
var renderFragment = null;
|
||||
if (viewOffset === 0 || protoView.type === viewModule.ViewType.EMBEDDED) {
|
||||
renderFragment = renderFragments[fragmentIdx++];
|
||||
}
|
||||
var currentView = new viewModule.AppView(renderer, protoView, viewOffset, elementOffset,
|
||||
textOffset, protoView.protoLocals, renderView,
|
||||
renderFragment, containerElementInjector);
|
||||
views[viewOffset] = currentView;
|
||||
if (isPresent(containerElementIndex)) {
|
||||
preBuiltObjects[containerElementIndex].nestedView = currentView;
|
||||
}
|
||||
var rootElementInjectors = [];
|
||||
var nestedViewOffset = viewOffset + 1;
|
||||
for (var binderIdx = 0; binderIdx < protoView.elementBinders.length; binderIdx++) {
|
||||
var binder = protoView.elementBinders[binderIdx];
|
||||
var boundElementIndex = elementOffset + binderIdx;
|
||||
var elementInjector = null;
|
||||
|
||||
if (isPresent(binder.nestedProtoView) && binder.nestedProtoView.isMergable) {
|
||||
containerElementIndicesByViewIndex[nestedViewOffset] = boundElementIndex;
|
||||
nestedViewOffset += binder.nestedProtoView.mergeInfo.viewCount;
|
||||
}
|
||||
|
||||
// elementInjectors and rootElementInjectors
|
||||
var protoElementInjector = binder.protoElementInjector;
|
||||
if (isPresent(protoElementInjector)) {
|
||||
if (isPresent(protoElementInjector.parent)) {
|
||||
var parentElementInjector =
|
||||
elementInjectors[elementOffset + protoElementInjector.parent.index];
|
||||
elementInjector = protoElementInjector.instantiate(parentElementInjector);
|
||||
} else {
|
||||
elementInjector = protoElementInjector.instantiate(null);
|
||||
rootElementInjectors.push(elementInjector);
|
||||
}
|
||||
}
|
||||
elementInjectors[boundElementIndex] = elementInjector;
|
||||
|
||||
// elementRefs
|
||||
var el = new ElementRef_(currentView.ref, boundElementIndex, renderer);
|
||||
elementRefs[el.boundElementIndex] = el;
|
||||
|
||||
// preBuiltObjects
|
||||
if (isPresent(elementInjector)) {
|
||||
var templateRef = isPresent(binder.nestedProtoView) &&
|
||||
binder.nestedProtoView.type === viewModule.ViewType.EMBEDDED ?
|
||||
new TemplateRef_(el) :
|
||||
null;
|
||||
preBuiltObjects[boundElementIndex] =
|
||||
new eli.PreBuiltObjects(viewManager, currentView, el, templateRef);
|
||||
}
|
||||
}
|
||||
currentView.init(protoView.changeDetectorFactory(currentView), elementInjectors,
|
||||
rootElementInjectors, preBuiltObjects, views, elementRefs, viewContainers);
|
||||
if (isPresent(parentView) && protoView.type === viewModule.ViewType.COMPONENT) {
|
||||
parentView.changeDetector.addViewChild(currentView.changeDetector);
|
||||
}
|
||||
elementOffset += protoView.elementBinders.length;
|
||||
textOffset += protoView.textBindingCount;
|
||||
}
|
||||
return views[0];
|
||||
}
|
||||
|
||||
hydrateRootHostView(hostView: viewModule.AppView, injector: Injector) {
|
||||
this._hydrateView(hostView, injector, null, new Object(), null);
|
||||
}
|
||||
|
||||
// Misnomer: this method is attaching next to the view container.
|
||||
attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||
index: number, view: viewModule.AppView) {
|
||||
if (isBlank(contextView)) {
|
||||
contextView = parentView;
|
||||
contextBoundElementIndex = boundElementIndex;
|
||||
}
|
||||
parentView.changeDetector.addContentChild(view.changeDetector);
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
if (isBlank(viewContainer)) {
|
||||
viewContainer = new viewModule.AppViewContainer();
|
||||
parentView.viewContainers[boundElementIndex] = viewContainer;
|
||||
}
|
||||
ListWrapper.insert(viewContainer.views, index, view);
|
||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
|
||||
|
||||
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
|
||||
if (isPresent(elementInjector.parent)) {
|
||||
view.rootElementInjectors[i].link(elementInjector.parent);
|
||||
}
|
||||
}
|
||||
elementInjector.traverseAndSetQueriesAsDirty();
|
||||
}
|
||||
|
||||
detachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number, index: number) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
|
||||
parentView.elementInjectors[boundElementIndex].traverseAndSetQueriesAsDirty();
|
||||
|
||||
view.changeDetector.remove();
|
||||
ListWrapper.removeAt(viewContainer.views, index);
|
||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
||||
var inj = view.rootElementInjectors[i];
|
||||
inj.unlink();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hydrateViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||
index: number, imperativelyCreatedProviders: ResolvedProvider[]) {
|
||||
if (isBlank(contextView)) {
|
||||
contextView = parentView;
|
||||
contextBoundElementIndex = boundElementIndex;
|
||||
}
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
|
||||
|
||||
var injector = isPresent(imperativelyCreatedProviders) ?
|
||||
Injector.fromResolvedProviders(imperativelyCreatedProviders) :
|
||||
null;
|
||||
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
|
||||
contextView.locals);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_hydrateView(initView: viewModule.AppView, imperativelyCreatedInjector: Injector,
|
||||
hostElementInjector: eli.ElementInjector, context: Object, parentLocals: Locals) {
|
||||
var viewIdx = initView.viewOffset;
|
||||
var endViewOffset = viewIdx + initView.proto.mergeInfo.viewCount - 1;
|
||||
while (viewIdx <= endViewOffset) {
|
||||
var currView = initView.views[viewIdx];
|
||||
var currProtoView = currView.proto;
|
||||
if (currView !== initView && currView.proto.type === viewModule.ViewType.EMBEDDED) {
|
||||
// Don't hydrate components of embedded fragment views.
|
||||
viewIdx += currView.proto.mergeInfo.viewCount;
|
||||
} else {
|
||||
if (currView !== initView) {
|
||||
// hydrate a nested component view
|
||||
imperativelyCreatedInjector = null;
|
||||
parentLocals = null;
|
||||
hostElementInjector = currView.containerElementInjector;
|
||||
context = hostElementInjector.getComponent();
|
||||
}
|
||||
currView.context = context;
|
||||
currView.locals.parent = parentLocals;
|
||||
var binders = currProtoView.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var boundElementIndex = binderIdx + currView.elementOffset;
|
||||
var elementInjector = initView.elementInjectors[boundElementIndex];
|
||||
|
||||
if (isPresent(elementInjector)) {
|
||||
elementInjector.hydrate(imperativelyCreatedInjector, hostElementInjector,
|
||||
currView.preBuiltObjects[boundElementIndex]);
|
||||
this._populateViewLocals(currView, elementInjector, boundElementIndex);
|
||||
this._setUpEventEmitters(currView, elementInjector, boundElementIndex);
|
||||
}
|
||||
}
|
||||
var pipes = isPresent(hostElementInjector) ?
|
||||
new Pipes(currView.proto.pipes, hostElementInjector.getInjector()) :
|
||||
null;
|
||||
currView.changeDetector.hydrate(currView.context, currView.locals, currView, pipes);
|
||||
viewIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_populateViewLocals(view: viewModule.AppView, elementInjector: eli.ElementInjector,
|
||||
boundElementIdx: number): void {
|
||||
if (isPresent(elementInjector.getDirectiveVariableBindings())) {
|
||||
elementInjector.getDirectiveVariableBindings().forEach((directiveIndex, name) => {
|
||||
if (isBlank(directiveIndex)) {
|
||||
view.locals.set(name, view.elementRefs[boundElementIdx].nativeElement);
|
||||
} else {
|
||||
view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setUpEventEmitters(view: viewModule.AppView, elementInjector: eli.ElementInjector,
|
||||
boundElementIndex: number) {
|
||||
var emitters = elementInjector.getEventEmitterAccessors();
|
||||
for (var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
|
||||
var directiveEmitters = emitters[directiveIndex];
|
||||
var directive = elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||
|
||||
for (var eventIndex = 0; eventIndex < directiveEmitters.length; ++eventIndex) {
|
||||
var eventEmitterAccessor = directiveEmitters[eventIndex];
|
||||
eventEmitterAccessor.subscribe(view, boundElementIndex, directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dehydrateView(initView: viewModule.AppView) {
|
||||
var endViewOffset = initView.viewOffset + initView.proto.mergeInfo.viewCount - 1;
|
||||
for (var viewIdx = initView.viewOffset; viewIdx <= endViewOffset; viewIdx++) {
|
||||
var currView = initView.views[viewIdx];
|
||||
if (currView.hydrated()) {
|
||||
if (isPresent(currView.locals)) {
|
||||
currView.locals.clearValues();
|
||||
}
|
||||
currView.context = null;
|
||||
currView.changeDetector.dehydrate();
|
||||
var binders = currView.proto.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var eli = initView.elementInjectors[currView.elementOffset + binderIdx];
|
||||
if (isPresent(eli)) {
|
||||
eli.dehydrate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
|
||||
|
||||
import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
import * as viewModule from './view';
|
||||
|
||||
export const APP_VIEW_POOL_CAPACITY = CONST_EXPR(new OpaqueToken('AppViewPool.viewPoolCapacity'));
|
||||
|
||||
@Injectable()
|
||||
export class AppViewPool {
|
||||
/** @internal */
|
||||
_poolCapacityPerProtoView: number;
|
||||
/** @internal */
|
||||
_pooledViewsPerProtoView = new Map<viewModule.AppProtoView, Array<viewModule.AppView>>();
|
||||
|
||||
constructor(@Inject(APP_VIEW_POOL_CAPACITY) poolCapacityPerProtoView) {
|
||||
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
||||
}
|
||||
|
||||
getView(protoView: viewModule.AppProtoView): viewModule.AppView {
|
||||
var pooledViews = this._pooledViewsPerProtoView.get(protoView);
|
||||
if (isPresent(pooledViews) && pooledViews.length > 0) {
|
||||
return pooledViews.pop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
returnView(view: viewModule.AppView): boolean {
|
||||
var protoView = view.proto;
|
||||
var pooledViews = this._pooledViewsPerProtoView.get(protoView);
|
||||
if (isBlank(pooledViews)) {
|
||||
pooledViews = [];
|
||||
this._pooledViewsPerProtoView.set(protoView, pooledViews);
|
||||
}
|
||||
var haveRemainingCapacity = pooledViews.length < this._poolCapacityPerProtoView;
|
||||
if (haveRemainingCapacity) {
|
||||
pooledViews.push(view);
|
||||
}
|
||||
return haveRemainingCapacity;
|
||||
}
|
||||
}
|
@ -1,20 +1,16 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import * as viewModule from './view';
|
||||
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||
import {RenderViewRef, RenderFragmentRef} from 'angular2/src/core/render/api';
|
||||
import {AppView, HostViewFactory} from './view';
|
||||
|
||||
// This is a workaround for privacy in Dart as we don't have library parts
|
||||
export function internalView(viewRef: ViewRef): viewModule.AppView {
|
||||
return (<ViewRef_>viewRef)._view;
|
||||
export abstract class ViewRef {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); };
|
||||
|
||||
get destroyed(): boolean { return unimplemented(); }
|
||||
}
|
||||
|
||||
// This is a workaround for privacy in Dart as we don't have library parts
|
||||
export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppProtoView {
|
||||
return isPresent(protoViewRef) ? (<ProtoViewRef_>protoViewRef)._protoView : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a View containing a single Element that is the Host Element of a {@link Component}
|
||||
* instance.
|
||||
@ -24,11 +20,8 @@ export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppPro
|
||||
* of the higher-level APIs: {@link AppViewManager#createRootHostView},
|
||||
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
|
||||
*/
|
||||
export interface HostViewRef {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
changeDetectorRef: ChangeDetectorRef;
|
||||
export abstract class HostViewRef extends ViewRef {
|
||||
get rootNodes(): any[] { return unimplemented(); };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,96 +77,43 @@ export interface HostViewRef {
|
||||
* <!-- /ViewRef: outer-0 -->
|
||||
* ```
|
||||
*/
|
||||
export abstract class ViewRef implements HostViewRef {
|
||||
export abstract class EmbeddedViewRef extends ViewRef {
|
||||
/**
|
||||
* Sets `value` of local variable called `variableName` in this View.
|
||||
*/
|
||||
abstract setLocal(variableName: string, value: any): void;
|
||||
|
||||
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }
|
||||
set changeDetectorRef(value: ChangeDetectorRef) {
|
||||
unimplemented(); // TODO: https://github.com/Microsoft/TypeScript/issues/12
|
||||
}
|
||||
/**
|
||||
* Checks whether this view has a local variable called `variableName`.
|
||||
*/
|
||||
abstract hasLocal(variableName: string): boolean;
|
||||
|
||||
get rootNodes(): any[] { return unimplemented(); };
|
||||
}
|
||||
|
||||
export class ViewRef_ extends ViewRef {
|
||||
private _changeDetectorRef: ChangeDetectorRef = null;
|
||||
/** @internal */
|
||||
public _view: viewModule.AppView;
|
||||
constructor(_view: viewModule.AppView) {
|
||||
super();
|
||||
this._view = _view;
|
||||
}
|
||||
export class ViewRef_ implements EmbeddedViewRef, HostViewRef {
|
||||
constructor(private _view: AppView) { this._view = _view; }
|
||||
|
||||
/**
|
||||
* Return `RenderViewRef`
|
||||
*/
|
||||
get render(): RenderViewRef { return this._view.render; }
|
||||
|
||||
/**
|
||||
* Return `RenderFragmentRef`
|
||||
*/
|
||||
get renderFragment(): RenderFragmentRef { return this._view.renderFragment; }
|
||||
get internalView(): AppView { return this._view; }
|
||||
|
||||
/**
|
||||
* Return `ChangeDetectorRef`
|
||||
*/
|
||||
get changeDetectorRef(): ChangeDetectorRef {
|
||||
if (this._changeDetectorRef === null) {
|
||||
this._changeDetectorRef = this._view.changeDetector.ref;
|
||||
}
|
||||
return this._changeDetectorRef;
|
||||
}
|
||||
get changeDetectorRef(): ChangeDetectorRef { return this._view.changeDetector.ref; }
|
||||
|
||||
get rootNodes(): any[] { return this._view.flatRootNodes; }
|
||||
|
||||
setLocal(variableName: string, value: any): void { this._view.setLocal(variableName, value); }
|
||||
|
||||
hasLocal(variableName: string): boolean { return this._view.hasLocal(variableName); }
|
||||
|
||||
get destroyed(): boolean { return this._view.destroyed; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an Angular ProtoView.
|
||||
*
|
||||
* A ProtoView is a prototypical {@link ViewRef View} that is the result of Template compilation and
|
||||
* is used by Angular to efficiently create an instance of this View based on the compiled Template.
|
||||
*
|
||||
* Most ProtoViews are created and used internally by Angular and you don't need to know about them,
|
||||
* except in advanced use-cases where you compile components yourself via the low-level
|
||||
* {@link Compiler#compileInHost} API.
|
||||
*
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* Given this template:
|
||||
*
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <li *ngFor="var item of items">{{item}}</li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Angular desugars and compiles the template into two ProtoViews:
|
||||
*
|
||||
* Outer ProtoView:
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <template ngFor var-item [ngForOf]="items"></template>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Inner ProtoView:
|
||||
* ```
|
||||
* <li>{{item}}</li>
|
||||
* ```
|
||||
*
|
||||
* Notice that the original template is broken down into two separate ProtoViews.
|
||||
*/
|
||||
export abstract class ProtoViewRef {}
|
||||
export abstract class HostViewFactoryRef {}
|
||||
|
||||
export class ProtoViewRef_ extends ProtoViewRef {
|
||||
/** @internal */
|
||||
public _protoView: viewModule.AppProtoView;
|
||||
constructor(_protoView: viewModule.AppProtoView) {
|
||||
super();
|
||||
this._protoView = _protoView;
|
||||
}
|
||||
}
|
||||
export class HostViewFactoryRef_ implements HostViewFactoryRef {
|
||||
constructor(private _hostViewFactory: HostViewFactory) {}
|
||||
|
||||
get internalHostViewFactory(): HostViewFactory { return this._hostViewFactory; }
|
||||
}
|
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 {
|
||||
var cached = StringMapWrapper.get(this._config, name);
|
||||
if (isPresent(cached)) return cached;
|
||||
|
||||
var p = this.proto.get(name);
|
||||
var transform = this.injector.instantiateResolved(p);
|
||||
var res = new cd.SelectedPipe(transform, p.pure);
|
||||
|
@ -1,19 +1,2 @@
|
||||
// Public API for render
|
||||
export {
|
||||
RenderEventDispatcher,
|
||||
Renderer,
|
||||
RenderElementRef,
|
||||
RenderViewRef,
|
||||
RenderProtoViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderViewWithFragments,
|
||||
RenderTemplateCmd,
|
||||
RenderCommandVisitor,
|
||||
RenderTextCmd,
|
||||
RenderNgContentCmd,
|
||||
RenderBeginElementCmd,
|
||||
RenderBeginComponentCmd,
|
||||
RenderEmbeddedTemplateCmd,
|
||||
RenderBeginCmd,
|
||||
RenderComponentTemplate
|
||||
} from './render/api';
|
||||
export {RootRenderer, Renderer, RenderComponentType} from './render/api';
|
||||
|
@ -1,196 +1,54 @@
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {Map} from 'angular2/src/facade/collection';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
|
||||
/**
|
||||
* Represents an Angular ProtoView in the Rendering Context.
|
||||
*
|
||||
* When you implement a custom {@link Renderer}, `RenderProtoViewRef` specifies what Render View
|
||||
* your renderer should create.
|
||||
*
|
||||
* `RenderProtoViewRef` is a counterpart to {@link ProtoViewRef} available in the Application
|
||||
* Context. But unlike `ProtoViewRef`, `RenderProtoViewRef` contains all static nested Proto Views
|
||||
* that are recursively merged into a single Render Proto View.
|
||||
|
||||
*
|
||||
* <!-- TODO: this is created by Renderer#createProtoView in the new compiler -->
|
||||
*/
|
||||
export class RenderProtoViewRef {}
|
||||
|
||||
/**
|
||||
* Represents a list of sibling Nodes that can be moved by the {@link Renderer} independently of
|
||||
* other Render Fragments.
|
||||
*
|
||||
* Any {@link RenderViewRef} has one Render Fragment.
|
||||
*
|
||||
* Additionally any View with an Embedded View that contains a {@link NgContentAst View Projection}
|
||||
* results in additional Render Fragment.
|
||||
*/
|
||||
/*
|
||||
<div>foo</div>
|
||||
{{bar}}
|
||||
|
||||
|
||||
<div>foo</div> -> view 1 / fragment 1
|
||||
<ul>
|
||||
<template ngFor>
|
||||
<li>{{fg}}</li> -> view 2 / fragment 1
|
||||
</template>
|
||||
</ul>
|
||||
{{bar}}
|
||||
|
||||
|
||||
<div>foo</div> -> view 1 / fragment 1
|
||||
<ul>
|
||||
<template ngIf>
|
||||
<li><ng-content></></li> -> view 1 / fragment 2
|
||||
</template>
|
||||
<template ngFor>
|
||||
<li><ng-content></></li> ->
|
||||
<li></li> -> view 1 / fragment 2 + view 2 / fragment 1..n-1
|
||||
</template>
|
||||
</ul>
|
||||
{{bar}}
|
||||
*/
|
||||
// TODO(i): refactor into an interface
|
||||
export class RenderFragmentRef {}
|
||||
|
||||
|
||||
/**
|
||||
* Represents an Angular View in the Rendering Context.
|
||||
*
|
||||
* `RenderViewRef` specifies to the {@link Renderer} what View to update or destroy.
|
||||
*
|
||||
* Unlike a {@link ViewRef} available in the Application Context, Render View contains all the
|
||||
* static Component Views that have been recursively merged into a single Render View.
|
||||
*
|
||||
* Each `RenderViewRef` contains one or more {@link RenderFragmentRef Render Fragments}, these
|
||||
* Fragments are created, hydrated, dehydrated and destroyed as a single unit together with the
|
||||
* View.
|
||||
*/
|
||||
// TODO(i): refactor into an interface
|
||||
export class RenderViewRef {}
|
||||
|
||||
/**
|
||||
* Abstract base class for commands to the Angular renderer, using the visitor pattern.
|
||||
*/
|
||||
export abstract class RenderTemplateCmd {
|
||||
abstract visit(visitor: RenderCommandVisitor, context: any): any;
|
||||
export class RenderComponentType {
|
||||
constructor(public id: string, public encapsulation: ViewEncapsulation,
|
||||
public styles: Array<string | any[]>) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Command to begin rendering.
|
||||
*/
|
||||
export abstract class RenderBeginCmd extends RenderTemplateCmd {
|
||||
get ngContentIndex(): number { return unimplemented(); };
|
||||
get isBound(): boolean { return unimplemented(); };
|
||||
}
|
||||
export interface ParentRenderer { renderComponent(componentType: RenderComponentType): Renderer; }
|
||||
|
||||
/**
|
||||
* Command to render text.
|
||||
*/
|
||||
export abstract class RenderTextCmd extends RenderBeginCmd {
|
||||
get value(): string { return unimplemented(); };
|
||||
}
|
||||
export abstract class Renderer implements ParentRenderer {
|
||||
abstract renderComponent(componentType: RenderComponentType): Renderer;
|
||||
|
||||
/**
|
||||
* Command to render projected content.
|
||||
*/
|
||||
export abstract class RenderNgContentCmd extends RenderTemplateCmd {
|
||||
// The index of this NgContent element
|
||||
get index(): number { return unimplemented(); };
|
||||
// The index of the NgContent element into which this
|
||||
// NgContent element should be projected (if any)
|
||||
get ngContentIndex(): number { return unimplemented(); };
|
||||
}
|
||||
abstract selectRootElement(selector: string): any;
|
||||
|
||||
/**
|
||||
* Command to begin rendering an element.
|
||||
*/
|
||||
export abstract class RenderBeginElementCmd extends RenderBeginCmd {
|
||||
get name(): string { return unimplemented(); };
|
||||
get attrNameAndValues(): string[] { return unimplemented(); };
|
||||
get eventTargetAndNames(): string[] { return unimplemented(); };
|
||||
}
|
||||
abstract createElement(parentElement: any, name: string): any;
|
||||
|
||||
/**
|
||||
* Command to begin rendering a component.
|
||||
*/
|
||||
export abstract class RenderBeginComponentCmd extends RenderBeginElementCmd {
|
||||
get templateId(): string { return unimplemented(); };
|
||||
}
|
||||
abstract createViewRoot(hostElement: any): any;
|
||||
|
||||
/**
|
||||
* Command to render a component's template.
|
||||
*/
|
||||
export abstract class RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
|
||||
get isMerged(): boolean { return unimplemented(); };
|
||||
get children(): RenderTemplateCmd[] { return unimplemented(); };
|
||||
}
|
||||
abstract createTemplateAnchor(parentElement: any): any;
|
||||
|
||||
/**
|
||||
* Visitor for a {@link RenderTemplateCmd}.
|
||||
*/
|
||||
export interface RenderCommandVisitor {
|
||||
visitText(cmd: RenderTextCmd, context: any): any;
|
||||
visitNgContent(cmd: RenderNgContentCmd, context: any): any;
|
||||
visitBeginElement(cmd: RenderBeginElementCmd, context: any): any;
|
||||
visitEndElement(context: any): any;
|
||||
visitBeginComponent(cmd: RenderBeginComponentCmd, context: any): any;
|
||||
visitEndComponent(context: any): any;
|
||||
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: any): any;
|
||||
}
|
||||
abstract createText(parentElement: any, value: string): any;
|
||||
|
||||
abstract projectNodes(parentElement: any, nodes: any[]);
|
||||
|
||||
/**
|
||||
* Container class produced by a {@link Renderer} when creating a Render View.
|
||||
*
|
||||
* An instance of `RenderViewWithFragments` contains a {@link RenderViewRef} and an array of
|
||||
* {@link RenderFragmentRef}s belonging to this Render View.
|
||||
*/
|
||||
// TODO(i): refactor this by RenderViewWithFragments and adding fragments directly to RenderViewRef
|
||||
export class RenderViewWithFragments {
|
||||
constructor(
|
||||
/**
|
||||
* Reference to the {@link RenderViewRef}.
|
||||
*/
|
||||
public viewRef: RenderViewRef,
|
||||
/**
|
||||
* Array of {@link RenderFragmentRef}s ordered in the depth-first order.
|
||||
*/
|
||||
public fragmentRefs: RenderFragmentRef[]) {}
|
||||
}
|
||||
abstract attachViewAfter(node: any, viewRootNodes: any[]);
|
||||
|
||||
/**
|
||||
* Represents an Element that is part of a {@link RenderViewRef Render View}.
|
||||
*
|
||||
* `RenderElementRef` is a counterpart to {@link ElementRef} available in the Application Context.
|
||||
*
|
||||
* When using `Renderer` from the Application Context, `ElementRef` can be used instead of
|
||||
* `RenderElementRef`.
|
||||
*/
|
||||
export interface RenderElementRef {
|
||||
/**
|
||||
* Reference to the Render View that contains this Element.
|
||||
*/
|
||||
renderView: RenderViewRef;
|
||||
abstract detachView(viewRootNodes: any[]);
|
||||
|
||||
abstract destroyView(hostElement: any, viewAllNodes: any[]);
|
||||
|
||||
abstract listen(renderElement: any, name: string, callback: Function);
|
||||
|
||||
abstract listenGlobal(target: string, name: string, callback: Function): Function;
|
||||
|
||||
abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: any);
|
||||
|
||||
abstract setElementAttribute(renderElement: any, attributeName: string, attributeValue: string);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Index of the Element (in the depth-first order) inside the Render View.
|
||||
*
|
||||
* This index is used internally by Angular to locate elements.
|
||||
* Used only in debug mode to serialize property changes to comment nodes,
|
||||
* such as <template> placeholders.
|
||||
*/
|
||||
boundElementIndex: number;
|
||||
}
|
||||
abstract setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string);
|
||||
|
||||
/**
|
||||
* Template for rendering a component, including commands and styles.
|
||||
*/
|
||||
export class RenderComponentTemplate {
|
||||
constructor(public id: string, public shortId: string, public encapsulation: ViewEncapsulation,
|
||||
public commands: RenderTemplateCmd[], public styles: string[]) {}
|
||||
abstract setElementClass(renderElement: any, className: string, isAdd: boolean);
|
||||
|
||||
abstract setElementStyle(renderElement: any, styleName: string, styleValue: string);
|
||||
|
||||
abstract invokeElementMethod(renderElement: any, methodName: string, args: any[]);
|
||||
|
||||
abstract setText(renderNode: any, text: string);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,184 +63,7 @@ export class RenderComponentTemplate {
|
||||
*
|
||||
* The default Renderer implementation is `DomRenderer`. Also available is `WebWorkerRenderer`.
|
||||
*/
|
||||
export abstract class Renderer {
|
||||
/**
|
||||
* Registers a component template represented as arrays of {@link RenderTemplateCmd}s and styles
|
||||
* with the Renderer.
|
||||
*
|
||||
* Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when
|
||||
* {@link #createProtoView creating Render ProtoView}.
|
||||
*/
|
||||
abstract registerComponentTemplate(template: RenderComponentTemplate);
|
||||
|
||||
/**
|
||||
* Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s.
|
||||
*/
|
||||
abstract createProtoView(componentTemplateId: string,
|
||||
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
|
||||
|
||||
/**
|
||||
* Creates a Root Host View based on the provided `hostProtoViewRef`.
|
||||
*
|
||||
* `fragmentCount` is the number of nested {@link RenderFragmentRef}s in this View. This parameter
|
||||
* is non-optional so that the renderer can create a result synchronously even when application
|
||||
* runs in a different context (e.g. in a Web Worker).
|
||||
*
|
||||
* `hostElementSelector` is a (CSS) selector for querying the main document to find the Host
|
||||
* Element. The newly created Root Host View should be attached to this element.
|
||||
*
|
||||
* Returns an instance of {@link RenderViewWithFragments}, representing the Render View.
|
||||
*/
|
||||
abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
|
||||
hostElementSelector: string): RenderViewWithFragments;
|
||||
|
||||
/**
|
||||
* Creates a Render View based on the provided `protoViewRef`.
|
||||
*
|
||||
* `fragmentCount` is the number of nested {@link RenderFragmentRef}s in this View. This parameter
|
||||
* is non-optional so that the renderer can create a result synchronously even when application
|
||||
* runs in a different context (e.g. in a Web Worker).
|
||||
*
|
||||
* Returns an instance of {@link RenderViewWithFragments}, representing the Render View.
|
||||
*/
|
||||
abstract createView(protoViewRef: RenderProtoViewRef,
|
||||
fragmentCount: number): RenderViewWithFragments;
|
||||
|
||||
/**
|
||||
* Destroys a Render View specified via `viewRef`.
|
||||
*
|
||||
* This operation should be performed only on a View that has already been dehydrated and
|
||||
* all of its Render Fragments have been detached.
|
||||
*
|
||||
* Destroying a View indicates to the Renderer that this View is not going to be referenced in any
|
||||
* future operations. If the Renderer created any renderer-specific objects for this View, these
|
||||
* objects should now be destroyed to prevent memory leaks.
|
||||
*/
|
||||
abstract destroyView(viewRef: RenderViewRef);
|
||||
|
||||
/**
|
||||
* Attaches the Nodes of a Render Fragment after the last Node of `previousFragmentRef`.
|
||||
*/
|
||||
abstract attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
|
||||
fragmentRef: RenderFragmentRef);
|
||||
|
||||
/**
|
||||
* Attaches the Nodes of the Render Fragment after an Element.
|
||||
*/
|
||||
abstract attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef);
|
||||
|
||||
/**
|
||||
* Detaches the Nodes of a Render Fragment from their parent.
|
||||
*
|
||||
* This operations should be called only on a View that has been already
|
||||
* {@link #dehydrateView dehydrated}.
|
||||
*/
|
||||
abstract detachFragment(fragmentRef: RenderFragmentRef);
|
||||
|
||||
/**
|
||||
* Notifies a custom Renderer to initialize a Render View.
|
||||
*
|
||||
* This method is called by Angular after a Render View has been created, or when a previously
|
||||
* dehydrated Render View is about to be reused.
|
||||
*/
|
||||
abstract hydrateView(viewRef: RenderViewRef);
|
||||
|
||||
/**
|
||||
* Notifies a custom Renderer that a Render View is no longer active.
|
||||
*
|
||||
* This method is called by Angular before a Render View will be destroyed, or when a hydrated
|
||||
* Render View is about to be put into a pool for future reuse.
|
||||
*/
|
||||
abstract dehydrateView(viewRef: RenderViewRef);
|
||||
|
||||
/**
|
||||
* Returns the underlying native element at the specified `location`, or `null` if direct access
|
||||
* to native elements is not supported (e.g. when the application runs in a web worker).
|
||||
*
|
||||
* <div class="callout is-critical">
|
||||
* <header>Use with caution</header>
|
||||
* <p>
|
||||
* Use this api as the last resort when direct access to DOM is needed. Use templating and
|
||||
* data-binding, or other {@link Renderer} methods instead.
|
||||
* </p>
|
||||
* <p>
|
||||
* Relying on direct DOM access creates tight coupling between your application and rendering
|
||||
* layers which will make it impossible to separate the two and deploy your application into a
|
||||
* web worker.
|
||||
* </p>
|
||||
* </div>
|
||||
*/
|
||||
abstract getNativeElementSync(location: RenderElementRef): any;
|
||||
|
||||
/**
|
||||
* Sets a property on the Element specified via `location`.
|
||||
*/
|
||||
abstract setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any);
|
||||
|
||||
/**
|
||||
* Sets an attribute on the Element specified via `location`.
|
||||
*
|
||||
* If `attributeValue` is `null`, the attribute is removed.
|
||||
*/
|
||||
abstract setElementAttribute(location: RenderElementRef, attributeName: string,
|
||||
attributeValue: string);
|
||||
|
||||
abstract setBindingDebugInfo(location: RenderElementRef, propertyName: string,
|
||||
propertyValue: string);
|
||||
|
||||
/**
|
||||
* Sets a (CSS) class on the Element specified via `location`.
|
||||
*
|
||||
* `isAdd` specifies if the class should be added or removed.
|
||||
*/
|
||||
abstract setElementClass(location: RenderElementRef, className: string, isAdd: boolean);
|
||||
|
||||
/**
|
||||
* Sets a (CSS) inline style on the Element specified via `location`.
|
||||
*
|
||||
* If `styleValue` is `null`, the style is removed.
|
||||
*/
|
||||
abstract setElementStyle(location: RenderElementRef, styleName: string, styleValue: string);
|
||||
|
||||
/**
|
||||
* Calls a method on the Element specified via `location`.
|
||||
*/
|
||||
abstract invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]);
|
||||
|
||||
/**
|
||||
* Sets the value of an interpolated TextNode at the specified index to the `text` value.
|
||||
*
|
||||
* `textNodeIndex` is the depth-first index of the Node among interpolated Nodes in the Render
|
||||
* View.
|
||||
*/
|
||||
abstract setText(viewRef: RenderViewRef, textNodeIndex: number, text: string);
|
||||
|
||||
/**
|
||||
* Sets a dispatcher to relay all events triggered in the given Render View.
|
||||
*
|
||||
* Each Render View can have only one Event Dispatcher, if this method is called multiple times,
|
||||
* the last provided dispatcher will be used.
|
||||
*/
|
||||
abstract setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* A dispatcher that relays all events that occur in a Render View.
|
||||
*
|
||||
* Use {@link Renderer#setEventDispatcher} to register a dispatcher for a particular Render View.
|
||||
*/
|
||||
export interface RenderEventDispatcher {
|
||||
/**
|
||||
* Called when Event called `eventName` was triggered on an Element with an Event Binding for this
|
||||
* Event.
|
||||
*
|
||||
* `elementIndex` specifies the depth-first index of the Element in the Render View.
|
||||
*
|
||||
* `locals` is a map for local variable to value mapping that should be used when evaluating the
|
||||
* Event Binding expression.
|
||||
*
|
||||
* Returns `false` if `preventDefault` should be called to stop the default behavior of the Event
|
||||
* in the Rendering Context.
|
||||
*/
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>): boolean;
|
||||
export abstract class RootRenderer implements ParentRenderer {
|
||||
abstract renderComponent(componentType: RenderComponentType): Renderer;
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
RenderComponentTemplate,
|
||||
RenderViewRef,
|
||||
RenderEventDispatcher,
|
||||
RenderTemplateCmd,
|
||||
RenderProtoViewRef,
|
||||
RenderFragmentRef
|
||||
} from './api';
|
||||
|
||||
export class DefaultProtoViewRef extends RenderProtoViewRef {
|
||||
constructor(public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
export class DefaultRenderFragmentRef<N> extends RenderFragmentRef {
|
||||
constructor(public nodes: N[]) { super(); }
|
||||
}
|
||||
|
||||
export class DefaultRenderView<N> extends RenderViewRef {
|
||||
hydrated: boolean = false;
|
||||
eventDispatcher: RenderEventDispatcher = null;
|
||||
globalEventRemovers: Function[] = null;
|
||||
|
||||
constructor(public fragments: DefaultRenderFragmentRef<N>[], public boundTextNodes: N[],
|
||||
public boundElements: N[], public nativeShadowRoots: N[],
|
||||
public globalEventAdders: Function[], public rootContentInsertionPoints: N[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
hydrate() {
|
||||
if (this.hydrated) throw new BaseException('The view is already hydrated.');
|
||||
this.hydrated = true;
|
||||
this.globalEventRemovers = ListWrapper.createFixedSize(this.globalEventAdders.length);
|
||||
for (var i = 0; i < this.globalEventAdders.length; i++) {
|
||||
this.globalEventRemovers[i] = this.globalEventAdders[i]();
|
||||
}
|
||||
}
|
||||
|
||||
dehydrate() {
|
||||
if (!this.hydrated) throw new BaseException('The view is already dehydrated.');
|
||||
for (var i = 0; i < this.globalEventRemovers.length; i++) {
|
||||
this.globalEventRemovers[i]();
|
||||
}
|
||||
this.globalEventRemovers = null;
|
||||
this.hydrated = false;
|
||||
}
|
||||
|
||||
setEventDispatcher(dispatcher: RenderEventDispatcher) { this.eventDispatcher = dispatcher; }
|
||||
|
||||
dispatchRenderEvent(boundElementIndex: number, eventName: string, event: any): boolean {
|
||||
var allowDefaultBehavior = true;
|
||||
if (isPresent(this.eventDispatcher)) {
|
||||
var locals = new Map<string, any>();
|
||||
locals.set('$event', event);
|
||||
allowDefaultBehavior =
|
||||
this.eventDispatcher.dispatchRenderEvent(boundElementIndex, eventName, locals);
|
||||
}
|
||||
return allowDefaultBehavior;
|
||||
}
|
||||
}
|
@ -1,321 +0,0 @@
|
||||
import {isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
RenderEventDispatcher,
|
||||
RenderTemplateCmd,
|
||||
RenderCommandVisitor,
|
||||
RenderBeginElementCmd,
|
||||
RenderBeginComponentCmd,
|
||||
RenderNgContentCmd,
|
||||
RenderTextCmd,
|
||||
RenderEmbeddedTemplateCmd,
|
||||
RenderComponentTemplate
|
||||
} from './api';
|
||||
import {DefaultRenderView, DefaultRenderFragmentRef} from './view';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
|
||||
export function encapsulateStyles(componentTemplate: RenderComponentTemplate): string[] {
|
||||
var processedStyles = componentTemplate.styles;
|
||||
if (componentTemplate.encapsulation === ViewEncapsulation.Emulated) {
|
||||
processedStyles = ListWrapper.createFixedSize(componentTemplate.styles.length);
|
||||
for (var i = 0; i < componentTemplate.styles.length; i++) {
|
||||
processedStyles[i] = StringWrapper.replaceAll(componentTemplate.styles[i], COMPONENT_REGEX,
|
||||
componentTemplate.shortId);
|
||||
}
|
||||
}
|
||||
return processedStyles;
|
||||
}
|
||||
|
||||
export function createRenderView(componentTemplate: RenderComponentTemplate,
|
||||
cmds: RenderTemplateCmd[], inplaceElement: any,
|
||||
nodeFactory: NodeFactory<any>): DefaultRenderView<any> {
|
||||
var view: DefaultRenderView<any>;
|
||||
var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) =>
|
||||
view.dispatchRenderEvent(boundElementIndex, eventName, event);
|
||||
var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement);
|
||||
context.build(componentTemplate, cmds);
|
||||
var fragments: DefaultRenderFragmentRef<any>[] = [];
|
||||
for (var i = 0; i < context.fragments.length; i++) {
|
||||
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
|
||||
}
|
||||
view = new DefaultRenderView<any>(fragments, context.boundTextNodes, context.boundElements,
|
||||
context.nativeShadowRoots, context.globalEventAdders,
|
||||
context.rootContentInsertionPoints);
|
||||
return view;
|
||||
}
|
||||
|
||||
export interface NodeFactory<N> {
|
||||
resolveComponentTemplate(templateId: string): RenderComponentTemplate;
|
||||
createTemplateAnchor(attrNameAndValues: string[]): N;
|
||||
createElement(name: string, attrNameAndValues: string[]): N;
|
||||
createRootContentInsertionPoint(): N;
|
||||
mergeElement(existing: N, attrNameAndValues: string[]);
|
||||
createShadowRoot(host: N, templateId: string): N;
|
||||
createText(value: string): N;
|
||||
appendChild(parent: N, child: N);
|
||||
on(element: N, eventName: string, callback: Function);
|
||||
globalOn(target: string, eventName: string, callback: Function): Function;
|
||||
}
|
||||
|
||||
class BuildContext<N> {
|
||||
constructor(private _eventDispatcher: Function, public factory: NodeFactory<N>,
|
||||
private _inplaceElement: N) {
|
||||
this.isHost = isPresent((_inplaceElement));
|
||||
}
|
||||
private _builders: RenderViewBuilder<N>[] = [];
|
||||
|
||||
globalEventAdders: Function[] = [];
|
||||
boundElements: N[] = [];
|
||||
boundTextNodes: N[] = [];
|
||||
nativeShadowRoots: N[] = [];
|
||||
fragments: N[][] = [];
|
||||
rootContentInsertionPoints: N[] = [];
|
||||
componentCount: number = 0;
|
||||
isHost: boolean;
|
||||
|
||||
build(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
|
||||
this.enqueueRootBuilder(template, cmds);
|
||||
this._build(this._builders[0]);
|
||||
}
|
||||
|
||||
private _build(builder: RenderViewBuilder<N>) {
|
||||
this._builders = [];
|
||||
builder.build(this);
|
||||
var enqueuedBuilders = this._builders;
|
||||
for (var i = 0; i < enqueuedBuilders.length; i++) {
|
||||
this._build(enqueuedBuilders[i]);
|
||||
}
|
||||
}
|
||||
|
||||
enqueueComponentBuilder(component: Component<N>) {
|
||||
this.componentCount++;
|
||||
this._builders.push(
|
||||
new RenderViewBuilder<N>(component, null, component.template, component.template.commands));
|
||||
}
|
||||
|
||||
enqueueFragmentBuilder(parentComponent: Component<N>, parentTemplate: RenderComponentTemplate,
|
||||
commands: RenderTemplateCmd[]) {
|
||||
var rootNodes = [];
|
||||
this.fragments.push(rootNodes);
|
||||
this._builders.push(
|
||||
new RenderViewBuilder<N>(parentComponent, rootNodes, parentTemplate, commands));
|
||||
}
|
||||
|
||||
enqueueRootBuilder(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
|
||||
var rootNodes = [];
|
||||
this.fragments.push(rootNodes);
|
||||
this._builders.push(new RenderViewBuilder<N>(null, rootNodes, template, cmds));
|
||||
}
|
||||
|
||||
consumeInplaceElement(): N {
|
||||
var result = this._inplaceElement;
|
||||
this._inplaceElement = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
addEventListener(boundElementIndex: number, target: string, eventName: string) {
|
||||
if (isPresent(target)) {
|
||||
var handler =
|
||||
createEventHandler(boundElementIndex, `${target}:${eventName}`, this._eventDispatcher);
|
||||
this.globalEventAdders.push(createGlobalEventAdder(target, eventName, handler, this.factory));
|
||||
} else {
|
||||
var handler = createEventHandler(boundElementIndex, eventName, this._eventDispatcher);
|
||||
this.factory.on(this.boundElements[boundElementIndex], eventName, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createEventHandler(boundElementIndex: number, eventName: string,
|
||||
eventDispatcher: Function): Function {
|
||||
return ($event) => eventDispatcher(boundElementIndex, eventName, $event);
|
||||
}
|
||||
|
||||
function createGlobalEventAdder(target: string, eventName: string, eventHandler: Function,
|
||||
nodeFactory: NodeFactory<any>): Function {
|
||||
return () => nodeFactory.globalOn(target, eventName, eventHandler);
|
||||
}
|
||||
|
||||
class RenderViewBuilder<N> implements RenderCommandVisitor {
|
||||
parentStack: Array<N | Component<N>>;
|
||||
|
||||
constructor(public parentComponent: Component<N>, public fragmentRootNodes: N[],
|
||||
public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
|
||||
var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot;
|
||||
this.parentStack = [rootNodesParent];
|
||||
}
|
||||
|
||||
build(context: BuildContext<N>) {
|
||||
var cmds = this.cmds;
|
||||
for (var i = 0; i < cmds.length; i++) {
|
||||
cmds[i].visit(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
get parent(): N | Component<N> { return this.parentStack[this.parentStack.length - 1]; }
|
||||
|
||||
visitText(cmd: RenderTextCmd, context: BuildContext<N>): any {
|
||||
var text = context.factory.createText(cmd.value);
|
||||
this._addChild(text, cmd.ngContentIndex, context);
|
||||
if (cmd.isBound) {
|
||||
context.boundTextNodes.push(text);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitNgContent(cmd: RenderNgContentCmd, context: BuildContext<N>): any {
|
||||
if (isPresent(this.parentComponent)) {
|
||||
if (this.parentComponent.isRoot) {
|
||||
var insertionPoint = context.factory.createRootContentInsertionPoint();
|
||||
if (this.parent instanceof Component) {
|
||||
context.factory.appendChild((<Component<N>>this.parent).shadowRoot, insertionPoint);
|
||||
} else {
|
||||
context.factory.appendChild(<N>this.parent, insertionPoint);
|
||||
}
|
||||
context.rootContentInsertionPoints.push(insertionPoint);
|
||||
} else {
|
||||
var projectedNodes = this.parentComponent.project(cmd.index);
|
||||
for (var i = 0; i < projectedNodes.length; i++) {
|
||||
var node = projectedNodes[i];
|
||||
this._addChild(node, cmd.ngContentIndex, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): any {
|
||||
this.parentStack.push(this._beginElement(cmd, context, null));
|
||||
return null;
|
||||
}
|
||||
visitEndElement(context: BuildContext<N>): any {
|
||||
this._endElement();
|
||||
return null;
|
||||
}
|
||||
visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext<N>): any {
|
||||
var templateId = cmd.templateId;
|
||||
var tpl = context.factory.resolveComponentTemplate(templateId);
|
||||
var el = this._beginElement(cmd, context, tpl);
|
||||
var root = el;
|
||||
|
||||
if (tpl.encapsulation === ViewEncapsulation.Native) {
|
||||
root = context.factory.createShadowRoot(el, templateId);
|
||||
context.nativeShadowRoots.push(root);
|
||||
}
|
||||
var isRoot = context.componentCount === 0 && context.isHost;
|
||||
var component = new Component(el, root, isRoot, tpl);
|
||||
context.enqueueComponentBuilder(component);
|
||||
this.parentStack.push(component);
|
||||
return null;
|
||||
}
|
||||
visitEndComponent(context: BuildContext<N>): any {
|
||||
this._endElement();
|
||||
return null;
|
||||
}
|
||||
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: BuildContext<N>): any {
|
||||
var el = context.factory.createTemplateAnchor(cmd.attrNameAndValues);
|
||||
this._addChild(el, cmd.ngContentIndex, context);
|
||||
context.boundElements.push(el);
|
||||
if (cmd.isMerged) {
|
||||
context.enqueueFragmentBuilder(this.parentComponent, this.template, cmd.children);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>,
|
||||
componentTemplate: RenderComponentTemplate): N {
|
||||
var el: N = context.consumeInplaceElement();
|
||||
var attrNameAndValues = cmd.attrNameAndValues;
|
||||
var templateEmulatedEncapsulation = this.template.encapsulation === ViewEncapsulation.Emulated;
|
||||
var componentEmulatedEncapsulation =
|
||||
isPresent(componentTemplate) &&
|
||||
componentTemplate.encapsulation === ViewEncapsulation.Emulated;
|
||||
var newAttrLength = attrNameAndValues.length + (templateEmulatedEncapsulation ? 2 : 0) +
|
||||
(componentEmulatedEncapsulation ? 2 : 0);
|
||||
if (newAttrLength > attrNameAndValues.length) {
|
||||
// Note: Need to clone attrNameAndValues to make it writable!
|
||||
var newAttrNameAndValues = ListWrapper.createFixedSize(newAttrLength);
|
||||
var attrIndex;
|
||||
for (attrIndex = 0; attrIndex < attrNameAndValues.length; attrIndex++) {
|
||||
newAttrNameAndValues[attrIndex] = attrNameAndValues[attrIndex];
|
||||
}
|
||||
if (templateEmulatedEncapsulation) {
|
||||
newAttrNameAndValues[attrIndex++] = _shimContentAttribute(this.template.shortId);
|
||||
newAttrNameAndValues[attrIndex++] = '';
|
||||
}
|
||||
if (componentEmulatedEncapsulation) {
|
||||
newAttrNameAndValues[attrIndex++] = _shimHostAttribute(componentTemplate.shortId);
|
||||
newAttrNameAndValues[attrIndex++] = '';
|
||||
}
|
||||
attrNameAndValues = newAttrNameAndValues;
|
||||
}
|
||||
if (isPresent(el)) {
|
||||
context.factory.mergeElement(el, attrNameAndValues);
|
||||
this.fragmentRootNodes.push(el);
|
||||
} else {
|
||||
el = context.factory.createElement(cmd.name, attrNameAndValues);
|
||||
this._addChild(el, cmd.ngContentIndex, context);
|
||||
}
|
||||
if (cmd.isBound) {
|
||||
var boundElementIndex = context.boundElements.length;
|
||||
context.boundElements.push(el);
|
||||
for (var i = 0; i < cmd.eventTargetAndNames.length; i += 2) {
|
||||
var target = cmd.eventTargetAndNames[i];
|
||||
var eventName = cmd.eventTargetAndNames[i + 1];
|
||||
context.addEventListener(boundElementIndex, target, eventName);
|
||||
}
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
private _endElement() { this.parentStack.pop(); }
|
||||
|
||||
private _addChild(node: N, ngContentIndex: number, context: BuildContext<N>) {
|
||||
var parent = this.parent;
|
||||
if (isPresent(parent)) {
|
||||
if (parent instanceof Component) {
|
||||
parent.addContentNode(ngContentIndex, node, context);
|
||||
} else {
|
||||
context.factory.appendChild(<N>parent, node);
|
||||
}
|
||||
} else {
|
||||
this.fragmentRootNodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Component<N> {
|
||||
private contentNodesByNgContentIndex: N[][] = [];
|
||||
|
||||
constructor(public hostElement: N, public shadowRoot: N, public isRoot: boolean,
|
||||
public template: RenderComponentTemplate) {}
|
||||
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
|
||||
if (isBlank(ngContentIndex)) {
|
||||
if (this.template.encapsulation === ViewEncapsulation.Native) {
|
||||
context.factory.appendChild(this.hostElement, node);
|
||||
}
|
||||
} else {
|
||||
while (this.contentNodesByNgContentIndex.length <= ngContentIndex) {
|
||||
this.contentNodesByNgContentIndex.push([]);
|
||||
}
|
||||
this.contentNodesByNgContentIndex[ngContentIndex].push(node);
|
||||
}
|
||||
}
|
||||
project(ngContentIndex: number): N[] {
|
||||
return ngContentIndex < this.contentNodesByNgContentIndex.length ?
|
||||
this.contentNodesByNgContentIndex[ngContentIndex] :
|
||||
[];
|
||||
}
|
||||
}
|
||||
|
||||
var COMPONENT_REGEX = /%COMP%/g;
|
||||
export const COMPONENT_VARIABLE = '%COMP%';
|
||||
export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
|
||||
function _shimContentAttribute(componentShortId: string): string {
|
||||
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentShortId);
|
||||
}
|
||||
|
||||
function _shimHostAttribute(componentShortId: string): string {
|
||||
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentShortId);
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import {ConcreteType, global, Type, isFunction, stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
var _nextClassId = 0;
|
||||
|
||||
/**
|
||||
* Declares the interface to be used with {@link Class}.
|
||||
*/
|
||||
@ -228,6 +230,10 @@ export function Class(clsDef: ClassDefinition): ConcreteType {
|
||||
Reflect.defineMetadata('annotations', this.annotations, constructor);
|
||||
}
|
||||
|
||||
if (!constructor['name']) {
|
||||
constructor['overriddenName'] = `class${_nextClassId++}`;
|
||||
}
|
||||
|
||||
return <ConcreteType>constructor;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user