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

This commit is contained in:
Tobias Bosch
2015-10-02 08:42:11 -07:00
parent 43cca2de76
commit 9f4fa1ab0a
41 changed files with 0 additions and 0 deletions
@@ -0,0 +1,52 @@
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);
}
@@ -0,0 +1,38 @@
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);
}
}
@@ -0,0 +1,21 @@
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);
}
@@ -0,0 +1,29 @@
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;
}
}
@@ -0,0 +1,140 @@
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
});
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,69 @@
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); }
}
@@ -0,0 +1,185 @@
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(); }
@@ -0,0 +1,31 @@
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)}`);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
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);
}
}
@@ -0,0 +1,55 @@
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); }
}
@@ -0,0 +1,189 @@
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);
}
}
@@ -0,0 +1,58 @@
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);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,151 @@
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);
}
}
@@ -0,0 +1,11 @@
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) {}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,264 @@
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();
}
}
}
}
}
}
@@ -0,0 +1,40 @@
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;
}
}
@@ -0,0 +1,173 @@
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) {}
}
@@ -0,0 +1,36 @@
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)}`);
}
}