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

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

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

View File

@ -11,13 +11,11 @@ import {
KeyValueDiffers,
defaultKeyValueDiffers
} from './change_detection/change_detection';
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from './linker/view_pool';
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
import {AppViewManager} from './linker/view_manager';
import {AppViewManager_} from "./linker/view_manager";
import {AppViewManagerUtils} from './linker/view_manager_utils';
import {ViewResolver} from './linker/view_resolver';
import {AppViewListener} from './linker/view_listener';
import {ProtoViewFactory} from './linker/proto_view_factory';
import {DirectiveResolver} from './linker/directive_resolver';
import {PipeResolver} from './linker/pipe_resolver';
import {Compiler} from './linker/compiler';
@ -32,12 +30,9 @@ import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
new Provider(Compiler, {useClass: Compiler_}),
APP_ID_RANDOM_PROVIDER,
AppViewPool,
new Provider(APP_VIEW_POOL_CAPACITY, {useValue: 10000}),
ResolvedMetadataCache,
new Provider(AppViewManager, {useClass: AppViewManager_}),
AppViewManagerUtils,
AppViewListener,
ProtoViewFactory,
ViewResolver,
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),

View File

@ -33,11 +33,11 @@ import {
ExceptionHandler,
unimplemented
} from 'angular2/src/facade/exceptions';
import {internalView} from 'angular2/src/core/linker/view_ref';
import {Console} from 'angular2/src/core/console';
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
import {lockMode} from 'angular2/src/facade/lang';
import {ElementRef_} from 'angular2/src/core/linker/element_ref';
/**
* Construct providers specific to an individual root component.
@ -56,10 +56,10 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
() => { appRef._unloadComponent(ref); })
.then((componentRef) => {
ref = componentRef;
if (isPresent(componentRef.location.nativeElement)) {
var testability = injector.getOptional(Testability);
if (isPresent(testability)) {
injector.get(TestabilityRegistry)
.registerApplication(componentRef.location.nativeElement,
injector.get(Testability));
.registerApplication(componentRef.location.nativeElement, testability);
}
return componentRef;
});
@ -439,7 +439,7 @@ export class ApplicationRef_ extends ApplicationRef {
/** @internal */
_loadComponent(ref): void {
var appChangeDetector = internalView(ref.hostView).changeDetector;
var appChangeDetector = (<ElementRef_>ref.location).internalElement.parentView.changeDetector;
this._changeDetectorRefs.push(appChangeDetector.ref);
this.tick();
this._rootComponents.push(ref);
@ -451,7 +451,8 @@ export class ApplicationRef_ extends ApplicationRef {
if (!ListWrapper.contains(this._rootComponents, ref)) {
return;
}
this.unregisterChangeDetector(internalView(ref.hostView).changeDetector.ref);
this.unregisterChangeDetector(
(<ElementRef_>ref.location).internalElement.parentView.changeDetector.ref);
ListWrapper.remove(this._rootComponents, ref);
}

View File

@ -8,7 +8,9 @@ import {Pipes} from './pipes';
import {
ChangeDetectionError,
ExpressionChangedAfterItHasBeenCheckedException,
DehydratedException
DehydratedException,
EventEvaluationErrorContext,
EventEvaluationError
} from './exceptions';
import {BindingTarget} from './binding_record';
import {Locals} from './parser/locals';
@ -43,9 +45,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
subscriptions: any[];
streams: any[];
constructor(public id: string, public dispatcher: ChangeDispatcher,
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
public directiveIndices: DirectiveIndex[], public strategy: ChangeDetectionStrategy) {
dispatcher: ChangeDispatcher;
constructor(public id: string, public numberOfPropertyProtoRecords: number,
public bindingTargets: BindingTarget[], public directiveIndices: DirectiveIndex[],
public strategy: ChangeDetectionStrategy) {
this.ref = new ChangeDetectorRef_(this);
}
@ -65,10 +70,24 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
remove(): void { this.parent.removeContentChild(this); }
handleEvent(eventName: string, elIndex: number, locals: Locals): boolean {
var res = this.handleEventInternal(eventName, elIndex, locals);
this.markPathToRootAsCheckOnce();
return res;
handleEvent(eventName: string, elIndex: number, event: any): boolean {
if (!this.hydrated()) {
return true;
}
try {
var locals = new Map<string, any>();
locals.set('$event', event);
var res = !this.handleEventInternal(eventName, elIndex, new Locals(this.locals, locals));
this.markPathToRootAsCheckOnce();
return res;
} catch (e) {
var c = this.dispatcher.getDebugContext(null, elIndex, null);
var context = isPresent(c) ?
new EventEvaluationErrorContext(c.element, c.componentElement, c.context,
c.locals, c.injector) :
null;
throw new EventEvaluationError(eventName, e, e.stack, context);
}
}
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean { return false; }
@ -133,7 +152,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// This method is not intended to be overridden. Subclasses should instead provide an
// implementation of `hydrateDirectives`.
hydrate(context: T, locals: Locals, directives: any, pipes: Pipes): void {
hydrate(context: T, locals: Locals, dispatcher: ChangeDispatcher, pipes: Pipes): void {
this.dispatcher = dispatcher;
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
this.context = context;
@ -143,12 +163,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
this.locals = locals;
this.pipes = pipes;
this.hydrateDirectives(directives);
this.hydrateDirectives(dispatcher);
this.state = ChangeDetectorState.NeverChecked;
}
// Subclasses should override this method to hydrate any directives.
hydrateDirectives(directives: any): void {}
hydrateDirectives(dispatcher: ChangeDispatcher): void {}
// This method is not intended to be overridden. Subclasses should instead provide an
// implementation of `dehydrateDirectives`.
@ -160,6 +180,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
this._unsubsribeFromObservables();
}
this.dispatcher = null;
this.context = null;
this.locals = null;
this.pipes = null;
@ -171,6 +192,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
hydrated(): boolean { return isPresent(this.context); }
destroyRecursive(): void {
this.dispatcher.notifyOnDestroy();
this.dehydrate();
var children = this.contentChildren;
for (var i = 0; i < children.length; i++) {
children[i].destroyRecursive();
}
children = this.viewChildren;
for (var i = 0; i < children.length; i++) {
children[i].destroyRecursive();
}
}
afterContentLifecycleCallbacks(): void {
this.dispatcher.notifyAfterContentChecked();
this.afterContentLifecycleCallbacksInternal();
@ -298,7 +332,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
private _throwError(exception: any, stack: any): void {
var error;
try {
var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
var c = this.dispatcher.getDebugContext(null, this._currentBinding().elementIndex, null);
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
c.injector, this._currentBinding().debug) :
null;

View File

@ -66,8 +66,8 @@ export class ChangeDetectorJITGenerator {
generate(): Function {
var factorySource = `
${this.generateSource()}
return function(dispatcher) {
return new ${this.typeName}(dispatcher);
return function() {
return new ${this.typeName}();
}
`;
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
@ -77,9 +77,9 @@ export class ChangeDetectorJITGenerator {
generateSource(): string {
return `
var ${this.typeName} = function ${this.typeName}(dispatcher) {
var ${this.typeName} = function ${this.typeName}() {
${this.abstractChangeDetectorVarName}.call(
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
this, ${JSON.stringify(this.id)}, ${this.records.length},
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
${codify(this.changeDetectionStrategy)});
this.dehydrateDirectives(false);
@ -199,13 +199,14 @@ export class ChangeDetectorJITGenerator {
/** @internal */
_maybeGenDehydrateDirectives(): string {
var destroyPipesCode = this._names.genPipeOnDestroy();
if (destroyPipesCode) {
destroyPipesCode = `if (destroyPipes) { ${destroyPipesCode} }`;
}
var destroyDirectivesCode = this._logic.genDirectivesOnDestroy(this.directiveRecords);
var dehydrateFieldsCode = this._names.genDehydrateFields();
if (!destroyPipesCode && !dehydrateFieldsCode) return '';
if (!destroyPipesCode && !destroyDirectivesCode && !dehydrateFieldsCode) return '';
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
${destroyPipesCode}
if (destroyPipes) {
${destroyPipesCode}
${destroyDirectivesCode}
}
${dehydrateFieldsCode}
}`;
}

View File

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

View File

@ -155,17 +155,49 @@ export class CodegenLogicUtil {
var res = [];
for (var i = 0; i < directiveRecords.length; ++i) {
var r = directiveRecords[i];
res.push(`${this._names.getDirectiveName(r.directiveIndex)} = ${this._genReadDirective(i)};`);
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
res.push(`${dirVarName} = ${this._genReadDirective(i)};`);
if (isPresent(r.outputs)) {
r.outputs.forEach(output => {
var eventHandlerExpr = this._genEventHandler(r.directiveIndex.elementIndex, output[1]);
if (IS_DART) {
res.push(`${dirVarName}.${output[0]}.listen(${eventHandlerExpr});`);
} else {
res.push(`${dirVarName}.${output[0]}.subscribe({next: ${eventHandlerExpr}});`);
}
});
}
}
return res.join("\n");
}
genDirectivesOnDestroy(directiveRecords: DirectiveRecord[]): string {
var res = [];
for (var i = 0; i < directiveRecords.length; ++i) {
var r = directiveRecords[i];
if (r.callOnDestroy) {
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
res.push(`${dirVarName}.ngOnDestroy();`);
}
}
return res.join("\n");
}
private _genEventHandler(boundElementIndex: number, eventName: string): string {
if (IS_DART) {
return `(event) => this.handleEvent('${eventName}', ${boundElementIndex}, event)`;
} else {
return `(function(event) { return this.handleEvent('${eventName}', ${boundElementIndex}, event); }).bind(this)`;
}
}
private _genReadDirective(index: number) {
var directiveExpr = `this.getDirectiveFor(directives, ${index})`;
// This is an experimental feature. Works only in Dart.
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
return `this.observeDirective(this.getDirectiveFor(directives, ${index}), ${index})`;
return `this.observeDirective(${directiveExpr}, ${index})`;
} else {
return `this.getDirectiveFor(directives, ${index})`;
return directiveExpr;
}
}

View File

@ -16,10 +16,14 @@ export class DirectiveRecord {
callOnChanges: boolean;
callDoCheck: boolean;
callOnInit: boolean;
callOnDestroy: boolean;
changeDetection: ChangeDetectionStrategy;
// array of [emitter property name, eventName]
outputs: string[][];
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, changeDetection}: {
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, callOnDestroy,
changeDetection, outputs}: {
directiveIndex?: DirectiveIndex,
callAfterContentInit?: boolean,
callAfterContentChecked?: boolean,
@ -28,7 +32,9 @@ export class DirectiveRecord {
callOnChanges?: boolean,
callDoCheck?: boolean,
callOnInit?: boolean,
changeDetection?: ChangeDetectionStrategy
callOnDestroy?: boolean,
changeDetection?: ChangeDetectionStrategy,
outputs?: string[][]
} = {}) {
this.directiveIndex = directiveIndex;
this.callAfterContentInit = normalizeBool(callAfterContentInit);
@ -38,7 +44,9 @@ export class DirectiveRecord {
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
this.callDoCheck = normalizeBool(callDoCheck);
this.callOnInit = normalizeBool(callOnInit);
this.callOnDestroy = normalizeBool(callOnDestroy);
this.changeDetection = changeDetection;
this.outputs = outputs;
}
isDefaultChangeDetection(): boolean {

View File

@ -11,21 +11,21 @@ import {ChangeDispatcher, ChangeDetectorGenConfig} from './interfaces';
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
import {ProtoRecord, RecordType} from './proto_record';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {ObservableWrapper} from 'angular2/src/facade/async';
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
values: any[];
changes: any[];
localPipes: any[];
prevContexts: any[];
directives: any = null;
constructor(id: string, dispatcher: ChangeDispatcher, numberOfPropertyProtoRecords: number,
constructor(id: string, numberOfPropertyProtoRecords: number,
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
private _genConfig: ChangeDetectorGenConfig) {
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
strategy);
super(id, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, strategy);
var len = _records.length + 1;
this.values = ListWrapper.createFixedSize(len);
this.localPipes = ListWrapper.createFixedSize(len);
@ -104,24 +104,41 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
return this._eventBindings.filter(eb => eb.eventName == eventName && eb.elIndex === elIndex);
}
hydrateDirectives(directives: any): void {
hydrateDirectives(dispatcher: ChangeDispatcher): void {
this.values[0] = this.context;
this.directives = directives;
this.dispatcher = dispatcher;
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
for (var i = 0; i < this.directiveIndices.length; ++i) {
var index = this.directiveIndices[i];
super.observeDirective(directives.getDirectiveFor(index), i);
super.observeDirective(this._getDirectiveFor(index), i);
}
}
for (var i = 0; i < this._directiveRecords.length; ++i) {
var r = this._directiveRecords[i];
if (isPresent(r.outputs)) {
r.outputs.forEach(output => {
var eventHandler =
<any>this._createEventHandler(r.directiveIndex.elementIndex, output[1]);
var directive = this._getDirectiveFor(r.directiveIndex);
var getter = reflector.getter(output[0]);
ObservableWrapper.subscribe(getter(directive), eventHandler);
});
}
}
}
private _createEventHandler(boundElementIndex: number, eventName: string): Function {
return (event) => this.handleEvent(eventName, boundElementIndex, event);
}
dehydrateDirectives(destroyPipes: boolean) {
if (destroyPipes) {
this._destroyPipes();
this._destroyDirectives();
}
this.values[0] = null;
this.directives = null;
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
ListWrapper.fill(this.changes, false);
ListWrapper.fill(this.localPipes, null);
@ -137,6 +154,16 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
}
}
/** @internal */
_destroyDirectives() {
for (var i = 0; i < this._directiveRecords.length; ++i) {
var record = this._directiveRecords[i];
if (record.callOnDestroy) {
this._getDirectiveFor(record.directiveIndex).ngOnDestroy();
}
}
}
checkNoChanges(): void { this.runDetectChanges(true); }
detectChangesInRecordsInternal(throwOnChange: boolean) {
@ -241,12 +268,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
}
/** @internal */
private _getDirectiveFor(directiveIndex) {
return this.directives.getDirectiveFor(directiveIndex);
private _getDirectiveFor(directiveIndex: DirectiveIndex) {
return this.dispatcher.getDirectiveFor(directiveIndex);
}
/** @internal */
private _getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
private _getDetectorFor(directiveIndex: DirectiveIndex) {
return this.dispatcher.getDetectorFor(directiveIndex);
}
/** @internal */
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],

View File

@ -93,3 +93,20 @@ export class ChangeDetectionError extends WrappedException {
export class DehydratedException extends BaseException {
constructor() { super('Attempt to detect changes on a dehydrated detector.'); }
}
/**
* Wraps an exception thrown by an event handler.
*/
export class EventEvaluationError extends WrappedException {
constructor(eventName: string, originalException: any, originalStack: any, context: any) {
super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
}
}
/**
* Error context included when an event handler throws an exception.
*/
export class EventEvaluationErrorContext {
constructor(public element: any, public componentElement: any, public context: any,
public locals: any, public injector: any) {}
}

View File

@ -1,6 +1,6 @@
import {Locals} from './parser/locals';
import {BindingTarget, BindingRecord} from './binding_record';
import {DirectiveIndex, DirectiveRecord} from './directive_record';
import {DirectiveRecord, DirectiveIndex} from './directive_record';
import {ChangeDetectionStrategy} from './constants';
import {ChangeDetectorRef} from './change_detector_ref';
@ -10,11 +10,14 @@ export class DebugContext {
}
export interface ChangeDispatcher {
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
getDebugContext(appElement: any, elementIndex: number, directiveIndex: number): DebugContext;
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
notifyAfterContentChecked(): void;
notifyAfterViewChecked(): void;
notifyOnDestroy(): void;
getDetectorFor(directiveIndex: DirectiveIndex): ChangeDetector;
getDirectiveFor(directiveIndex: DirectiveIndex): any;
}
export interface ChangeDetector {
@ -27,16 +30,18 @@ export interface ChangeDetector {
removeContentChild(cd: ChangeDetector): void;
removeViewChild(cd: ChangeDetector): void;
remove(): void;
hydrate(context: any, locals: Locals, directives: any, pipes: any): void;
hydrate(context: any, locals: Locals, dispatcher: ChangeDispatcher, pipes: any): void;
dehydrate(): void;
markPathToRootAsCheckOnce(): void;
handleEvent(eventName: string, elIndex: number, locals: Locals);
handleEvent(eventName: string, elIndex: number, event: any);
detectChanges(): void;
checkNoChanges(): void;
destroyRecursive(): void;
markAsCheckOnce(): void;
}
export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; }
export interface ProtoChangeDetector { instantiate(): ChangeDetector; }
export class ChangeDetectorGenConfig {
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,

View File

@ -3,11 +3,11 @@ library change_detection.jit_proto_change_detector;
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
class JitProtoChangeDetector implements ProtoChangeDetector {
JitProtoChangeDetector(definition) : super();
JitProtoChangeDetector(definition);
static bool isSupported() => false;
ChangeDetector instantiate(dispatcher) {
ChangeDetector instantiate() {
throw "Jit Change Detection not supported in Dart";
}
}

View File

@ -14,7 +14,7 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
static isSupported(): boolean { return true; }
instantiate(dispatcher: any): ChangeDetector { return this._factory(dispatcher); }
instantiate(): ChangeDetector { return this._factory(); }
/** @internal */
_createFactory(definition: ChangeDetectorDefinition) {

View File

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

View File

@ -1,8 +1,5 @@
library angular2.src.change_detection.pregen_proto_change_detector;
import 'package:angular2/src/core/change_detection/interfaces.dart';
import 'package:angular2/src/facade/lang.dart' show looseIdentical;
export 'dart:core' show List;
export 'package:angular2/src/core/change_detection/abstract_change_detector.dart'
show AbstractChangeDetector;
@ -20,34 +17,3 @@ export 'package:angular2/src/core/change_detection/proto_record.dart'
export 'package:angular2/src/core/change_detection/change_detection_util.dart'
show ChangeDetectionUtil;
export 'package:angular2/src/facade/lang.dart' show assertionsEnabled, looseIdentical;
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
ChangeDetectorDefinition definition);
typedef ChangeDetector InstantiateMethod(dynamic dispatcher);
/// Implementation of [ProtoChangeDetector] for use by pre-generated change
/// detectors in Angular 2 Dart.
/// Classes generated by the `TemplateCompiler` use this. The `export`s above
/// allow the generated code to `import` a single library and get all
/// dependencies.
class PregenProtoChangeDetector extends ProtoChangeDetector {
/// The [ChangeDetectorDefinition#id]. Strictly informational.
final String id;
/// Closure used to generate an actual [ChangeDetector].
final InstantiateMethod _instantiateMethod;
/// Internal ctor.
PregenProtoChangeDetector._(this.id, this._instantiateMethod);
static bool isSupported() => true;
factory PregenProtoChangeDetector(
InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) {
return new PregenProtoChangeDetector._(def.id, instantiateMethod);
}
@override
instantiate(dynamic dispatcher) => _instantiateMethod(dispatcher);
}

View File

@ -1,14 +1 @@
import {BaseException} from 'angular2/src/facade/exceptions';
import {ProtoChangeDetector, ChangeDetector} from './interfaces';
import {coalesce} from './coalesce';
export {Function as PregenProtoChangeDetectorFactory};
export class PregenProtoChangeDetector implements ProtoChangeDetector {
static isSupported(): boolean { return false; }
instantiate(dispatcher: any): ChangeDetector {
throw new BaseException('Pregen change detection not supported in Js');
}
}
// empty file as we only need the dart version

View File

@ -54,12 +54,11 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
this._directiveIndices = this._definition.directiveRecords.map(d => d.directiveIndex);
}
instantiate(dispatcher: any): ChangeDetector {
instantiate(): ChangeDetector {
return new DynamicChangeDetector(
this._definition.id, dispatcher, this._propertyBindingRecords.length,
this._propertyBindingTargets, this._directiveIndices, this._definition.strategy,
this._propertyBindingRecords, this._eventBindingRecords, this._definition.directiveRecords,
this._definition.genConfig);
this._definition.id, this._propertyBindingRecords.length, this._propertyBindingTargets,
this._directiveIndices, this._definition.strategy, this._propertyBindingRecords,
this._eventBindingRecords, this._definition.directiveRecords, this._definition.genConfig);
}
}

View File

@ -1,9 +1,9 @@
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {ElementInjector} from 'angular2/src/core/linker/element_injector';
import {AppView, ViewType} from 'angular2/src/core/linker/view';
import {internalView} from 'angular2/src/core/linker/view_ref';
import {AppElement} from 'angular2/src/core/linker/element';
import {AppView} from 'angular2/src/core/linker/view';
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
/**
@ -103,79 +103,68 @@ export abstract class DebugElement {
}
export class DebugElement_ extends DebugElement {
/** @internal */
_elementInjector: ElementInjector;
constructor(private _parentView: AppView, private _boundElementIndex: number) {
super();
this._elementInjector = this._parentView.elementInjectors[this._boundElementIndex];
}
constructor(private _appElement: AppElement) { super(); }
get componentInstance(): any {
if (!isPresent(this._elementInjector)) {
if (!isPresent(this._appElement)) {
return null;
}
return this._elementInjector.getComponent();
return this._appElement.getComponent();
}
get nativeElement(): any { return this.elementRef.nativeElement; }
get elementRef(): ElementRef { return this._parentView.elementRefs[this._boundElementIndex]; }
get elementRef(): ElementRef { return this._appElement.ref; }
getDirectiveInstance(directiveIndex: number): any {
return this._elementInjector.getDirectiveAtIndex(directiveIndex);
return this._appElement.getDirectiveAtIndex(directiveIndex);
}
get children(): DebugElement[] {
return this._getChildElements(this._parentView, this._boundElementIndex);
return this._getChildElements(this._appElement.parentView, this._appElement);
}
get componentViewChildren(): DebugElement[] {
var shadowView = this._parentView.getNestedView(this._boundElementIndex);
if (!isPresent(shadowView) || shadowView.proto.type !== ViewType.COMPONENT) {
if (!isPresent(this._appElement.componentView)) {
// The current element is not a component.
return [];
}
return this._getChildElements(shadowView, null);
return this._getChildElements(this._appElement.componentView, null);
}
triggerEventHandler(eventName: string, eventObj: Event): void {
this._parentView.triggerEventHandlers(eventName, eventObj, this._boundElementIndex);
this._appElement.parentView.triggerEventHandlers(eventName, eventObj,
this._appElement.proto.index);
}
hasDirective(type: Type): boolean {
if (!isPresent(this._elementInjector)) {
if (!isPresent(this._appElement)) {
return false;
}
return this._elementInjector.hasDirective(type);
return this._appElement.hasDirective(type);
}
inject(type: Type): any {
if (!isPresent(this._elementInjector)) {
if (!isPresent(this._appElement)) {
return null;
}
return this._elementInjector.get(type);
return this._appElement.get(type);
}
getLocal(name: string): any { return this._parentView.locals.get(name); }
getLocal(name: string): any { return this._appElement.parentView.locals.get(name); }
/** @internal */
_getChildElements(view: AppView, parentBoundElementIndex: number): DebugElement[] {
_getChildElements(view: AppView, parentAppElement: AppElement): DebugElement[] {
var els = [];
var parentElementBinder = null;
if (isPresent(parentBoundElementIndex)) {
parentElementBinder = view.proto.elementBinders[parentBoundElementIndex - view.elementOffset];
}
for (var i = 0; i < view.proto.elementBinders.length; ++i) {
var binder = view.proto.elementBinders[i];
if (binder.parent == parentElementBinder) {
els.push(new DebugElement_(view, view.elementOffset + i));
for (var i = 0; i < view.appElements.length; ++i) {
var appEl = view.appElements[i];
if (appEl.parent == parentAppElement) {
els.push(new DebugElement_(appEl));
var views = view.viewContainers[view.elementOffset + i];
var views = appEl.nestedViews;
if (isPresent(views)) {
views.views.forEach(
views.forEach(
(nextView) => { els = els.concat(this._getChildElements(nextView, null)); });
}
}
@ -191,8 +180,7 @@ export class DebugElement_ extends DebugElement {
* @return {DebugElement}
*/
export function inspectElement(elementRef: ElementRef): DebugElement {
return new DebugElement_(internalView((<ElementRef_>elementRef).parentView),
(<ElementRef_>elementRef).boundElementIndex);
return new DebugElement_((<ElementRef_>elementRef).internalElement);
}
/**

View File

@ -194,6 +194,11 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
}
export class ProtoInjector {
static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
return new ProtoInjector(bd);
}
/** @internal */
_strategy: ProtoInjectorStrategy;
numberOfProviders: number;
@ -215,7 +220,6 @@ export interface InjectorStrategy {
getObjAtIndex(index: number): any;
getMaxNumberOfObjects(): number;
attach(parent: Injector, isHost: boolean): void;
resetConstructionCounter(): void;
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any;
}
@ -240,12 +244,6 @@ export class InjectorInlineStrategy implements InjectorStrategy {
return this.injector._new(provider, visibility);
}
attach(parent: Injector, isHost: boolean): void {
var inj = this.injector;
inj._parent = parent;
inj._isHost = isHost;
}
getObjByKeyId(keyId: number, visibility: Visibility): any {
var p = this.protoStrategy;
var inj = this.injector;
@ -346,12 +344,6 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
return this.injector._new(provider, visibility);
}
attach(parent: Injector, isHost: boolean): void {
var inj = this.injector;
inj._parent = parent;
inj._isHost = isHost;
}
getObjByKeyId(keyId: number, visibility: Visibility): any {
var p = this.protoStrategy;
@ -516,9 +508,7 @@ export class Injector {
* ```
*/
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
var proto = new ProtoInjector(bd);
return new Injector(proto, null, null);
return new Injector(ProtoInjector.fromResolvedProviders(providers));
}
/**
@ -531,8 +521,6 @@ export class Injector {
/** @internal */
_strategy: InjectorStrategy;
/** @internal */
_isHost: boolean = false;
/** @internal */
_constructionCounter: number = 0;
/** @internal */
public _proto: any /* ProtoInjector */;
@ -542,6 +530,7 @@ export class Injector {
* Private
*/
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
private _isHostBoundary: boolean = false,
private _depProvider: any /* DependencyProvider */ = null,
private _debugContext: Function = null) {
this._proto = _proto;
@ -549,6 +538,12 @@ export class Injector {
this._strategy = _proto._strategy.createInjectorStrategy(this);
}
/**
* Whether this injector is a boundary to a host.
* @internal
*/
get hostBoundary() { return this._isHostBoundary; }
/**
* @internal
*/
@ -692,7 +687,7 @@ export class Injector {
createChildFromResolved(providers: ResolvedProvider[]): Injector {
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
var proto = new ProtoInjector(bd);
var inj = new Injector(proto, null, null);
var inj = new Injector(proto);
inj._parent = this;
return inj;
}
@ -935,7 +930,7 @@ export class Injector {
var inj: Injector = this;
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
if (inj._isHost) {
if (inj._isHostBoundary) {
return this._getPrivateDependency(key, optional, inj);
} else {
inj = inj._parent;
@ -946,7 +941,7 @@ export class Injector {
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
if (obj !== UNDEFINED) return obj;
if (isPresent(inj._parent) && inj._isHost) {
if (isPresent(inj._parent) && inj._isHostBoundary) {
return this._getPrivateDependency(key, optional, inj);
} else {
inj = inj._parent;
@ -968,7 +963,7 @@ export class Injector {
var inj: Injector = this;
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
inj = inj._parent;
}
@ -976,7 +971,7 @@ export class Injector {
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
if (obj !== UNDEFINED) return obj;
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
inj = inj._parent;
}

View File

@ -538,50 +538,62 @@ export function resolveFactory(provider: Provider): ResolvedFactory {
* convenience provider syntax.
*/
export function resolveProvider(provider: Provider): ResolvedProvider {
return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], false);
return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], provider.multi);
}
/**
* Resolve a list of Providers.
*/
export function resolveProviders(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
var normalized = _createListOfProviders(_normalizeProviders(
providers, new Map<number, _NormalizedProvider | _NormalizedProvider[]>()));
return normalized.map(b => {
if (b instanceof _NormalizedProvider) {
return new ResolvedProvider_(b.key, [b.resolvedFactory], false);
} else {
var arr = <_NormalizedProvider[]>b;
return new ResolvedProvider_(arr[0].key, arr.map(_ => _.resolvedFactory), true);
}
});
var normalized = _normalizeProviders(providers, []);
var resolved = normalized.map(resolveProvider);
return MapWrapper.values(mergeResolvedProviders(resolved, new Map<number, ResolvedProvider>()));
}
/**
* The algorithm works as follows:
*
* [Provider] -> [_NormalizedProvider|[_NormalizedProvider]] -> [ResolvedProvider]
*
* _NormalizedProvider is essentially a resolved provider before it was grouped by key.
* Merges a list of ResolvedProviders into a list where
* each key is contained exactly once and multi providers
* have been merged.
*/
class _NormalizedProvider {
constructor(public key: Key, public resolvedFactory: ResolvedFactory) {}
}
function _createListOfProviders(flattenedProviders: Map<number, any>): any[] {
return MapWrapper.values(flattenedProviders);
export function mergeResolvedProviders(
providers: ResolvedProvider[],
normalizedProvidersMap: Map<number, ResolvedProvider>): Map<number, ResolvedProvider> {
for (var i = 0; i < providers.length; i++) {
var provider = providers[i];
var existing = normalizedProvidersMap.get(provider.key.id);
if (isPresent(existing)) {
if (provider.multiProvider !== existing.multiProvider) {
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
}
if (provider.multiProvider) {
for (var j = 0; j < provider.resolvedFactories.length; j++) {
existing.resolvedFactories.push(provider.resolvedFactories[j]);
}
} else {
normalizedProvidersMap.set(provider.key.id, provider);
}
} else {
var resolvedProvider;
if (provider.multiProvider) {
resolvedProvider = new ResolvedProvider_(
provider.key, ListWrapper.clone(provider.resolvedFactories), provider.multiProvider);
} else {
resolvedProvider = provider;
}
normalizedProvidersMap.set(provider.key.id, resolvedProvider);
}
}
return normalizedProvidersMap;
}
function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder | any[]>,
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>):
Map<number, _NormalizedProvider | _NormalizedProvider[]> {
res: Provider[]): Provider[] {
providers.forEach(b => {
if (b instanceof Type) {
_normalizeProvider(provide(b, {useClass: b}), res);
res.push(provide(b, {useClass: b}));
} else if (b instanceof Provider) {
_normalizeProvider(b, res);
res.push(b);
} else if (b instanceof Array) {
_normalizeProviders(b, res);
@ -597,36 +609,6 @@ function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder
return res;
}
function _normalizeProvider(b: Provider,
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>): void {
var key = Key.get(b.token);
var factory = resolveFactory(b);
var normalized = new _NormalizedProvider(key, factory);
if (b.multi) {
var existingProvider = res.get(key.id);
if (existingProvider instanceof Array) {
existingProvider.push(normalized);
} else if (isBlank(existingProvider)) {
res.set(key.id, [normalized]);
} else {
throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
}
} else {
var existingProvider = res.get(key.id);
if (existingProvider instanceof Array) {
throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
}
res.set(key.id, normalized);
}
}
function _constructDependencies(factoryFunction: Function, dependencies: any[]): Dependency[] {
if (isBlank(dependencies)) {
return _dependenciesFor(factoryFunction);

View File

@ -17,6 +17,6 @@ export {QueryList} from './linker/query_list';
export {DynamicComponentLoader} from './linker/dynamic_component_loader';
export {ElementRef} from './linker/element_ref';
export {TemplateRef} from './linker/template_ref';
export {ViewRef, HostViewRef, ProtoViewRef} from './linker/view_ref';
export {EmbeddedViewRef, HostViewRef, ViewRef, HostViewFactoryRef} from './linker/view_ref';
export {ViewContainerRef} from './linker/view_container_ref';
export {ComponentRef} from './linker/dynamic_component_loader';

View File

@ -1,12 +1,12 @@
import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
import {HostViewFactoryRef} from 'angular2/src/core/linker/view_ref';
import {Injectable} from 'angular2/src/core/di';
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
import {HostViewFactory} from 'angular2/src/core/linker/view';
import {HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
/**
* Low-level service for compiling {@link Component}s into {@link ProtoViewRef ProtoViews}s, which
@ -16,37 +16,25 @@ import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
* both compiles and instantiates a Component.
*/
export abstract class Compiler {
abstract compileInHost(componentType: Type): Promise<ProtoViewRef>;
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
abstract clearCache();
}
function _isCompiledHostTemplate(type: any): boolean {
return type instanceof CompiledHostTemplate;
function isHostViewFactory(type: any): boolean {
return type instanceof HostViewFactory;
}
@Injectable()
export class Compiler_ extends Compiler {
constructor(private _protoViewFactory: ProtoViewFactory) { super(); }
compileInHost(componentType: Type): Promise<ProtoViewRef> {
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
var metadatas = reflector.annotations(componentType);
var compiledHostTemplate = metadatas.find(_isCompiledHostTemplate);
var hostViewFactory = metadatas.find(isHostViewFactory);
if (isBlank(compiledHostTemplate)) {
throw new BaseException(
`No precompiled template for component ${stringify(componentType)} found`);
if (isBlank(hostViewFactory)) {
throw new BaseException(`No precompiled component ${stringify(componentType)} found`);
}
return PromiseWrapper.resolve(this._createProtoView(compiledHostTemplate));
return PromiseWrapper.resolve(new HostViewFactoryRef_(hostViewFactory));
}
private _createProtoView(compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
return this._protoViewFactory.createHost(compiledHostTemplate).ref;
}
clearCache() { this._protoViewFactory.clearCache(); }
}
export function internalCreateProtoView(compiler: Compiler,
compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
return (<any>compiler)._createProtoView(compiledHostTemplate);
clearCache() {}
}

View File

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

View File

@ -3,8 +3,8 @@ import {Compiler} from './compiler';
import {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/async';
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
import {ElementRef} from './element_ref';
import {ViewRef, HostViewRef} from './view_ref';
import {ElementRef, ElementRef_} from './element_ref';
import {HostViewRef} from './view_ref';
/**
* Represents an instance of a Component created via {@link DynamicComponentLoader}.
@ -42,7 +42,9 @@ export abstract class ComponentRef {
/**
* The {@link ViewRef} of the Host View of this Component instance.
*/
get hostView(): HostViewRef { return this.location.parentView; }
get hostView(): HostViewRef {
return (<ElementRef_>this.location).internalElement.parentView.ref;
}
/**
* @internal
@ -140,7 +142,7 @@ export abstract class DynamicComponentLoader {
* ```
*/
abstract loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
onDispose?: () => void): Promise<ComponentRef>;
onDispose?: () => void, projectableNodes?: any[][]): Promise<ComponentRef>;
/**
* Creates an instance of a Component and attaches it to a View Container located inside of the
@ -190,7 +192,8 @@ export abstract class DynamicComponentLoader {
* ```
*/
abstract loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
providers?: ResolvedProvider[]): Promise<ComponentRef>;
providers?: ResolvedProvider[],
projectableNodes?: any[][]): Promise<ComponentRef>;
/**
* Creates an instance of a Component and attaches it to the View Container found at the
@ -232,19 +235,19 @@ export abstract class DynamicComponentLoader {
* <child-component>Child</child-component>
* ```
*/
abstract loadNextToLocation(type: Type, location: ElementRef,
providers?: ResolvedProvider[]): Promise<ComponentRef>;
abstract loadNextToLocation(type: Type, location: ElementRef, providers?: ResolvedProvider[],
projectableNodes?: any[][]): Promise<ComponentRef>;
}
@Injectable()
export class DynamicComponentLoader_ extends DynamicComponentLoader {
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); }
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
onDispose?: () => void): Promise<ComponentRef> {
loadAsRoot(type: Type, overrideSelector: string, injector: Injector, onDispose?: () => void,
projectableNodes?: any[][]): Promise<ComponentRef> {
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
var hostViewRef =
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector,
injector, projectableNodes);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
@ -259,24 +262,25 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader {
}
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
providers: ResolvedProvider[] = null,
projectableNodes: any[][] = null): Promise<ComponentRef> {
return this.loadNextToLocation(
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
providers);
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), providers,
projectableNodes);
}
loadNextToLocation(type: Type, location: ElementRef,
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
loadNextToLocation(type: Type, location: ElementRef, providers: ResolvedProvider[] = null,
projectableNodes: any[][] = null): Promise<ComponentRef> {
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
var viewContainer = this._viewManager.getViewContainer(location);
var hostViewRef =
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, providers);
var hostViewRef = viewContainer.createHostView(hostProtoViewRef, viewContainer.length,
providers, projectableNodes);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
var dispose = () => {
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
if (index !== -1) {
var index = viewContainer.indexOf(hostViewRef);
if (!hostViewRef.destroyed && index !== -1) {
viewContainer.remove(index);
}
};

View File

@ -0,0 +1,867 @@
import {
isPresent,
isBlank,
Type,
stringify,
CONST_EXPR,
StringWrapper
} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {
Injector,
Key,
Dependency,
provide,
Provider,
ResolvedProvider,
NoProviderError,
AbstractProviderError,
CyclicDependencyError,
resolveForwardRef,
Injectable
} from 'angular2/src/core/di';
import {mergeResolvedProviders} from 'angular2/src/core/di/provider';
import {
UNDEFINED,
ProtoInjector,
Visibility,
InjectorInlineStrategy,
InjectorDynamicStrategy,
ProviderWithVisibility,
DependencyProvider
} from 'angular2/src/core/di/injector';
import {resolveProvider, ResolvedFactory, ResolvedProvider_} from 'angular2/src/core/di/provider';
import {AttributeMetadata, QueryMetadata} from '../metadata/di';
import {AppView} from './view';
import {ViewType} from './view_type';
import {ElementRef_} from './element_ref';
import {ViewContainerRef} from './view_container_ref';
import {ElementRef} from './element_ref';
import {Renderer} from 'angular2/src/core/render/api';
import {TemplateRef, TemplateRef_} from './template_ref';
import {DirectiveMetadata, ComponentMetadata} from '../metadata/directives';
import {
ChangeDetector,
ChangeDetectorRef
} from 'angular2/src/core/change_detection/change_detection';
import {QueryList} from './query_list';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {SetterFn} from 'angular2/src/core/reflection/types';
import {AfterViewChecked} from 'angular2/src/core/linker/interfaces';
import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider';
import {ViewContainerRef_} from "./view_container_ref";
import {ResolvedMetadataCache} from './resolved_metadata_cache';
var _staticKeys;
export class StaticKeys {
templateRefId: number;
viewContainerId: number;
changeDetectorRefId: number;
elementRefId: number;
rendererId: number;
constructor() {
this.templateRefId = Key.get(TemplateRef).id;
this.viewContainerId = Key.get(ViewContainerRef).id;
this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
this.elementRefId = Key.get(ElementRef).id;
this.rendererId = Key.get(Renderer).id;
}
static instance(): StaticKeys {
if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
return _staticKeys;
}
}
export class DirectiveDependency extends Dependency {
constructor(key: Key, optional: boolean, lowerBoundVisibility: Object,
upperBoundVisibility: Object, properties: any[], public attributeName: string,
public queryDecorator: QueryMetadata) {
super(key, optional, lowerBoundVisibility, upperBoundVisibility, properties);
this._verify();
}
/** @internal */
_verify(): void {
var count = 0;
if (isPresent(this.queryDecorator)) count++;
if (isPresent(this.attributeName)) count++;
if (count > 1)
throw new BaseException(
'A directive injectable can contain only one of the following @Attribute or @Query.');
}
static createFrom(d: Dependency): DirectiveDependency {
return new DirectiveDependency(
d.key, d.optional, d.lowerBoundVisibility, d.upperBoundVisibility, d.properties,
DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties));
}
/** @internal */
static _attributeName(properties: any[]): string {
var p = <AttributeMetadata>properties.find(p => p instanceof AttributeMetadata);
return isPresent(p) ? p.attributeName : null;
}
/** @internal */
static _query(properties: any[]): QueryMetadata {
return <QueryMetadata>properties.find(p => p instanceof QueryMetadata);
}
}
export class DirectiveProvider extends ResolvedProvider_ {
constructor(key: Key, factory: Function, deps: Dependency[], public isComponent: boolean,
public providers: ResolvedProvider[], public viewProviders: ResolvedProvider[],
public queries: QueryMetadataWithSetter[]) {
super(key, [new ResolvedFactory(factory, deps)], false);
}
get displayName(): string { return this.key.displayName; }
static createFromType(type: Type, meta: DirectiveMetadata): DirectiveProvider {
var provider = new Provider(type, {useClass: type});
if (isBlank(meta)) {
meta = new DirectiveMetadata();
}
var rb = resolveProvider(provider);
var rf = rb.resolvedFactories[0];
var deps: DirectiveDependency[] = rf.dependencies.map(DirectiveDependency.createFrom);
var isComponent = meta instanceof ComponentMetadata;
var resolvedProviders = isPresent(meta.providers) ? Injector.resolve(meta.providers) : null;
var resolvedViewProviders = meta instanceof ComponentMetadata && isPresent(meta.viewProviders) ?
Injector.resolve(meta.viewProviders) :
null;
var queries = [];
if (isPresent(meta.queries)) {
StringMapWrapper.forEach(meta.queries, (meta, fieldName) => {
var setter = reflector.setter(fieldName);
queries.push(new QueryMetadataWithSetter(setter, meta));
});
}
// queries passed into the constructor.
// TODO: remove this after constructor queries are no longer supported
deps.forEach(d => {
if (isPresent(d.queryDecorator)) {
queries.push(new QueryMetadataWithSetter(null, d.queryDecorator));
}
});
return new DirectiveProvider(rb.key, rf.factory, deps, isComponent, resolvedProviders,
resolvedViewProviders, queries);
}
}
export class QueryMetadataWithSetter {
constructor(public setter: SetterFn, public metadata: QueryMetadata) {}
}
function setProvidersVisibility(providers: ResolvedProvider[], visibility: Visibility,
result: Map<number, Visibility>) {
for (var i = 0; i < providers.length; i++) {
result.set(providers[i].key.id, visibility);
}
}
export class AppProtoElement {
protoInjector: ProtoInjector;
static create(metadataCache: ResolvedMetadataCache, index: number,
attributes: {[key: string]: string}, directiveTypes: Type[],
directiveVariableBindings: {[key: string]: number}): AppProtoElement {
var componentDirProvider = null;
var mergedProvidersMap: Map<number, ResolvedProvider> = new Map<number, ResolvedProvider>();
var providerVisibilityMap: Map<number, Visibility> = new Map<number, Visibility>();
var providers = ListWrapper.createGrowableSize(directiveTypes.length);
var protoQueryRefs = [];
for (var i = 0; i < directiveTypes.length; i++) {
var dirProvider = metadataCache.getResolvedDirectiveMetadata(directiveTypes[i]);
providers[i] = new ProviderWithVisibility(
dirProvider, dirProvider.isComponent ? Visibility.PublicAndPrivate : Visibility.Public);
if (dirProvider.isComponent) {
componentDirProvider = dirProvider;
} else {
if (isPresent(dirProvider.providers)) {
mergeResolvedProviders(dirProvider.providers, mergedProvidersMap);
setProvidersVisibility(dirProvider.providers, Visibility.Public, providerVisibilityMap);
}
}
if (isPresent(dirProvider.viewProviders)) {
mergeResolvedProviders(dirProvider.viewProviders, mergedProvidersMap);
setProvidersVisibility(dirProvider.viewProviders, Visibility.Private,
providerVisibilityMap);
}
for (var queryIdx = 0; queryIdx < dirProvider.queries.length; queryIdx++) {
var q = dirProvider.queries[queryIdx];
protoQueryRefs.push(new ProtoQueryRef(i, q.setter, q.metadata));
}
}
if (isPresent(componentDirProvider) && isPresent(componentDirProvider.providers)) {
// directive providers need to be prioritized over component providers
mergeResolvedProviders(componentDirProvider.providers, mergedProvidersMap);
setProvidersVisibility(componentDirProvider.providers, Visibility.Public,
providerVisibilityMap);
}
mergedProvidersMap.forEach((provider, _) => {
providers.push(
new ProviderWithVisibility(provider, providerVisibilityMap.get(provider.key.id)));
});
return new AppProtoElement(isPresent(componentDirProvider), index, attributes, providers,
protoQueryRefs, directiveVariableBindings);
}
constructor(public firstProviderIsComponent: boolean, public index: number,
public attributes: {[key: string]: string}, pwvs: ProviderWithVisibility[],
public protoQueryRefs: ProtoQueryRef[],
public directiveVariableBindings: {[key: string]: number}) {
var length = pwvs.length;
if (length > 0) {
this.protoInjector = new ProtoInjector(pwvs);
} else {
this.protoInjector = null;
this.protoQueryRefs = [];
}
}
getProviderAtIndex(index: number): any { return this.protoInjector.getProviderAtIndex(index); }
}
class _Context {
constructor(public element: any, public componentElement: any, public injector: any) {}
}
export class InjectorWithHostBoundary {
constructor(public injector: Injector, public hostInjectorBoundary: boolean) {}
}
export class AppElement implements DependencyProvider, ElementRef, AfterViewChecked {
static getViewParentInjector(parentViewType: ViewType, containerAppElement: AppElement,
imperativelyCreatedProviders: ResolvedProvider[],
rootInjector: Injector): InjectorWithHostBoundary {
var parentInjector;
var hostInjectorBoundary;
switch (parentViewType) {
case ViewType.COMPONENT:
parentInjector = containerAppElement._injector;
hostInjectorBoundary = true;
break;
case ViewType.EMBEDDED:
parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
containerAppElement._injector.parent :
containerAppElement._injector;
hostInjectorBoundary = containerAppElement._injector.hostBoundary;
break;
case ViewType.HOST:
if (isPresent(containerAppElement)) {
// host view is attached to a container
parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
containerAppElement._injector.parent :
containerAppElement._injector;
if (isPresent(imperativelyCreatedProviders)) {
var imperativeProvidersWithVisibility = imperativelyCreatedProviders.map(
p => new ProviderWithVisibility(p, Visibility.Public));
// The imperative injector is similar to having an element between
// the dynamic-loaded component and its parent => no boundary between
// the component and imperativelyCreatedInjector.
parentInjector = new Injector(new ProtoInjector(imperativeProvidersWithVisibility),
parentInjector, true, null, null);
hostInjectorBoundary = false;
} else {
hostInjectorBoundary = containerAppElement._injector.hostBoundary;
}
} else {
// bootstrap
parentInjector = rootInjector;
hostInjectorBoundary = true;
}
break;
}
return new InjectorWithHostBoundary(parentInjector, hostInjectorBoundary);
}
public nestedViews: AppView[] = null;
public componentView: AppView = null;
private _queryStrategy: _QueryStrategy;
private _injector: Injector;
private _strategy: _ElementDirectiveStrategy;
public ref: ElementRef_;
constructor(public proto: AppProtoElement, public parentView: AppView, public parent: AppElement,
public nativeElement: any, public embeddedViewFactory: Function) {
this.ref = new ElementRef_(this);
var parentInjector = isPresent(parent) ? parent._injector : parentView.parentInjector;
if (isPresent(this.proto.protoInjector)) {
var isBoundary;
if (isPresent(parent) && isPresent(parent.proto.protoInjector)) {
isBoundary = false;
} else {
isBoundary = parentView.hostInjectorBoundary;
}
this._queryStrategy = this._buildQueryStrategy();
this._injector = new Injector(this.proto.protoInjector, parentInjector, isBoundary, this,
() => this._debugContext());
// we couple ourselves to the injector strategy to avoid polymorphic calls
var injectorStrategy = <any>this._injector.internalStrategy;
this._strategy = injectorStrategy instanceof InjectorInlineStrategy ?
new ElementDirectiveInlineStrategy(injectorStrategy, this) :
new ElementDirectiveDynamicStrategy(injectorStrategy, this);
this._strategy.init();
} else {
this._queryStrategy = null;
this._injector = parentInjector;
this._strategy = null;
}
}
attachComponentView(componentView: AppView) { this.componentView = componentView; }
private _debugContext(): any {
var c = this.parentView.getDebugContext(this, null, null);
return isPresent(c) ? new _Context(c.element, c.componentElement, c.injector) : null;
}
hasVariableBinding(name: string): boolean {
var vb = this.proto.directiveVariableBindings;
return isPresent(vb) && StringMapWrapper.contains(vb, name);
}
getVariableBinding(name: string): any {
var index = this.proto.directiveVariableBindings[name];
return isPresent(index) ? this.getDirectiveAtIndex(<number>index) : this.getElementRef();
}
get(token: any): any { return this._injector.get(token); }
hasDirective(type: Type): boolean { return isPresent(this._injector.getOptional(type)); }
getComponent(): any { return isPresent(this._strategy) ? this._strategy.getComponent() : null; }
getInjector(): Injector { return this._injector; }
getElementRef(): ElementRef { return this.ref; }
getViewContainerRef(): ViewContainerRef { return new ViewContainerRef_(this); }
getTemplateRef(): TemplateRef {
if (isPresent(this.embeddedViewFactory)) {
return new TemplateRef_(this.ref);
}
return null;
}
getDependency(injector: Injector, provider: ResolvedProvider, dep: Dependency): any {
if (provider instanceof DirectiveProvider) {
var dirDep = <DirectiveDependency>dep;
if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep);
if (isPresent(dirDep.queryDecorator))
return this._queryStrategy.findQuery(dirDep.queryDecorator).list;
if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) {
// We provide the component's view change detector to components and
// the surrounding component's change detector to directives.
if (this.proto.firstProviderIsComponent) {
// Note: The component view is not yet created when
// this method is called!
return new _ComponentViewChangeDetectorRef(this);
} else {
return this.parentView.changeDetector.ref;
}
}
if (dirDep.key.id === StaticKeys.instance().elementRefId) {
return this.getElementRef();
}
if (dirDep.key.id === StaticKeys.instance().viewContainerId) {
return this.getViewContainerRef();
}
if (dirDep.key.id === StaticKeys.instance().templateRefId) {
var tr = this.getTemplateRef();
if (isBlank(tr) && !dirDep.optional) {
throw new NoProviderError(null, dirDep.key);
}
return tr;
}
if (dirDep.key.id === StaticKeys.instance().rendererId) {
return this.parentView.renderer;
}
} else if (provider instanceof PipeProvider) {
if (dep.key.id === StaticKeys.instance().changeDetectorRefId) {
// We provide the component's view change detector to components and
// the surrounding component's change detector to directives.
if (this.proto.firstProviderIsComponent) {
// Note: The component view is not yet created when
// this method is called!
return new _ComponentViewChangeDetectorRef(this);
} else {
return this.parentView.changeDetector;
}
}
}
return UNDEFINED;
}
private _buildAttribute(dep: DirectiveDependency): string {
var attributes = this.proto.attributes;
if (isPresent(attributes) && StringMapWrapper.contains(attributes, dep.attributeName)) {
return attributes[dep.attributeName];
} else {
return null;
}
}
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
var templateRef = this.getTemplateRef();
if (query.selector === TemplateRef && isPresent(templateRef)) {
list.push(templateRef);
}
if (this._strategy != null) {
this._strategy.addDirectivesMatchingQuery(query, list);
}
}
private _buildQueryStrategy(): _QueryStrategy {
if (this.proto.protoQueryRefs.length === 0) {
return _emptyQueryStrategy;
} else if (this.proto.protoQueryRefs.length <=
InlineQueryStrategy.NUMBER_OF_SUPPORTED_QUERIES) {
return new InlineQueryStrategy(this);
} else {
return new DynamicQueryStrategy(this);
}
}
getDirectiveAtIndex(index: number): any { return this._injector.getAt(index); }
ngAfterViewChecked(): void {
if (isPresent(this._queryStrategy)) this._queryStrategy.updateViewQueries();
}
ngAfterContentChecked(): void {
if (isPresent(this._queryStrategy)) this._queryStrategy.updateContentQueries();
}
traverseAndSetQueriesAsDirty(): void {
var inj: AppElement = this;
while (isPresent(inj)) {
inj._setQueriesAsDirty();
inj = inj.parent;
}
}
private _setQueriesAsDirty(): void {
if (isPresent(this._queryStrategy)) {
this._queryStrategy.setContentQueriesAsDirty();
}
if (this.parentView.proto.type === ViewType.COMPONENT) {
this.parentView.containerAppElement._queryStrategy.setViewQueriesAsDirty();
}
}
}
interface _QueryStrategy {
setContentQueriesAsDirty(): void;
setViewQueriesAsDirty(): void;
updateContentQueries(): void;
updateViewQueries(): void;
findQuery(query: QueryMetadata): QueryRef;
}
class _EmptyQueryStrategy implements _QueryStrategy {
setContentQueriesAsDirty(): void {}
setViewQueriesAsDirty(): void {}
updateContentQueries(): void {}
updateViewQueries(): void {}
findQuery(query: QueryMetadata): QueryRef {
throw new BaseException(`Cannot find query for directive ${query}.`);
}
}
var _emptyQueryStrategy = new _EmptyQueryStrategy();
class InlineQueryStrategy implements _QueryStrategy {
static NUMBER_OF_SUPPORTED_QUERIES = 3;
query0: QueryRef;
query1: QueryRef;
query2: QueryRef;
constructor(ei: AppElement) {
var protoRefs = ei.proto.protoQueryRefs;
if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], ei);
if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], ei);
if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], ei);
}
setContentQueriesAsDirty(): void {
if (isPresent(this.query0) && !this.query0.isViewQuery) this.query0.dirty = true;
if (isPresent(this.query1) && !this.query1.isViewQuery) this.query1.dirty = true;
if (isPresent(this.query2) && !this.query2.isViewQuery) this.query2.dirty = true;
}
setViewQueriesAsDirty(): void {
if (isPresent(this.query0) && this.query0.isViewQuery) this.query0.dirty = true;
if (isPresent(this.query1) && this.query1.isViewQuery) this.query1.dirty = true;
if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true;
}
updateContentQueries() {
if (isPresent(this.query0) && !this.query0.isViewQuery) {
this.query0.update();
}
if (isPresent(this.query1) && !this.query1.isViewQuery) {
this.query1.update();
}
if (isPresent(this.query2) && !this.query2.isViewQuery) {
this.query2.update();
}
}
updateViewQueries() {
if (isPresent(this.query0) && this.query0.isViewQuery) {
this.query0.update();
}
if (isPresent(this.query1) && this.query1.isViewQuery) {
this.query1.update();
}
if (isPresent(this.query2) && this.query2.isViewQuery) {
this.query2.update();
}
}
findQuery(query: QueryMetadata): QueryRef {
if (isPresent(this.query0) && this.query0.protoQueryRef.query === query) {
return this.query0;
}
if (isPresent(this.query1) && this.query1.protoQueryRef.query === query) {
return this.query1;
}
if (isPresent(this.query2) && this.query2.protoQueryRef.query === query) {
return this.query2;
}
throw new BaseException(`Cannot find query for directive ${query}.`);
}
}
class DynamicQueryStrategy implements _QueryStrategy {
queries: QueryRef[];
constructor(ei: AppElement) {
this.queries = ei.proto.protoQueryRefs.map(p => new QueryRef(p, ei));
}
setContentQueriesAsDirty(): void {
for (var i = 0; i < this.queries.length; ++i) {
var q = this.queries[i];
if (!q.isViewQuery) q.dirty = true;
}
}
setViewQueriesAsDirty(): void {
for (var i = 0; i < this.queries.length; ++i) {
var q = this.queries[i];
if (q.isViewQuery) q.dirty = true;
}
}
updateContentQueries() {
for (var i = 0; i < this.queries.length; ++i) {
var q = this.queries[i];
if (!q.isViewQuery) {
q.update();
}
}
}
updateViewQueries() {
for (var i = 0; i < this.queries.length; ++i) {
var q = this.queries[i];
if (q.isViewQuery) {
q.update();
}
}
}
findQuery(query: QueryMetadata): QueryRef {
for (var i = 0; i < this.queries.length; ++i) {
var q = this.queries[i];
if (q.protoQueryRef.query === query) {
return q;
}
}
throw new BaseException(`Cannot find query for directive ${query}.`);
}
}
interface _ElementDirectiveStrategy {
getComponent(): any;
isComponentKey(key: Key): boolean;
addDirectivesMatchingQuery(q: QueryMetadata, res: any[]): void;
init(): void;
}
/**
* Strategy used by the `ElementInjector` when the number of providers is 10 or less.
* In such a case, inlining fields is beneficial for performances.
*/
class ElementDirectiveInlineStrategy implements _ElementDirectiveStrategy {
constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: AppElement) {}
init(): void {
var i = this.injectorStrategy;
var p = i.protoStrategy;
i.resetConstructionCounter();
if (p.provider0 instanceof DirectiveProvider && isPresent(p.keyId0) && i.obj0 === UNDEFINED)
i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
if (p.provider1 instanceof DirectiveProvider && isPresent(p.keyId1) && i.obj1 === UNDEFINED)
i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
if (p.provider2 instanceof DirectiveProvider && isPresent(p.keyId2) && i.obj2 === UNDEFINED)
i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
if (p.provider3 instanceof DirectiveProvider && isPresent(p.keyId3) && i.obj3 === UNDEFINED)
i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
if (p.provider4 instanceof DirectiveProvider && isPresent(p.keyId4) && i.obj4 === UNDEFINED)
i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
if (p.provider5 instanceof DirectiveProvider && isPresent(p.keyId5) && i.obj5 === UNDEFINED)
i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
if (p.provider6 instanceof DirectiveProvider && isPresent(p.keyId6) && i.obj6 === UNDEFINED)
i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
if (p.provider7 instanceof DirectiveProvider && isPresent(p.keyId7) && i.obj7 === UNDEFINED)
i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
if (p.provider8 instanceof DirectiveProvider && isPresent(p.keyId8) && i.obj8 === UNDEFINED)
i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
if (p.provider9 instanceof DirectiveProvider && isPresent(p.keyId9) && i.obj9 === UNDEFINED)
i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
}
getComponent(): any { return this.injectorStrategy.obj0; }
isComponentKey(key: Key): boolean {
return this._ei.proto.firstProviderIsComponent && isPresent(key) &&
key.id === this.injectorStrategy.protoStrategy.keyId0;
}
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
var i = this.injectorStrategy;
var p = i.protoStrategy;
if (isPresent(p.provider0) && p.provider0.key.token === query.selector) {
if (i.obj0 === UNDEFINED) i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
list.push(i.obj0);
}
if (isPresent(p.provider1) && p.provider1.key.token === query.selector) {
if (i.obj1 === UNDEFINED) i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
list.push(i.obj1);
}
if (isPresent(p.provider2) && p.provider2.key.token === query.selector) {
if (i.obj2 === UNDEFINED) i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
list.push(i.obj2);
}
if (isPresent(p.provider3) && p.provider3.key.token === query.selector) {
if (i.obj3 === UNDEFINED) i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
list.push(i.obj3);
}
if (isPresent(p.provider4) && p.provider4.key.token === query.selector) {
if (i.obj4 === UNDEFINED) i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
list.push(i.obj4);
}
if (isPresent(p.provider5) && p.provider5.key.token === query.selector) {
if (i.obj5 === UNDEFINED) i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
list.push(i.obj5);
}
if (isPresent(p.provider6) && p.provider6.key.token === query.selector) {
if (i.obj6 === UNDEFINED) i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
list.push(i.obj6);
}
if (isPresent(p.provider7) && p.provider7.key.token === query.selector) {
if (i.obj7 === UNDEFINED) i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
list.push(i.obj7);
}
if (isPresent(p.provider8) && p.provider8.key.token === query.selector) {
if (i.obj8 === UNDEFINED) i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
list.push(i.obj8);
}
if (isPresent(p.provider9) && p.provider9.key.token === query.selector) {
if (i.obj9 === UNDEFINED) i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
list.push(i.obj9);
}
}
}
/**
* Strategy used by the `ElementInjector` when the number of bindings is 11 or more.
* In such a case, there are too many fields to inline (see ElementInjectorInlineStrategy).
*/
class ElementDirectiveDynamicStrategy implements _ElementDirectiveStrategy {
constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: AppElement) {}
init(): void {
var inj = this.injectorStrategy;
var p = inj.protoStrategy;
inj.resetConstructionCounter();
for (var i = 0; i < p.keyIds.length; i++) {
if (p.providers[i] instanceof DirectiveProvider && isPresent(p.keyIds[i]) &&
inj.objs[i] === UNDEFINED) {
inj.objs[i] = inj.instantiateProvider(p.providers[i], p.visibilities[i]);
}
}
}
getComponent(): any { return this.injectorStrategy.objs[0]; }
isComponentKey(key: Key): boolean {
var p = this.injectorStrategy.protoStrategy;
return this._ei.proto.firstProviderIsComponent && isPresent(key) && key.id === p.keyIds[0];
}
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
var ist = this.injectorStrategy;
var p = ist.protoStrategy;
for (var i = 0; i < p.providers.length; i++) {
if (p.providers[i].key.token === query.selector) {
if (ist.objs[i] === UNDEFINED) {
ist.objs[i] = ist.instantiateProvider(p.providers[i], p.visibilities[i]);
}
list.push(ist.objs[i]);
}
}
}
}
export class ProtoQueryRef {
constructor(public dirIndex: number, public setter: SetterFn, public query: QueryMetadata) {}
get usesPropertySyntax(): boolean { return isPresent(this.setter); }
}
export class QueryRef {
public list: QueryList<any>;
public dirty: boolean;
constructor(public protoQueryRef: ProtoQueryRef, private originator: AppElement) {
this.list = new QueryList<any>();
this.dirty = true;
}
get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; }
update(): void {
if (!this.dirty) return;
this._update();
this.dirty = false;
// TODO delete the check once only field queries are supported
if (this.protoQueryRef.usesPropertySyntax) {
var dir = this.originator.getDirectiveAtIndex(this.protoQueryRef.dirIndex);
if (this.protoQueryRef.query.first) {
this.protoQueryRef.setter(dir, this.list.length > 0 ? this.list.first : null);
} else {
this.protoQueryRef.setter(dir, this.list);
}
}
this.list.notifyOnChanges();
}
private _update(): void {
var aggregator = [];
if (this.protoQueryRef.query.isViewQuery) {
// intentionally skipping originator for view queries.
var nestedView = this.originator.componentView;
if (isPresent(nestedView)) this._visitView(nestedView, aggregator);
} else {
this._visit(this.originator, aggregator);
}
this.list.reset(aggregator);
};
private _visit(inj: AppElement, aggregator: any[]): void {
var view = inj.parentView;
var startIdx = inj.proto.index;
for (var i = startIdx; i < view.appElements.length; i++) {
var curInj = view.appElements[i];
// The first injector after inj, that is outside the subtree rooted at
// inj has to have a null parent or a parent that is an ancestor of inj.
if (i > startIdx && (isBlank(curInj.parent) || curInj.parent.proto.index < startIdx)) {
break;
}
if (!this.protoQueryRef.query.descendants &&
!(curInj.parent == this.originator || curInj == this.originator))
continue;
// We visit the view container(VC) views right after the injector that contains
// the VC. Theoretically, that might not be the right order if there are
// child injectors of said injector. Not clear whether if such case can
// even be constructed with the current apis.
this._visitInjector(curInj, aggregator);
this._visitViewContainerViews(curInj.nestedViews, aggregator);
}
}
private _visitInjector(inj: AppElement, aggregator: any[]) {
if (this.protoQueryRef.query.isVarBindingQuery) {
this._aggregateVariableBinding(inj, aggregator);
} else {
this._aggregateDirective(inj, aggregator);
}
}
private _visitViewContainerViews(views: AppView[], aggregator: any[]) {
if (isPresent(views)) {
for (var j = 0; j < views.length; j++) {
this._visitView(views[j], aggregator);
}
}
}
private _visitView(view: AppView, aggregator: any[]) {
for (var i = 0; i < view.appElements.length; i++) {
var inj = view.appElements[i];
this._visitInjector(inj, aggregator);
this._visitViewContainerViews(inj.nestedViews, aggregator);
}
}
private _aggregateVariableBinding(inj: AppElement, aggregator: any[]): void {
var vb = this.protoQueryRef.query.varBindings;
for (var i = 0; i < vb.length; ++i) {
if (inj.hasVariableBinding(vb[i])) {
aggregator.push(inj.getVariableBinding(vb[i]));
}
}
}
private _aggregateDirective(inj: AppElement, aggregator: any[]): void {
inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator);
}
}
class _ComponentViewChangeDetectorRef extends ChangeDetectorRef {
constructor(private _appElement: AppElement) { super(); }
markForCheck(): void { this._appElement.componentView.changeDetector.ref.markForCheck(); }
detach(): void { this._appElement.componentView.changeDetector.ref.detach(); }
detectChanges(): void { this._appElement.componentView.changeDetector.ref.detectChanges(); }
checkNoChanges(): void { this._appElement.componentView.changeDetector.ref.checkNoChanges(); }
reattach(): void { this._appElement.componentView.changeDetector.ref.reattach(); }
}

View File

@ -1,16 +0,0 @@
import {isBlank} from 'angular2/src/facade/lang';
import {BaseException} from 'angular2/src/facade/exceptions';
import * as eiModule from './element_injector';
import {DirectiveProvider} from './element_injector';
import * as viewModule from './view';
export class ElementBinder {
constructor(public index: number, public parent: ElementBinder, public distanceToParent: number,
public protoElementInjector: eiModule.ProtoElementInjector,
public componentDirective: DirectiveProvider,
public nestedProtoView: viewModule.AppProtoView) {
if (isBlank(index)) {
throw new BaseException('null index not allowed.');
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
import {ViewRef, ViewRef_} from './view_ref';
import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/render/api';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {AppElement} from './element';
/**
* Represents a location in a View that has an injection, change-detection and render context
@ -12,23 +11,7 @@ import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/rende
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
* element.
*/
export abstract class ElementRef implements RenderElementRef {
/**
* @internal
*
* Reference to the {@link ViewRef} that this `ElementRef` is part of.
*/
parentView: ViewRef;
/**
* @internal
*
* Index of the element inside the {@link ViewRef}.
*
* This is used internally by the Angular framework to locate elements.
*/
boundElementIndex: number;
export abstract class ElementRef {
/**
* The underlying native element or `null` if direct access to native elements is not supported
* (e.g. when the application runs in a web worker).
@ -48,24 +31,13 @@ export abstract class ElementRef implements RenderElementRef {
* </p>
* </div>
*/
get nativeElement(): any { return unimplemented(); };
get renderView(): RenderViewRef { return unimplemented(); }
get nativeElement(): any { return unimplemented(); }
}
export class ElementRef_ extends ElementRef {
constructor(public parentView: ViewRef,
export class ElementRef_ implements ElementRef {
constructor(private _appElement: AppElement) {}
/**
* Index of the element inside the {@link ViewRef}.
*
* This is used internally by the Angular framework to locate elements.
*/
public boundElementIndex: number, private _renderer: Renderer) {
super();
}
get internalElement(): AppElement { return this._appElement; }
get renderView(): RenderViewRef { return (<ViewRef_>this.parentView).render; }
set renderView(value) { unimplemented(); }
get nativeElement(): any { return this._renderer.getNativeElementSync(this); }
get nativeElement() { return this._appElement.nativeElement; }
}

View File

@ -1,22 +0,0 @@
export const EVENT_TARGET_SEPARATOR = ':';
export class EventConfig {
constructor(public fieldName: string, public eventName: string, public isLongForm: boolean) {}
static parse(eventConfig: string): EventConfig {
var fieldName = eventConfig, eventName = eventConfig, isLongForm = false;
var separatorIdx = eventConfig.indexOf(EVENT_TARGET_SEPARATOR);
if (separatorIdx > -1) {
// long format: 'fieldName: eventName'
fieldName = eventConfig.substring(0, separatorIdx).trim();
eventName = eventConfig.substring(separatorIdx + 1).trim();
isLongForm = true;
}
return new EventConfig(fieldName, eventName, isLongForm);
}
getFullName(): string {
return this.isLongForm ? `${this.fieldName}${EVENT_TARGET_SEPARATOR}${this.eventName}` :
this.eventName;
}
}

View File

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

View File

@ -1,341 +0,0 @@
import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/facade/lang';
import {RenderProtoViewRef, RenderComponentTemplate} from 'angular2/src/core/render/api';
import {Optional, Injectable, Provider, resolveForwardRef, Inject} from 'angular2/src/core/di';
import {PipeProvider} from '../pipes/pipe_provider';
import {ProtoPipes} from '../pipes/pipes';
import {AppProtoView, AppProtoViewMergeInfo, ViewType} from './view';
import {ElementBinder} from './element_binder';
import {ProtoElementInjector, DirectiveProvider} from './element_injector';
import {DirectiveResolver} from './directive_resolver';
import {ViewResolver} from './view_resolver';
import {PipeResolver} from './pipe_resolver';
import {ViewMetadata, ViewEncapsulation} from '../metadata/view';
import {PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
import {
visitAllCommands,
CompiledComponentTemplate,
CompiledHostTemplate,
TemplateCmd,
CommandVisitor,
EmbeddedTemplateCmd,
BeginComponentCmd,
BeginElementCmd,
IBeginElementCmd,
TextCmd,
NgContentCmd
} from './template_commands';
import {Renderer} from 'angular2/src/core/render/api';
import {APP_ID} from 'angular2/src/core/application_tokens';
@Injectable()
export class ProtoViewFactory {
private _cache: Map<string, AppProtoView> = new Map<string, AppProtoView>();
private _nextTemplateId: number = 0;
constructor(private _renderer: Renderer,
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Array<Type | any[]>,
private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
private _pipeResolver: PipeResolver, @Inject(APP_ID) private _appId: string) {}
clearCache() { this._cache.clear(); }
createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
var compiledTemplate = compiledHostTemplate.template;
var result = this._cache.get(compiledTemplate.id);
if (isBlank(result)) {
var emptyMap: {[key: string]: PipeProvider} = {};
var shortId = `${this._appId}-${this._nextTemplateId++}`;
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
compiledTemplate.id, shortId, ViewEncapsulation.None, compiledTemplate.commands, []));
result =
new AppProtoView(compiledTemplate.id, compiledTemplate.commands, ViewType.HOST, true,
compiledTemplate.changeDetectorFactory, null, new ProtoPipes(emptyMap));
this._cache.set(compiledTemplate.id, result);
}
return result;
}
private _createComponent(cmd: BeginComponentCmd): AppProtoView {
var nestedProtoView = this._cache.get(cmd.templateId);
if (isBlank(nestedProtoView)) {
var component = cmd.directives[0];
var view = this._viewResolver.resolve(component);
var compiledTemplate = cmd.templateGetter();
var styles = _flattenStyleArr(compiledTemplate.styles, []);
var shortId = `${this._appId}-${this._nextTemplateId++}`;
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
compiledTemplate.id, shortId, cmd.encapsulation, compiledTemplate.commands, styles));
var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
nestedProtoView = new AppProtoView(
compiledTemplate.id, compiledTemplate.commands, ViewType.COMPONENT, true,
compiledTemplate.changeDetectorFactory, null, ProtoPipes.fromProviders(boundPipes));
// Note: The cache is updated before recursing
// to be able to resolve cycles
this._cache.set(compiledTemplate.id, nestedProtoView);
this._initializeProtoView(nestedProtoView, null);
}
return nestedProtoView;
}
private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView {
var nestedProtoView = new AppProtoView(
parent.templateId, cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory,
arrayToMap(cmd.variableNameAndValues, true), new ProtoPipes(parent.pipes.config));
if (cmd.isMerged) {
this.initializeProtoViewIfNeeded(nestedProtoView);
}
return nestedProtoView;
}
initializeProtoViewIfNeeded(protoView: AppProtoView) {
if (!protoView.isInitialized()) {
var render = this._renderer.createProtoView(protoView.templateId, protoView.templateCmds);
this._initializeProtoView(protoView, render);
}
}
private _initializeProtoView(protoView: AppProtoView, render: RenderProtoViewRef) {
var initializer = new _ProtoViewInitializer(protoView, this._directiveResolver, this);
visitAllCommands(initializer, protoView.templateCmds);
var mergeInfo =
new AppProtoViewMergeInfo(initializer.mergeEmbeddedViewCount, initializer.mergeElementCount,
initializer.mergeViewCount);
protoView.init(render, initializer.elementBinders, initializer.boundTextCount, mergeInfo,
initializer.variableLocations);
}
private _bindPipe(typeOrProvider): PipeProvider {
let meta = this._pipeResolver.resolve(typeOrProvider);
return PipeProvider.createFromType(typeOrProvider, meta);
}
private _flattenPipes(view: ViewMetadata): any[] {
let pipes = [];
if (isPresent(this._platformPipes)) {
_flattenArray(this._platformPipes, pipes);
}
if (isPresent(view.pipes)) {
_flattenArray(view.pipes, pipes);
}
return pipes;
}
}
function createComponent(protoViewFactory: ProtoViewFactory, cmd: BeginComponentCmd): AppProtoView {
return (<any>protoViewFactory)._createComponent(cmd);
}
function createEmbeddedTemplate(protoViewFactory: ProtoViewFactory, cmd: EmbeddedTemplateCmd,
parent: AppProtoView): AppProtoView {
return (<any>protoViewFactory)._createEmbeddedTemplate(cmd, parent);
}
class _ProtoViewInitializer implements CommandVisitor {
variableLocations: Map<string, number> = new Map<string, number>();
boundTextCount: number = 0;
boundElementIndex: number = 0;
elementBinderStack: ElementBinder[] = [];
distanceToParentElementBinder: number = 0;
distanceToParentProtoElementInjector: number = 0;
elementBinders: ElementBinder[] = [];
mergeEmbeddedViewCount: number = 0;
mergeElementCount: number = 0;
mergeViewCount: number = 1;
constructor(private _protoView: AppProtoView, private _directiveResolver: DirectiveResolver,
private _protoViewFactory: ProtoViewFactory) {}
visitText(cmd: TextCmd, context: any): any {
if (cmd.isBound) {
this.boundTextCount++;
}
return null;
}
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
visitBeginElement(cmd: BeginElementCmd, context: any): any {
if (cmd.isBound) {
this._visitBeginBoundElement(cmd, null);
} else {
this._visitBeginElement(cmd, null, null);
}
return null;
}
visitEndElement(context: any): any { return this._visitEndElement(); }
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
var nestedProtoView = createComponent(this._protoViewFactory, cmd);
return this._visitBeginBoundElement(cmd, nestedProtoView);
}
visitEndComponent(context: any): any { return this._visitEndElement(); }
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
var nestedProtoView = createEmbeddedTemplate(this._protoViewFactory, cmd, this._protoView);
if (cmd.isMerged) {
this.mergeEmbeddedViewCount++;
}
this._visitBeginBoundElement(cmd, nestedProtoView);
return this._visitEndElement();
}
private _visitBeginBoundElement(cmd: IBeginElementCmd, nestedProtoView: AppProtoView): any {
if (isPresent(nestedProtoView) && nestedProtoView.isMergable) {
this.mergeElementCount += nestedProtoView.mergeInfo.elementCount;
this.mergeViewCount += nestedProtoView.mergeInfo.viewCount;
this.mergeEmbeddedViewCount += nestedProtoView.mergeInfo.embeddedViewCount;
}
var elementBinder = _createElementBinder(
this._directiveResolver, nestedProtoView, this.elementBinderStack, this.boundElementIndex,
this.distanceToParentElementBinder, this.distanceToParentProtoElementInjector, cmd);
this.elementBinders.push(elementBinder);
var protoElementInjector = elementBinder.protoElementInjector;
for (var i = 0; i < cmd.variableNameAndValues.length; i += 2) {
this.variableLocations.set(<string>cmd.variableNameAndValues[i], this.boundElementIndex);
}
this.boundElementIndex++;
this.mergeElementCount++;
return this._visitBeginElement(cmd, elementBinder, protoElementInjector);
}
private _visitBeginElement(cmd: IBeginElementCmd, elementBinder: ElementBinder,
protoElementInjector: ProtoElementInjector): any {
this.distanceToParentElementBinder =
isPresent(elementBinder) ? 1 : this.distanceToParentElementBinder + 1;
this.distanceToParentProtoElementInjector =
isPresent(protoElementInjector) ? 1 : this.distanceToParentProtoElementInjector + 1;
this.elementBinderStack.push(elementBinder);
return null;
}
private _visitEndElement(): any {
var parentElementBinder = this.elementBinderStack.pop();
var parentProtoElementInjector =
isPresent(parentElementBinder) ? parentElementBinder.protoElementInjector : null;
this.distanceToParentElementBinder = isPresent(parentElementBinder) ?
parentElementBinder.distanceToParent :
this.distanceToParentElementBinder - 1;
this.distanceToParentProtoElementInjector = isPresent(parentProtoElementInjector) ?
parentProtoElementInjector.distanceToParent :
this.distanceToParentProtoElementInjector - 1;
return null;
}
}
function _createElementBinder(directiveResolver: DirectiveResolver, nestedProtoView: AppProtoView,
elementBinderStack: ElementBinder[], boundElementIndex: number,
distanceToParentBinder: number, distanceToParentPei: number,
beginElementCmd: IBeginElementCmd): ElementBinder {
var parentElementBinder: ElementBinder = null;
var parentProtoElementInjector: ProtoElementInjector = null;
if (distanceToParentBinder > 0) {
parentElementBinder = elementBinderStack[elementBinderStack.length - distanceToParentBinder];
}
if (isBlank(parentElementBinder)) {
distanceToParentBinder = -1;
}
if (distanceToParentPei > 0) {
var peiBinder = elementBinderStack[elementBinderStack.length - distanceToParentPei];
if (isPresent(peiBinder)) {
parentProtoElementInjector = peiBinder.protoElementInjector;
}
}
if (isBlank(parentProtoElementInjector)) {
distanceToParentPei = -1;
}
var componentDirectiveProvider: DirectiveProvider = null;
var isEmbeddedTemplate = false;
var directiveProviders: DirectiveProvider[] =
beginElementCmd.directives.map(type => provideDirective(directiveResolver, type));
if (beginElementCmd instanceof BeginComponentCmd) {
componentDirectiveProvider = directiveProviders[0];
} else if (beginElementCmd instanceof EmbeddedTemplateCmd) {
isEmbeddedTemplate = true;
}
var protoElementInjector = null;
// Create a protoElementInjector for any element that either has bindings *or* has one
// or more var- defined *or* for <template> elements:
// - Elements with a var- defined need a their own element injector
// so that, when hydrating, $implicit can be set to the element.
// - <template> elements need their own ElementInjector so that we can query their TemplateRef
var hasVariables = beginElementCmd.variableNameAndValues.length > 0;
if (directiveProviders.length > 0 || hasVariables || isEmbeddedTemplate) {
var directiveVariableBindings = new Map<string, number>();
if (!isEmbeddedTemplate) {
directiveVariableBindings = createDirectiveVariableBindings(
beginElementCmd.variableNameAndValues, directiveProviders);
}
protoElementInjector = ProtoElementInjector.create(
parentProtoElementInjector, boundElementIndex, directiveProviders,
isPresent(componentDirectiveProvider), distanceToParentPei, directiveVariableBindings);
protoElementInjector.attributes = arrayToMap(beginElementCmd.attrNameAndValues, false);
}
return new ElementBinder(boundElementIndex, parentElementBinder, distanceToParentBinder,
protoElementInjector, componentDirectiveProvider, nestedProtoView);
}
function provideDirective(directiveResolver: DirectiveResolver, type: Type): DirectiveProvider {
let annotation = directiveResolver.resolve(type);
return DirectiveProvider.createFromType(type, annotation);
}
export function createDirectiveVariableBindings(
variableNameAndValues: Array<string | number>,
directiveProviders: DirectiveProvider[]): Map<string, number> {
var directiveVariableBindings = new Map<string, number>();
for (var i = 0; i < variableNameAndValues.length; i += 2) {
var templateName = <string>variableNameAndValues[i];
var dirIndex = <number>variableNameAndValues[i + 1];
if (isNumber(dirIndex)) {
directiveVariableBindings.set(templateName, dirIndex);
} else {
// a variable without a directive index -> reference the element
directiveVariableBindings.set(templateName, null);
}
}
return directiveVariableBindings;
}
function arrayToMap(arr: string[], inverse: boolean): Map<string, string> {
var result = new Map<string, string>();
for (var i = 0; i < arr.length; i += 2) {
if (inverse) {
result.set(arr[i + 1], arr[i]);
} else {
result.set(arr[i], arr[i + 1]);
}
}
return result;
}
function _flattenArray(tree: any[], out: Array<Type | Provider | any[]>): void {
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
_flattenArray(item, out);
} else {
out.push(item);
}
}
}
function _flattenStyleArr(arr: Array<string | any[]>, out: string[]): string[] {
for (var i = 0; i < arr.length; i++) {
var entry = arr[i];
if (isArray(entry)) {
_flattenStyleArr(<any[]>entry, out);
} else {
out.push(<string>entry);
}
}
return out;
}

View File

@ -0,0 +1,35 @@
import {Injectable} from '../di';
import {Type, isBlank} from 'angular2/src/facade/lang';
import {DirectiveProvider} from './element';
import {DirectiveResolver, CODEGEN_DIRECTIVE_RESOLVER} from './directive_resolver';
import {PipeProvider} from '../pipes/pipe_provider';
import {PipeResolver, CODEGEN_PIPE_RESOLVER} from './pipe_resolver';
@Injectable()
export class ResolvedMetadataCache {
private _directiveCache: Map<Type, DirectiveProvider> = new Map<Type, DirectiveProvider>();
private _pipeCache: Map<Type, PipeProvider> = new Map<Type, PipeProvider>();
constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver) {}
getResolvedDirectiveMetadata(type: Type): DirectiveProvider {
var result = this._directiveCache.get(type);
if (isBlank(result)) {
result = DirectiveProvider.createFromType(type, this._directiveResolver.resolve(type));
this._directiveCache.set(type, result);
}
return result;
}
getResolvedPipeMetadata(type: Type): PipeProvider {
var result = this._pipeCache.get(type);
if (isBlank(result)) {
result = PipeProvider.createFromType(type, this._pipeResolver.resolve(type));
this._pipeCache.set(type, result);
}
return result;
}
}
export var CODEGEN_RESOLVED_METADATA_CACHE =
new ResolvedMetadataCache(CODEGEN_DIRECTIVE_RESOLVER, CODEGEN_PIPE_RESOLVER);

View File

@ -1,141 +0,0 @@
import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {
RenderTemplateCmd,
RenderCommandVisitor,
RenderBeginElementCmd,
RenderTextCmd,
RenderNgContentCmd,
RenderBeginComponentCmd,
RenderEmbeddedTemplateCmd
} from 'angular2/src/core/render/api';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
// Export ViewEncapsulation so that compiled templates only need to depend
// on template_commands.
export {ViewEncapsulation} from 'angular2/src/core/metadata';
/**
* A compiled host template.
*
* This is const as we are storing it as annotation
* for the compiled component type.
*/
@CONST()
export class CompiledHostTemplate {
constructor(public template: CompiledComponentTemplate) {}
}
/**
* A compiled template.
*/
@CONST()
export class CompiledComponentTemplate {
constructor(public id: string, public changeDetectorFactory: Function,
public commands: TemplateCmd[], public styles: string[]) {}
}
const EMPTY_ARR = CONST_EXPR([]);
export interface TemplateCmd extends RenderTemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any;
}
@CONST()
export class TextCmd implements TemplateCmd, RenderTextCmd {
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitText(this, context);
}
}
@CONST()
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
isBound: boolean = false;
constructor(public index: number, public ngContentIndex: number) {}
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitNgContent(this, context);
}
}
export abstract class IBeginElementCmd extends RenderBeginElementCmd implements TemplateCmd {
get variableNameAndValues(): Array<string | number> { return unimplemented(); }
get eventTargetAndNames(): string[] { return unimplemented(); }
get directives(): Type[] { return unimplemented(); }
abstract visit(visitor: RenderCommandVisitor, context: any): any;
}
@CONST()
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
constructor(public name: string, public attrNameAndValues: string[],
public eventTargetAndNames: string[],
public variableNameAndValues: Array<string | number>, public directives: Type[],
public isBound: boolean, public ngContentIndex: number) {}
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginElement(this, context);
}
}
@CONST()
export class EndElementCmd implements TemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndElement(context);
}
}
@CONST()
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
isBound: boolean = true;
constructor(public name: string, public attrNameAndValues: string[],
public eventTargetAndNames: string[],
public variableNameAndValues: Array<string | number>, public directives: Type[],
public encapsulation: ViewEncapsulation, public ngContentIndex: number,
// Note: the template needs to be stored as a function
// so that we can resolve cycles
public templateGetter: Function /*() => CompiledComponentTemplate*/) {}
get templateId(): string { return this.templateGetter().id; }
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginComponent(this, context);
}
}
@CONST()
export class EndComponentCmd implements TemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndComponent(context);
}
}
@CONST()
export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
RenderEmbeddedTemplateCmd {
isBound: boolean = true;
name: string = null;
eventTargetAndNames: string[] = EMPTY_ARR;
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEmbeddedTemplate(this, context);
}
}
export interface CommandVisitor extends RenderCommandVisitor {
visitText(cmd: TextCmd, context: any): any;
visitNgContent(cmd: NgContentCmd, context: any): any;
visitBeginElement(cmd: BeginElementCmd, context: any): any;
visitEndElement(context: any): any;
visitBeginComponent(cmd: BeginComponentCmd, context: any): any;
visitEndComponent(context: any): any;
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any;
}
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
context: any = null) {
for (var i = 0; i < cmds.length; i++) {
cmds[i].visit(visitor, context);
}
}

View File

@ -1,6 +1,4 @@
import {internalView, ProtoViewRef} from './view_ref';
import {ElementRef, ElementRef_} from './element_ref';
import * as viewModule from './view';
/**
* Represents an Embedded Template that can be used to instantiate Embedded Views.
@ -28,33 +26,10 @@ export abstract class TemplateRef {
*/
// TODO(i): rename to anchor or location
elementRef: ElementRef;
/**
* Allows you to check if this Embedded Template defines Local Variable with name matching `name`.
*/
abstract hasLocal(name: string): boolean;
}
export class TemplateRef_ extends TemplateRef {
constructor(elementRef: ElementRef) {
super();
this.elementRef = elementRef;
}
constructor(private _elementRef: ElementRef_) { super(); }
private _getProtoView(): viewModule.AppProtoView {
let elementRef = <ElementRef_>this.elementRef;
var parentView = internalView(elementRef.parentView);
return parentView.proto.elementBinders[elementRef.boundElementIndex - parentView.elementOffset]
.nestedProtoView;
}
/**
* Reference to the ProtoView used for creating Embedded Views that are based on the compiled
* Embedded Template.
*/
get protoViewRef(): ProtoViewRef { return this._getProtoView().ref; }
hasLocal(name: string): boolean {
return this._getProtoView().templateVariableBindings.has(name);
}
get elementRef(): ElementRef_ { return this._elementRef; }
}

View File

@ -10,86 +10,53 @@ import {
DirectiveIndex,
BindingTarget,
Locals,
ProtoChangeDetector
ProtoChangeDetector,
ChangeDetectorRef
} from 'angular2/src/core/change_detection/change_detection';
import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
import {DebugContext} from 'angular2/src/core/change_detection/interfaces';
import {AppProtoElement, AppElement, DirectiveProvider} from './element';
import {
ProtoElementInjector,
ElementInjector,
PreBuiltObjects,
DirectiveProvider
} from './element_injector';
import {ElementBinder} from './element_binder';
import {isPresent} from 'angular2/src/facade/lang';
isPresent,
isBlank,
Type,
isArray,
isNumber,
CONST,
CONST_EXPR
} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
import * as renderApi from 'angular2/src/core/render/api';
import {RenderEventDispatcher} from 'angular2/src/core/render/api';
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
import {ElementRef} from './element_ref';
import {Renderer, RootRenderer} from 'angular2/src/core/render/api';
import {ViewRef_, HostViewFactoryRef} from './view_ref';
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
import {camelCaseToDashCase} from 'angular2/src/core/render/util';
import {TemplateCmd} from './template_commands';
import {ViewRef_, ProtoViewRef_} from "./view_ref";
export {DebugContext} from 'angular2/src/core/change_detection/interfaces';
import {Pipes} from 'angular2/src/core/pipes/pipes';
import {AppViewManager_, AppViewManager} from './view_manager';
import {ResolvedMetadataCache} from './resolved_metadata_cache';
import {ViewType} from './view_type';
const REFLECT_PREFIX: string = 'ng-reflect-';
export enum ViewType {
// A view that contains the host element with bound component directive.
// Contains a COMPONENT view
HOST,
// The view of the component
// Can contain 0 to n EMBEDDED views
COMPONENT,
// A view that is embedded into another View via a <template> element
// inside of a COMPONENT view
EMBEDDED
}
export class AppViewContainer {
// The order in this list matches the DOM order.
views: AppView[] = [];
}
const EMPTY_CONTEXT = CONST_EXPR(new Object());
/**
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
*
*/
export class AppView implements ChangeDispatcher, RenderEventDispatcher {
// AppViews that have been merged in depth first order.
// This list is shared between all merged views. Use this.elementOffset to get the local
// entries.
views: AppView[] = null;
// root elementInjectors of this AppView
// This list is local to this AppView and not shared with other Views.
rootElementInjectors: ElementInjector[];
// ElementInjectors of all AppViews in views grouped by view.
// This list is shared between all merged views. Use this.elementOffset to get the local
// entries.
elementInjectors: ElementInjector[] = null;
// ViewContainers of all AppViews in views grouped by view.
// This list is shared between all merged views. Use this.elementOffset to get the local
// entries.
viewContainers: AppViewContainer[] = null;
// PreBuiltObjects of all AppViews in views grouped by view.
// This list is shared between all merged views. Use this.elementOffset to get the local
// entries.
preBuiltObjects: PreBuiltObjects[] = null;
// ElementRef of all AppViews in views grouped by view.
// This list is shared between all merged views. Use this.elementOffset to get the local
// entries.
elementRefs: ElementRef[];
ref: ViewRef;
changeDetector: ChangeDetector = null;
export class AppView implements ChangeDispatcher {
ref: ViewRef_;
rootNodesOrAppElements: any[];
allNodes: any[];
disposables: Function[];
appElements: AppElement[];
/**
* The context against which data-binding expressions in this view are evaluated against.
* This is always a component instance.
*/
context: any = null;
/**
@ -99,70 +66,133 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
*/
locals: Locals;
constructor(public renderer: renderApi.Renderer, public proto: AppProtoView,
public viewOffset: number, public elementOffset: number, public textOffset: number,
protoLocals: Map<string, any>, public render: renderApi.RenderViewRef,
public renderFragment: renderApi.RenderFragmentRef,
public containerElementInjector: ElementInjector) {
this.ref = new ViewRef_(this);
pipes: Pipes;
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); // TODO optimize this
parentInjector: Injector;
/**
* Whether root injectors of this view
* have a hostBoundary.
*/
hostInjectorBoundary: boolean;
destroyed: boolean = false;
constructor(public proto: AppProtoView, public renderer: Renderer,
public viewManager: AppViewManager_, public projectableNodes: Array<any | any[]>,
public containerAppElement: AppElement,
imperativelyCreatedProviders: ResolvedProvider[], rootInjector: Injector,
public changeDetector: ChangeDetector) {
this.ref = new ViewRef_(this);
var injectorWithHostBoundary = AppElement.getViewParentInjector(
this.proto.type, containerAppElement, imperativelyCreatedProviders, rootInjector);
this.parentInjector = injectorWithHostBoundary.injector;
this.hostInjectorBoundary = injectorWithHostBoundary.hostInjectorBoundary;
var pipes;
var context;
switch (proto.type) {
case ViewType.COMPONENT:
pipes = new Pipes(proto.protoPipes, containerAppElement.getInjector());
context = containerAppElement.getComponent();
break;
case ViewType.EMBEDDED:
pipes = containerAppElement.parentView.pipes;
context = containerAppElement.parentView.context;
break;
case ViewType.HOST:
pipes = null;
context = EMPTY_CONTEXT;
break;
}
this.pipes = pipes;
this.context = context;
}
init(changeDetector: ChangeDetector, elementInjectors: ElementInjector[],
rootElementInjectors: ElementInjector[], preBuiltObjects: PreBuiltObjects[],
views: AppView[], elementRefs: ElementRef[], viewContainers: AppViewContainer[]) {
this.changeDetector = changeDetector;
this.elementInjectors = elementInjectors;
this.rootElementInjectors = rootElementInjectors;
this.preBuiltObjects = preBuiltObjects;
this.views = views;
this.elementRefs = elementRefs;
this.viewContainers = viewContainers;
init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[],
appElements: AppElement[]) {
this.rootNodesOrAppElements = rootNodesOrAppElements;
this.allNodes = allNodes;
this.disposables = disposables;
this.appElements = appElements;
var localsMap = new Map<string, any>();
StringMapWrapper.forEach(this.proto.templateVariableBindings,
(templateName, _) => { localsMap.set(templateName, null); });
for (var i = 0; i < appElements.length; i++) {
var appEl = appElements[i];
StringMapWrapper.forEach(appEl.proto.directiveVariableBindings, (directiveIndex, name) => {
if (isBlank(directiveIndex)) {
localsMap.set(name, appEl.nativeElement);
} else {
localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
}
});
}
var parentLocals = null;
if (this.proto.type !== ViewType.COMPONENT) {
parentLocals =
isPresent(this.containerAppElement) ? this.containerAppElement.parentView.locals : null;
}
if (this.proto.type === ViewType.COMPONENT) {
// Note: the render nodes have been attached to their host element
// in the ViewFactory already.
this.containerAppElement.attachComponentView(this);
this.containerAppElement.parentView.changeDetector.addViewChild(this.changeDetector);
}
this.locals = new Locals(parentLocals, localsMap);
this.changeDetector.hydrate(this.context, this.locals, this, this.pipes);
this.viewManager.onViewCreated(this);
}
destroy() {
if (this.destroyed) {
throw new BaseException('This view has already been destroyed!');
}
this.changeDetector.destroyRecursive();
}
notifyOnDestroy() {
this.destroyed = true;
var hostElement =
this.proto.type === ViewType.COMPONENT ? this.containerAppElement.nativeElement : null;
this.renderer.destroyView(hostElement, this.allNodes);
for (var i = 0; i < this.disposables.length; i++) {
this.disposables[i]();
}
this.viewManager.onViewDestroyed(this);
}
get changeDetectorRef(): ChangeDetectorRef { return this.changeDetector.ref; }
get flatRootNodes(): any[] { return flattenNestedViewRenderNodes(this.rootNodesOrAppElements); }
hasLocal(contextName: string): boolean {
return StringMapWrapper.contains(this.proto.templateVariableBindings, contextName);
}
setLocal(contextName: string, value: any): void {
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
if (!this.proto.templateVariableBindings.has(contextName)) {
if (!this.hasLocal(contextName)) {
return;
}
var templateName = this.proto.templateVariableBindings.get(contextName);
var templateName = this.proto.templateVariableBindings[contextName];
this.locals.set(templateName, value);
}
hydrated(): boolean { return isPresent(this.context); }
/**
* Triggers the event handlers for the element and the directives.
*
* This method is intended to be called from directive EventEmitters.
*
* @param {string} eventName
* @param {*} eventObj
* @param {number} boundElementIndex
*/
triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): void {
var locals = new Map<string, any>();
locals.set('$event', eventObj);
this.dispatchEvent(boundElementIndex, eventName, locals);
}
// dispatch to element injector or text nodes based on context
notifyOnBinding(b: BindingTarget, currentValue: any): void {
if (b.isTextNode()) {
this.renderer.setText(this.render, b.elementIndex + this.textOffset, currentValue);
this.renderer.setText(this.allNodes[b.elementIndex], currentValue);
} else {
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
var nativeElement = this.appElements[b.elementIndex].nativeElement;
if (b.isElementProperty()) {
this.renderer.setElementProperty(elementRef, b.name, currentValue);
this.renderer.setElementProperty(nativeElement, b.name, currentValue);
} else if (b.isElementAttribute()) {
this.renderer.setElementAttribute(elementRef, b.name,
this.renderer.setElementAttribute(nativeElement, b.name,
isPresent(currentValue) ? `${currentValue}` : null);
} else if (b.isElementClass()) {
this.renderer.setElementClass(elementRef, b.name, currentValue);
this.renderer.setElementClass(nativeElement, b.name, currentValue);
} else if (b.isElementStyle()) {
var unit = isPresent(b.unit) ? b.unit : '';
this.renderer.setElementStyle(elementRef, b.name,
this.renderer.setElementStyle(nativeElement, b.name,
isPresent(currentValue) ? `${currentValue}${unit}` : null);
} else {
throw new BaseException('Unsupported directive record');
@ -172,57 +202,39 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
logBindingUpdate(b: BindingTarget, value: any): void {
if (b.isDirective() || b.isElementProperty()) {
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
var nativeElement = this.appElements[b.elementIndex].nativeElement;
this.renderer.setBindingDebugInfo(
elementRef, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
nativeElement, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
}
}
notifyAfterContentChecked(): void {
var eiCount = this.proto.elementBinders.length;
var ei = this.elementInjectors;
for (var i = eiCount - 1; i >= 0; i--) {
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterContentChecked();
var count = this.appElements.length;
for (var i = count - 1; i >= 0; i--) {
this.appElements[i].ngAfterContentChecked();
}
}
notifyAfterViewChecked(): void {
var eiCount = this.proto.elementBinders.length;
var ei = this.elementInjectors;
for (var i = eiCount - 1; i >= 0; i--) {
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterViewChecked();
var count = this.appElements.length;
for (var i = count - 1; i >= 0; i--) {
this.appElements[i].ngAfterViewChecked();
}
}
getDirectiveFor(directive: DirectiveIndex): any {
var elementInjector = this.elementInjectors[this.elementOffset + directive.elementIndex];
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
}
getNestedView(boundElementIndex: number): AppView {
var eli = this.elementInjectors[boundElementIndex];
return isPresent(eli) ? eli.getNestedView() : null;
}
getContainerElement(): ElementRef {
return isPresent(this.containerElementInjector) ?
this.containerElementInjector.getElementRef() :
null;
}
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext {
getDebugContext(appElement: AppElement, elementIndex: number,
directiveIndex: number): DebugContext {
try {
var offsettedIndex = this.elementOffset + elementIndex;
var hasRefForIndex = offsettedIndex < this.elementRefs.length;
if (isBlank(appElement) && elementIndex < this.appElements.length) {
appElement = this.appElements[elementIndex];
}
var container = this.containerAppElement;
var elementRef = hasRefForIndex ? this.elementRefs[this.elementOffset + elementIndex] : null;
var container = this.getContainerElement();
var ei = hasRefForIndex ? this.elementInjectors[this.elementOffset + elementIndex] : null;
var element = isPresent(elementRef) ? elementRef.nativeElement : null;
var element = isPresent(appElement) ? appElement.nativeElement : null;
var componentElement = isPresent(container) ? container.nativeElement : null;
var directive = isPresent(directiveIndex) ? this.getDirectiveFor(directiveIndex) : null;
var injector = isPresent(ei) ? ei.getInjector() : null;
var directive =
isPresent(directiveIndex) ? appElement.getDirectiveAtIndex(directiveIndex) : null;
var injector = isPresent(appElement) ? appElement.getInjector() : null;
return new DebugContext(element, componentElement, directive, this.context,
_localsToStringMap(this.locals), injector);
@ -234,43 +246,28 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
}
}
getDirectiveFor(directive: DirectiveIndex): any {
return this.appElements[directive.elementIndex].getDirectiveAtIndex(directive.directiveIndex);
}
getDetectorFor(directive: DirectiveIndex): any {
var childView = this.getNestedView(this.elementOffset + directive.elementIndex);
return isPresent(childView) ? childView.changeDetector : null;
var componentView = this.appElements[directive.elementIndex].componentView;
return isPresent(componentView) ? componentView.changeDetector : null;
}
invokeElementMethod(elementIndex: number, methodName: string, args: any[]) {
this.renderer.invokeElementMethod(this.elementRefs[elementIndex], methodName, args);
/**
* Triggers the event handlers for the element and the directives.
*
* This method is intended to be called from directive EventEmitters.
*
* @param {string} eventName
* @param {*} eventObj
* @param {number} boundElementIndex
* @return false if preventDefault must be applied to the DOM event
*/
triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): boolean {
return this.changeDetector.handleEvent(eventName, boundElementIndex, eventObj);
}
// implementation of RenderEventDispatcher#dispatchRenderEvent
dispatchRenderEvent(boundElementIndex: number, eventName: string,
locals: Map<string, any>): boolean {
var elementRef = this.elementRefs[boundElementIndex];
var view = internalView(elementRef.parentView);
return view.dispatchEvent(elementRef.boundElementIndex, eventName, locals);
}
// returns false if preventDefault must be applied to the DOM event
dispatchEvent(boundElementIndex: number, eventName: string, locals: Map<string, any>): boolean {
try {
if (this.hydrated()) {
return !this.changeDetector.handleEvent(eventName, boundElementIndex - this.elementOffset,
new Locals(this.locals, locals));
} else {
return true;
}
} catch (e) {
var c = this.getDebugContext(boundElementIndex - this.elementOffset, null);
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
c.injector) :
null;
throw new EventEvaluationError(eventName, e, e.stack, context);
}
}
get ownBindersCount(): number { return this.proto.elementBinders.length; }
}
function _localsToStringMap(locals: Locals): {[key: string]: any} {
@ -283,69 +280,61 @@ function _localsToStringMap(locals: Locals): {[key: string]: any} {
return res;
}
/**
* Error context included when an event handler throws an exception.
*/
class _Context {
constructor(public element: any, public componentElement: any, public context: any,
public locals: any, public injector: any) {}
}
/**
* Wraps an exception thrown by an event handler.
*/
class EventEvaluationError extends WrappedException {
constructor(eventName: string, originalException: any, originalStack: any, context: any) {
super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
}
}
export class AppProtoViewMergeInfo {
constructor(public embeddedViewCount: number, public elementCount: number,
public viewCount: number) {}
}
/**
*
*/
export class AppProtoView {
ref: ProtoViewRef;
protoLocals: Map<string, any>;
elementBinders: ElementBinder[] = null;
mergeInfo: AppProtoViewMergeInfo = null;
variableLocations: Map<string, number> = null;
textBindingCount = null;
render: renderApi.RenderProtoViewRef = null;
constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
public isMergable: boolean, public changeDetectorFactory: Function,
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
this.ref = new ProtoViewRef_(this);
static create(metadataCache: ResolvedMetadataCache, type: ViewType, pipes: Type[],
templateVariableBindings: {[key: string]: string}): AppProtoView {
var protoPipes = null;
if (isPresent(pipes) && pipes.length > 0) {
var boundPipes = ListWrapper.createFixedSize(pipes.length);
for (var i = 0; i < pipes.length; i++) {
boundPipes[i] = metadataCache.getResolvedPipeMetadata(pipes[i]);
}
protoPipes = ProtoPipes.fromProviders(boundPipes);
}
return new AppProtoView(type, protoPipes, templateVariableBindings);
}
init(render: renderApi.RenderProtoViewRef, elementBinders: ElementBinder[],
textBindingCount: number, mergeInfo: AppProtoViewMergeInfo,
variableLocations: Map<string, number>) {
this.render = render;
this.elementBinders = elementBinders;
this.textBindingCount = textBindingCount;
this.mergeInfo = mergeInfo;
this.variableLocations = variableLocations;
this.protoLocals = new Map<string, any>();
if (isPresent(this.templateVariableBindings)) {
this.templateVariableBindings.forEach(
(templateName, _) => { this.protoLocals.set(templateName, null); });
}
if (isPresent(variableLocations)) {
// The view's locals needs to have a full set of variable names at construction time
// in order to prevent new variables from being set later in the lifecycle. Since we don't
// want
// to actually create variable bindings for the $implicit bindings, add to the
// protoLocals manually.
variableLocations.forEach((_, templateName) => { this.protoLocals.set(templateName, null); });
}
}
isInitialized(): boolean { return isPresent(this.elementBinders); }
constructor(public type: ViewType, public protoPipes: ProtoPipes,
public templateVariableBindings: {[key: string]: string}) {}
}
@CONST()
export class HostViewFactory {
constructor(public selector: string, public viewFactory: Function) {}
}
export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
return _flattenNestedViewRenderNodes(nodes, []);
}
function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node instanceof AppElement) {
var appEl = <AppElement>node;
renderNodes.push(appEl.nativeElement);
if (isPresent(appEl.nestedViews)) {
for (var k = 0; k < appEl.nestedViews.length; k++) {
_flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
}
}
} else {
renderNodes.push(node);
}
}
return renderNodes;
}
export function checkSlotCount(componentName: string, expectedSlotCount: number,
projectableNodes: any[][]): void {
var givenSlotCount = isPresent(projectableNodes) ? projectableNodes.length : 0;
if (givenSlotCount < expectedSlotCount) {
throw new BaseException(
`The component ${componentName} has ${expectedSlotCount} <ng-content> elements,` +
` but only ${givenSlotCount} slots were provided.`);
}
}

View File

@ -3,12 +3,18 @@ import {unimplemented} from 'angular2/src/facade/exceptions';
import {ResolvedProvider} from 'angular2/src/core/di';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import * as avmModule from './view_manager';
import * as viewModule from './view';
import {AppElement} from './element';
import {ElementRef, ElementRef_} from './element_ref';
import {TemplateRef} from './template_ref';
import {ViewRef, HostViewRef, ProtoViewRef, internalView} from './view_ref';
import {TemplateRef, TemplateRef_} from './template_ref';
import {
EmbeddedViewRef,
HostViewRef,
HostViewFactoryRef,
HostViewFactoryRef_,
ViewRef,
ViewRef_
} from './view_ref';
/**
* Represents a container where one or more Views can be attached.
@ -35,7 +41,7 @@ export abstract class ViewContainerRef {
* Anchor element that specifies the location of this container in the containing View.
* <!-- TODO: rename to anchorElement -->
*/
public element: ElementRef;
get element(): ElementRef { return unimplemented(); }
/**
* Destroys all Views in this container.
@ -64,7 +70,7 @@ export abstract class ViewContainerRef {
*
* Returns the {@link ViewRef} for the newly created View.
*/
abstract createEmbeddedView(templateRef: TemplateRef, index?: number): ViewRef;
abstract createEmbeddedView(templateRef: TemplateRef, index?: number): EmbeddedViewRef;
/**
* Instantiates a single {@link Component} and inserts its Host View into this container at the
@ -80,8 +86,9 @@ export abstract class ViewContainerRef {
*
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
*/
abstract createHostView(protoViewRef?: ProtoViewRef, index?: number,
dynamicallyCreatedProviders?: ResolvedProvider[]): HostViewRef;
abstract createHostView(hostViewFactoryRef: HostViewFactoryRef, index?: number,
dynamicallyCreatedProviders?: ResolvedProvider[],
projectableNodes?: any[][]): HostViewRef;
/**
* Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
@ -90,7 +97,7 @@ export abstract class ViewContainerRef {
*
* Returns the inserted {@link ViewRef}.
*/
abstract insert(viewRef: ViewRef, index?: number): ViewRef;
abstract insert(viewRef: EmbeddedViewRef, index?: number): EmbeddedViewRef;
/**
* Returns the index of the View, specified via {@link ViewRef}, within the current container or
@ -110,58 +117,60 @@ export abstract class ViewContainerRef {
*
* If the `index` param is omitted, the last {@link ViewRef} is detached.
*/
abstract detach(index?: number): ViewRef;
abstract detach(index?: number): EmbeddedViewRef;
}
export class ViewContainerRef_ extends ViewContainerRef {
constructor(public viewManager: avmModule.AppViewManager, element: ElementRef) {
super();
this.element = element;
constructor(private _element: AppElement) { super(); }
get(index: number): EmbeddedViewRef { return this._element.nestedViews[index].ref; }
get length(): number {
var views = this._element.nestedViews;
return isPresent(views) ? views.length : 0;
}
private _getViews(): Array<viewModule.AppView> {
let element = <ElementRef_>this.element;
var vc = internalView(element.parentView).viewContainers[element.boundElementIndex];
return isPresent(vc) ? vc.views : [];
}
get(index: number): ViewRef { return this._getViews()[index].ref; }
get length(): number { return this._getViews().length; }
get element(): ElementRef_ { return this._element.ref; }
// TODO(rado): profile and decide whether bounds checks should be added
// to the methods below.
createEmbeddedView(templateRef: TemplateRef, index: number = -1): ViewRef {
createEmbeddedView(templateRef: TemplateRef_, index: number = -1): EmbeddedViewRef {
if (index == -1) index = this.length;
return this.viewManager.createEmbeddedViewInContainer(this.element, index, templateRef);
var vm = this._element.parentView.viewManager;
return vm.createEmbeddedViewInContainer(this._element.ref, index, templateRef);
}
createHostView(protoViewRef: ProtoViewRef = null, index: number = -1,
dynamicallyCreatedProviders: ResolvedProvider[] = null): HostViewRef {
createHostView(hostViewFactoryRef: HostViewFactoryRef_, index: number = -1,
dynamicallyCreatedProviders: ResolvedProvider[] = null,
projectableNodes: any[][] = null): HostViewRef {
if (index == -1) index = this.length;
return this.viewManager.createHostViewInContainer(this.element, index, protoViewRef,
dynamicallyCreatedProviders);
var vm = this._element.parentView.viewManager;
return vm.createHostViewInContainer(this._element.ref, index, hostViewFactoryRef,
dynamicallyCreatedProviders, projectableNodes);
}
// TODO(i): refactor insert+remove into move
insert(viewRef: ViewRef, index: number = -1): ViewRef {
insert(viewRef: ViewRef_, index: number = -1): EmbeddedViewRef {
if (index == -1) index = this.length;
return this.viewManager.attachViewInContainer(this.element, index, viewRef);
var vm = this._element.parentView.viewManager;
return vm.attachViewInContainer(this._element.ref, index, viewRef);
}
indexOf(viewRef: ViewRef): number {
return ListWrapper.indexOf(this._getViews(), internalView(viewRef));
indexOf(viewRef: ViewRef_): number {
return ListWrapper.indexOf(this._element.nestedViews, viewRef.internalView);
}
// TODO(i): rename to destroy
remove(index: number = -1): void {
if (index == -1) index = this.length - 1;
this.viewManager.destroyViewInContainer(this.element, index);
var vm = this._element.parentView.viewManager;
return vm.destroyViewInContainer(this._element.ref, index);
// view is intentionally not returned to the client.
}
// TODO(i): refactor insert+remove into move
detach(index: number = -1): ViewRef {
detach(index: number = -1): EmbeddedViewRef {
if (index == -1) index = this.length - 1;
return this.viewManager.detachViewInContainer(this.element, index);
var vm = this._element.parentView.viewManager;
return vm.detachViewInContainer(this._element.ref, index);
}
}

View File

@ -6,24 +6,27 @@ import {
ResolvedProvider,
forwardRef
} from 'angular2/src/core/di';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {isPresent, isBlank, isArray} from 'angular2/src/facade/lang';
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {BaseException} from 'angular2/src/facade/exceptions';
import * as viewModule from './view';
import {AppView, HostViewFactory, flattenNestedViewRenderNodes} from './view';
import {AppElement} from './element';
import {ElementRef, ElementRef_} from './element_ref';
import {ProtoViewRef, ViewRef, HostViewRef, internalView, internalProtoView} from './view_ref';
import {
HostViewFactoryRef,
HostViewFactoryRef_,
EmbeddedViewRef,
HostViewRef,
ViewRef_
} from './view_ref';
import {ViewContainerRef} from './view_container_ref';
import {TemplateRef, TemplateRef_} from './template_ref';
import {
Renderer,
RenderViewRef,
RenderFragmentRef,
RenderViewWithFragments
} from 'angular2/src/core/render/api';
import {AppViewManagerUtils} from './view_manager_utils';
import {AppViewPool} from './view_pool';
import {AppViewListener} from './view_listener';
import {RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
import {ProtoViewFactory} from './proto_view_factory';
import {APP_ID} from 'angular2/src/core/application_tokens';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {ViewType} from './view_type';
/**
* Service exposing low level API for creating, moving and destroying Views.
@ -40,13 +43,7 @@ export abstract class AppViewManager {
/**
* Returns the {@link ElementRef} that makes up the specified Host View.
*/
getHostElement(hostViewRef: HostViewRef): ElementRef {
var hostView = internalView(<ViewRef>hostViewRef);
if (hostView.proto.type !== viewModule.ViewType.HOST) {
throw new BaseException('This operation is only allowed on host views');
}
return hostView.elementRefs[hostView.elementOffset];
}
abstract getHostElement(hostViewRef: HostViewRef): ElementRef;
/**
* Searches the Component View of the Component specified via `hostLocation` and returns the
@ -70,7 +67,8 @@ export abstract class AppViewManager {
* This as a low-level way to bootstrap an application and upgrade an existing Element to a
* Host Element. Most applications should use {@link DynamicComponentLoader#loadAsRoot} instead.
*
* The Component and its View are created based on the `hostProtoViewRef` which can be obtained
* The Component and its View are created based on the `hostProtoComponentRef` which can be
* obtained
* by compiling the component with {@link Compiler#compileInHost}.
*
* Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host
@ -101,7 +99,7 @@ export abstract class AppViewManager {
* viewRef: ng.ViewRef;
*
* constructor(public appViewManager: ng.AppViewManager, compiler: ng.Compiler) {
* compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoViewRef) => {
* compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoComponentRef) => {
* this.viewRef = appViewManager.createRootHostView(protoView, 'some-component', null);
* })
* }
@ -115,8 +113,8 @@ export abstract class AppViewManager {
* ng.bootstrap(MyApp);
* ```
*/
abstract createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
injector: Injector): HostViewRef;
abstract createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string,
injector: Injector, projectableNodes?: any[][]): HostViewRef;
/**
* Destroys the Host View created via {@link AppViewManager#createRootHostView}.
@ -140,7 +138,7 @@ export abstract class AppViewManager {
// TODO(i): this low-level version of ViewContainerRef#createEmbeddedView doesn't add anything new
// we should make it private, otherwise we have two apis to do the same thing.
abstract createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
templateRef: TemplateRef): ViewRef;
templateRef: TemplateRef): EmbeddedViewRef;
/**
* Instantiates a single {@link Component} and inserts its Host View into the View Container
@ -150,16 +148,16 @@ export abstract class AppViewManager {
* The component is instantiated using its {@link ProtoViewRef `protoViewRef`} which can be
* obtained via {@link Compiler#compileInHost}.
*
* You can optionally specify `imperativelyCreatedInjector`, which configure the {@link Injector}
* You can optionally specify `dynamicallyCreatedProviders`, which configure the {@link Injector}
* that will be created for the Host View.
*
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
*
* Use {@link AppViewManager#destroyViewInContainer} to destroy the created Host View.
*/
abstract createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
protoViewRef: ProtoViewRef,
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef;
abstract createHostViewInContainer(
viewContainerLocation: ElementRef, index: number, hostViewFactoryRef: HostViewFactoryRef,
dynamicallyCreatedProviders: ResolvedProvider[], projectableNodes: any[][]): HostViewRef;
/**
* Destroys an Embedded or Host View attached to a View Container at the specified `index`.
@ -174,85 +172,75 @@ export abstract class AppViewManager {
*/
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
abstract attachViewInContainer(viewContainerLocation: ElementRef, index: number,
viewRef: ViewRef): ViewRef;
viewRef: EmbeddedViewRef): EmbeddedViewRef;
/**
* See {@link AppViewManager#attachViewInContainer}.
*/
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef;
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): EmbeddedViewRef;
}
@Injectable()
export class AppViewManager_ extends AppViewManager {
private _protoViewFactory: ProtoViewFactory;
private _nextCompTypeId: number = 0;
constructor(private _viewPool: AppViewPool, private _viewListener: AppViewListener,
private _utils: AppViewManagerUtils, private _renderer: Renderer,
@Inject(forwardRef(() => ProtoViewFactory)) _protoViewFactory) {
constructor(private _renderer: RootRenderer, private _viewListener: AppViewListener,
@Inject(APP_ID) private _appId: string) {
super();
this._protoViewFactory = _protoViewFactory;
}
getViewContainer(location: ElementRef): ViewContainerRef {
var hostView = internalView((<ElementRef_>location).parentView);
return hostView.elementInjectors[(<ElementRef_>location).boundElementIndex]
.getViewContainerRef();
getViewContainer(location: ElementRef_): ViewContainerRef {
return location.internalElement.getViewContainerRef();
}
getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
var hostView = internalView((<ElementRef_>hostLocation).parentView);
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
var componentView = hostView.getNestedView(boundElementIndex);
getHostElement(hostViewRef: ViewRef_): ElementRef {
var hostView = hostViewRef.internalView;
if (hostView.proto.type !== ViewType.HOST) {
throw new BaseException('This operation is only allowed on host views');
}
return hostView.appElements[0].ref;
}
getNamedElementInComponentView(hostLocation: ElementRef_, variableName: string): ElementRef {
var appEl = hostLocation.internalElement;
var componentView = appEl.componentView;
if (isBlank(componentView)) {
throw new BaseException(`There is no component directive at element ${boundElementIndex}`);
throw new BaseException(`There is no component directive at element ${hostLocation}`);
}
var binderIdx = componentView.proto.variableLocations.get(variableName);
if (isBlank(binderIdx)) {
throw new BaseException(`Could not find variable ${variableName}`);
for (var i = 0; i < componentView.appElements.length; i++) {
var compAppEl = componentView.appElements[i];
if (StringMapWrapper.contains(compAppEl.proto.directiveVariableBindings, variableName)) {
return compAppEl.ref;
}
}
return componentView.elementRefs[componentView.elementOffset + binderIdx];
throw new BaseException(`Could not find variable ${variableName}`);
}
getComponent(hostLocation: ElementRef): any {
var hostView = internalView((<ElementRef_>hostLocation).parentView);
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
return this._utils.getComponentInstance(hostView, boundElementIndex);
getComponent(hostLocation: ElementRef_): any {
return hostLocation.internalElement.getComponent();
}
/** @internal */
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()');
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
injector: Injector): HostViewRef {
createRootHostView(hostViewFactoryRef: HostViewFactoryRef_, overrideSelector: string,
injector: Injector, projectableNodes: any[][] = null): HostViewRef {
var s = this._createRootHostViewScope();
var hostProtoView: viewModule.AppProtoView = internalProtoView(hostProtoViewRef);
this._protoViewFactory.initializeProtoViewIfNeeded(hostProtoView);
var hostElementSelector = overrideSelector;
if (isBlank(hostElementSelector)) {
hostElementSelector = hostProtoView.elementBinders[0].componentDirective.metadata.selector;
}
var renderViewWithFragments = this._renderer.createRootHostView(
hostProtoView.render, hostProtoView.mergeInfo.embeddedViewCount + 1, hostElementSelector);
var hostView = this._createMainView(hostProtoView, renderViewWithFragments);
this._renderer.hydrateView(hostView.render);
this._utils.hydrateRootHostView(hostView, injector);
return wtfLeave(s, hostView.ref);
var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
var selector = isPresent(overrideSelector) ? overrideSelector : hostViewFactory.selector;
var view = hostViewFactory.viewFactory(this._renderer, this, null, projectableNodes, selector,
null, injector);
return wtfLeave(s, view.ref);
}
/** @internal */
_destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()');
destroyRootHostView(hostViewRef: HostViewRef) {
// Note: Don't put the hostView into the view pool
// as it is depending on the element for which it was created.
destroyRootHostView(hostViewRef: ViewRef_) {
var s = this._destroyRootHostViewScope();
var hostView = internalView(<ViewRef>hostViewRef);
this._renderer.detachFragment(hostView.renderFragment);
this._renderer.dehydrateView(hostView.render);
this._viewDehydrateRecurse(hostView);
this._viewListener.onViewDestroyed(hostView);
this._renderer.destroyView(hostView.render);
var hostView = hostViewRef.internalView;
hostView.renderer.detachView(flattenNestedViewRenderNodes(hostView.rootNodesOrAppElements));
hostView.destroy();
wtfLeave(s);
}
@ -260,97 +248,43 @@ export class AppViewManager_ extends AppViewManager {
_createEmbeddedViewInContainerScope: WtfScopeFn =
wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()');
createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
templateRef: TemplateRef): ViewRef {
createEmbeddedViewInContainer(viewContainerLocation: ElementRef_, index: number,
templateRef: TemplateRef_): EmbeddedViewRef {
var s = this._createEmbeddedViewInContainerScope();
var protoView = internalProtoView((<TemplateRef_>templateRef).protoViewRef);
if (protoView.type !== viewModule.ViewType.EMBEDDED) {
throw new BaseException('This method can only be called with embedded ProtoViews!');
}
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
return wtfLeave(s, this._createViewInContainer(viewContainerLocation, index, protoView,
templateRef.elementRef, null));
var contextEl = templateRef.elementRef.internalElement;
var view: AppView =
contextEl.embeddedViewFactory(contextEl.parentView.renderer, this, contextEl,
contextEl.parentView.projectableNodes, null, null, null);
this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
return wtfLeave(s, view.ref);
}
/** @internal */
_createHostViewInContainerScope: WtfScopeFn =
wtfCreateScope('AppViewManager#createHostViewInContainer()');
createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
protoViewRef: ProtoViewRef,
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef {
createHostViewInContainer(viewContainerLocation: ElementRef_, index: number,
hostViewFactoryRef: HostViewFactoryRef_,
dynamicallyCreatedProviders: ResolvedProvider[],
projectableNodes: any[][]): HostViewRef {
var s = this._createHostViewInContainerScope();
var protoView = internalProtoView(protoViewRef);
if (protoView.type !== viewModule.ViewType.HOST) {
throw new BaseException('This method can only be called with host ProtoViews!');
}
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
return wtfLeave(
s, this._createViewInContainer(viewContainerLocation, index, protoView,
viewContainerLocation, imperativelyCreatedInjector));
}
/**
*
* See {@link AppViewManager#destroyViewInContainer}.
* @internal
*/
_createViewInContainer(viewContainerLocation: ElementRef, index: number,
protoView: viewModule.AppProtoView, context: ElementRef,
imperativelyCreatedInjector: ResolvedProvider[]): ViewRef {
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
var contextView = internalView((<ElementRef_>context).parentView);
var contextBoundElementIndex = (<ElementRef_>context).boundElementIndex;
var embeddedFragmentView = contextView.getNestedView(contextBoundElementIndex);
var view;
if (protoView.type === viewModule.ViewType.EMBEDDED && isPresent(embeddedFragmentView) &&
!embeddedFragmentView.hydrated()) {
// Case 1: instantiate the first view of a template that has been merged into a parent
view = embeddedFragmentView;
this._attachRenderView(parentView, boundElementIndex, index, view);
} else {
// Case 2: instantiate another copy of the template or a host ProtoView.
// This is a separate case
// as we only inline one copy of the template into the parent view.
view = this._createPooledView(protoView);
this._attachRenderView(parentView, boundElementIndex, index, view);
this._renderer.hydrateView(view.render);
}
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
contextBoundElementIndex, index, view);
try {
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
contextBoundElementIndex, index,
imperativelyCreatedInjector);
} catch (e) {
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
throw e;
}
return view.ref;
}
/** @internal */
_attachRenderView(parentView: viewModule.AppView, boundElementIndex: number, index: number,
view: viewModule.AppView) {
var elementRef = parentView.elementRefs[boundElementIndex];
if (index === 0) {
this._renderer.attachFragmentAfterElement(elementRef, view.renderFragment);
} else {
var prevView = parentView.viewContainers[boundElementIndex].views[index - 1];
this._renderer.attachFragmentAfterFragment(prevView.renderFragment, view.renderFragment);
}
// TODO(tbosch): This should be specifiable via an additional argument!
var contextEl = viewContainerLocation.internalElement;
var hostViewFactory = hostViewFactoryRef.internalHostViewFactory;
var view = hostViewFactory.viewFactory(
contextEl.parentView.renderer, contextEl.parentView.viewManager, contextEl,
projectableNodes, null, dynamicallyCreatedProviders, null);
this._attachViewToContainer(view, viewContainerLocation.internalElement, index);
return wtfLeave(s, view.ref);
}
/** @internal */
_destroyViewInContainerScope = wtfCreateScope('AppViewMananger#destroyViewInContainer()');
destroyViewInContainer(viewContainerLocation: ElementRef, index: number) {
destroyViewInContainer(viewContainerLocation: ElementRef_, index: number) {
var s = this._destroyViewInContainerScope();
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
this._destroyViewInContainer(parentView, boundElementIndex, index);
var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
view.destroy();
wtfLeave(s);
}
@ -358,20 +292,10 @@ export class AppViewManager_ extends AppViewManager {
_attachViewInContainerScope = wtfCreateScope('AppViewMananger#attachViewInContainer()');
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
attachViewInContainer(viewContainerLocation: ElementRef, index: number,
viewRef: ViewRef): ViewRef {
attachViewInContainer(viewContainerLocation: ElementRef_, index: number,
viewRef: ViewRef_): EmbeddedViewRef {
var s = this._attachViewInContainerScope();
var view = internalView(viewRef);
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
// TODO(tbosch): the public methods attachViewInContainer/detachViewInContainer
// are used for moving elements without the same container.
// We will change this into an atomic `move` operation, which should preserve the
// previous parent injector (see https://github.com/angular/angular/issues/1377).
// Right now we are destroying any special
// context view that might have been used.
this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, index, view);
this._attachRenderView(parentView, boundElementIndex, index, view);
this._attachViewToContainer(viewRef.internalView, viewContainerLocation.internalElement, index);
return wtfLeave(s, viewRef);
}
@ -379,88 +303,72 @@ export class AppViewManager_ extends AppViewManager {
_detachViewInContainerScope = wtfCreateScope('AppViewMananger#detachViewInContainer()');
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef {
detachViewInContainer(viewContainerLocation: ElementRef_, index: number): EmbeddedViewRef {
var s = this._detachViewInContainerScope();
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
var viewContainer = parentView.viewContainers[boundElementIndex];
var view = viewContainer.views[index];
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
this._renderer.detachFragment(view.renderFragment);
var view = this._detachViewInContainer(viewContainerLocation.internalElement, index);
return wtfLeave(s, view.ref);
}
/** @internal */
_createMainView(protoView: viewModule.AppProtoView,
renderViewWithFragments: RenderViewWithFragments): viewModule.AppView {
var mergedParentView =
this._utils.createView(protoView, renderViewWithFragments, this, this._renderer);
this._renderer.setEventDispatcher(mergedParentView.render, mergedParentView);
this._viewListener.onViewCreated(mergedParentView);
return mergedParentView;
}
onViewCreated(view: AppView) { this._viewListener.onViewCreated(view); }
/** @internal */
_createPooledView(protoView: viewModule.AppProtoView): viewModule.AppView {
var view = this._viewPool.getView(protoView);
if (isBlank(view)) {
view = this._createMainView(
protoView,
this._renderer.createView(protoView.render, protoView.mergeInfo.embeddedViewCount + 1));
onViewDestroyed(view: AppView) { this._viewListener.onViewDestroyed(view); }
/** @internal */
createRenderComponentType(encapsulation: ViewEncapsulation,
styles: Array<string | any[]>): RenderComponentType {
return new RenderComponentType(`${this._appId}-${this._nextCompTypeId++}`, encapsulation,
styles);
}
private _attachViewToContainer(view: AppView, vcAppElement: AppElement, viewIndex: number) {
if (view.proto.type === ViewType.COMPONENT) {
throw new BaseException(`Component views can't be moved!`);
}
var nestedViews = vcAppElement.nestedViews;
if (nestedViews == null) {
nestedViews = [];
vcAppElement.nestedViews = nestedViews;
}
ListWrapper.insert(nestedViews, viewIndex, view);
var refNode;
if (viewIndex > 0) {
var prevView = nestedViews[viewIndex - 1];
refNode = prevView.rootNodesOrAppElements.length > 0 ?
prevView.rootNodesOrAppElements[prevView.rootNodesOrAppElements.length - 1] :
null;
} else {
refNode = vcAppElement.nativeElement;
}
if (isPresent(refNode)) {
var refRenderNode;
if (refNode instanceof AppElement) {
refRenderNode = (<AppElement>refNode).nativeElement;
} else {
refRenderNode = refNode;
}
view.renderer.attachViewAfter(refRenderNode,
flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
}
// TODO: This is only needed when a view is destroyed,
// not when it is detached for reordering with ng-for...
vcAppElement.parentView.changeDetector.addContentChild(view.changeDetector);
vcAppElement.traverseAndSetQueriesAsDirty();
}
private _detachViewInContainer(vcAppElement: AppElement, viewIndex: number): AppView {
var view = ListWrapper.removeAt(vcAppElement.nestedViews, viewIndex);
if (view.proto.type === ViewType.COMPONENT) {
throw new BaseException(`Component views can't be moved!`);
}
vcAppElement.traverseAndSetQueriesAsDirty();
view.renderer.detachView(flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
// TODO: This is only needed when a view is destroyed,
// not when it is detached for reordering with ng-for...
view.changeDetector.remove();
return view;
}
/** @internal */
_destroyPooledView(view: viewModule.AppView) {
var wasReturned = this._viewPool.returnView(view);
if (!wasReturned) {
this._viewListener.onViewDestroyed(view);
this._renderer.destroyView(view.render);
}
}
/** @internal */
_destroyViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
index: number) {
var viewContainer = parentView.viewContainers[boundElementIndex];
var view = viewContainer.views[index];
this._viewDehydrateRecurse(view);
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
if (view.viewOffset > 0) {
// Case 1: a view that is part of another view.
// Just detach the fragment
this._renderer.detachFragment(view.renderFragment);
} else {
// Case 2: a view that is not part of another view.
// dehydrate and destroy it.
this._renderer.dehydrateView(view.render);
this._renderer.detachFragment(view.renderFragment);
this._destroyPooledView(view);
}
}
/** @internal */
_viewDehydrateRecurse(view: viewModule.AppView) {
if (view.hydrated()) {
this._utils.dehydrateView(view);
}
var viewContainers = view.viewContainers;
var startViewOffset = view.viewOffset;
var endViewOffset = view.viewOffset + view.proto.mergeInfo.viewCount - 1;
var elementOffset = view.elementOffset;
for (var viewIdx = startViewOffset; viewIdx <= endViewOffset; viewIdx++) {
var currView = view.views[viewIdx];
for (var binderIdx = 0; binderIdx < currView.proto.elementBinders.length;
binderIdx++, elementOffset++) {
var vc = viewContainers[elementOffset];
if (isPresent(vc)) {
for (var j = vc.views.length - 1; j >= 0; j--) {
this._destroyViewInContainer(currView, elementOffset, j);
}
}
}
}
}
}

View File

@ -1,266 +0,0 @@
import {Injector, Provider, Injectable, ResolvedProvider} from 'angular2/src/core/di';
import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facade/collection';
import * as eli from './element_injector';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import * as viewModule from './view';
import * as avmModule from './view_manager';
import {ElementRef, ElementRef_} from './element_ref';
import {TemplateRef, TemplateRef_} from './template_ref';
import {Renderer, RenderViewWithFragments} from 'angular2/src/core/render/api';
import {Locals} from 'angular2/src/core/change_detection/change_detection';
import {Pipes} from 'angular2/src/core/pipes/pipes';
@Injectable()
export class AppViewManagerUtils {
constructor() {}
getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
var eli = parentView.elementInjectors[boundElementIndex];
return eli.getComponent();
}
createView(mergedParentViewProto: viewModule.AppProtoView,
renderViewWithFragments: RenderViewWithFragments,
viewManager: avmModule.AppViewManager, renderer: Renderer): viewModule.AppView {
var renderFragments = renderViewWithFragments.fragmentRefs;
var renderView = renderViewWithFragments.viewRef;
var elementCount = mergedParentViewProto.mergeInfo.elementCount;
var viewCount = mergedParentViewProto.mergeInfo.viewCount;
var elementRefs: ElementRef[] = ListWrapper.createFixedSize(elementCount);
var viewContainers = ListWrapper.createFixedSize(elementCount);
var preBuiltObjects: eli.PreBuiltObjects[] = ListWrapper.createFixedSize(elementCount);
var elementInjectors: eli.ElementInjector[] = ListWrapper.createFixedSize(elementCount);
var views = ListWrapper.createFixedSize(viewCount);
var elementOffset = 0;
var textOffset = 0;
var fragmentIdx = 0;
var containerElementIndicesByViewIndex: number[] = ListWrapper.createFixedSize(viewCount);
for (var viewOffset = 0; viewOffset < viewCount; viewOffset++) {
var containerElementIndex = containerElementIndicesByViewIndex[viewOffset];
var containerElementInjector =
isPresent(containerElementIndex) ? elementInjectors[containerElementIndex] : null;
var parentView =
isPresent(containerElementInjector) ? preBuiltObjects[containerElementIndex].view : null;
var protoView =
isPresent(containerElementIndex) ?
parentView.proto.elementBinders[containerElementIndex - parentView.elementOffset]
.nestedProtoView :
mergedParentViewProto;
var renderFragment = null;
if (viewOffset === 0 || protoView.type === viewModule.ViewType.EMBEDDED) {
renderFragment = renderFragments[fragmentIdx++];
}
var currentView = new viewModule.AppView(renderer, protoView, viewOffset, elementOffset,
textOffset, protoView.protoLocals, renderView,
renderFragment, containerElementInjector);
views[viewOffset] = currentView;
if (isPresent(containerElementIndex)) {
preBuiltObjects[containerElementIndex].nestedView = currentView;
}
var rootElementInjectors = [];
var nestedViewOffset = viewOffset + 1;
for (var binderIdx = 0; binderIdx < protoView.elementBinders.length; binderIdx++) {
var binder = protoView.elementBinders[binderIdx];
var boundElementIndex = elementOffset + binderIdx;
var elementInjector = null;
if (isPresent(binder.nestedProtoView) && binder.nestedProtoView.isMergable) {
containerElementIndicesByViewIndex[nestedViewOffset] = boundElementIndex;
nestedViewOffset += binder.nestedProtoView.mergeInfo.viewCount;
}
// elementInjectors and rootElementInjectors
var protoElementInjector = binder.protoElementInjector;
if (isPresent(protoElementInjector)) {
if (isPresent(protoElementInjector.parent)) {
var parentElementInjector =
elementInjectors[elementOffset + protoElementInjector.parent.index];
elementInjector = protoElementInjector.instantiate(parentElementInjector);
} else {
elementInjector = protoElementInjector.instantiate(null);
rootElementInjectors.push(elementInjector);
}
}
elementInjectors[boundElementIndex] = elementInjector;
// elementRefs
var el = new ElementRef_(currentView.ref, boundElementIndex, renderer);
elementRefs[el.boundElementIndex] = el;
// preBuiltObjects
if (isPresent(elementInjector)) {
var templateRef = isPresent(binder.nestedProtoView) &&
binder.nestedProtoView.type === viewModule.ViewType.EMBEDDED ?
new TemplateRef_(el) :
null;
preBuiltObjects[boundElementIndex] =
new eli.PreBuiltObjects(viewManager, currentView, el, templateRef);
}
}
currentView.init(protoView.changeDetectorFactory(currentView), elementInjectors,
rootElementInjectors, preBuiltObjects, views, elementRefs, viewContainers);
if (isPresent(parentView) && protoView.type === viewModule.ViewType.COMPONENT) {
parentView.changeDetector.addViewChild(currentView.changeDetector);
}
elementOffset += protoView.elementBinders.length;
textOffset += protoView.textBindingCount;
}
return views[0];
}
hydrateRootHostView(hostView: viewModule.AppView, injector: Injector) {
this._hydrateView(hostView, injector, null, new Object(), null);
}
// Misnomer: this method is attaching next to the view container.
attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
contextView: viewModule.AppView, contextBoundElementIndex: number,
index: number, view: viewModule.AppView) {
if (isBlank(contextView)) {
contextView = parentView;
contextBoundElementIndex = boundElementIndex;
}
parentView.changeDetector.addContentChild(view.changeDetector);
var viewContainer = parentView.viewContainers[boundElementIndex];
if (isBlank(viewContainer)) {
viewContainer = new viewModule.AppViewContainer();
parentView.viewContainers[boundElementIndex] = viewContainer;
}
ListWrapper.insert(viewContainer.views, index, view);
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
if (isPresent(elementInjector.parent)) {
view.rootElementInjectors[i].link(elementInjector.parent);
}
}
elementInjector.traverseAndSetQueriesAsDirty();
}
detachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number, index: number) {
var viewContainer = parentView.viewContainers[boundElementIndex];
var view = viewContainer.views[index];
parentView.elementInjectors[boundElementIndex].traverseAndSetQueriesAsDirty();
view.changeDetector.remove();
ListWrapper.removeAt(viewContainer.views, index);
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
var inj = view.rootElementInjectors[i];
inj.unlink();
}
}
hydrateViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
contextView: viewModule.AppView, contextBoundElementIndex: number,
index: number, imperativelyCreatedProviders: ResolvedProvider[]) {
if (isBlank(contextView)) {
contextView = parentView;
contextBoundElementIndex = boundElementIndex;
}
var viewContainer = parentView.viewContainers[boundElementIndex];
var view = viewContainer.views[index];
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
var injector = isPresent(imperativelyCreatedProviders) ?
Injector.fromResolvedProviders(imperativelyCreatedProviders) :
null;
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
contextView.locals);
}
/** @internal */
_hydrateView(initView: viewModule.AppView, imperativelyCreatedInjector: Injector,
hostElementInjector: eli.ElementInjector, context: Object, parentLocals: Locals) {
var viewIdx = initView.viewOffset;
var endViewOffset = viewIdx + initView.proto.mergeInfo.viewCount - 1;
while (viewIdx <= endViewOffset) {
var currView = initView.views[viewIdx];
var currProtoView = currView.proto;
if (currView !== initView && currView.proto.type === viewModule.ViewType.EMBEDDED) {
// Don't hydrate components of embedded fragment views.
viewIdx += currView.proto.mergeInfo.viewCount;
} else {
if (currView !== initView) {
// hydrate a nested component view
imperativelyCreatedInjector = null;
parentLocals = null;
hostElementInjector = currView.containerElementInjector;
context = hostElementInjector.getComponent();
}
currView.context = context;
currView.locals.parent = parentLocals;
var binders = currProtoView.elementBinders;
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
var boundElementIndex = binderIdx + currView.elementOffset;
var elementInjector = initView.elementInjectors[boundElementIndex];
if (isPresent(elementInjector)) {
elementInjector.hydrate(imperativelyCreatedInjector, hostElementInjector,
currView.preBuiltObjects[boundElementIndex]);
this._populateViewLocals(currView, elementInjector, boundElementIndex);
this._setUpEventEmitters(currView, elementInjector, boundElementIndex);
}
}
var pipes = isPresent(hostElementInjector) ?
new Pipes(currView.proto.pipes, hostElementInjector.getInjector()) :
null;
currView.changeDetector.hydrate(currView.context, currView.locals, currView, pipes);
viewIdx++;
}
}
}
/** @internal */
_populateViewLocals(view: viewModule.AppView, elementInjector: eli.ElementInjector,
boundElementIdx: number): void {
if (isPresent(elementInjector.getDirectiveVariableBindings())) {
elementInjector.getDirectiveVariableBindings().forEach((directiveIndex, name) => {
if (isBlank(directiveIndex)) {
view.locals.set(name, view.elementRefs[boundElementIdx].nativeElement);
} else {
view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
}
});
}
}
/** @internal */
_setUpEventEmitters(view: viewModule.AppView, elementInjector: eli.ElementInjector,
boundElementIndex: number) {
var emitters = elementInjector.getEventEmitterAccessors();
for (var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
var directiveEmitters = emitters[directiveIndex];
var directive = elementInjector.getDirectiveAtIndex(directiveIndex);
for (var eventIndex = 0; eventIndex < directiveEmitters.length; ++eventIndex) {
var eventEmitterAccessor = directiveEmitters[eventIndex];
eventEmitterAccessor.subscribe(view, boundElementIndex, directive);
}
}
}
dehydrateView(initView: viewModule.AppView) {
var endViewOffset = initView.viewOffset + initView.proto.mergeInfo.viewCount - 1;
for (var viewIdx = initView.viewOffset; viewIdx <= endViewOffset; viewIdx++) {
var currView = initView.views[viewIdx];
if (currView.hydrated()) {
if (isPresent(currView.locals)) {
currView.locals.clearValues();
}
currView.context = null;
currView.changeDetector.dehydrate();
var binders = currView.proto.elementBinders;
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
var eli = initView.elementInjectors[currView.elementOffset + binderIdx];
if (isPresent(eli)) {
eli.dehydrate();
}
}
}
}
}
}

View File

@ -1,42 +0,0 @@
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
import {MapWrapper, Map} from 'angular2/src/facade/collection';
import * as viewModule from './view';
export const APP_VIEW_POOL_CAPACITY = CONST_EXPR(new OpaqueToken('AppViewPool.viewPoolCapacity'));
@Injectable()
export class AppViewPool {
/** @internal */
_poolCapacityPerProtoView: number;
/** @internal */
_pooledViewsPerProtoView = new Map<viewModule.AppProtoView, Array<viewModule.AppView>>();
constructor(@Inject(APP_VIEW_POOL_CAPACITY) poolCapacityPerProtoView) {
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
}
getView(protoView: viewModule.AppProtoView): viewModule.AppView {
var pooledViews = this._pooledViewsPerProtoView.get(protoView);
if (isPresent(pooledViews) && pooledViews.length > 0) {
return pooledViews.pop();
}
return null;
}
returnView(view: viewModule.AppView): boolean {
var protoView = view.proto;
var pooledViews = this._pooledViewsPerProtoView.get(protoView);
if (isBlank(pooledViews)) {
pooledViews = [];
this._pooledViewsPerProtoView.set(protoView, pooledViews);
}
var haveRemainingCapacity = pooledViews.length < this._poolCapacityPerProtoView;
if (haveRemainingCapacity) {
pooledViews.push(view);
}
return haveRemainingCapacity;
}
}

View File

@ -1,20 +1,16 @@
import {isPresent} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
import * as viewModule from './view';
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {RenderViewRef, RenderFragmentRef} from 'angular2/src/core/render/api';
import {AppView, HostViewFactory} from './view';
// This is a workaround for privacy in Dart as we don't have library parts
export function internalView(viewRef: ViewRef): viewModule.AppView {
return (<ViewRef_>viewRef)._view;
export abstract class ViewRef {
/**
* @internal
*/
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); };
get destroyed(): boolean { return unimplemented(); }
}
// This is a workaround for privacy in Dart as we don't have library parts
export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppProtoView {
return isPresent(protoViewRef) ? (<ProtoViewRef_>protoViewRef)._protoView : null;
}
/**
* Represents a View containing a single Element that is the Host Element of a {@link Component}
* instance.
@ -24,11 +20,8 @@ export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppPro
* of the higher-level APIs: {@link AppViewManager#createRootHostView},
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
*/
export interface HostViewRef {
/**
* @internal
*/
changeDetectorRef: ChangeDetectorRef;
export abstract class HostViewRef extends ViewRef {
get rootNodes(): any[] { return unimplemented(); };
}
/**
@ -84,96 +77,43 @@ export interface HostViewRef {
* <!-- /ViewRef: outer-0 -->
* ```
*/
export abstract class ViewRef implements HostViewRef {
export abstract class EmbeddedViewRef extends ViewRef {
/**
* Sets `value` of local variable called `variableName` in this View.
*/
abstract setLocal(variableName: string, value: any): void;
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }
set changeDetectorRef(value: ChangeDetectorRef) {
unimplemented(); // TODO: https://github.com/Microsoft/TypeScript/issues/12
}
/**
* Checks whether this view has a local variable called `variableName`.
*/
abstract hasLocal(variableName: string): boolean;
get rootNodes(): any[] { return unimplemented(); };
}
export class ViewRef_ extends ViewRef {
private _changeDetectorRef: ChangeDetectorRef = null;
/** @internal */
public _view: viewModule.AppView;
constructor(_view: viewModule.AppView) {
super();
this._view = _view;
}
export class ViewRef_ implements EmbeddedViewRef, HostViewRef {
constructor(private _view: AppView) { this._view = _view; }
/**
* Return `RenderViewRef`
*/
get render(): RenderViewRef { return this._view.render; }
/**
* Return `RenderFragmentRef`
*/
get renderFragment(): RenderFragmentRef { return this._view.renderFragment; }
get internalView(): AppView { return this._view; }
/**
* Return `ChangeDetectorRef`
*/
get changeDetectorRef(): ChangeDetectorRef {
if (this._changeDetectorRef === null) {
this._changeDetectorRef = this._view.changeDetector.ref;
}
return this._changeDetectorRef;
}
get changeDetectorRef(): ChangeDetectorRef { return this._view.changeDetector.ref; }
get rootNodes(): any[] { return this._view.flatRootNodes; }
setLocal(variableName: string, value: any): void { this._view.setLocal(variableName, value); }
hasLocal(variableName: string): boolean { return this._view.hasLocal(variableName); }
get destroyed(): boolean { return this._view.destroyed; }
}
/**
* Represents an Angular ProtoView.
*
* A ProtoView is a prototypical {@link ViewRef View} that is the result of Template compilation and
* is used by Angular to efficiently create an instance of this View based on the compiled Template.
*
* Most ProtoViews are created and used internally by Angular and you don't need to know about them,
* except in advanced use-cases where you compile components yourself via the low-level
* {@link Compiler#compileInHost} API.
*
*
* ### Example
*
* Given this template:
*
* ```
* Count: {{items.length}}
* <ul>
* <li *ngFor="var item of items">{{item}}</li>
* </ul>
* ```
*
* Angular desugars and compiles the template into two ProtoViews:
*
* Outer ProtoView:
* ```
* Count: {{items.length}}
* <ul>
* <template ngFor var-item [ngForOf]="items"></template>
* </ul>
* ```
*
* Inner ProtoView:
* ```
* <li>{{item}}</li>
* ```
*
* Notice that the original template is broken down into two separate ProtoViews.
*/
export abstract class ProtoViewRef {}
export abstract class HostViewFactoryRef {}
export class ProtoViewRef_ extends ProtoViewRef {
/** @internal */
public _protoView: viewModule.AppProtoView;
constructor(_protoView: viewModule.AppProtoView) {
super();
this._protoView = _protoView;
}
}
export class HostViewFactoryRef_ implements HostViewFactoryRef {
constructor(private _hostViewFactory: HostViewFactory) {}
get internalHostViewFactory(): HostViewFactory { return this._hostViewFactory; }
}

View File

@ -0,0 +1,11 @@
export enum ViewType {
// A view that contains the host element with bound component directive.
// Contains a COMPONENT view
HOST,
// The view of the component
// Can contain 0 to n EMBEDDED views
COMPONENT,
// A view that is embedded into another View via a <template> element
// inside of a COMPONENT view
EMBEDDED
}

View File

@ -45,7 +45,6 @@ export class Pipes implements cd.Pipes {
get(name: string): cd.SelectedPipe {
var cached = StringMapWrapper.get(this._config, name);
if (isPresent(cached)) return cached;
var p = this.proto.get(name);
var transform = this.injector.instantiateResolved(p);
var res = new cd.SelectedPipe(transform, p.pure);

View File

@ -1,19 +1,2 @@
// Public API for render
export {
RenderEventDispatcher,
Renderer,
RenderElementRef,
RenderViewRef,
RenderProtoViewRef,
RenderFragmentRef,
RenderViewWithFragments,
RenderTemplateCmd,
RenderCommandVisitor,
RenderTextCmd,
RenderNgContentCmd,
RenderBeginElementCmd,
RenderBeginComponentCmd,
RenderEmbeddedTemplateCmd,
RenderBeginCmd,
RenderComponentTemplate
} from './render/api';
export {RootRenderer, Renderer, RenderComponentType} from './render/api';

View File

@ -1,196 +1,54 @@
import {unimplemented} from 'angular2/src/facade/exceptions';
import {Map} from 'angular2/src/facade/collection';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
/**
* Represents an Angular ProtoView in the Rendering Context.
*
* When you implement a custom {@link Renderer}, `RenderProtoViewRef` specifies what Render View
* your renderer should create.
*
* `RenderProtoViewRef` is a counterpart to {@link ProtoViewRef} available in the Application
* Context. But unlike `ProtoViewRef`, `RenderProtoViewRef` contains all static nested Proto Views
* that are recursively merged into a single Render Proto View.
*
* <!-- TODO: this is created by Renderer#createProtoView in the new compiler -->
*/
export class RenderProtoViewRef {}
/**
* Represents a list of sibling Nodes that can be moved by the {@link Renderer} independently of
* other Render Fragments.
*
* Any {@link RenderViewRef} has one Render Fragment.
*
* Additionally any View with an Embedded View that contains a {@link NgContentAst View Projection}
* results in additional Render Fragment.
*/
/*
<div>foo</div>
{{bar}}
<div>foo</div> -> view 1 / fragment 1
<ul>
<template ngFor>
<li>{{fg}}</li> -> view 2 / fragment 1
</template>
</ul>
{{bar}}
<div>foo</div> -> view 1 / fragment 1
<ul>
<template ngIf>
<li><ng-content></></li> -> view 1 / fragment 2
</template>
<template ngFor>
<li><ng-content></></li> ->
<li></li> -> view 1 / fragment 2 + view 2 / fragment 1..n-1
</template>
</ul>
{{bar}}
*/
// TODO(i): refactor into an interface
export class RenderFragmentRef {}
/**
* Represents an Angular View in the Rendering Context.
*
* `RenderViewRef` specifies to the {@link Renderer} what View to update or destroy.
*
* Unlike a {@link ViewRef} available in the Application Context, Render View contains all the
* static Component Views that have been recursively merged into a single Render View.
*
* Each `RenderViewRef` contains one or more {@link RenderFragmentRef Render Fragments}, these
* Fragments are created, hydrated, dehydrated and destroyed as a single unit together with the
* View.
*/
// TODO(i): refactor into an interface
export class RenderViewRef {}
/**
* Abstract base class for commands to the Angular renderer, using the visitor pattern.
*/
export abstract class RenderTemplateCmd {
abstract visit(visitor: RenderCommandVisitor, context: any): any;
export class RenderComponentType {
constructor(public id: string, public encapsulation: ViewEncapsulation,
public styles: Array<string | any[]>) {}
}
/**
* Command to begin rendering.
*/
export abstract class RenderBeginCmd extends RenderTemplateCmd {
get ngContentIndex(): number { return unimplemented(); };
get isBound(): boolean { return unimplemented(); };
}
export interface ParentRenderer { renderComponent(componentType: RenderComponentType): Renderer; }
/**
* Command to render text.
*/
export abstract class RenderTextCmd extends RenderBeginCmd {
get value(): string { return unimplemented(); };
}
export abstract class Renderer implements ParentRenderer {
abstract renderComponent(componentType: RenderComponentType): Renderer;
/**
* Command to render projected content.
*/
export abstract class RenderNgContentCmd extends RenderTemplateCmd {
// The index of this NgContent element
get index(): number { return unimplemented(); };
// The index of the NgContent element into which this
// NgContent element should be projected (if any)
get ngContentIndex(): number { return unimplemented(); };
}
abstract selectRootElement(selector: string): any;
/**
* Command to begin rendering an element.
*/
export abstract class RenderBeginElementCmd extends RenderBeginCmd {
get name(): string { return unimplemented(); };
get attrNameAndValues(): string[] { return unimplemented(); };
get eventTargetAndNames(): string[] { return unimplemented(); };
}
abstract createElement(parentElement: any, name: string): any;
/**
* Command to begin rendering a component.
*/
export abstract class RenderBeginComponentCmd extends RenderBeginElementCmd {
get templateId(): string { return unimplemented(); };
}
abstract createViewRoot(hostElement: any): any;
/**
* Command to render a component's template.
*/
export abstract class RenderEmbeddedTemplateCmd extends RenderBeginElementCmd {
get isMerged(): boolean { return unimplemented(); };
get children(): RenderTemplateCmd[] { return unimplemented(); };
}
abstract createTemplateAnchor(parentElement: any): any;
/**
* Visitor for a {@link RenderTemplateCmd}.
*/
export interface RenderCommandVisitor {
visitText(cmd: RenderTextCmd, context: any): any;
visitNgContent(cmd: RenderNgContentCmd, context: any): any;
visitBeginElement(cmd: RenderBeginElementCmd, context: any): any;
visitEndElement(context: any): any;
visitBeginComponent(cmd: RenderBeginComponentCmd, context: any): any;
visitEndComponent(context: any): any;
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: any): any;
}
abstract createText(parentElement: any, value: string): any;
abstract projectNodes(parentElement: any, nodes: any[]);
/**
* Container class produced by a {@link Renderer} when creating a Render View.
*
* An instance of `RenderViewWithFragments` contains a {@link RenderViewRef} and an array of
* {@link RenderFragmentRef}s belonging to this Render View.
*/
// TODO(i): refactor this by RenderViewWithFragments and adding fragments directly to RenderViewRef
export class RenderViewWithFragments {
constructor(
/**
* Reference to the {@link RenderViewRef}.
*/
public viewRef: RenderViewRef,
/**
* Array of {@link RenderFragmentRef}s ordered in the depth-first order.
*/
public fragmentRefs: RenderFragmentRef[]) {}
}
abstract attachViewAfter(node: any, viewRootNodes: any[]);
/**
* Represents an Element that is part of a {@link RenderViewRef Render View}.
*
* `RenderElementRef` is a counterpart to {@link ElementRef} available in the Application Context.
*
* When using `Renderer` from the Application Context, `ElementRef` can be used instead of
* `RenderElementRef`.
*/
export interface RenderElementRef {
/**
* Reference to the Render View that contains this Element.
*/
renderView: RenderViewRef;
abstract detachView(viewRootNodes: any[]);
abstract destroyView(hostElement: any, viewAllNodes: any[]);
abstract listen(renderElement: any, name: string, callback: Function);
abstract listenGlobal(target: string, name: string, callback: Function): Function;
abstract setElementProperty(renderElement: any, propertyName: string, propertyValue: any);
abstract setElementAttribute(renderElement: any, attributeName: string, attributeValue: string);
/**
* @internal
*
* Index of the Element (in the depth-first order) inside the Render View.
*
* This index is used internally by Angular to locate elements.
* Used only in debug mode to serialize property changes to comment nodes,
* such as <template> placeholders.
*/
boundElementIndex: number;
}
abstract setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string);
/**
* Template for rendering a component, including commands and styles.
*/
export class RenderComponentTemplate {
constructor(public id: string, public shortId: string, public encapsulation: ViewEncapsulation,
public commands: RenderTemplateCmd[], public styles: string[]) {}
abstract setElementClass(renderElement: any, className: string, isAdd: boolean);
abstract setElementStyle(renderElement: any, styleName: string, styleValue: string);
abstract invokeElementMethod(renderElement: any, methodName: string, args: any[]);
abstract setText(renderNode: any, text: string);
}
/**
@ -205,184 +63,7 @@ export class RenderComponentTemplate {
*
* The default Renderer implementation is `DomRenderer`. Also available is `WebWorkerRenderer`.
*/
export abstract class Renderer {
/**
* Registers a component template represented as arrays of {@link RenderTemplateCmd}s and styles
* with the Renderer.
*
* Once a template is registered it can be referenced via {@link RenderBeginComponentCmd} when
* {@link #createProtoView creating Render ProtoView}.
*/
abstract registerComponentTemplate(template: RenderComponentTemplate);
/**
* Creates a {@link RenderProtoViewRef} from an array of {@link RenderTemplateCmd}`s.
*/
abstract createProtoView(componentTemplateId: string,
cmds: RenderTemplateCmd[]): RenderProtoViewRef;
/**
* Creates a Root Host View based on the provided `hostProtoViewRef`.
*
* `fragmentCount` is the number of nested {@link RenderFragmentRef}s in this View. This parameter
* is non-optional so that the renderer can create a result synchronously even when application
* runs in a different context (e.g. in a Web Worker).
*
* `hostElementSelector` is a (CSS) selector for querying the main document to find the Host
* Element. The newly created Root Host View should be attached to this element.
*
* Returns an instance of {@link RenderViewWithFragments}, representing the Render View.
*/
abstract createRootHostView(hostProtoViewRef: RenderProtoViewRef, fragmentCount: number,
hostElementSelector: string): RenderViewWithFragments;
/**
* Creates a Render View based on the provided `protoViewRef`.
*
* `fragmentCount` is the number of nested {@link RenderFragmentRef}s in this View. This parameter
* is non-optional so that the renderer can create a result synchronously even when application
* runs in a different context (e.g. in a Web Worker).
*
* Returns an instance of {@link RenderViewWithFragments}, representing the Render View.
*/
abstract createView(protoViewRef: RenderProtoViewRef,
fragmentCount: number): RenderViewWithFragments;
/**
* Destroys a Render View specified via `viewRef`.
*
* This operation should be performed only on a View that has already been dehydrated and
* all of its Render Fragments have been detached.
*
* Destroying a View indicates to the Renderer that this View is not going to be referenced in any
* future operations. If the Renderer created any renderer-specific objects for this View, these
* objects should now be destroyed to prevent memory leaks.
*/
abstract destroyView(viewRef: RenderViewRef);
/**
* Attaches the Nodes of a Render Fragment after the last Node of `previousFragmentRef`.
*/
abstract attachFragmentAfterFragment(previousFragmentRef: RenderFragmentRef,
fragmentRef: RenderFragmentRef);
/**
* Attaches the Nodes of the Render Fragment after an Element.
*/
abstract attachFragmentAfterElement(elementRef: RenderElementRef, fragmentRef: RenderFragmentRef);
/**
* Detaches the Nodes of a Render Fragment from their parent.
*
* This operations should be called only on a View that has been already
* {@link #dehydrateView dehydrated}.
*/
abstract detachFragment(fragmentRef: RenderFragmentRef);
/**
* Notifies a custom Renderer to initialize a Render View.
*
* This method is called by Angular after a Render View has been created, or when a previously
* dehydrated Render View is about to be reused.
*/
abstract hydrateView(viewRef: RenderViewRef);
/**
* Notifies a custom Renderer that a Render View is no longer active.
*
* This method is called by Angular before a Render View will be destroyed, or when a hydrated
* Render View is about to be put into a pool for future reuse.
*/
abstract dehydrateView(viewRef: RenderViewRef);
/**
* Returns the underlying native element at the specified `location`, or `null` if direct access
* to native elements is not supported (e.g. when the application runs in a web worker).
*
* <div class="callout is-critical">
* <header>Use with caution</header>
* <p>
* Use this api as the last resort when direct access to DOM is needed. Use templating and
* data-binding, or other {@link Renderer} methods instead.
* </p>
* <p>
* Relying on direct DOM access creates tight coupling between your application and rendering
* layers which will make it impossible to separate the two and deploy your application into a
* web worker.
* </p>
* </div>
*/
abstract getNativeElementSync(location: RenderElementRef): any;
/**
* Sets a property on the Element specified via `location`.
*/
abstract setElementProperty(location: RenderElementRef, propertyName: string, propertyValue: any);
/**
* Sets an attribute on the Element specified via `location`.
*
* If `attributeValue` is `null`, the attribute is removed.
*/
abstract setElementAttribute(location: RenderElementRef, attributeName: string,
attributeValue: string);
abstract setBindingDebugInfo(location: RenderElementRef, propertyName: string,
propertyValue: string);
/**
* Sets a (CSS) class on the Element specified via `location`.
*
* `isAdd` specifies if the class should be added or removed.
*/
abstract setElementClass(location: RenderElementRef, className: string, isAdd: boolean);
/**
* Sets a (CSS) inline style on the Element specified via `location`.
*
* If `styleValue` is `null`, the style is removed.
*/
abstract setElementStyle(location: RenderElementRef, styleName: string, styleValue: string);
/**
* Calls a method on the Element specified via `location`.
*/
abstract invokeElementMethod(location: RenderElementRef, methodName: string, args: any[]);
/**
* Sets the value of an interpolated TextNode at the specified index to the `text` value.
*
* `textNodeIndex` is the depth-first index of the Node among interpolated Nodes in the Render
* View.
*/
abstract setText(viewRef: RenderViewRef, textNodeIndex: number, text: string);
/**
* Sets a dispatcher to relay all events triggered in the given Render View.
*
* Each Render View can have only one Event Dispatcher, if this method is called multiple times,
* the last provided dispatcher will be used.
*/
abstract setEventDispatcher(viewRef: RenderViewRef, dispatcher: RenderEventDispatcher);
}
/**
* A dispatcher that relays all events that occur in a Render View.
*
* Use {@link Renderer#setEventDispatcher} to register a dispatcher for a particular Render View.
*/
export interface RenderEventDispatcher {
/**
* Called when Event called `eventName` was triggered on an Element with an Event Binding for this
* Event.
*
* `elementIndex` specifies the depth-first index of the Element in the Render View.
*
* `locals` is a map for local variable to value mapping that should be used when evaluating the
* Event Binding expression.
*
* Returns `false` if `preventDefault` should be called to stop the default behavior of the Event
* in the Rendering Context.
*/
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>): boolean;
export abstract class RootRenderer implements ParentRenderer {
abstract renderComponent(componentType: RenderComponentType): Renderer;
}

View File

@ -1,65 +0,0 @@
import {BaseException} from 'angular2/src/facade/exceptions';
import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isBlank, stringify} from 'angular2/src/facade/lang';
import {
RenderComponentTemplate,
RenderViewRef,
RenderEventDispatcher,
RenderTemplateCmd,
RenderProtoViewRef,
RenderFragmentRef
} from './api';
export class DefaultProtoViewRef extends RenderProtoViewRef {
constructor(public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
super();
}
}
export class DefaultRenderFragmentRef<N> extends RenderFragmentRef {
constructor(public nodes: N[]) { super(); }
}
export class DefaultRenderView<N> extends RenderViewRef {
hydrated: boolean = false;
eventDispatcher: RenderEventDispatcher = null;
globalEventRemovers: Function[] = null;
constructor(public fragments: DefaultRenderFragmentRef<N>[], public boundTextNodes: N[],
public boundElements: N[], public nativeShadowRoots: N[],
public globalEventAdders: Function[], public rootContentInsertionPoints: N[]) {
super();
}
hydrate() {
if (this.hydrated) throw new BaseException('The view is already hydrated.');
this.hydrated = true;
this.globalEventRemovers = ListWrapper.createFixedSize(this.globalEventAdders.length);
for (var i = 0; i < this.globalEventAdders.length; i++) {
this.globalEventRemovers[i] = this.globalEventAdders[i]();
}
}
dehydrate() {
if (!this.hydrated) throw new BaseException('The view is already dehydrated.');
for (var i = 0; i < this.globalEventRemovers.length; i++) {
this.globalEventRemovers[i]();
}
this.globalEventRemovers = null;
this.hydrated = false;
}
setEventDispatcher(dispatcher: RenderEventDispatcher) { this.eventDispatcher = dispatcher; }
dispatchRenderEvent(boundElementIndex: number, eventName: string, event: any): boolean {
var allowDefaultBehavior = true;
if (isPresent(this.eventDispatcher)) {
var locals = new Map<string, any>();
locals.set('$event', event);
allowDefaultBehavior =
this.eventDispatcher.dispatchRenderEvent(boundElementIndex, eventName, locals);
}
return allowDefaultBehavior;
}
}

View File

@ -1,321 +0,0 @@
import {isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
import {
RenderEventDispatcher,
RenderTemplateCmd,
RenderCommandVisitor,
RenderBeginElementCmd,
RenderBeginComponentCmd,
RenderNgContentCmd,
RenderTextCmd,
RenderEmbeddedTemplateCmd,
RenderComponentTemplate
} from './api';
import {DefaultRenderView, DefaultRenderFragmentRef} from './view';
import {ViewEncapsulation} from 'angular2/src/core/metadata';
import {ListWrapper} from 'angular2/src/facade/collection';
export function encapsulateStyles(componentTemplate: RenderComponentTemplate): string[] {
var processedStyles = componentTemplate.styles;
if (componentTemplate.encapsulation === ViewEncapsulation.Emulated) {
processedStyles = ListWrapper.createFixedSize(componentTemplate.styles.length);
for (var i = 0; i < componentTemplate.styles.length; i++) {
processedStyles[i] = StringWrapper.replaceAll(componentTemplate.styles[i], COMPONENT_REGEX,
componentTemplate.shortId);
}
}
return processedStyles;
}
export function createRenderView(componentTemplate: RenderComponentTemplate,
cmds: RenderTemplateCmd[], inplaceElement: any,
nodeFactory: NodeFactory<any>): DefaultRenderView<any> {
var view: DefaultRenderView<any>;
var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) =>
view.dispatchRenderEvent(boundElementIndex, eventName, event);
var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement);
context.build(componentTemplate, cmds);
var fragments: DefaultRenderFragmentRef<any>[] = [];
for (var i = 0; i < context.fragments.length; i++) {
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
}
view = new DefaultRenderView<any>(fragments, context.boundTextNodes, context.boundElements,
context.nativeShadowRoots, context.globalEventAdders,
context.rootContentInsertionPoints);
return view;
}
export interface NodeFactory<N> {
resolveComponentTemplate(templateId: string): RenderComponentTemplate;
createTemplateAnchor(attrNameAndValues: string[]): N;
createElement(name: string, attrNameAndValues: string[]): N;
createRootContentInsertionPoint(): N;
mergeElement(existing: N, attrNameAndValues: string[]);
createShadowRoot(host: N, templateId: string): N;
createText(value: string): N;
appendChild(parent: N, child: N);
on(element: N, eventName: string, callback: Function);
globalOn(target: string, eventName: string, callback: Function): Function;
}
class BuildContext<N> {
constructor(private _eventDispatcher: Function, public factory: NodeFactory<N>,
private _inplaceElement: N) {
this.isHost = isPresent((_inplaceElement));
}
private _builders: RenderViewBuilder<N>[] = [];
globalEventAdders: Function[] = [];
boundElements: N[] = [];
boundTextNodes: N[] = [];
nativeShadowRoots: N[] = [];
fragments: N[][] = [];
rootContentInsertionPoints: N[] = [];
componentCount: number = 0;
isHost: boolean;
build(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
this.enqueueRootBuilder(template, cmds);
this._build(this._builders[0]);
}
private _build(builder: RenderViewBuilder<N>) {
this._builders = [];
builder.build(this);
var enqueuedBuilders = this._builders;
for (var i = 0; i < enqueuedBuilders.length; i++) {
this._build(enqueuedBuilders[i]);
}
}
enqueueComponentBuilder(component: Component<N>) {
this.componentCount++;
this._builders.push(
new RenderViewBuilder<N>(component, null, component.template, component.template.commands));
}
enqueueFragmentBuilder(parentComponent: Component<N>, parentTemplate: RenderComponentTemplate,
commands: RenderTemplateCmd[]) {
var rootNodes = [];
this.fragments.push(rootNodes);
this._builders.push(
new RenderViewBuilder<N>(parentComponent, rootNodes, parentTemplate, commands));
}
enqueueRootBuilder(template: RenderComponentTemplate, cmds: RenderTemplateCmd[]) {
var rootNodes = [];
this.fragments.push(rootNodes);
this._builders.push(new RenderViewBuilder<N>(null, rootNodes, template, cmds));
}
consumeInplaceElement(): N {
var result = this._inplaceElement;
this._inplaceElement = null;
return result;
}
addEventListener(boundElementIndex: number, target: string, eventName: string) {
if (isPresent(target)) {
var handler =
createEventHandler(boundElementIndex, `${target}:${eventName}`, this._eventDispatcher);
this.globalEventAdders.push(createGlobalEventAdder(target, eventName, handler, this.factory));
} else {
var handler = createEventHandler(boundElementIndex, eventName, this._eventDispatcher);
this.factory.on(this.boundElements[boundElementIndex], eventName, handler);
}
}
}
function createEventHandler(boundElementIndex: number, eventName: string,
eventDispatcher: Function): Function {
return ($event) => eventDispatcher(boundElementIndex, eventName, $event);
}
function createGlobalEventAdder(target: string, eventName: string, eventHandler: Function,
nodeFactory: NodeFactory<any>): Function {
return () => nodeFactory.globalOn(target, eventName, eventHandler);
}
class RenderViewBuilder<N> implements RenderCommandVisitor {
parentStack: Array<N | Component<N>>;
constructor(public parentComponent: Component<N>, public fragmentRootNodes: N[],
public template: RenderComponentTemplate, public cmds: RenderTemplateCmd[]) {
var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot;
this.parentStack = [rootNodesParent];
}
build(context: BuildContext<N>) {
var cmds = this.cmds;
for (var i = 0; i < cmds.length; i++) {
cmds[i].visit(this, context);
}
}
get parent(): N | Component<N> { return this.parentStack[this.parentStack.length - 1]; }
visitText(cmd: RenderTextCmd, context: BuildContext<N>): any {
var text = context.factory.createText(cmd.value);
this._addChild(text, cmd.ngContentIndex, context);
if (cmd.isBound) {
context.boundTextNodes.push(text);
}
return null;
}
visitNgContent(cmd: RenderNgContentCmd, context: BuildContext<N>): any {
if (isPresent(this.parentComponent)) {
if (this.parentComponent.isRoot) {
var insertionPoint = context.factory.createRootContentInsertionPoint();
if (this.parent instanceof Component) {
context.factory.appendChild((<Component<N>>this.parent).shadowRoot, insertionPoint);
} else {
context.factory.appendChild(<N>this.parent, insertionPoint);
}
context.rootContentInsertionPoints.push(insertionPoint);
} else {
var projectedNodes = this.parentComponent.project(cmd.index);
for (var i = 0; i < projectedNodes.length; i++) {
var node = projectedNodes[i];
this._addChild(node, cmd.ngContentIndex, context);
}
}
}
return null;
}
visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): any {
this.parentStack.push(this._beginElement(cmd, context, null));
return null;
}
visitEndElement(context: BuildContext<N>): any {
this._endElement();
return null;
}
visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext<N>): any {
var templateId = cmd.templateId;
var tpl = context.factory.resolveComponentTemplate(templateId);
var el = this._beginElement(cmd, context, tpl);
var root = el;
if (tpl.encapsulation === ViewEncapsulation.Native) {
root = context.factory.createShadowRoot(el, templateId);
context.nativeShadowRoots.push(root);
}
var isRoot = context.componentCount === 0 && context.isHost;
var component = new Component(el, root, isRoot, tpl);
context.enqueueComponentBuilder(component);
this.parentStack.push(component);
return null;
}
visitEndComponent(context: BuildContext<N>): any {
this._endElement();
return null;
}
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: BuildContext<N>): any {
var el = context.factory.createTemplateAnchor(cmd.attrNameAndValues);
this._addChild(el, cmd.ngContentIndex, context);
context.boundElements.push(el);
if (cmd.isMerged) {
context.enqueueFragmentBuilder(this.parentComponent, this.template, cmd.children);
}
return null;
}
private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>,
componentTemplate: RenderComponentTemplate): N {
var el: N = context.consumeInplaceElement();
var attrNameAndValues = cmd.attrNameAndValues;
var templateEmulatedEncapsulation = this.template.encapsulation === ViewEncapsulation.Emulated;
var componentEmulatedEncapsulation =
isPresent(componentTemplate) &&
componentTemplate.encapsulation === ViewEncapsulation.Emulated;
var newAttrLength = attrNameAndValues.length + (templateEmulatedEncapsulation ? 2 : 0) +
(componentEmulatedEncapsulation ? 2 : 0);
if (newAttrLength > attrNameAndValues.length) {
// Note: Need to clone attrNameAndValues to make it writable!
var newAttrNameAndValues = ListWrapper.createFixedSize(newAttrLength);
var attrIndex;
for (attrIndex = 0; attrIndex < attrNameAndValues.length; attrIndex++) {
newAttrNameAndValues[attrIndex] = attrNameAndValues[attrIndex];
}
if (templateEmulatedEncapsulation) {
newAttrNameAndValues[attrIndex++] = _shimContentAttribute(this.template.shortId);
newAttrNameAndValues[attrIndex++] = '';
}
if (componentEmulatedEncapsulation) {
newAttrNameAndValues[attrIndex++] = _shimHostAttribute(componentTemplate.shortId);
newAttrNameAndValues[attrIndex++] = '';
}
attrNameAndValues = newAttrNameAndValues;
}
if (isPresent(el)) {
context.factory.mergeElement(el, attrNameAndValues);
this.fragmentRootNodes.push(el);
} else {
el = context.factory.createElement(cmd.name, attrNameAndValues);
this._addChild(el, cmd.ngContentIndex, context);
}
if (cmd.isBound) {
var boundElementIndex = context.boundElements.length;
context.boundElements.push(el);
for (var i = 0; i < cmd.eventTargetAndNames.length; i += 2) {
var target = cmd.eventTargetAndNames[i];
var eventName = cmd.eventTargetAndNames[i + 1];
context.addEventListener(boundElementIndex, target, eventName);
}
}
return el;
}
private _endElement() { this.parentStack.pop(); }
private _addChild(node: N, ngContentIndex: number, context: BuildContext<N>) {
var parent = this.parent;
if (isPresent(parent)) {
if (parent instanceof Component) {
parent.addContentNode(ngContentIndex, node, context);
} else {
context.factory.appendChild(<N>parent, node);
}
} else {
this.fragmentRootNodes.push(node);
}
}
}
class Component<N> {
private contentNodesByNgContentIndex: N[][] = [];
constructor(public hostElement: N, public shadowRoot: N, public isRoot: boolean,
public template: RenderComponentTemplate) {}
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
if (isBlank(ngContentIndex)) {
if (this.template.encapsulation === ViewEncapsulation.Native) {
context.factory.appendChild(this.hostElement, node);
}
} else {
while (this.contentNodesByNgContentIndex.length <= ngContentIndex) {
this.contentNodesByNgContentIndex.push([]);
}
this.contentNodesByNgContentIndex[ngContentIndex].push(node);
}
}
project(ngContentIndex: number): N[] {
return ngContentIndex < this.contentNodesByNgContentIndex.length ?
this.contentNodesByNgContentIndex[ngContentIndex] :
[];
}
}
var COMPONENT_REGEX = /%COMP%/g;
export const COMPONENT_VARIABLE = '%COMP%';
export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
function _shimContentAttribute(componentShortId: string): string {
return StringWrapper.replaceAll(CONTENT_ATTR, COMPONENT_REGEX, componentShortId);
}
function _shimHostAttribute(componentShortId: string): string {
return StringWrapper.replaceAll(HOST_ATTR, COMPONENT_REGEX, componentShortId);
}

View File

@ -1,5 +1,7 @@
import {ConcreteType, global, Type, isFunction, stringify} from 'angular2/src/facade/lang';
var _nextClassId = 0;
/**
* Declares the interface to be used with {@link Class}.
*/
@ -228,6 +230,10 @@ export function Class(clsDef: ClassDefinition): ConcreteType {
Reflect.defineMetadata('annotations', this.annotations, constructor);
}
if (!constructor['name']) {
constructor['overriddenName'] = `class${_nextClassId++}`;
}
return <ConcreteType>constructor;
}