refactor(core): move core/compiler to core/linker
This commit is contained in:
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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
@ -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); }
|
||||
}
|
@ -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(); }
|
@ -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)}`);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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); }
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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); }
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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) {}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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) {}
|
||||
}
|
@ -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)}`);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user