refactor(core): move core/compiler to core/linker

This commit is contained in:
Tobias Bosch
2015-10-02 07:28:42 -07:00
parent 43cca2de76
commit 9f4fa1ab0a
41 changed files with 0 additions and 0 deletions

View File

@ -1,52 +0,0 @@
import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
import {Injectable} from 'angular2/src/core/di';
import {Type, isBlank, stringify} from 'angular2/src/core/facade/lang';
import {BaseException} from 'angular2/src/core/facade/exceptions';
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
import {reflector} from 'angular2/src/core/reflection/reflection';
import {CompiledHostTemplate} from 'angular2/src/core/compiler/template_commands';
/**
* Low-level service for compiling {@link Component}s into {@link ProtoViewRef ProtoViews}s, which
* can later be used to create and render a Component instance.
*
* Most applications should instead use higher-level {@link DynamicComponentLoader} service, which
* both compiles and instantiates a Component.
*/
@Injectable()
export class Compiler {
/**
* @private
*/
constructor(private _protoViewFactory: ProtoViewFactory) {}
compileInHost(componentType: Type): Promise<ProtoViewRef> {
var metadatas = reflector.annotations(componentType);
var compiledHostTemplate = null;
for (var i = 0; i < metadatas.length; i++) {
var metadata = metadatas[i];
if (metadata instanceof CompiledHostTemplate) {
compiledHostTemplate = metadata;
break;
}
}
if (isBlank(compiledHostTemplate)) {
throw new BaseException(
`No precompiled template for component ${stringify(componentType)} found`);
}
return PromiseWrapper.resolve(this._createProtoView(compiledHostTemplate));
}
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);
}

View File

@ -1,38 +0,0 @@
import {Injectable} from 'angular2/src/core/di';
import {Type, isPresent} from 'angular2/src/core/facade/lang';
import {Map, MapWrapper} from 'angular2/src/core/facade/collection';
import {reflector} from 'angular2/src/core/reflection/reflection';
/**
* Resolve a `Type` from a {@link ComponentMetadata} into a URL.
*
* This interface can be overridden by the application developer to create custom behavior.
*
* See {@link Compiler}
*/
@Injectable()
export class ComponentUrlMapper {
/**
* Returns the base URL to the component source file.
* The returned URL could be:
* - an absolute URL,
* - a path relative to the application
*/
getUrl(component: Type): string {
return reflector.isReflectionEnabled() ? reflector.importUri(component) : './';
}
}
export class RuntimeComponentUrlMapper extends ComponentUrlMapper {
_componentUrls = new Map<Type, string>();
constructor() { super(); }
setComponentUrl(component: Type, url: string) { this._componentUrls.set(component, url); }
getUrl(component: Type): string {
var url = this._componentUrls.get(component);
if (isPresent(url)) return url;
return super.getUrl(component);
}
}

View File

@ -1,21 +0,0 @@
library angular2.src.core.compiler.directive_lifecycle_reflector;
import 'package:angular2/src/core/reflection/reflection.dart';
import 'package:angular2/src/core/compiler/interfaces.dart';
const INTERFACES = const {
LifecycleHooks.OnInit: OnInit,
LifecycleHooks.OnDestroy: OnDestroy,
LifecycleHooks.DoCheck: DoCheck,
LifecycleHooks.OnChanges: OnChanges,
LifecycleHooks.AfterContentInit: AfterContentInit,
LifecycleHooks.AfterContentChecked: AfterContentChecked,
LifecycleHooks.AfterViewInit: AfterViewInit,
LifecycleHooks.AfterViewChecked: AfterViewChecked,
};
bool hasLifecycleHook(LifecycleHooks interface, token) {
if (token is! Type) return false;
Type interfaceType = INTERFACES[interface];
return reflector.interfaces(token).contains(interfaceType);
}

View File

@ -1,29 +0,0 @@
import {Type} from 'angular2/src/core/facade/lang';
import {LifecycleHooks} from './interfaces';
export function hasLifecycleHook(lcInterface: LifecycleHooks, token): boolean {
if (!(token instanceof Type)) return false;
var proto = (<any>token).prototype;
switch (lcInterface) {
case LifecycleHooks.AfterContentInit:
return !!proto.afterContentInit;
case LifecycleHooks.AfterContentChecked:
return !!proto.afterContentChecked;
case LifecycleHooks.AfterViewInit:
return !!proto.afterViewInit;
case LifecycleHooks.AfterViewChecked:
return !!proto.afterViewChecked;
case LifecycleHooks.OnChanges:
return !!proto.onChanges;
case LifecycleHooks.DoCheck:
return !!proto.doCheck;
case LifecycleHooks.OnDestroy:
return !!proto.onDestroy;
case LifecycleHooks.OnInit:
return !!proto.onInit;
default:
return false;
}
}

View File

@ -1,140 +0,0 @@
import {resolveForwardRef, Injectable} from 'angular2/src/core/di';
import {Type, isPresent, stringify} from 'angular2/src/core/facade/lang';
import {BaseException} from 'angular2/src/core/facade/exceptions';
import {ListWrapper, StringMap, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {
DirectiveMetadata,
ComponentMetadata,
InputMetadata,
OutputMetadata,
HostBindingMetadata,
HostListenerMetadata,
ContentChildrenMetadata,
ViewChildrenMetadata,
ContentChildMetadata,
ViewChildMetadata
} from 'angular2/src/core/metadata';
import {reflector} from 'angular2/src/core/reflection/reflection';
/*
* Resolve a `Type` for {@link DirectiveMetadata}.
*
* This interface can be overridden by the application developer to create custom behavior.
*
* See {@link Compiler}
*/
@Injectable()
export class DirectiveResolver {
/**
* Return {@link DirectiveMetadata} for a given `Type`.
*/
resolve(type: Type): DirectiveMetadata {
var typeMetadata = reflector.annotations(resolveForwardRef(type));
if (isPresent(typeMetadata)) {
for (var i = 0; i < typeMetadata.length; i++) {
var metadata = typeMetadata[i];
if (metadata instanceof DirectiveMetadata) {
var propertyMetadata = reflector.propMetadata(type);
return this._mergeWithPropertyMetadata(metadata, propertyMetadata);
}
}
}
throw new BaseException(`No Directive annotation found on ${stringify(type)}`);
}
private _mergeWithPropertyMetadata(dm: DirectiveMetadata,
propertyMetadata:
StringMap<string, any[]>): DirectiveMetadata {
var inputs = [];
var outputs = [];
var host = {};
var queries = {};
StringMapWrapper.forEach(propertyMetadata, (metadata: any[], propName: string) => {
metadata.forEach(a => {
if (a instanceof InputMetadata) {
if (isPresent(a.bindingPropertyName)) {
inputs.push(`${propName}: ${a.bindingPropertyName}`);
} else {
inputs.push(propName);
}
}
if (a instanceof OutputMetadata) {
if (isPresent(a.bindingPropertyName)) {
outputs.push(`${propName}: ${a.bindingPropertyName}`);
} else {
outputs.push(propName);
}
}
if (a instanceof HostBindingMetadata) {
if (isPresent(a.hostPropertyName)) {
host[`[${a.hostPropertyName}]`] = propName;
} else {
host[`[${propName}]`] = propName;
}
}
if (a instanceof HostListenerMetadata) {
var args = isPresent(a.args) ? (<any[]>a.args).join(', ') : '';
host[`(${a.eventName})`] = `${propName}(${args})`;
}
if (a instanceof ContentChildrenMetadata) {
queries[propName] = a;
}
if (a instanceof ViewChildrenMetadata) {
queries[propName] = a;
}
if (a instanceof ContentChildMetadata) {
queries[propName] = a;
}
if (a instanceof ViewChildMetadata) {
queries[propName] = a;
}
});
});
return this._merge(dm, inputs, outputs, host, queries);
}
private _merge(dm: DirectiveMetadata, inputs: string[], outputs: string[],
host: StringMap<string, string>,
queries: StringMap<string, any>): DirectiveMetadata {
var mergedInputs = isPresent(dm.inputs) ? ListWrapper.concat(dm.inputs, inputs) : inputs;
var mergedOutputs = isPresent(dm.outputs) ? ListWrapper.concat(dm.outputs, outputs) : outputs;
var mergedHost = isPresent(dm.host) ? StringMapWrapper.merge(dm.host, host) : host;
var mergedQueries =
isPresent(dm.queries) ? StringMapWrapper.merge(dm.queries, queries) : queries;
if (dm instanceof ComponentMetadata) {
return new ComponentMetadata({
selector: dm.selector,
inputs: mergedInputs,
outputs: mergedOutputs,
host: mergedHost,
bindings: dm.bindings,
exportAs: dm.exportAs,
moduleId: dm.moduleId,
queries: mergedQueries,
changeDetection: dm.changeDetection,
viewBindings: dm.viewBindings
});
} else {
return new DirectiveMetadata({
selector: dm.selector,
inputs: mergedInputs,
outputs: mergedOutputs,
host: mergedHost,
bindings: dm.bindings,
exportAs: dm.exportAs,
moduleId: dm.moduleId,
queries: mergedQueries
});
}
}
}

View File

@ -1,300 +0,0 @@
import {Key, Injector, ResolvedBinding, Binding, bind, Injectable} from 'angular2/src/core/di';
import {Compiler} from './compiler';
import {isType, Type, stringify, isPresent} from 'angular2/src/core/facade/lang';
import {Promise} from 'angular2/src/core/facade/async';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
import {ElementRef} from './element_ref';
import {ViewRef, HostViewRef} from './view_ref';
/**
* Represents an instance of a Component created via {@link DynamicComponentLoader}.
*
* `ComponentRef` provides access to the Component Instance as well other objects related to this
* Component Instance and allows you to destroy the Component Instance via the {@link #dispose}
* method.
*/
export class ComponentRef {
/**
* Location of the Host Element of this Component Instance.
*/
location: ElementRef;
/**
* The instance of the Component.
*/
instance: any;
/**
* The user defined component type, represented via the constructor function.
*
* <!-- TODO: customize wording for Dart docs -->
*/
componentType: Type;
/**
* @private
*
* The injector provided {@link DynamicComponentLoader#loadAsRoot}.
*
* TODO(i): this api is useless and should be replaced by an injector retrieved from
* the HostElementRef, which is currently not possible.
*/
injector: Injector;
/**
* @private
*
* TODO(i): refactor into public/private fields
*/
constructor(location: ElementRef, instance: any, componentType: Type, injector: Injector,
private _dispose: () => void) {
this.location = location;
this.instance = instance;
this.componentType = componentType;
this.injector = injector;
}
/**
* The {@link ViewRef} of the Host View of this Component instance.
*/
get hostView(): HostViewRef { return this.location.parentView; }
/**
* @private
*
* Returns the type of this Component instance.
*
* TODO(i): this api should be removed
*/
get hostComponentType(): Type { return this.componentType; }
/**
* @private
*
* The instance of the component.
*
* TODO(i): this api should be removed
*/
get hostComponent(): any { return this.instance; }
/**
* Destroys the component instance and all of the data structures associated with it.
*
* TODO(i): rename to destroy to be consistent with AppViewManager and ViewContainerRef
*/
dispose() { this._dispose(); }
}
/**
* Service for instantiating a Component and attaching it to a View at a specified location.
*/
@Injectable()
export class DynamicComponentLoader {
/**
* @private
*/
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) {}
/**
* Creates an instance of a Component `type` and attaches it to the first element in the
* platform-specific global view that matches the component's selector.
*
* In a browser the platform-specific global view is the main DOM Document.
*
* If needed, the component's selector can be overridden via `overrideSelector`.
*
* You can optionally provide `injector` and this {@link Injector} will be used to instantiate the
* Component.
*
* To be notified when this Component instance is destroyed, you can also optionally provide
* `onDispose` callback.
*
* Returns a promise for the {@link ComponentRef} representing the newly created Component.
*
*
* ## Example
*
* ```
* @ng.Component({
* selector: 'child-component'
* })
* @ng.View({
* template: 'Child'
* })
* class ChildComponent {
* }
*
*
*
* @ng.Component({
* selector: 'my-app'
* })
* @ng.View({
* template: `
* Parent (<child id="child"></child>)
* `
* })
* class MyApp {
* constructor(dynamicComponentLoader: ng.DynamicComponentLoader, injector: ng.Injector) {
* dynamicComponentLoader.loadAsRoot(ChildComponent, '#child', injector);
* }
* }
*
* ng.bootstrap(MyApp);
* ```
*
* Resulting DOM:
*
* ```
* <my-app>
* Parent (
* <child id="child">
* Child
* </child>
* )
* </my-app>
* ```
*/
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
onDispose?: () => void): Promise<ComponentRef> {
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
var hostViewRef =
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
var dispose = () => {
this._viewManager.destroyRootHostView(hostViewRef);
if (isPresent(onDispose)) {
onDispose();
}
};
return new ComponentRef(newLocation, component, type, injector, dispose);
});
}
/**
* Creates an instance of a Component and attaches it to a View Container located inside of the
* Component View of another Component instance.
*
* The targeted Component Instance is specified via its `hostLocation` {@link ElementRef}. The
* location within the Component View of this Component Instance is specified via `anchorName`
* Template Variable Name.
*
* You can optionally provide `bindings` to configure the {@link Injector} provisioned for this
* Component Instance.
*
* Returns a promise for the {@link ComponentRef} representing the newly created Component.
*
*
* ## Example
*
* ```
* @ng.Component({
* selector: 'child-component'
* })
* @ng.View({
* template: 'Child'
* })
* class ChildComponent {
* }
*
*
* @ng.Component({
* selector: 'my-app'
* })
* @ng.View({
* template: `
* Parent (<div #child></div>)
* `
* })
* class MyApp {
* constructor(dynamicComponentLoader: ng.DynamicComponentLoader, elementRef: ng.ElementRef) {
* dynamicComponentLoader.loadIntoLocation(ChildComponent, elementRef, 'child');
* }
* }
*
* ng.bootstrap(MyApp);
* ```
*
* Resulting DOM:
*
* ```
* <my-app>
* Parent (
* <div #child="" class="ng-binding"></div>
* <child-component class="ng-binding">Child</child-component>
* )
* </my-app>
* ```
*/
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
return this.loadNextToLocation(
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), bindings);
}
/**
* Creates an instance of a Component and attaches it to the View Container found at the
* `location` specified as {@link ElementRef}.
*
* You can optionally provide `bindings` to configure the {@link Injector} provisioned for this
* Component Instance.
*
* Returns a promise for the {@link ComponentRef} representing the newly created Component.
*
*
* ## Example
*
* ```
* @ng.Component({
* selector: 'child-component'
* })
* @ng.View({
* template: 'Child'
* })
* class ChildComponent {
* }
*
*
* @ng.Component({
* selector: 'my-app'
* })
* @ng.View({
* template: `Parent`
* })
* class MyApp {
* constructor(dynamicComponentLoader: ng.DynamicComponentLoader, elementRef: ng.ElementRef) {
* dynamicComponentLoader.loadNextToLocation(ChildComponent, elementRef);
* }
* }
*
* ng.bootstrap(MyApp);
* ```
*
* Resulting DOM:
*
* ```
* <my-app>Parent</my-app>
* <child-component>Child</child-component>
* ```
*/
loadNextToLocation(type: Type, location: ElementRef,
bindings: ResolvedBinding[] = null): Promise<ComponentRef> {
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
var viewContainer = this._viewManager.getViewContainer(location);
var hostViewRef =
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, bindings);
var newLocation = this._viewManager.getHostElement(hostViewRef);
var component = this._viewManager.getComponent(newLocation);
var dispose = () => {
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
if (index !== -1) {
viewContainer.remove(index);
}
};
return new ComponentRef(newLocation, component, type, null, dispose);
});
}
}

View File

@ -1,16 +0,0 @@
import {isBlank} from 'angular2/src/core/facade/lang';
import {BaseException} from 'angular2/src/core/facade/exceptions';
import * as eiModule from './element_injector';
import {DirectiveBinding} 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: DirectiveBinding,
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,69 +0,0 @@
import {BaseException} from 'angular2/src/core/facade/exceptions';
import {ViewRef} from './view_ref';
import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/render/api';
/**
* Represents a location in a View that has an injection, change-detection and render context
* associated with it.
*
* An `ElementRef` is created for each element in the Template that contains a Directive, Component
* or data-binding.
*
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
* element.
*/
export class ElementRef implements RenderElementRef {
/**
* @private
*
* Reference to the {@link ViewRef} that this `ElementRef` is part of.
*/
parentView: ViewRef;
/**
* @private
*
* Index of the element inside the {@link ViewRef}.
*
* This is used internally by the Angular framework to locate elements.
*/
boundElementIndex: number;
/**
* @private
*/
constructor(parentView: ViewRef, boundElementIndex: number, private _renderer: Renderer) {
this.parentView = parentView;
this.boundElementIndex = boundElementIndex;
}
/**
* @private
*/
get renderView(): RenderViewRef { return this.parentView.render; }
// TODO(tbosch): remove this once Typescript supports declaring interfaces
// that contain getters
// https://github.com/Microsoft/TypeScript/issues/3745
set renderView(viewRef: RenderViewRef) { throw new BaseException('Abstract setter'); }
/**
* 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).
*
* <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 provided by Angular 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>
* <!-- TODO: add info about custom renderers that should be used instead -->
* </div>
*/
get nativeElement(): any { return this._renderer.getNativeElementSync(this); }
}

View File

@ -1,185 +0,0 @@
import {StringMap, MapWrapper} from 'angular2/src/core/facade/collection';
import {SimpleChange} from 'angular2/src/core/change_detection/change_detection_util';
export enum LifecycleHooks {
OnInit,
OnDestroy,
DoCheck,
OnChanges,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked
}
export var LIFECYCLE_HOOKS_VALUES = [
LifecycleHooks.OnInit,
LifecycleHooks.OnDestroy,
LifecycleHooks.DoCheck,
LifecycleHooks.OnChanges,
LifecycleHooks.AfterContentInit,
LifecycleHooks.AfterContentChecked,
LifecycleHooks.AfterViewInit,
LifecycleHooks.AfterViewChecked
];
/**
* Lifecycle hooks are guaranteed to be called in the following order:
* - `OnChanges` (if any bindings have changed),
* - `OnInit` (after the first check only),
* - `DoCheck`,
* - `AfterContentInit`,
* - `AfterContentChecked`,
* - `OnDestroy` (at the very end before destruction)
*/
/**
* Notify a directive when any of its bindings have changed.
*
* `onChanges` is called right after the directive's bindings have been checked,
* and before any of its children's bindings have been checked.
*
* It is invoked only if at least one of the directive's bindings has changed.
*
* ## Example:
*
* ```
* @Component(...)
* class MyComponent implements OnChanges {
* propA;
* propB;
*
* onChanges(changes: {[idx: string, PropertyUpdate]}): void {
* // This will get called after any of the inputs have been updated.
* if (changes['propA']) {
* // if propA was updated
* }
* if (changes['propA']) {
* // if propB was updated
* }
* }
* }
* ```
*/
export interface OnChanges { onChanges(changes: StringMap<string, SimpleChange>); }
/**
* Notify a directive when it has been checked the first time.
*
* `onInit` is called right after the directive's bindings have been checked for the first time,
* and before any of its children's bindings have been checked.
*
* It is invoked only once.
*
* ## Example
*
* ```
* @Component(...)
* class MyComponent implements OnInit {
* onInit(): void {
* }
* }
* ```
*/
export interface OnInit { onInit(); }
/**
* Overrides the default change detection.
*
* `doCheck()` gets called to check the changes in the directives instead of the default
* change detection mechanism.
*
* It is invoked every time the change detection is triggered.
*
* ## Example
*
* ```
* @Component(...)
* class MyComponent implements DoCheck {
* doCheck(): void {
* // Custom logic to detect changes
* }
* }
* ```
*/
export interface DoCheck { doCheck(); }
/**
* Notify a directive whenever a {@link ViewMetadata} that contains it is destroyed.
*
* ## Example
*
* ```
* @Component(...)
* class MyComponent implements OnDestroy {
* onDestroy(): void {
* // invoked to notify directive of the containing view destruction.
* }
* }
* ```
*/
export interface OnDestroy { onDestroy(); }
/**
* Notify a directive when the bindings of all its content children have been checked the first
* time (whether they have changed or not).
*
* ## Example
*
* ```
* @Component(...)
* class MyComponent implements AfterContentInit {
* afterContentInit(): void {
* }
* }
* ```
*/
export interface AfterContentInit { afterContentInit(); }
/**
* Notify a directive when the bindings of all its content children have been checked (whether
* they have changed or not).
*
* ## Example
*
* ```
* @Component(...)
* class MyComponent implements AfterContentChecked {
* afterContentChecked(): void {
* }
* }
* ```
*/
export interface AfterContentChecked { afterContentChecked(); }
/**
* Notify a directive when the bindings of all its view children have been checked the first time
* (whether they have changed or not).
*
* ## Example
*
* ```
* @Component(...)
* class MyComponent implements AfterViewInit {
* afterViewInit(): void {
* }
* }
* ```
*/
export interface AfterViewInit { afterViewInit(); }
/**
* Notify a directive when the bindings of all its view children have been checked (whether they
* have changed or not).
*
* ## Example
*
* ```
* @Component(...)
* class MyComponent implements AfterViewChecked {
* afterViewChecked(): void {
* }
* }
* ```
*/
export interface AfterViewChecked { afterViewChecked(); }

View File

@ -1,31 +0,0 @@
import {resolveForwardRef, Injectable} from 'angular2/src/core/di';
import {Type, isPresent, stringify} from 'angular2/src/core/facade/lang';
import {BaseException} from 'angular2/src/core/facade/exceptions';
import {PipeMetadata} from 'angular2/src/core/metadata';
import {reflector} from 'angular2/src/core/reflection/reflection';
/**
* Resolve a `Type` for {@link PipeMetadata}.
*
* This interface can be overridden by the application developer to create custom behavior.
*
* See {@link Compiler}
*/
@Injectable()
export class PipeResolver {
/**
* Return {@link PipeMetadata} for a given `Type`.
*/
resolve(type: Type): PipeMetadata {
var metas = reflector.annotations(resolveForwardRef(type));
if (isPresent(metas)) {
for (var i = 0; i < metas.length; i++) {
var annotation = metas[i];
if (annotation instanceof PipeMetadata) {
return annotation;
}
}
}
throw new BaseException(`No Pipe decorator found on ${stringify(type)}`);
}
}

View File

@ -1,325 +0,0 @@
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/core/facade/lang';
import {ViewType, RenderProtoViewRef} from 'angular2/src/core/render/api';
import {Injectable, Binding, resolveForwardRef, Inject} from 'angular2/src/core/di';
import {PipeBinding} from '../pipes/pipe_binding';
import {ProtoPipes} from '../pipes/pipes';
import {AppProtoView, AppProtoViewMergeInfo} from './view';
import {ElementBinder} from './element_binder';
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
import {DirectiveResolver} from './directive_resolver';
import {ViewResolver} from './view_resolver';
import {PipeResolver} from './pipe_resolver';
import {ViewMetadata} from '../metadata/view';
import {DEFAULT_PIPES_TOKEN} from 'angular2/src/core/pipes';
import {
visitAllCommands,
CompiledTemplate,
CompiledHostTemplate,
TemplateCmd,
CommandVisitor,
EmbeddedTemplateCmd,
BeginComponentCmd,
BeginElementCmd,
IBeginElementCmd,
TextCmd,
NgContentCmd
} from './template_commands';
import {Renderer} from 'angular2/render';
import {APP_ID} from 'angular2/src/core/render/dom/dom_tokens';
@Injectable()
export class ProtoViewFactory {
private _cache: Map<number, AppProtoView> = new Map<number, AppProtoView>();
private _defaultPipes: Type[];
private _appId: string;
constructor(private _renderer: Renderer, @Inject(DEFAULT_PIPES_TOKEN) defaultPipes: Type[],
private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
private _pipeResolver: PipeResolver, @Inject(APP_ID) appId: string) {
this._defaultPipes = defaultPipes;
this._appId = appId;
}
clearCache() { this._cache.clear(); }
createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
var compiledTemplate = compiledHostTemplate.getTemplate();
var result = this._cache.get(compiledTemplate.id);
if (isBlank(result)) {
var templateData = compiledTemplate.getData(this._appId);
result =
new AppProtoView(templateData.commands, ViewType.HOST, true,
templateData.changeDetectorFactory, null, new ProtoPipes(new Map()));
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 compiledTemplateData = cmd.template.getData(this._appId);
this._renderer.registerComponentTemplate(cmd.templateId, compiledTemplateData.commands,
compiledTemplateData.styles);
var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
nestedProtoView = new AppProtoView(compiledTemplateData.commands, ViewType.COMPONENT, true,
compiledTemplateData.changeDetectorFactory, null,
ProtoPipes.fromBindings(boundPipes));
// Note: The cache is updated before recursing
// to be able to resolve cycles
this._cache.set(cmd.template.id, nestedProtoView);
this._initializeProtoView(nestedProtoView, null);
}
return nestedProtoView;
}
private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView {
var nestedProtoView = new AppProtoView(
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.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(typeOrBinding): PipeBinding {
let meta = this._pipeResolver.resolve(typeOrBinding);
return PipeBinding.createFromType(typeOrBinding, meta);
}
private _flattenPipes(view: ViewMetadata): any[] {
if (isBlank(view.pipes)) return this._defaultPipes;
var pipes = ListWrapper.clone(this._defaultPipes);
_flattenList(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 componentDirectiveBinding: DirectiveBinding = null;
var isEmbeddedTemplate = false;
var directiveBindings: DirectiveBinding[] =
beginElementCmd.directives.map(type => bindDirective(directiveResolver, type));
if (beginElementCmd instanceof BeginComponentCmd) {
componentDirectiveBinding = directiveBindings[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 (directiveBindings.length > 0 || hasVariables || isEmbeddedTemplate) {
var directiveVariableBindings = new Map<string, number>();
if (!isEmbeddedTemplate) {
directiveVariableBindings =
createDirectiveVariableBindings(beginElementCmd.variableNameAndValues, directiveBindings);
}
protoElementInjector = ProtoElementInjector.create(
parentProtoElementInjector, boundElementIndex, directiveBindings,
isPresent(componentDirectiveBinding), distanceToParentPei, directiveVariableBindings);
protoElementInjector.attributes = arrayToMap(beginElementCmd.attrNameAndValues, false);
}
return new ElementBinder(boundElementIndex, parentElementBinder, distanceToParentBinder,
protoElementInjector, componentDirectiveBinding, nestedProtoView);
}
function bindDirective(directiveResolver: DirectiveResolver, type: Type): DirectiveBinding {
let annotation = directiveResolver.resolve(type);
return DirectiveBinding.createFromType(type, annotation);
}
export function createDirectiveVariableBindings(variableNameAndValues: Array<string | number>,
directiveBindings: DirectiveBinding[]):
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 _flattenList(tree: any[], out: Array<Type | Binding | any[]>): void {
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
_flattenList(item, out);
} else {
out.push(item);
}
}
}

View File

@ -1,39 +0,0 @@
library angular2.src.core.compiler.query_list;
import 'dart:collection';
import 'package:angular2/src/core/facade/async.dart';
/**
* See query_list.ts
*/
class QueryList<T> extends Object
with IterableMixin<T> {
List<T> _results = [];
EventEmitter _emitter = new EventEmitter();
Iterator<T> get iterator => _results.iterator;
Stream<Iterable<T>> get changes => _emitter;
int get length => _results.length;
T get first => _results.first;
T get last => _results.last;
String toString() {
return _results.toString();
}
List map(fn(T)) {
// Note: we need to return a list instead of iterable to match JS.
return this._results.map(fn).toList();
}
/** @private */
void reset(List<T> newList) {
_results = newList;
}
/** @private */
void notifyOnChanges() {
_emitter.add(this);
}
}

View File

@ -1,55 +0,0 @@
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
import {getSymbolIterator} from 'angular2/src/core/facade/lang';
import {Observable, EventEmitter} from 'angular2/src/core/facade/async';
/**
* An unmodifiable list of items that Angular keeps up to date when the state
* of the application changes.
*
* The type of object that {@link QueryMetadata} and {@link ViewQueryMetadata} provide.
*
* Implements an iterable interface, therefore it can be used in both ES6
* javascript `for (var i of items)` loops as well as in Angular templates with
* `*ng-for="#i of myList"`.
*
* Changes can be observed by subscribing to the changes `Observable`.
*
* NOTE: In the future this class will implement an `Observable` interface.
*
* ### Example ([live demo](http://plnkr.co/edit/RX8sJnQYl9FWuSCWme5z?p=preview))
* ```javascript
* @Component({...})
* class Container {
* constructor(@Query(Item) items: QueryList<Item>) {
* items.changes.subscribe(_ => console.log(items.length));
* }
* }
* ```
*/
export class QueryList<T> {
private _results: Array<T> = [];
private _emitter = new EventEmitter();
get changes(): Observable { return this._emitter; }
get length(): number { return this._results.length; }
get first(): T { return ListWrapper.first(this._results); }
get last(): T { return ListWrapper.last(this._results); }
/**
* returns a new list with the passsed in function applied to each element.
*/
map<U>(fn: (item: T) => U): U[] { return this._results.map(fn); }
[getSymbolIterator()](): any { return this._results[getSymbolIterator()](); }
toString(): string { return this._results.toString(); }
/**
* @private
*/
reset(res: T[]): void { this._results = res; }
/** @private */
notifyOnChanges(): void { this._emitter.next(this); }
}

View File

@ -1,189 +0,0 @@
import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {
RenderTemplateCmd,
RenderCommandVisitor,
RenderBeginElementCmd,
RenderTextCmd,
RenderNgContentCmd,
RenderBeginComponentCmd,
RenderEmbeddedTemplateCmd
} from 'angular2/src/core/render/render';
var _nextTemplateId: number = 0;
export function nextTemplateId(): number {
return _nextTemplateId++;
}
/**
* A compiled host template.
*
* This is const as we are storing it as annotation
* for the compiled component type.
*/
@CONST()
export class CompiledHostTemplate {
// Note: _templateGetter is a function so that CompiledHostTemplate can be
// a const!
constructor(private _templateGetter: Function) {}
getTemplate(): CompiledTemplate { return this._templateGetter(); }
}
/**
* A compiled template.
*/
export class CompiledTemplate {
// Note: paramGetter is a function so that we can have cycles between templates!
// paramGetter returns a tuple with:
// - ChangeDetector factory function
// - TemplateCmd[]
// - styles
constructor(public id: number,
private _dataGetter: /*()=>Array<Function, TemplateCmd[], string[]>*/ Function) {}
getData(appId: string): CompiledTemplateData {
var data = this._dataGetter(appId, this.id);
return new CompiledTemplateData(data[0], data[1], data[2]);
}
}
export class CompiledTemplateData {
constructor(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;
}
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);
}
}
export function text(value: string, isBound: boolean, ngContentIndex: number): TextCmd {
return new TextCmd(value, isBound, ngContentIndex);
}
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
isBound: boolean = false;
constructor(public ngContentIndex: number) {}
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitNgContent(this, context);
}
}
export function ngContent(ngContentIndex: number): NgContentCmd {
return new NgContentCmd(ngContentIndex);
}
export interface IBeginElementCmd extends TemplateCmd, RenderBeginElementCmd {
variableNameAndValues: Array<string | number>;
eventTargetAndNames: string[];
directives: Type[];
visit(visitor: RenderCommandVisitor, context: any): any;
}
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);
}
}
export function beginElement(name: string, attrNameAndValues: string[],
eventTargetAndNames: string[],
variableNameAndValues: Array<string | number>, directives: Type[],
isBound: boolean, ngContentIndex: number): BeginElementCmd {
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
directives, isBound, ngContentIndex);
}
export class EndElementCmd implements TemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndElement(context);
}
}
export function endElement(): TemplateCmd {
return new EndElementCmd();
}
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
isBound: boolean = true;
templateId: number;
constructor(public name: string, public attrNameAndValues: string[],
public eventTargetAndNames: string[],
public variableNameAndValues: Array<string | number>, public directives: Type[],
public nativeShadow: boolean, public ngContentIndex: number,
public template: CompiledTemplate) {
this.templateId = template.id;
}
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitBeginComponent(this, context);
}
}
export function beginComponent(
name: string, attrNameAnsValues: string[], eventTargetAndNames: string[],
variableNameAndValues: Array<string | number>, directives: Type[], nativeShadow: boolean,
ngContentIndex: number, template: CompiledTemplate): BeginComponentCmd {
return new BeginComponentCmd(name, attrNameAnsValues, eventTargetAndNames, variableNameAndValues,
directives, nativeShadow, ngContentIndex, template);
}
export class EndComponentCmd implements TemplateCmd {
visit(visitor: RenderCommandVisitor, context: any): any {
return visitor.visitEndComponent(context);
}
}
export function endComponent(): TemplateCmd {
return new EndComponentCmd();
}
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 function embeddedTemplate(attrNameAndValues: string[], variableNameAndValues: string[],
directives: Type[], isMerged: boolean, ngContentIndex: number,
changeDetectorFactory: Function, children: TemplateCmd[]):
EmbeddedTemplateCmd {
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues, directives, isMerged,
ngContentIndex, changeDetectorFactory, children);
}
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,58 +0,0 @@
import {internalView, ProtoViewRef} from './view_ref';
import {ElementRef} from './element_ref';
import * as viewModule from './view';
/**
* Represents an Embedded Template that can be used to instantiate Embedded Views.
*
* You can access a `TemplateRef`, in two ways. Via a directive placed on a `<template>` element (or
* directive prefixed with `*`) and have the `TemplateRef` for this Embedded View injected into the
* constructor of the directive using the `TemplateRef` Token. Alternatively you can query for the
* `TemplateRef` from a Component or a Directive via {@link Query}.
*
* To instantiate Embedded Views based on a Template, use
* {@link ViewContainerRef#createEmbeddedView}, which will create the View and attach it to the
* View Container.
*/
export class TemplateRef {
/**
* The location in the View where the Embedded View logically belongs to.
*
* The data-binding and injection contexts of Embedded Views created from this `TemplateRef`
* inherit from the contexts of this location.
*
* Typically new Embedded Views are attached to the View Container of this location, but in
* advanced use-cases, the View can be attached to a different container while keeping the
* data-binding and injection context from the original location.
*
*/
// TODO(i): rename to anchor or location
elementRef: ElementRef;
/**
* @private
*/
constructor(elementRef: ElementRef) { this.elementRef = elementRef; }
private _getProtoView(): viewModule.AppProtoView {
var parentView = internalView(this.elementRef.parentView);
return parentView.proto
.elementBinders[this.elementRef.boundElementIndex - parentView.elementOffset]
.nestedProtoView;
}
/**
* @private
*
* Reference to the ProtoView used for creating Embedded Views that are based on the compiled
* Embedded Template.
*/
get protoViewRef(): ProtoViewRef { return this._getProtoView().ref; }
/**
* Allows you to check if this Embedded Template defines Local Variable with name matching `name`.
*/
hasLocal(name: string): boolean {
return this._getProtoView().templateVariableBindings.has(name);
}
}

View File

@ -1,342 +0,0 @@
import {
ListWrapper,
MapWrapper,
Map,
StringMapWrapper,
StringMap
} from 'angular2/src/core/facade/collection';
import {
AST,
ChangeDetector,
ChangeDetectorRef,
ChangeDispatcher,
DirectiveIndex,
DirectiveRecord,
BindingTarget,
Locals,
ProtoChangeDetector
} from 'angular2/src/core/change_detection/change_detection';
import {DebugContext} from 'angular2/src/core/change_detection/interfaces';
import {
ProtoElementInjector,
ElementInjector,
PreBuiltObjects,
DirectiveBinding
} from './element_injector';
import {ElementBinder} from './element_binder';
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/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 {ProtoPipes} from 'angular2/src/core/pipes/pipes';
import {camelCaseToDashCase} from 'angular2/src/core/render/dom/util';
import {TemplateCmd} from './template_commands';
export {DebugContext} from 'angular2/src/core/change_detection/interfaces';
const REFLECT_PREFIX: string = 'ng-reflect-';
export class AppViewContainer {
// The order in this list matches the DOM order.
views: AppView[] = [];
}
/**
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
*
*/
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;
/**
* The context against which data-binding expressions in this view are evaluated against.
* This is always a component instance.
*/
context: any = null;
/**
* Variables, local to this view, that can be used in binding expressions (in addition to the
* context). This is used for thing like `<video #player>` or
* `<li template="for #item of items">`, where "player" and "item" are locals, respectively.
*/
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);
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); // TODO optimize this
}
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;
}
setLocal(contextName: string, value: any): void {
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
if (!this.proto.templateVariableBindings.has(contextName)) {
return;
}
var templateName = this.proto.templateVariableBindings.get(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);
} else {
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
if (b.isElementProperty()) {
this.renderer.setElementProperty(elementRef, b.name, currentValue);
} else if (b.isElementAttribute()) {
this.renderer.setElementAttribute(elementRef, b.name,
isPresent(currentValue) ? `${currentValue}` : null);
} else if (b.isElementClass()) {
this.renderer.setElementClass(elementRef, b.name, currentValue);
} else if (b.isElementStyle()) {
var unit = isPresent(b.unit) ? b.unit : '';
this.renderer.setElementStyle(elementRef, b.name, `${currentValue}${unit}`);
} else {
throw new BaseException('Unsupported directive record');
}
}
}
logBindingUpdate(b: BindingTarget, value: any): void {
if (b.isDirective() || b.isElementProperty()) {
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
this.renderer.setElementAttribute(
elementRef, `${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].afterContentChecked();
}
}
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].afterViewChecked();
}
}
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 {
try {
var offsettedIndex = this.elementOffset + elementIndex;
var hasRefForIndex = offsettedIndex < this.elementRefs.length;
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 componentElement = isPresent(container) ? container.nativeElement : null;
var directive = isPresent(directiveIndex) ? this.getDirectiveFor(directiveIndex) : null;
var injector = isPresent(ei) ? ei.getInjector() : null;
return new DebugContext(element, componentElement, directive, this.context,
_localsToStringMap(this.locals), injector);
} catch (e) {
// TODO: vsavkin log the exception once we have a good way to log errors and warnings
// if an error happens during getting the debug context, we return null.
return null;
}
}
getDetectorFor(directive: DirectiveIndex): any {
var childView = this.getNestedView(this.elementOffset + directive.elementIndex);
return isPresent(childView) ? childView.changeDetector : null;
}
invokeElementMethod(elementIndex: number, methodName: string, args: any[]) {
this.renderer.invokeElementMethod(this.elementRefs[elementIndex], methodName, args);
}
// 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): StringMap<string, any> {
var res = {};
var c = locals;
while (isPresent(c)) {
res = StringMapWrapper.merge(res, MapWrapper.toStringMap(c.current));
c = c.parent;
}
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 templateCmds: TemplateCmd[], public type: renderApi.ViewType,
public isMergable: boolean, public changeDetectorFactory: Function,
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
this.ref = new ProtoViewRef(this);
}
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)) {
MapWrapper.forEach(this.templateVariableBindings,
(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.
MapWrapper.forEach(variableLocations,
(_, templateName) => { this.protoLocals.set(templateName, null); });
}
}
isInitialized(): boolean { return isPresent(this.elementBinders); }
}

View File

@ -1,151 +0,0 @@
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {ResolvedBinding} from 'angular2/src/core/di';
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
import * as avmModule from './view_manager';
import * as viewModule from './view';
import {ElementRef} from './element_ref';
import {TemplateRef} from './template_ref';
import {ViewRef, HostViewRef, ProtoViewRef, internalView} from './view_ref';
/**
* Represents a container where one or more Views can be attached.
*
* The container can contain two kinds of Views. Host Views, created by instantiating a
* {@link Component} via {@link #createHostView}, and Embedded Views, created by instantiating an
* {@link TemplateRef Embedded Template} via {@link #createEmbeddedView}.
*
* The location of the View Container within the containing View is specified by the Anchor
* `element`. Each View Container can have only one Anchor Element and each Anchor Element can only
* have a single View Container.
*
* Root elements of Views attached to this container become siblings of the Anchor Element in
* the Rendered View.
*
* To access a `ViewContainerRef` of an Element, you can either place a {@link Directive} injected
* with `ViewContainerRef` on the Element, or you obtain it via
* {@link AppViewManager#getViewContainer}.
*
* <!-- TODO(i): we are also considering ElementRef#viewContainer api -->
*/
export class ViewContainerRef {
/**
* @private
*/
constructor(
/**
* @private
*/
public viewManager: avmModule.AppViewManager,
/**
* Anchor element that specifies the location of this container in the containing View.
* <!-- TODO: rename to anchorElement -->
*/
public element: ElementRef) {}
private _getViews(): Array<viewModule.AppView> {
var vc = internalView(this.element.parentView).viewContainers[this.element.boundElementIndex];
return isPresent(vc) ? vc.views : [];
}
/**
* Destroys all Views in this container.
*/
clear(): void {
for (var i = this.length - 1; i >= 0; i--) {
this.remove(i);
}
}
/**
* Returns the {@link ViewRef} for the View located in this container at the specified index.
*/
get(index: number): ViewRef { return this._getViews()[index].ref; }
/**
* Returns the number of Views currently attached to this container.
*/
get length(): number { return this._getViews().length; }
/**
* Instantiates an Embedded View based on the {@link TemplateRef `templateRef`} and inserts it
* into this container at the specified `index`.
*
* If `index` is not specified, the new View will be inserted as the last View in the container.
*
* Returns the {@link ViewRef} for the newly created View.
*/
// TODO(rado): profile and decide whether bounds checks should be added
// to the methods below.
createEmbeddedView(templateRef: TemplateRef, index: number = -1): ViewRef {
if (index == -1) index = this.length;
return this.viewManager.createEmbeddedViewInContainer(this.element, index, templateRef);
}
/**
* Instantiates a single {@link Component} and inserts its Host View into this container at the
* specified `index`.
*
* The component is instantiated using its {@link ProtoViewRef `protoView`} which can be
* obtained via {@link Compiler#compileInHost}.
*
* If `index` is not specified, the new View will be inserted as the last View in the container.
*
* You can optionally specify `dynamicallyCreatedBindings`, 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.
*/
createHostView(protoViewRef: ProtoViewRef = null, index: number = -1,
dynamicallyCreatedBindings: ResolvedBinding[] = null): HostViewRef {
if (index == -1) index = this.length;
return this.viewManager.createHostViewInContainer(this.element, index, protoViewRef,
dynamicallyCreatedBindings);
}
/**
* Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
*
* If `index` is not specified, the new View will be inserted as the last View in the container.
*
* Returns the inserted {@link ViewRef}.
*/
// TODO(i): refactor insert+remove into move
insert(viewRef: ViewRef, index: number = -1): ViewRef {
if (index == -1) index = this.length;
return this.viewManager.attachViewInContainer(this.element, index, viewRef);
}
/**
* Returns the index of the View, specified via {@link ViewRef}, within the current container or
* `-1` if this container doesn't contain the View.
*/
indexOf(viewRef: ViewRef): number {
return ListWrapper.indexOf(this._getViews(), internalView(viewRef));
}
/**
* Destroys a View attached to this container at the specified `index`.
*
* If `index` is not specified, the last View in the container will be removed.
*/
// TODO(i): rename to destroy
remove(index: number = -1): void {
if (index == -1) index = this.length - 1;
this.viewManager.destroyViewInContainer(this.element, index);
// view is intentionally not returned to the client.
}
/**
* Use along with {@link #insert} to move a View within the current container.
*
* If the `index` param is omitted, the last {@link ViewRef} is detached.
*/
// TODO(i): refactor insert+remove into move
detach(index: number = -1): ViewRef {
if (index == -1) index = this.length - 1;
return this.viewManager.detachViewInContainer(this.element, index);
}
}

View File

@ -1,11 +0,0 @@
import {Injectable} from 'angular2/src/core/di';
import * as viewModule from './view';
/**
* Listener for view creation / destruction.
*/
@Injectable()
export class AppViewListener {
viewCreated(view: viewModule.AppView) {}
viewDestroyed(view: viewModule.AppView) {}
}

View File

@ -1,417 +0,0 @@
import {
Injector,
Inject,
Binding,
Injectable,
ResolvedBinding,
forwardRef
} from 'angular2/src/core/di';
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
import {BaseException} from 'angular2/src/core/facade/exceptions';
import * as viewModule from './view';
import {ElementRef} from './element_ref';
import {ProtoViewRef, ViewRef, HostViewRef, internalView, internalProtoView} from './view_ref';
import {ViewContainerRef} from './view_container_ref';
import {TemplateRef} from './template_ref';
import {
Renderer,
RenderViewRef,
RenderFragmentRef,
RenderViewWithFragments,
ViewType
} from 'angular2/src/core/render/api';
import {AppViewManagerUtils} from './view_manager_utils';
import {AppViewPool} from './view_pool';
import {AppViewListener} from './view_listener';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
import {ProtoViewFactory} from './proto_view_factory';
/**
* Service exposing low level API for creating, moving and destroying Views.
*
* Most applications should use higher-level abstractions like {@link DynamicComponentLoader} and
* {@link ViewContainerRef} instead.
*/
@Injectable()
export class AppViewManager {
private _protoViewFactory: ProtoViewFactory;
/**
* @private
*/
constructor(private _viewPool: AppViewPool, private _viewListener: AppViewListener,
private _utils: AppViewManagerUtils, private _renderer: Renderer,
@Inject(forwardRef(() => ProtoViewFactory)) _protoViewFactory) {
this._protoViewFactory = _protoViewFactory;
}
/**
* Returns a {@link ViewContainerRef} of the View Container at the specified location.
*/
getViewContainer(location: ElementRef): ViewContainerRef {
var hostView = internalView(location.parentView);
return hostView.elementInjectors[location.boundElementIndex].getViewContainerRef();
}
/**
* Returns the {@link ElementRef} that makes up the specified Host View.
*/
getHostElement(hostViewRef: HostViewRef): ElementRef {
var hostView = internalView(<ViewRef>hostViewRef);
if (hostView.proto.type !== ViewType.HOST) {
throw new BaseException('This operation is only allowed on host views');
}
return hostView.elementRefs[hostView.elementOffset];
}
/**
* Searches the Component View of the Component specified via `hostLocation` and returns the
* {@link ElementRef} for the Element identified via a Variable Name `variableName`.
*
* Throws an exception if the specified `hostLocation` is not a Host Element of a Component, or if
* variable `variableName` couldn't be found in the Component View of this Component.
*/
getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
var hostView = internalView(hostLocation.parentView);
var boundElementIndex = hostLocation.boundElementIndex;
var componentView = hostView.getNestedView(boundElementIndex);
if (isBlank(componentView)) {
throw new BaseException(`There is no component directive at element ${boundElementIndex}`);
}
var binderIdx = componentView.proto.variableLocations.get(variableName);
if (isBlank(binderIdx)) {
throw new BaseException(`Could not find variable ${variableName}`);
}
return componentView.elementRefs[componentView.elementOffset + binderIdx];
}
/**
* Returns the component instance for the provided Host Element.
*/
getComponent(hostLocation: ElementRef): any {
var hostView = internalView(hostLocation.parentView);
var boundElementIndex = hostLocation.boundElementIndex;
return this._utils.getComponentInstance(hostView, boundElementIndex);
}
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()');
/**
* Creates an instance of a Component and attaches it to the first element in the global View
* (usually DOM Document) that matches the component's selector or `overrideSelector`.
*
* 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
* by compiling the component with {@link Compiler#compileInHost}.
*
* Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host
* View.
*
* ## Example
*
* ```
* @ng.Component({
* selector: 'child-component'
* })
* @ng.View({
* template: 'Child'
* })
* class ChildComponent {
*
* }
*
* @ng.Component({
* selector: 'my-app'
* })
* @ng.View({
* template: `
* Parent (<some-component></some-component>)
* `
* })
* class MyApp {
* viewRef: ng.ViewRef;
*
* constructor(public appViewManager: ng.AppViewManager, compiler: ng.Compiler) {
* compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoViewRef) => {
* this.viewRef = appViewManager.createRootHostView(protoView, 'some-component', null);
* })
* }
*
* onDestroy() {
* this.appViewManager.destroyRootHostView(this.viewRef);
* this.viewRef = null;
* }
* }
*
* ng.bootstrap(MyApp);
* ```
*/
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
injector: Injector): 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);
}
_destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()');
/**
* Destroys the Host View created via {@link AppViewManager#createRootHostView}.
*
* Along with the Host View, the Component Instance as well as all nested View and Components are
* destroyed as well.
*/
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.
var s = this._destroyRootHostViewScope();
var hostView = internalView(<ViewRef>hostViewRef);
this._renderer.detachFragment(hostView.renderFragment);
this._renderer.dehydrateView(hostView.render);
this._viewDehydrateRecurse(hostView);
this._viewListener.viewDestroyed(hostView);
this._renderer.destroyView(hostView.render);
wtfLeave(s);
}
_createEmbeddedViewInContainerScope: WtfScopeFn =
wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()');
/**
* Instantiates an Embedded View based on the {@link TemplateRef `templateRef`} and inserts it
* into the View Container specified via `viewContainerLocation` at the specified `index`.
*
* Returns the {@link ViewRef} for the newly created View.
*
* This as a low-level way to create and attach an Embedded via to a View Container. Most
* applications should used {@link ViewContainerRef#createEmbeddedView} instead.
*
* Use {@link AppViewManager#destroyViewInContainer} to destroy the created Embedded View.
*/
// 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.
createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
templateRef: TemplateRef): ViewRef {
var s = this._createEmbeddedViewInContainerScope();
var protoView = internalProtoView(templateRef.protoViewRef);
if (protoView.type !== 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));
}
_createHostViewInContainerScope: WtfScopeFn =
wtfCreateScope('AppViewManager#createHostViewInContainer()');
/**
* Instantiates a single {@link Component} and inserts its Host View into the View Container
* found at `viewContainerLocation`. Within the container, the view will be inserted at position
* specified via `index`.
*
* 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}
* 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.
*/
createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
protoViewRef: ProtoViewRef,
imperativelyCreatedInjector: ResolvedBinding[]): HostViewRef {
var s = this._createHostViewInContainerScope();
var protoView = internalProtoView(protoViewRef);
if (protoView.type !== 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}.
*/
_createViewInContainer(viewContainerLocation: ElementRef, index: number,
protoView: viewModule.AppProtoView, context: ElementRef,
imperativelyCreatedInjector: ResolvedBinding[]): ViewRef {
var parentView = internalView(viewContainerLocation.parentView);
var boundElementIndex = viewContainerLocation.boundElementIndex;
var contextView = internalView(context.parentView);
var contextBoundElementIndex = context.boundElementIndex;
var embeddedFragmentView = contextView.getNestedView(contextBoundElementIndex);
var view;
if (protoView.type === 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);
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
contextBoundElementIndex, index,
imperativelyCreatedInjector);
return view.ref;
}
_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);
}
}
_destroyViewInContainerScope = wtfCreateScope('AppViewMananger#destroyViewInContainer()');
/**
* Destroys an Embedded or Host View attached to a View Container at the specified `index`.
*
* The View Container is located via `viewContainerLocation`.
*/
destroyViewInContainer(viewContainerLocation: ElementRef, index: number) {
var s = this._destroyViewInContainerScope();
var parentView = internalView(viewContainerLocation.parentView);
var boundElementIndex = viewContainerLocation.boundElementIndex;
this._destroyViewInContainer(parentView, boundElementIndex, index);
wtfLeave(s);
}
_attachViewInContainerScope = wtfCreateScope('AppViewMananger#attachViewInContainer()');
/**
*
* See {@link AppViewManager#detachViewInContainer}.
*/
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
attachViewInContainer(viewContainerLocation: ElementRef, index: number,
viewRef: ViewRef): ViewRef {
var s = this._attachViewInContainerScope();
var view = internalView(viewRef);
var parentView = internalView(viewContainerLocation.parentView);
var boundElementIndex = viewContainerLocation.boundElementIndex;
// TODO(tbosch): the public methods attachViewInContainer/detachViewInContainer
// are used for moving elements without the same container.
// We will change this into an atomic `move` operation, which should preserve the
// previous parent injector (see https://github.com/angular/angular/issues/1377).
// Right now we are destroying any special
// context view that might have been used.
this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, index, view);
this._attachRenderView(parentView, boundElementIndex, index, view);
return wtfLeave(s, viewRef);
}
_detachViewInContainerScope = wtfCreateScope('AppViewMananger#detachViewInContainer()');
/**
* See {@link AppViewManager#attachViewInContainer}.
*/
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef {
var s = this._detachViewInContainerScope();
var parentView = internalView(viewContainerLocation.parentView);
var boundElementIndex = viewContainerLocation.boundElementIndex;
var viewContainer = parentView.viewContainers[boundElementIndex];
var view = viewContainer.views[index];
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
this._renderer.detachFragment(view.renderFragment);
return wtfLeave(s, view.ref);
}
_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.viewCreated(mergedParentView);
return mergedParentView;
}
_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));
}
return view;
}
_destroyPooledView(view: viewModule.AppView) {
var wasReturned = this._viewPool.returnView(view);
if (!wasReturned) {
this._viewListener.viewDestroyed(view);
this._renderer.destroyView(view.render);
}
}
_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);
}
}
_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,264 +0,0 @@
import {Injector, Binding, Injectable, ResolvedBinding} from 'angular2/src/core/di';
import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/core/facade/collection';
import * as eli from './element_injector';
import {isPresent, isBlank} from 'angular2/src/core/facade/lang';
import * as viewModule from './view';
import * as avmModule from './view_manager';
import {ElementRef} from './element_ref';
import {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';
import {RenderViewRef, RenderFragmentRef, ViewType} from 'angular2/src/core/render/api';
@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 === 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 === 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 === ViewType.COMPONENT) {
parentView.changeDetector.addShadowDomChild(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.addChild(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, imperativelyCreatedBindings: ResolvedBinding[]) {
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(imperativelyCreatedBindings) ?
Injector.fromResolvedBindings(imperativelyCreatedBindings) :
null;
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
contextView.locals);
}
_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 === 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++;
}
}
}
_populateViewLocals(view: viewModule.AppView, elementInjector: eli.ElementInjector,
boundElementIdx: number): void {
if (isPresent(elementInjector.getDirectiveVariableBindings())) {
MapWrapper.forEach(elementInjector.getDirectiveVariableBindings(), (directiveIndex, name) => {
if (isBlank(directiveIndex)) {
view.locals.set(name, view.elementRefs[boundElementIdx].nativeElement);
} else {
view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
}
});
}
}
_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,40 +0,0 @@
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
import {ListWrapper, MapWrapper, Map} from 'angular2/src/core/facade/collection';
import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/core/facade/lang';
import * as viewModule from './view';
export const APP_VIEW_POOL_CAPACITY = CONST_EXPR(new OpaqueToken('AppViewPool.viewPoolCapacity'));
@Injectable()
export class AppViewPool {
_poolCapacityPerProtoView: number;
_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 ListWrapper.removeLast(pooledViews);
}
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,173 +0,0 @@
import {isPresent} from 'angular2/src/core/facade/lang';
import * as viewModule from './view';
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {RenderViewRef, RenderFragmentRef} from 'angular2/src/core/render/api';
// This is a workaround for privacy in Dart as we don't have library parts
export function internalView(viewRef: ViewRef): viewModule.AppView {
return viewRef._view;
}
// 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._protoView : null;
}
/**
* Represents a View containing a single Element that is the Host Element of a {@link Component}
* instance.
*
* A Host View is created for every dynamically created Component that was compiled on its own (as
* opposed to as a part of another Component's Template) via {@link Compiler#compileInHost} or one
* of the higher-level APIs: {@link AppViewManager#createRootHostView},
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
*/
export interface HostViewRef {
/**
* @private
*/
changeDetectorRef: ChangeDetectorRef;
}
/**
* Represents an Angular View.
*
* <!-- TODO: move the next two paragraphs to the dev guide -->
* A View is a fundamental building block of the application UI. It is the smallest grouping of
* Elements which are created and destroyed together.
*
* Properties of elements in a View can change, but the structure (number and order) of elements in
* a View cannot. Changing the structure of Elements can only be done by inserting, moving or
* removing nested Views via a {@link ViewContainer}. Each View can contain many View Containers.
* <!-- /TODO -->
*
* ## Example
*
* Given this template...
*
* ```
* Count: {{items.length}}
* <ul>
* <li *ng-for="var item of items">{{item}}</li>
* </ul>
* ```
*
* ... we have two {@link ProtoViewRef}s:
*
* Outer {@link ProtoViewRef}:
* ```
* Count: {{items.length}}
* <ul>
* <template ng-for var-item [ng-for-of]="items"></template>
* </ul>
* ```
*
* Inner {@link ProtoViewRef}:
* ```
* <li>{{item}}</li>
* ```
*
* Notice that the original template is broken down into two separate {@link ProtoViewRef}s.
*
* The outer/inner {@link ProtoViewRef}s are then assembled into views like so:
*
* ```
* <!-- ViewRef: outer-0 -->
* Count: 2
* <ul>
* <template view-container-ref></template>
* <!-- ViewRef: inner-1 --><li>first</li><!-- /ViewRef: inner-1 -->
* <!-- ViewRef: inner-2 --><li>second</li><!-- /ViewRef: inner-2 -->
* </ul>
* <!-- /ViewRef: outer-0 -->
* ```
*/
export class ViewRef implements HostViewRef {
private _changeDetectorRef: ChangeDetectorRef = null;
/**
* @private
*/
constructor(public _view: viewModule.AppView) {}
/**
* @private
*
* Return `RenderViewRef`
*/
get render(): RenderViewRef { return this._view.render; }
/**
* @private
*
* Return `RenderFragmentRef`
*/
get renderFragment(): RenderFragmentRef { return this._view.renderFragment; }
/**
* @private
*
* Return `ChangeDetectorRef`
*/
get changeDetectorRef(): ChangeDetectorRef {
if (this._changeDetectorRef === null) {
this._changeDetectorRef = this._view.changeDetector.ref;
}
return this._changeDetectorRef;
}
set changeDetectorRef(value: ChangeDetectorRef) {
throw "readonly"; // TODO: https://github.com/Microsoft/TypeScript/issues/12
}
/**
* Sets `value` of local variable called `variableName` in this View.
*/
setLocal(variableName: string, value: any): void { this._view.setLocal(variableName, value); }
}
/**
* 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 *ng-for="var item of items">{{item}}</li>
* </ul>
* ```
*
* Angular desugars and compiles the template into two ProtoViews:
*
* Outer ProtoView:
* ```
* Count: {{items.length}}
* <ul>
* <template ng-for var-item [ng-for-of]="items"></template>
* </ul>
* ```
*
* Inner ProtoView:
* ```
* <li>{{item}}</li>
* ```
*
* Notice that the original template is broken down into two separate ProtoViews.
*/
export class ProtoViewRef {
/**
* @private
*/
constructor(public _protoView: viewModule.AppProtoView) {}
}

View File

@ -1,36 +0,0 @@
import {Injectable} from 'angular2/src/core/di';
import {ViewMetadata} from '../metadata/view';
import {Type, stringify, isBlank} from 'angular2/src/core/facade/lang';
import {BaseException} from 'angular2/src/core/facade/exceptions';
import {Map, MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {reflector} from 'angular2/src/core/reflection/reflection';
@Injectable()
export class ViewResolver {
_cache = new Map<Type, ViewMetadata>();
resolve(component: Type): ViewMetadata {
var view = this._cache.get(component);
if (isBlank(view)) {
view = this._resolve(component);
this._cache.set(component, view);
}
return view;
}
_resolve(component: Type): ViewMetadata {
var annotations = reflector.annotations(component);
for (var i = 0; i < annotations.length; i++) {
var annotation = annotations[i];
if (annotation instanceof ViewMetadata) {
return annotation;
}
}
throw new BaseException(`No View annotation found on component ${stringify(component)}`);
}
}