refactor(core): ts’ify core
This commit is contained in:
parent
aabc898f3b
commit
e61d82b9be
1
modules/angular2/core.js
vendored
1
modules/angular2/core.js
vendored
@ -5,7 +5,6 @@
|
|||||||
* Define angular core API here.
|
* Define angular core API here.
|
||||||
*/
|
*/
|
||||||
export * from './src/core/annotations/visibility';
|
export * from './src/core/annotations/visibility';
|
||||||
export * from './src/core/compiler/interfaces';
|
|
||||||
export * from './src/core/annotations/view';
|
export * from './src/core/annotations/view';
|
||||||
export * from './src/core/application';
|
export * from './src/core/application';
|
||||||
export * from './src/core/application_tokens';
|
export * from './src/core/application_tokens';
|
||||||
|
1
modules/angular2/globals.d.ts
vendored
1
modules/angular2/globals.d.ts
vendored
@ -27,4 +27,5 @@ interface Window {
|
|||||||
Reflect: any;
|
Reflect: any;
|
||||||
zone: Zone;
|
zone: Zone;
|
||||||
Hammer: HammerStatic;
|
Hammer: HammerStatic;
|
||||||
|
getAngularTestability: Function;
|
||||||
}
|
}
|
||||||
|
@ -77,19 +77,19 @@ export var preGeneratedProtoDetectors = {};
|
|||||||
*/
|
*/
|
||||||
export class PreGeneratedChangeDetection extends ChangeDetection {
|
export class PreGeneratedChangeDetection extends ChangeDetection {
|
||||||
_dynamicChangeDetection: ChangeDetection;
|
_dynamicChangeDetection: ChangeDetection;
|
||||||
_protoChangeDetectors: any;
|
_protoChangeDetectorFactories: StringMap<string, Function>;
|
||||||
|
|
||||||
constructor(private registry: PipeRegistry, protoChangeDetectors?) {
|
constructor(private registry: PipeRegistry, protoChangeDetectors?) {
|
||||||
super();
|
super();
|
||||||
this._dynamicChangeDetection = new DynamicChangeDetection(registry);
|
this._dynamicChangeDetection = new DynamicChangeDetection(registry);
|
||||||
this._protoChangeDetectors =
|
this._protoChangeDetectorFactories =
|
||||||
isPresent(protoChangeDetectors) ? protoChangeDetectors : preGeneratedProtoDetectors;
|
isPresent(protoChangeDetectors) ? protoChangeDetectors : preGeneratedProtoDetectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||||
var id = definition.id;
|
var id = definition.id;
|
||||||
if (StringMapWrapper.contains(this._protoChangeDetectors, id)) {
|
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) {
|
||||||
return StringMapWrapper.get(this._protoChangeDetectors, id)(this.registry);
|
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(this.registry);
|
||||||
}
|
}
|
||||||
return this._dynamicChangeDetection.createProtoChangeDetector(definition);
|
return this._dynamicChangeDetection.createProtoChangeDetector(definition);
|
||||||
}
|
}
|
||||||
|
@ -6,5 +6,7 @@
|
|||||||
export {
|
export {
|
||||||
Component as ComponentAnnotation,
|
Component as ComponentAnnotation,
|
||||||
Directive as DirectiveAnnotation,
|
Directive as DirectiveAnnotation,
|
||||||
onDestroy, onChange, onAllChangesDone
|
onDestroy,
|
||||||
|
onChange,
|
||||||
|
onAllChangesDone
|
||||||
} from '../annotations_impl/annotations';
|
} from '../annotations_impl/annotations';
|
@ -1,7 +1,4 @@
|
|||||||
import {
|
import {ComponentAnnotation, DirectiveAnnotation} from './annotations';
|
||||||
ComponentAnnotation,
|
|
||||||
DirectiveAnnotation
|
|
||||||
} from './annotations';
|
|
||||||
import {ViewAnnotation} from './view';
|
import {ViewAnnotation} from './view';
|
||||||
import {AncestorAnnotation, ParentAnnotation} from './visibility';
|
import {AncestorAnnotation, ParentAnnotation} from './visibility';
|
||||||
import {AttributeAnnotation, QueryAnnotation} from './di';
|
import {AttributeAnnotation, QueryAnnotation} from './di';
|
@ -1,3 +0,0 @@
|
|||||||
export {
|
|
||||||
View as ViewAnnotation,
|
|
||||||
} from '../annotations_impl/view';
|
|
3
modules/angular2/src/core/annotations/view.ts
Normal file
3
modules/angular2/src/core/annotations/view.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export {
|
||||||
|
View as ViewAnnotation,
|
||||||
|
} from '../annotations_impl/view';
|
@ -1,4 +1,4 @@
|
|||||||
export {
|
export {
|
||||||
Ancestor as AncestorAnnotation,
|
Ancestor as AncestorAnnotation,
|
||||||
Parent as ParentAnnotation,
|
Parent as ParentAnnotation,
|
||||||
} from '../annotations_impl/visibility';
|
} from '../annotations_impl/visibility';
|
@ -1,4 +1,4 @@
|
|||||||
import {CONST, normalizeBlank, isPresent} from 'angular2/src/facade/lang';
|
import {CONST, normalizeBlank, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||||
import {DEFAULT} from 'angular2/change_detection';
|
import {DEFAULT} from 'angular2/change_detection';
|
||||||
@ -10,29 +10,38 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
*
|
*
|
||||||
* {@link Directive}s with an embedded view are called {@link Component}s.
|
* {@link Directive}s with an embedded view are called {@link Component}s.
|
||||||
*
|
*
|
||||||
* A directive consists of a single directive annotation and a controller class. When the directive's `selector` matches
|
* A directive consists of a single directive annotation and a controller class. When the
|
||||||
|
* directive's `selector` matches
|
||||||
* elements in the DOM, the following steps occur:
|
* elements in the DOM, the following steps occur:
|
||||||
*
|
*
|
||||||
* 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor arguments.
|
* 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor
|
||||||
* 2. Angular instantiates directives for each matched element using `ElementInjector` in a depth-first order,
|
* arguments.
|
||||||
|
* 2. Angular instantiates directives for each matched element using `ElementInjector` in a
|
||||||
|
* depth-first order,
|
||||||
* as declared in the HTML.
|
* as declared in the HTML.
|
||||||
*
|
*
|
||||||
* ## Understanding How Injection Works
|
* ## Understanding How Injection Works
|
||||||
*
|
*
|
||||||
* There are three stages of injection resolution.
|
* There are three stages of injection resolution.
|
||||||
* - *Pre-existing Injectors*:
|
* - *Pre-existing Injectors*:
|
||||||
* - The terminal {@link Injector} cannot resolve dependencies. It either throws an error or, if the dependency was
|
* - The terminal {@link Injector} cannot resolve dependencies. It either throws an error or, if
|
||||||
|
* the dependency was
|
||||||
* specified as `@Optional`, returns `null`.
|
* specified as `@Optional`, returns `null`.
|
||||||
* - The platform injector resolves browser singleton resources, such as: cookies, title, location, and others.
|
* - The platform injector resolves browser singleton resources, such as: cookies, title,
|
||||||
* - *Component Injectors*: Each component instance has its own {@link Injector}, and they follow the same parent-child hierarchy
|
* location, and others.
|
||||||
|
* - *Component Injectors*: Each component instance has its own {@link Injector}, and they follow
|
||||||
|
* the same parent-child hierarchy
|
||||||
* as the component instances in the DOM.
|
* as the component instances in the DOM.
|
||||||
* - *Element Injectors*: Each component instance has a Shadow DOM. Within the Shadow DOM each element has an `ElementInjector`
|
* - *Element Injectors*: Each component instance has a Shadow DOM. Within the Shadow DOM each
|
||||||
|
* element has an `ElementInjector`
|
||||||
* which follow the same parent-child hierarchy as the DOM elements themselves.
|
* which follow the same parent-child hierarchy as the DOM elements themselves.
|
||||||
*
|
*
|
||||||
* When a template is instantiated, it also must instantiate the corresponding directives in a depth-first order. The
|
* When a template is instantiated, it also must instantiate the corresponding directives in a
|
||||||
|
* depth-first order. The
|
||||||
* current `ElementInjector` resolves the constructor dependencies for each directive.
|
* current `ElementInjector` resolves the constructor dependencies for each directive.
|
||||||
*
|
*
|
||||||
* Angular then resolves dependencies as follows, according to the order in which they appear in the {@link View}:
|
* Angular then resolves dependencies as follows, according to the order in which they appear in the
|
||||||
|
* {@link View}:
|
||||||
*
|
*
|
||||||
* 1. Dependencies on the current element
|
* 1. Dependencies on the current element
|
||||||
* 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary
|
* 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM boundary
|
||||||
@ -40,26 +49,34 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* 4. Dependencies on pre-existing injectors
|
* 4. Dependencies on pre-existing injectors
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* The `ElementInjector` can inject other directives, element-specific special objects, or it can delegate to the parent
|
* The `ElementInjector` can inject other directives, element-specific special objects, or it can
|
||||||
|
* delegate to the parent
|
||||||
* injector.
|
* injector.
|
||||||
*
|
*
|
||||||
* To inject other directives, declare the constructor parameter as:
|
* To inject other directives, declare the constructor parameter as:
|
||||||
* - `directive:DirectiveType`: a directive on the current element only
|
* - `directive:DirectiveType`: a directive on the current element only
|
||||||
* - `@Ancestor() directive:DirectiveType`: any directive that matches the type between the current element and the
|
* - `@Ancestor() directive:DirectiveType`: any directive that matches the type between the current
|
||||||
* Shadow DOM root. Current element is not included in the resolution, therefore even if it could resolve it, it will
|
* element and the
|
||||||
|
* Shadow DOM root. Current element is not included in the resolution, therefore even if it could
|
||||||
|
* resolve it, it will
|
||||||
* be ignored.
|
* be ignored.
|
||||||
* - `@Parent() directive:DirectiveType`: any directive that matches the type on a direct parent element only.
|
* - `@Parent() directive:DirectiveType`: any directive that matches the type on a direct parent
|
||||||
* - `@Query(DirectiveType) query:QueryList<DirectiveType>`: A live collection of direct child directives.
|
* element only.
|
||||||
* - `@QueryDescendants(DirectiveType) query:QueryList<DirectiveType>`: A live collection of any child directives.
|
* - `@Query(DirectiveType) query:QueryList<DirectiveType>`: A live collection of direct child
|
||||||
|
* directives.
|
||||||
|
* - `@QueryDescendants(DirectiveType) query:QueryList<DirectiveType>`: A live collection of any
|
||||||
|
* child directives.
|
||||||
*
|
*
|
||||||
* To inject element-specific special objects, declare the constructor parameter as:
|
* To inject element-specific special objects, declare the constructor parameter as:
|
||||||
* - `element: ElementRef` to obtain a reference to logical element in the view.
|
* - `element: ElementRef` to obtain a reference to logical element in the view.
|
||||||
* - `viewContainer: ViewContainerRef` to control child template instantiation, for {@link Directive} directives only
|
* - `viewContainer: ViewContainerRef` to control child template instantiation, for {@link
|
||||||
|
* Directive} directives only
|
||||||
* - `bindingPropagation: BindingPropagation` to control change detection in a more granular way.
|
* - `bindingPropagation: BindingPropagation` to control change detection in a more granular way.
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
*
|
*
|
||||||
* The following example demonstrates how dependency injection resolves constructor arguments in practice.
|
* The following example demonstrates how dependency injection resolves constructor arguments in
|
||||||
|
* practice.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Assume this HTML template:
|
* Assume this HTML template:
|
||||||
@ -100,7 +117,8 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
*
|
*
|
||||||
* ### No injection
|
* ### No injection
|
||||||
*
|
*
|
||||||
* Here the constructor is declared with no arguments, therefore nothing is injected into `MyDirective`.
|
* Here the constructor is declared with no arguments, therefore nothing is injected into
|
||||||
|
* `MyDirective`.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* @Directive({ selector: '[my-directive]' })
|
* @Directive({ selector: '[my-directive]' })
|
||||||
@ -115,9 +133,11 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
*
|
*
|
||||||
* ### Component-level injection
|
* ### Component-level injection
|
||||||
*
|
*
|
||||||
* Directives can inject any injectable instance from the closest component injector or any of its parents.
|
* Directives can inject any injectable instance from the closest component injector or any of its
|
||||||
|
* parents.
|
||||||
*
|
*
|
||||||
* Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type from the parent
|
* Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type
|
||||||
|
* from the parent
|
||||||
* component's injector.
|
* component's injector.
|
||||||
* ```
|
* ```
|
||||||
* @Directive({ selector: '[my-directive]' })
|
* @Directive({ selector: '[my-directive]' })
|
||||||
@ -142,13 +162,16 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* This directive would be instantiated with `Dependency` declared at the same element, in this case `dependency="3"`.
|
* This directive would be instantiated with `Dependency` declared at the same element, in this case
|
||||||
|
* `dependency="3"`.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* ### Injecting a directive from a direct parent element
|
* ### Injecting a directive from a direct parent element
|
||||||
*
|
*
|
||||||
* Directives can inject other directives declared on a direct parent element. By definition, a directive with a
|
* Directives can inject other directives declared on a direct parent element. By definition, a
|
||||||
* `@Parent` annotation does not attempt to resolve dependencies for the current element, even if this would satisfy
|
* directive with a
|
||||||
|
* `@Parent` annotation does not attempt to resolve dependencies for the current element, even if
|
||||||
|
* this would satisfy
|
||||||
* the dependency.
|
* the dependency.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
@ -159,13 +182,16 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* This directive would be instantiated with `Dependency` declared at the parent element, in this case `dependency="2"`.
|
* This directive would be instantiated with `Dependency` declared at the parent element, in this
|
||||||
|
* case `dependency="2"`.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* ### Injecting a directive from any ancestor elements
|
* ### Injecting a directive from any ancestor elements
|
||||||
*
|
*
|
||||||
* Directives can inject other directives declared on any ancestor element (in the current Shadow DOM), i.e. on the
|
* Directives can inject other directives declared on any ancestor element (in the current Shadow
|
||||||
* parent element and its parents. By definition, a directive with an `@Ancestor` annotation does not attempt to
|
* DOM), i.e. on the
|
||||||
|
* parent element and its parents. By definition, a directive with an `@Ancestor` annotation does
|
||||||
|
* not attempt to
|
||||||
* resolve dependencies for the current element, even if this would satisfy the dependency.
|
* resolve dependencies for the current element, even if this would satisfy the dependency.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
@ -178,16 +204,19 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Unlike the `@Parent` which only checks the parent, `@Ancestor` checks the parent, as well as its
|
* Unlike the `@Parent` which only checks the parent, `@Ancestor` checks the parent, as well as its
|
||||||
* parents recursively. If `dependency="2"` didn't exist on the direct parent, this injection would have returned
|
* parents recursively. If `dependency="2"` didn't exist on the direct parent, this injection would
|
||||||
|
* have returned
|
||||||
* `dependency="1"`.
|
* `dependency="1"`.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* ### Injecting a live collection of direct child directives
|
* ### Injecting a live collection of direct child directives
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* A directive can also query for other child directives. Since parent directives are instantiated before child
|
* A directive can also query for other child directives. Since parent directives are instantiated
|
||||||
|
* before child
|
||||||
* directives, a directive can't simply inject the list of child directives. Instead, the directive
|
* directives, a directive can't simply inject the list of child directives. Instead, the directive
|
||||||
* injects a {@link QueryList}, which updates its contents as children are added, removed, or moved by a directive
|
* injects a {@link QueryList}, which updates its contents as children are added, removed, or moved
|
||||||
|
* by a directive
|
||||||
* that uses a {@link ViewContainerRef} such as a `for`, an `if`, or a `switch`.
|
* that uses a {@link ViewContainerRef} such as a `for`, an `if`, or a `switch`.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
@ -198,7 +227,8 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and 6. Here, `Dependency`
|
* This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and
|
||||||
|
* 6. Here, `Dependency`
|
||||||
* 5 would not be included, because it is not a direct child.
|
* 5 would not be included, because it is not a direct child.
|
||||||
*
|
*
|
||||||
* ### Injecting a live collection of descendant directives
|
* ### Injecting a live collection of descendant directives
|
||||||
@ -219,9 +249,12 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
*
|
*
|
||||||
* ### Optional injection
|
* ### Optional injection
|
||||||
*
|
*
|
||||||
* The normal behavior of directives is to return an error when a specified dependency cannot be resolved. If you
|
* The normal behavior of directives is to return an error when a specified dependency cannot be
|
||||||
* would like to inject `null` on unresolved dependency instead, you can annotate that dependency with `@Optional()`.
|
* resolved. If you
|
||||||
* This explicitly permits the author of a template to treat some of the surrounding directives as optional.
|
* would like to inject `null` on unresolved dependency instead, you can annotate that dependency
|
||||||
|
* with `@Optional()`.
|
||||||
|
* This explicitly permits the author of a template to treat some of the surrounding directives as
|
||||||
|
* optional.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* @Directive({ selector: '[my-directive]' })
|
* @Directive({ selector: '[my-directive]' })
|
||||||
@ -231,7 +264,8 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* This directive would be instantiated with a `Dependency` directive found on the current element. If none can be
|
* This directive would be instantiated with a `Dependency` directive found on the current element.
|
||||||
|
* If none can be
|
||||||
* found, the injector supplies `null` instead of throwing an error.
|
* found, the injector supplies `null` instead of throwing an error.
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
@ -269,24 +303,31 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* In our HTML template, we can then add this behavior to a `<div>` or any other element with the `tooltip` selector,
|
* In our HTML template, we can then add this behavior to a `<div>` or any other element with the
|
||||||
|
* `tooltip` selector,
|
||||||
* like so:
|
* like so:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* <div tooltip="some text here"></div>
|
* <div tooltip="some text here"></div>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Directives can also control the instantiation, destruction, and positioning of inline template elements:
|
* Directives can also control the instantiation, destruction, and positioning of inline template
|
||||||
|
* elements:
|
||||||
*
|
*
|
||||||
* A directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at runtime.
|
* A directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at
|
||||||
* The {@link ViewContainerRef} is created as a result of `<template>` element, and represents a location in the current view
|
* runtime.
|
||||||
|
* The {@link ViewContainerRef} is created as a result of `<template>` element, and represents a
|
||||||
|
* location in the current view
|
||||||
* where these actions are performed.
|
* where these actions are performed.
|
||||||
*
|
*
|
||||||
* Views are always created as children of the current {@link View}, and as siblings of the `<template>` element. Thus a
|
* Views are always created as children of the current {@link View}, and as siblings of the
|
||||||
|
* `<template>` element. Thus a
|
||||||
* directive in a child view cannot inject the directive that created it.
|
* directive in a child view cannot inject the directive that created it.
|
||||||
*
|
*
|
||||||
* Since directives that create views via ViewContainers are common in Angular, and using the full `<template>` element syntax is wordy, Angular
|
* Since directives that create views via ViewContainers are common in Angular, and using the full
|
||||||
* also supports a shorthand notation: `<li *foo="bar">` and `<li template="foo: bar">` are equivalent.
|
* `<template>` element syntax is wordy, Angular
|
||||||
|
* also supports a shorthand notation: `<li *foo="bar">` and `<li template="foo: bar">` are
|
||||||
|
* equivalent.
|
||||||
*
|
*
|
||||||
* Thus,
|
* Thus,
|
||||||
*
|
*
|
||||||
@ -306,7 +347,8 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* </ul>
|
* </ul>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Notice that although the shorthand places `*foo="bar"` within the `<li>` element, the binding for the directive
|
* Notice that although the shorthand places `*foo="bar"` within the `<li>` element, the binding for
|
||||||
|
* the directive
|
||||||
* controller is correctly instantiated on the `<template>` element rather than the `<li>` element.
|
* controller is correctly instantiated on the `<template>` element rather than the `<li>` element.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@ -353,7 +395,8 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* </ul>
|
* </ul>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Once the directive instantiates the child view, the shorthand notation for the template expands and the result is:
|
* Once the directive instantiates the child view, the shorthand notation for the template expands
|
||||||
|
* and the result is:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* <ul>
|
* <ul>
|
||||||
@ -364,16 +407,19 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||||||
* </ul>
|
* </ul>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Note also that although the `<li></li>` template still exists inside the `<template></template>`, the instantiated
|
* Note also that although the `<li></li>` template still exists inside the `<template></template>`,
|
||||||
|
* the instantiated
|
||||||
* view occurs on the second `<li></li>` which is a sibling to the `<template>` element.
|
* view occurs on the second `<li></li>` which is a sibling to the `<template>` element.
|
||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Directive extends Injectable {
|
export class Directive extends Injectable {
|
||||||
/**
|
/**
|
||||||
* The CSS selector that triggers the instantiation of a directive.
|
* The CSS selector that triggers the instantiation of a directive.
|
||||||
*
|
*
|
||||||
* Angular only allows directives to trigger on CSS selectors that do not cross element boundaries.
|
* Angular only allows directives to trigger on CSS selectors that do not cross element
|
||||||
|
* boundaries.
|
||||||
*
|
*
|
||||||
* `selector` may be declared as one of the following:
|
* `selector` may be declared as one of the following:
|
||||||
*
|
*
|
||||||
@ -401,7 +447,7 @@ export class Directive extends Injectable {
|
|||||||
* The directive would only be instantiated on the `<input type="text">` element.
|
* The directive would only be instantiated on the `<input type="text">` element.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
selector:string;
|
selector: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerates the set of properties that accept data binding for a directive.
|
* Enumerates the set of properties that accept data binding for a directive.
|
||||||
@ -412,7 +458,8 @@ export class Directive extends Injectable {
|
|||||||
* - `directiveProperty` specifies the component property where the value is written.
|
* - `directiveProperty` specifies the component property where the value is written.
|
||||||
* - `bindingProperty` specifies the DOM property where the value is read from.
|
* - `bindingProperty` specifies the DOM property where the value is read from.
|
||||||
*
|
*
|
||||||
* You can include a {@link Pipe} when specifying a `bindingProperty` to allow for data transformation and structural
|
* You can include a {@link Pipe} when specifying a `bindingProperty` to allow for data
|
||||||
|
* transformation and structural
|
||||||
* change detection of the value. These pipes will be evaluated in the context of this component.
|
* change detection of the value. These pipes will be evaluated in the context of this component.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@ -431,7 +478,8 @@ export class Directive extends Injectable {
|
|||||||
*
|
*
|
||||||
* ## Basic Property Binding
|
* ## Basic Property Binding
|
||||||
*
|
*
|
||||||
* We can easily build a simple `Tooltip` directive that exposes a `tooltip` property, which can be used in templates
|
* We can easily build a simple `Tooltip` directive that exposes a `tooltip` property, which can
|
||||||
|
* be used in templates
|
||||||
* with standard Angular syntax. For example:
|
* with standard Angular syntax. For example:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
@ -448,7 +496,8 @@ export class Directive extends Injectable {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* We can then bind to the `tooltip' property as either an expression (`someExpression`) or as a string literal, as
|
* We can then bind to the `tooltip' property as either an expression (`someExpression`) or as a
|
||||||
|
* string literal, as
|
||||||
* shown in the HTML template below:
|
* shown in the HTML template below:
|
||||||
*
|
*
|
||||||
* ```html
|
* ```html
|
||||||
@ -465,7 +514,8 @@ export class Directive extends Injectable {
|
|||||||
*
|
*
|
||||||
* You can also use pipes when writing binding definitions for a directive.
|
* You can also use pipes when writing binding definitions for a directive.
|
||||||
*
|
*
|
||||||
* For example, we could write a binding that updates the directive on structural changes, rather than on reference
|
* For example, we could write a binding that updates the directive on structural changes, rather
|
||||||
|
* than on reference
|
||||||
* changes, as normally occurs in change detection.
|
* changes, as normally occurs in change detection.
|
||||||
*
|
*
|
||||||
* See {@link Pipe} and {@link keyValDiff} documentation for more details.
|
* See {@link Pipe} and {@link keyValDiff} documentation for more details.
|
||||||
@ -490,10 +540,11 @@ export class Directive extends Injectable {
|
|||||||
* <div [class-set]="someExpression | somePipe">
|
* <div [class-set]="someExpression | somePipe">
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* In this case, the two pipes compose as if they were inlined: `someExpression | somePipe | keyValDiff`.
|
* In this case, the two pipes compose as if they were inlined: `someExpression | somePipe |
|
||||||
|
* keyValDiff`.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
properties:any; // StringMap
|
properties: StringMap<string, string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumerates the set of emitted events.
|
* Enumerates the set of emitted events.
|
||||||
@ -517,7 +568,7 @@ export class Directive extends Injectable {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
events:List<string>;
|
events: List<string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies which DOM hostListeners a directive listens to.
|
* Specifies which DOM hostListeners a directive listens to.
|
||||||
@ -526,14 +577,16 @@ export class Directive extends Injectable {
|
|||||||
*
|
*
|
||||||
* - `event1`: the DOM event that the directive listens to.
|
* - `event1`: the DOM event that the directive listens to.
|
||||||
* - `statement`: the statement to execute when the event occurs.
|
* - `statement`: the statement to execute when the event occurs.
|
||||||
* If the evalutation of the statement returns `false`, then `preventDefault`is applied on the DOM event.
|
* If the evalutation of the statement returns `false`, then `preventDefault`is applied on the DOM
|
||||||
|
* event.
|
||||||
*
|
*
|
||||||
* To listen to global events, a target must be added to the event name.
|
* To listen to global events, a target must be added to the event name.
|
||||||
* The target can be `window`, `document` or `body`.
|
* The target can be `window`, `document` or `body`.
|
||||||
*
|
*
|
||||||
* When writing a directive event binding, you can also refer to the following local variables:
|
* When writing a directive event binding, you can also refer to the following local variables:
|
||||||
* - `$event`: Current event object which triggered the event.
|
* - `$event`: Current event object which triggered the event.
|
||||||
* - `$target`: The source of the event. This will be either a DOM element or an Angular directive.
|
* - `$target`: The source of the event. This will be either a DOM element or an Angular
|
||||||
|
* directive.
|
||||||
* (will be implemented in later release)
|
* (will be implemented in later release)
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@ -551,7 +604,8 @@ export class Directive extends Injectable {
|
|||||||
*
|
*
|
||||||
* ## Basic Event Binding:
|
* ## Basic Event Binding:
|
||||||
*
|
*
|
||||||
* Suppose you want to write a directive that triggers on `change` events in the DOM and on `resize` events in window.
|
* Suppose you want to write a directive that triggers on `change` events in the DOM and on
|
||||||
|
* `resize` events in window.
|
||||||
* You would define the event binding as follows:
|
* You would define the event binding as follows:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
@ -570,10 +624,11 @@ export class Directive extends Injectable {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Here the `onChange` method of `InputDirective` is invoked whenever the DOM element fires the 'change' event.
|
* Here the `onChange` method of `InputDirective` is invoked whenever the DOM element fires the
|
||||||
|
* 'change' event.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
hostListeners:any; // StringMap
|
hostListeners: StringMap<string, string>;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -592,14 +647,16 @@ export class Directive extends Injectable {
|
|||||||
* value:string;
|
* value:string;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* In this example every time the value property of the decorator changes, Angular will update the value property of
|
* In this example every time the value property of the decorator changes, Angular will update the
|
||||||
|
* value property of
|
||||||
* the host element.
|
* the host element.
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
hostProperties:any; // String map
|
hostProperties: StringMap<string, string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies static attributes that should be propagated to a host element. Attributes specified in `hostAttributes`
|
* Specifies static attributes that should be propagated to a host element. Attributes specified
|
||||||
|
* in `hostAttributes`
|
||||||
* are propagated only if a given attribute is not present on a host element.
|
* are propagated only if a given attribute is not present on a host element.
|
||||||
*
|
*
|
||||||
* ## Syntax
|
* ## Syntax
|
||||||
@ -614,11 +671,12 @@ export class Directive extends Injectable {
|
|||||||
* class MyButton {
|
* class MyButton {
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* In this example using `my-button` directive (ex.: `<div my-button></div>`) on a host element (here: `<div>` )
|
* In this example using `my-button` directive (ex.: `<div my-button></div>`) on a host element
|
||||||
|
* (here: `<div>` )
|
||||||
* will ensure that this element will get the "button" role.
|
* will ensure that this element will get the "button" role.
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
hostAttributes:any; // String map
|
hostAttributes: StringMap<string, string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies which DOM methods a directive can invoke.
|
* Specifies which DOM methods a directive can invoke.
|
||||||
@ -642,26 +700,28 @@ export class Directive extends Injectable {
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* In this example calling focus on InputDirective will result in calling focus on the DOM element.
|
* In this example calling focus on InputDirective will result in calling focus on the DOM
|
||||||
|
* element.
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
hostActions:any; // String map
|
hostActions: StringMap<string, string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a set of lifecycle hostListeners in which the directive participates.
|
* Specifies a set of lifecycle hostListeners in which the directive participates.
|
||||||
*
|
*
|
||||||
* See {@link onChange}, {@link onDestroy}, {@link onAllChangesDone} for details.
|
* See {@link onChange}, {@link onDestroy}, {@link onAllChangesDone} for details.
|
||||||
*/
|
*/
|
||||||
lifecycle:List; //List<LifecycleEvent>
|
lifecycle: List<LifecycleEvent>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to true the compiler does not compile the children of this directive.
|
* If set to true the compiler does not compile the children of this directive.
|
||||||
*/
|
*/
|
||||||
//TODO(vsavkin): This would better fall under the Macro directive concept.
|
// TODO(vsavkin): This would better fall under the Macro directive concept.
|
||||||
compileChildren: boolean;
|
compileChildren: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the set of injectable objects that are visible to a Directive and its light dom children.
|
* Defines the set of injectable objects that are visible to a Directive and its light dom
|
||||||
|
* children.
|
||||||
*
|
*
|
||||||
* ## Simple Example
|
* ## Simple Example
|
||||||
*
|
*
|
||||||
@ -689,9 +749,8 @@ export class Directive extends Injectable {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
hostInjector:List;
|
hostInjector: List<any>;
|
||||||
|
|
||||||
@CONST()
|
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
properties,
|
properties,
|
||||||
@ -704,16 +763,16 @@ export class Directive extends Injectable {
|
|||||||
hostInjector,
|
hostInjector,
|
||||||
compileChildren = true,
|
compileChildren = true,
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector?:string,
|
||||||
properties:any,
|
properties?:any,
|
||||||
events:List,
|
events?:List<string>,
|
||||||
hostListeners: any,
|
hostListeners?: StringMap<string, string>,
|
||||||
hostProperties: any,
|
hostProperties?: StringMap<string, string>,
|
||||||
hostAttributes: any,
|
hostAttributes?: StringMap<string, string>,
|
||||||
hostActions: any,
|
hostActions?: StringMap<string, string>,
|
||||||
lifecycle:List,
|
lifecycle?:List<LifecycleEvent>,
|
||||||
hostInjector:List,
|
hostInjector?:List<any>,
|
||||||
compileChildren:boolean
|
compileChildren?:boolean
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
@ -734,7 +793,7 @@ export class Directive extends Injectable {
|
|||||||
*
|
*
|
||||||
* See {@link onChange}, {@link onDestroy}, {@link onAllChangesDone} for details.
|
* See {@link onChange}, {@link onDestroy}, {@link onAllChangesDone} for details.
|
||||||
*/
|
*/
|
||||||
hasLifecycleHook(hook:string):boolean {
|
hasLifecycleHook(hook: LifecycleEvent): boolean {
|
||||||
return isPresent(this.lifecycle) ? ListWrapper.contains(this.lifecycle, hook) : false;
|
return isPresent(this.lifecycle) ? ListWrapper.contains(this.lifecycle, hook) : false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -742,13 +801,16 @@ export class Directive extends Injectable {
|
|||||||
/**
|
/**
|
||||||
* Declare reusable UI building blocks for an application.
|
* Declare reusable UI building blocks for an application.
|
||||||
*
|
*
|
||||||
* Each Angular component requires a single `@Component` and at least one `@View` annotation. The `@Component`
|
* Each Angular component requires a single `@Component` and at least one `@View` annotation. The
|
||||||
* annotation specifies when a component is instantiated, and which properties and hostListeners it binds to.
|
* `@Component`
|
||||||
|
* annotation specifies when a component is instantiated, and which properties and hostListeners it
|
||||||
|
* binds to.
|
||||||
*
|
*
|
||||||
* When a component is instantiated, Angular
|
* When a component is instantiated, Angular
|
||||||
* - creates a shadow DOM for the component.
|
* - creates a shadow DOM for the component.
|
||||||
* - loads the selected template into the shadow DOM.
|
* - loads the selected template into the shadow DOM.
|
||||||
* - creates a child {@link Injector} which is configured with the `appInjector` for the {@link Component}.
|
* - creates a child {@link Injector} which is configured with the `appInjector` for the {@link
|
||||||
|
* Component}.
|
||||||
*
|
*
|
||||||
* All template expressions and statements are then evaluated against the component instance.
|
* All template expressions and statements are then evaluated against the component instance.
|
||||||
*
|
*
|
||||||
@ -775,18 +837,22 @@ export class Directive extends Injectable {
|
|||||||
*
|
*
|
||||||
* Dynamically loading a component at runtime:
|
* Dynamically loading a component at runtime:
|
||||||
*
|
*
|
||||||
* Regular Angular components are statically resolved. Dynamic components allows to resolve a component at runtime
|
* Regular Angular components are statically resolved. Dynamic components allows to resolve a
|
||||||
* instead by providing a placeholder into which a regular Angular component can be dynamically loaded. Once loaded,
|
* component at runtime
|
||||||
|
* instead by providing a placeholder into which a regular Angular component can be dynamically
|
||||||
|
* loaded. Once loaded,
|
||||||
* the dynamically-loaded component becomes permanent and cannot be changed.
|
* the dynamically-loaded component becomes permanent and cannot be changed.
|
||||||
* Dynamic components are declared just like components, but without a `@View` annotation.
|
* Dynamic components are declared just like components, but without a `@View` annotation.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
*
|
*
|
||||||
* Here we have `DynamicComp` which acts as the placeholder for `HelloCmp`. At runtime, the dynamic component
|
* Here we have `DynamicComp` which acts as the placeholder for `HelloCmp`. At runtime, the dynamic
|
||||||
|
* component
|
||||||
* `DynamicComp` requests loading of the `HelloCmp` component.
|
* `DynamicComp` requests loading of the `HelloCmp` component.
|
||||||
*
|
*
|
||||||
* There is nothing special about `HelloCmp`, which is a regular Angular component. It can also be used in other static
|
* There is nothing special about `HelloCmp`, which is a regular Angular component. It can also be
|
||||||
|
* used in other static
|
||||||
* locations.
|
* locations.
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
@ -819,31 +885,39 @@ export class Directive extends Injectable {
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Component extends Directive {
|
export class Component extends Directive {
|
||||||
/**
|
/**
|
||||||
* Defines the used change detection strategy.
|
* Defines the used change detection strategy.
|
||||||
*
|
*
|
||||||
* When a component is instantiated, Angular creates a change detector, which is responsible for propagating
|
* When a component is instantiated, Angular creates a change detector, which is responsible for
|
||||||
|
* propagating
|
||||||
* the component's bindings.
|
* the component's bindings.
|
||||||
*
|
*
|
||||||
* The `changeDetection` property defines, whether the change detection will be checked every time or only when the component
|
* The `changeDetection` property defines, whether the change detection will be checked every time
|
||||||
|
* or only when the component
|
||||||
* tells it to do so.
|
* tells it to do so.
|
||||||
*/
|
*/
|
||||||
changeDetection:string;
|
changeDetection: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the set of injectable objects that are visible to a Component and its children.
|
* Defines the set of injectable objects that are visible to a Component and its children.
|
||||||
*
|
*
|
||||||
* The `appInjector` defined in the Component annotation allow you to configure a set of bindings for the component's
|
* The `appInjector` defined in the Component annotation allow you to configure a set of bindings
|
||||||
|
* for the component's
|
||||||
* injector.
|
* injector.
|
||||||
*
|
*
|
||||||
* When a component is instantiated, Angular creates a new child Injector, which is configured with the bindings in
|
* When a component is instantiated, Angular creates a new child Injector, which is configured
|
||||||
* the Component `appInjector` annotation. The injectable objects then become available for injection to the component
|
* with the bindings in
|
||||||
* itself and any of the directives in the component's template, i.e. they are not available to the directives which
|
* the Component `appInjector` annotation. The injectable objects then become available for
|
||||||
|
* injection to the component
|
||||||
|
* itself and any of the directives in the component's template, i.e. they are not available to
|
||||||
|
* the directives which
|
||||||
* are children in the component's light DOM.
|
* are children in the component's light DOM.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* The syntax for configuring the `appInjector` injectable is identical to {@link Injector} injectable configuration.
|
* The syntax for configuring the `appInjector` injectable is identical to {@link Injector}
|
||||||
|
* injectable configuration.
|
||||||
* See {@link Injector} for additional detail.
|
* See {@link Injector} for additional detail.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@ -877,7 +951,7 @@ export class Component extends Directive {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
appInjector:List;
|
appInjector: List<any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the set of injectable objects that are visible to its view dom children.
|
* Defines the set of injectable objects that are visible to its view dom children.
|
||||||
@ -919,9 +993,8 @@ export class Component extends Directive {
|
|||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
viewInjector:List;
|
viewInjector: List<any>;
|
||||||
|
|
||||||
@CONST()
|
|
||||||
constructor({
|
constructor({
|
||||||
selector,
|
selector,
|
||||||
properties,
|
properties,
|
||||||
@ -937,19 +1010,19 @@ export class Component extends Directive {
|
|||||||
changeDetection = DEFAULT,
|
changeDetection = DEFAULT,
|
||||||
compileChildren = true
|
compileChildren = true
|
||||||
}:{
|
}:{
|
||||||
selector:string,
|
selector?:string,
|
||||||
properties:Object,
|
properties?:Object,
|
||||||
events:List,
|
events?:List<string>,
|
||||||
hostListeners:any,
|
hostListeners?:Map<string,string>,
|
||||||
hostProperties:any,
|
hostProperties?:any,
|
||||||
hostAttributes:any,
|
hostAttributes?:any,
|
||||||
hostActions:any,
|
hostActions?:any,
|
||||||
appInjector:List,
|
appInjector?:List<any>,
|
||||||
lifecycle:List,
|
lifecycle?:List<LifecycleEvent>,
|
||||||
hostInjector:List,
|
hostInjector?:List<any>,
|
||||||
viewInjector:List,
|
viewInjector?:List<any>,
|
||||||
changeDetection:string,
|
changeDetection?:string,
|
||||||
compileChildren:boolean
|
compileChildren?:boolean
|
||||||
}={})
|
}={})
|
||||||
{
|
{
|
||||||
super({
|
super({
|
||||||
@ -972,7 +1045,10 @@ export class Component extends Directive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//TODO(misko): turn into LifecycleEvent class once we switch to TypeScript;
|
@CONST()
|
||||||
|
export class LifecycleEvent {
|
||||||
|
constructor(public name: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify a directive whenever a {@link View} that contains it is destroyed.
|
* Notify a directive whenever a {@link View} that contains it is destroyed.
|
||||||
@ -992,7 +1068,7 @@ export class Component extends Directive {
|
|||||||
* ```
|
* ```
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
export const onDestroy = "onDestroy";
|
export const onDestroy = CONST_EXPR(new LifecycleEvent("onDestroy"));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1030,7 +1106,7 @@ export const onDestroy = "onDestroy";
|
|||||||
* ```
|
* ```
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
export const onChange = "onChange";
|
export const onChange = CONST_EXPR(new LifecycleEvent("onChange"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify a directive when the bindings of all its children have been changed.
|
* Notify a directive when the bindings of all its children have been changed.
|
||||||
@ -1051,4 +1127,4 @@ export const onChange = "onChange";
|
|||||||
* ```
|
* ```
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
export const onAllChangesDone = "onAllChangesDone";
|
export const onAllChangesDone = CONST_EXPR(new LifecycleEvent("onAllChangesDone"));
|
@ -29,19 +29,16 @@ import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Attribute extends DependencyAnnotation {
|
export class Attribute extends DependencyAnnotation {
|
||||||
attributeName: string;
|
constructor(public attributeName: string) { super(); }
|
||||||
@CONST()
|
|
||||||
constructor(attributeName) {
|
|
||||||
super();
|
|
||||||
this.attributeName = attributeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get token() {
|
get token() {
|
||||||
//Normally one would default a token to a type of an injected value but here
|
// Normally one would default a token to a type of an injected value but here
|
||||||
//the type of a variable is "string" and we can't use primitive type as a return value
|
// the type of a variable is "string" and we can't use primitive type as a return value
|
||||||
//so we use instance of Attribute instead. This doesn't matter much in practice as arguments
|
// so we use instance of Attribute instead. This doesn't matter much in practice as arguments
|
||||||
//with @Attribute annotation are injected by ElementInjector that doesn't take tokens into account.
|
// with @Attribute annotation are injected by ElementInjector that doesn't take tokens into
|
||||||
|
// account.
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,11 +50,7 @@ export class Attribute extends DependencyAnnotation {
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Query extends DependencyAnnotation {
|
export class Query extends DependencyAnnotation {
|
||||||
directive;
|
constructor(public directive: any) { super(); }
|
||||||
@CONST()
|
|
||||||
constructor(directive) {
|
|
||||||
super();
|
|
||||||
this.directive = directive;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,10 +3,13 @@ import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
|||||||
/**
|
/**
|
||||||
* Declares the available HTML templates for an application.
|
* Declares the available HTML templates for an application.
|
||||||
*
|
*
|
||||||
* Each angular component requires a single `@Component` and at least one `@View` annotation. The @View
|
* Each angular component requires a single `@Component` and at least one `@View` annotation. The
|
||||||
* annotation specifies the HTML template to use, and lists the directives that are active within the template.
|
* @View
|
||||||
|
* annotation specifies the HTML template to use, and lists the directives that are active within
|
||||||
|
* the template.
|
||||||
*
|
*
|
||||||
* When a component is instantiated, the template is loaded into the component's shadow root, and the
|
* When a component is instantiated, the template is loaded into the component's shadow root, and
|
||||||
|
* the
|
||||||
* expressions and statements in the template are evaluated against the component.
|
* expressions and statements in the template are evaluated against the component.
|
||||||
*
|
*
|
||||||
* For details on the `@Component` annotation, see {@link Component}.
|
* For details on the `@Component` annotation, see {@link Component}.
|
||||||
@ -32,20 +35,21 @@ import {ABSTRACT, CONST, Type} from 'angular2/src/facade/lang';
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class View {
|
export class View {
|
||||||
/**
|
/**
|
||||||
* Specifies a template URL for an angular component.
|
* Specifies a template URL for an angular component.
|
||||||
*
|
*
|
||||||
* NOTE: either `templateUrl` or `template` should be used, but not both.
|
* NOTE: either `templateUrl` or `template` should be used, but not both.
|
||||||
*/
|
*/
|
||||||
templateUrl:string;
|
templateUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies an inline template for an angular component.
|
* Specifies an inline template for an angular component.
|
||||||
*
|
*
|
||||||
* NOTE: either `templateUrl` or `template` should be used, but not both.
|
* NOTE: either `templateUrl` or `template` should be used, but not both.
|
||||||
*/
|
*/
|
||||||
template:string;
|
template: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies a list of directives that can be used within a template.
|
* Specifies a list of directives that can be used within a template.
|
||||||
@ -69,26 +73,25 @@ export class View {
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
directives:List<Type>;
|
directives: List<Type>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a custom renderer for this View.
|
* Specify a custom renderer for this View.
|
||||||
* If this is set, neither `template`, `templateURL` nor `directives` are used.
|
* If this is set, neither `template`, `templateURL` nor `directives` are used.
|
||||||
*/
|
*/
|
||||||
renderer:any; // string;
|
renderer: string;
|
||||||
|
|
||||||
@CONST()
|
|
||||||
constructor({
|
constructor({
|
||||||
templateUrl,
|
templateUrl,
|
||||||
template,
|
template,
|
||||||
directives,
|
directives,
|
||||||
renderer
|
renderer
|
||||||
}: {
|
}: {
|
||||||
templateUrl: string,
|
templateUrl?: string,
|
||||||
template: string,
|
template?: string,
|
||||||
directives: List<Type>,
|
directives?: List<Type>,
|
||||||
renderer: string
|
renderer?: string
|
||||||
})
|
} = {})
|
||||||
{
|
{
|
||||||
this.templateUrl = templateUrl;
|
this.templateUrl = templateUrl;
|
||||||
this.template = template;
|
this.template = template;
|
@ -1,20 +1,11 @@
|
|||||||
import {CONST, CONST_EXPR} from 'angular2/src/facade/lang';
|
import {CONST, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
|
import {DependencyAnnotation} from 'angular2/src/di/annotations_impl';
|
||||||
|
|
||||||
|
@CONST()
|
||||||
export class Visibility extends DependencyAnnotation {
|
export class Visibility extends DependencyAnnotation {
|
||||||
depth: number;
|
constructor(public depth: number, public crossComponentBoundaries: boolean) { super(); }
|
||||||
crossComponentBoundaries: boolean;
|
|
||||||
|
|
||||||
@CONST()
|
shouldIncludeSelf(): boolean { return this.depth === 0; }
|
||||||
constructor(depth:number, crossComponentBoundaries:boolean) {
|
|
||||||
super();
|
|
||||||
this.depth = depth;
|
|
||||||
this.crossComponentBoundaries = crossComponentBoundaries;
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldIncludeSelf():boolean {
|
|
||||||
return this.depth === 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,11 +45,9 @@ export class Visibility extends DependencyAnnotation {
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Self extends Visibility {
|
export class Self extends Visibility {
|
||||||
@CONST()
|
constructor() { super(0, false); }
|
||||||
constructor() {
|
|
||||||
super(0, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make constants after switching to ts2dart
|
// make constants after switching to ts2dart
|
||||||
@ -100,20 +89,21 @@ export var self = new Self();
|
|||||||
* <div dependency="2" my-directive></div>
|
* <div dependency="2" my-directive></div>
|
||||||
* </div>
|
* </div>
|
||||||
* ```
|
* ```
|
||||||
* The `@Parent()` annotation in our constructor forces the injector to retrieve the dependency from the
|
* The `@Parent()` annotation in our constructor forces the injector to retrieve the dependency from
|
||||||
* parent element (even thought the current element could resolve it): Angular injects `dependency=1`.
|
* the
|
||||||
|
* parent element (even thought the current element could resolve it): Angular injects
|
||||||
|
* `dependency=1`.
|
||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Parent extends Visibility {
|
export class Parent extends Visibility {
|
||||||
@CONST()
|
constructor() { super(1, false); }
|
||||||
constructor() {
|
|
||||||
super(1, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies that an injector should retrieve a dependency from any ancestor element within the same shadow boundary.
|
* Specifies that an injector should retrieve a dependency from any ancestor element within the same
|
||||||
|
* shadow boundary.
|
||||||
*
|
*
|
||||||
* An ancestor is any element between the parent element and shadow root.
|
* An ancestor is any element between the parent element and shadow root.
|
||||||
*
|
*
|
||||||
@ -156,7 +146,8 @@ export class Parent extends Visibility {
|
|||||||
* </div>
|
* </div>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* The `@Ancestor()` annotation in our constructor forces the injector to retrieve the dependency from the
|
* The `@Ancestor()` annotation in our constructor forces the injector to retrieve the dependency
|
||||||
|
* from the
|
||||||
* nearest ancestor element:
|
* nearest ancestor element:
|
||||||
* - The current element `dependency="3"` is skipped because it is not an ancestor.
|
* - The current element `dependency="3"` is skipped because it is not an ancestor.
|
||||||
* - Next parent has no directives `<div>`
|
* - Next parent has no directives `<div>`
|
||||||
@ -166,11 +157,9 @@ export class Parent extends Visibility {
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Ancestor extends Visibility {
|
export class Ancestor extends Visibility {
|
||||||
@CONST()
|
constructor() { super(999999, false); }
|
||||||
constructor() {
|
|
||||||
super(999999, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,9 +196,7 @@ export class Ancestor extends Visibility {
|
|||||||
*
|
*
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
|
@CONST()
|
||||||
export class Unbounded extends Visibility {
|
export class Unbounded extends Visibility {
|
||||||
@CONST()
|
constructor() { super(999999, true); }
|
||||||
constructor() {
|
|
||||||
super(999999, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
307
modules/angular2/src/core/application.js
vendored
307
modules/angular2/src/core/application.js
vendored
@ -1,307 +0,0 @@
|
|||||||
import {Injector, bind, OpaqueToken} from 'angular2/di';
|
|
||||||
import {NumberWrapper, Type, isBlank, isPresent, BaseException,
|
|
||||||
assertionsEnabled, print, stringify} from 'angular2/src/facade/lang';
|
|
||||||
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
||||||
import {Compiler, CompilerCache} from './compiler/compiler';
|
|
||||||
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
|
||||||
import {Parser, Lexer, ChangeDetection, DynamicChangeDetection, PipeRegistry, defaultPipeRegistry} from 'angular2/change_detection';
|
|
||||||
import {ExceptionHandler} from './exception_handler';
|
|
||||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
|
||||||
import {TemplateResolver} from './compiler/template_resolver';
|
|
||||||
import {DirectiveResolver} from './compiler/directive_resolver';
|
|
||||||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
|
||||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
|
||||||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
|
||||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
|
||||||
import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
|
||||||
import {XHR} from 'angular2/src/services/xhr';
|
|
||||||
import {XHRImpl} from 'angular2/src/services/xhr_impl';
|
|
||||||
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
|
||||||
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
|
||||||
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
|
|
||||||
import {Binding} from 'angular2/src/di/binding';
|
|
||||||
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
|
||||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
|
||||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
|
||||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
|
||||||
import {ComponentRef, DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
|
||||||
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
|
|
||||||
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
|
||||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
|
||||||
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
|
||||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
|
||||||
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
|
||||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
|
||||||
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
|
||||||
|
|
||||||
import {
|
|
||||||
appComponentRefToken,
|
|
||||||
appComponentTypeToken
|
|
||||||
} from './application_tokens';
|
|
||||||
|
|
||||||
var _rootInjector: Injector;
|
|
||||||
|
|
||||||
// Contains everything that is safe to share between applications.
|
|
||||||
var _rootBindings = [
|
|
||||||
bind(Reflector).toValue(reflector),
|
|
||||||
TestabilityRegistry
|
|
||||||
];
|
|
||||||
|
|
||||||
function _injectorBindings(appComponentType): List<Binding> {
|
|
||||||
return [
|
|
||||||
bind(DOCUMENT_TOKEN).toValue(DOM.defaultDoc()),
|
|
||||||
bind(appComponentTypeToken).toValue(appComponentType),
|
|
||||||
bind(appComponentRefToken).toAsyncFactory((dynamicComponentLoader, injector,
|
|
||||||
testability, registry) => {
|
|
||||||
|
|
||||||
// TODO(rado): investigate whether to support bindings on root component.
|
|
||||||
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector).then( (componentRef) => {
|
|
||||||
var domView = resolveInternalDomView(componentRef.hostView.render);
|
|
||||||
// We need to do this here to ensure that we create Testability and
|
|
||||||
// it's ready on the window for users.
|
|
||||||
registry.registerApplication(domView.boundElements[0], testability);
|
|
||||||
|
|
||||||
return componentRef;
|
|
||||||
});
|
|
||||||
}, [DynamicComponentLoader, Injector,
|
|
||||||
Testability, TestabilityRegistry]),
|
|
||||||
|
|
||||||
bind(appComponentType).toFactory((ref) => ref.instance,
|
|
||||||
[appComponentRefToken]),
|
|
||||||
bind(LifeCycle).toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),[ExceptionHandler]),
|
|
||||||
bind(EventManager).toFactory((ngZone) => {
|
|
||||||
var plugins = [new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
|
|
||||||
return new EventManager(plugins, ngZone);
|
|
||||||
}, [NgZone]),
|
|
||||||
bind(ShadowDomStrategy).toFactory(
|
|
||||||
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
|
||||||
[StyleUrlResolver, DOCUMENT_TOKEN]),
|
|
||||||
// TODO(tbosch): We need an explicit factory here, as
|
|
||||||
// we are getting errors in dart2js with mirrors...
|
|
||||||
bind(DomRenderer).toFactory(
|
|
||||||
(eventManager, shadowDomStrategy, doc) => new DomRenderer(eventManager, shadowDomStrategy, doc),
|
|
||||||
[EventManager, ShadowDomStrategy, DOCUMENT_TOKEN]
|
|
||||||
),
|
|
||||||
DefaultDomCompiler,
|
|
||||||
bind(Renderer).toAlias(DomRenderer),
|
|
||||||
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
|
||||||
ProtoViewFactory,
|
|
||||||
// TODO(tbosch): We need an explicit factory here, as
|
|
||||||
// we are getting errors in dart2js with mirrors...
|
|
||||||
bind(AppViewPool).toFactory(
|
|
||||||
(capacity) => new AppViewPool(capacity),
|
|
||||||
[APP_VIEW_POOL_CAPACITY]
|
|
||||||
),
|
|
||||||
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
|
||||||
AppViewManager,
|
|
||||||
AppViewManagerUtils,
|
|
||||||
Compiler,
|
|
||||||
CompilerCache,
|
|
||||||
TemplateResolver,
|
|
||||||
bind(PipeRegistry).toValue(defaultPipeRegistry),
|
|
||||||
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
|
||||||
TemplateLoader,
|
|
||||||
DirectiveResolver,
|
|
||||||
Parser,
|
|
||||||
Lexer,
|
|
||||||
ExceptionHandler,
|
|
||||||
bind(XHR).toValue(new XHRImpl()),
|
|
||||||
ComponentUrlMapper,
|
|
||||||
UrlResolver,
|
|
||||||
StyleUrlResolver,
|
|
||||||
StyleInliner,
|
|
||||||
DynamicComponentLoader,
|
|
||||||
Testability
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function _createNgZone(givenReporter:Function): NgZone {
|
|
||||||
var defaultErrorReporter = (exception, stackTrace) => {
|
|
||||||
var longStackTrace = ListWrapper.join(stackTrace, "\n\n-----async gap-----\n");
|
|
||||||
DOM.logError(`${exception}\n\n${longStackTrace}`);
|
|
||||||
throw exception;
|
|
||||||
};
|
|
||||||
|
|
||||||
var reporter = isPresent(givenReporter) ? givenReporter : defaultErrorReporter;
|
|
||||||
|
|
||||||
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
|
||||||
zone.initCallbacks({onErrorHandler: reporter});
|
|
||||||
return zone;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrapping for Angular applications.
|
|
||||||
*
|
|
||||||
* You instantiate an Angular application by explicitly specifying a component to use as the root component for your
|
|
||||||
* application via the `bootstrap()` method.
|
|
||||||
*
|
|
||||||
* ## Simple Example
|
|
||||||
*
|
|
||||||
* Assuming this `index.html`:
|
|
||||||
*
|
|
||||||
* ```html
|
|
||||||
* <html>
|
|
||||||
* <!-- load Angular script tags here. -->
|
|
||||||
* <body>
|
|
||||||
* <my-app>loading...</my-app>
|
|
||||||
* </body>
|
|
||||||
* </html>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* An application is bootstrapped inside an existing browser DOM, typically `index.html`. Unlike Angular 1, Angular 2
|
|
||||||
* does not compile/process bindings in `index.html`. This is mainly for security reasons, as well as architectural
|
|
||||||
* changes in Angular 2. This means that `index.html` can safely be processed using server-side technologies such as
|
|
||||||
* bindings. Bindings can thus use double-curly `{{ syntax }}` without collision from Angular 2 component double-curly
|
|
||||||
* `{{ syntax }}`.
|
|
||||||
*
|
|
||||||
* We can use this script code:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-app'
|
|
||||||
* })
|
|
||||||
* @View({
|
|
||||||
* template: 'Hello {{ name }}!'
|
|
||||||
* })
|
|
||||||
* class MyApp {
|
|
||||||
* name:string;
|
|
||||||
*
|
|
||||||
* constructor() {
|
|
||||||
* this.name = 'World';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* main() {
|
|
||||||
* return bootstrap(MyApp);
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* When the app developer invokes `bootstrap()` with the root component `MyApp` as its argument, Angular performs the
|
|
||||||
* following tasks:
|
|
||||||
*
|
|
||||||
* 1. It uses the component's `selector` property to locate the DOM element which needs to be upgraded into
|
|
||||||
* the angular component.
|
|
||||||
* 2. It creates a new child injector (from the platform injector) and configures the injector with the component's
|
|
||||||
* `appInjector`. Optionally, you can also override the injector configuration for an app by invoking
|
|
||||||
* `bootstrap` with the `componentInjectableBindings` argument.
|
|
||||||
* 3. It creates a new `Zone` and connects it to the angular application's change detection domain instance.
|
|
||||||
* 4. It creates a shadow DOM on the selected component's host element and loads the template into it.
|
|
||||||
* 5. It instantiates the specified component.
|
|
||||||
* 6. Finally, Angular performs change detection to apply the initial data bindings for the application.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ## Instantiating Multiple Applications on a Single Page
|
|
||||||
*
|
|
||||||
* There are two ways to do this.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ### Isolated Applications
|
|
||||||
*
|
|
||||||
* Angular creates a new application each time that the `bootstrap()` method is invoked. When multiple applications
|
|
||||||
* are created for a page, Angular treats each application as independent within an isolated change detection and
|
|
||||||
* `Zone` domain. If you need to share data between applications, use the strategy described in the next
|
|
||||||
* section, "Applications That Share Change Detection."
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ### Applications That Share Change Detection
|
|
||||||
*
|
|
||||||
* If you need to bootstrap multiple applications that share common data, the applications must share a common
|
|
||||||
* change detection and zone. To do that, create a meta-component that lists the application components in its template.
|
|
||||||
* By only invoking the `bootstrap()` method once, with the meta-component as its argument, you ensure that only a
|
|
||||||
* single change detection zone is created and therefore data can be shared across the applications.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ## Platform Injector
|
|
||||||
*
|
|
||||||
* When working within a browser window, there are many singleton resources: cookies, title, location, and others.
|
|
||||||
* Angular services that represent these resources must likewise be shared across all Angular applications that
|
|
||||||
* occupy the same browser window. For this reason, Angular creates exactly one global platform injector which stores
|
|
||||||
* all shared services, and each angular application injector has the platform injector as its parent.
|
|
||||||
*
|
|
||||||
* Each application has its own private injector as well. When there are multiple applications on a page, Angular treats
|
|
||||||
* each application injector's services as private to that application.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* # API
|
|
||||||
* - `appComponentType`: The root component which should act as the application. This is a reference to a `Type`
|
|
||||||
* which is annotated with `@Component(...)`.
|
|
||||||
* - `componentInjectableBindings`: An additional set of bindings that can be added to `appInjector` for the
|
|
||||||
* {@link Component} to override default injection behavior.
|
|
||||||
* - `errorReporter`: `function(exception:any, stackTrace:string)` a default error reporter for unhandled exceptions.
|
|
||||||
*
|
|
||||||
* Returns a `Promise` with the application`s private {@link Injector}.
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/core
|
|
||||||
*/
|
|
||||||
export function bootstrap(appComponentType: Type,
|
|
||||||
componentInjectableBindings: List<Binding> = null,
|
|
||||||
errorReporter: Function = null): Promise<ApplicationRef> {
|
|
||||||
BrowserDomAdapter.makeCurrent();
|
|
||||||
var bootstrapProcess = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
var zone = _createNgZone(errorReporter);
|
|
||||||
zone.run(() => {
|
|
||||||
// TODO(rado): prepopulate template cache, so applications with only
|
|
||||||
// index.html and main.js are possible.
|
|
||||||
|
|
||||||
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone);
|
|
||||||
|
|
||||||
PromiseWrapper.then(appInjector.asyncGet(appComponentRefToken),
|
|
||||||
(componentRef) => {
|
|
||||||
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
|
|
||||||
// retrieve life cycle: may have already been created if injected in root component
|
|
||||||
var lc = appInjector.get(LifeCycle);
|
|
||||||
lc.registerWith(zone, appChangeDetector);
|
|
||||||
lc.tick(); //the first tick that will bootstrap the app
|
|
||||||
|
|
||||||
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
|
||||||
},
|
|
||||||
|
|
||||||
(err, stackTrace) => {
|
|
||||||
bootstrapProcess.reject(err, stackTrace)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return bootstrapProcess.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ApplicationRef {
|
|
||||||
_hostComponent:ComponentRef;
|
|
||||||
_injector:Injector;
|
|
||||||
_hostComponentType:Type;
|
|
||||||
constructor(hostComponent:ComponentRef, hostComponentType:Type, injector:Injector) {
|
|
||||||
this._hostComponent = hostComponent;
|
|
||||||
this._injector = injector;
|
|
||||||
this._hostComponentType = hostComponentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hostComponentType() {
|
|
||||||
return this._hostComponentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hostComponent() {
|
|
||||||
return this._hostComponent.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
// TODO: We also need to clean up the Zone, ... here!
|
|
||||||
return this._hostComponent.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
get injector() {
|
|
||||||
return this._injector;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _createAppInjector(appComponentType: Type, bindings: List<Binding>, zone: NgZone): Injector {
|
|
||||||
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
|
|
||||||
var mergedBindings = isPresent(bindings) ?
|
|
||||||
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
|
|
||||||
_injectorBindings(appComponentType);
|
|
||||||
ListWrapper.push(mergedBindings, bind(NgZone).toValue(zone));
|
|
||||||
return _rootInjector.resolveAndCreateChild(mergedBindings);
|
|
||||||
}
|
|
348
modules/angular2/src/core/application.ts
Normal file
348
modules/angular2/src/core/application.ts
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
import {Injector, bind, OpaqueToken, Binding} from 'angular2/di';
|
||||||
|
import {
|
||||||
|
NumberWrapper,
|
||||||
|
Type,
|
||||||
|
isBlank,
|
||||||
|
isPresent,
|
||||||
|
BaseException,
|
||||||
|
assertionsEnabled,
|
||||||
|
print,
|
||||||
|
stringify
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {Compiler, CompilerCache} from './compiler/compiler';
|
||||||
|
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
import {
|
||||||
|
Parser,
|
||||||
|
Lexer,
|
||||||
|
ChangeDetection,
|
||||||
|
DynamicChangeDetection,
|
||||||
|
PipeRegistry,
|
||||||
|
defaultPipeRegistry
|
||||||
|
} from 'angular2/change_detection';
|
||||||
|
import {ExceptionHandler} from './exception_handler';
|
||||||
|
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||||
|
import {TemplateResolver} from './compiler/template_resolver';
|
||||||
|
import {DirectiveResolver} from './compiler/directive_resolver';
|
||||||
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||||
|
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||||
|
import {
|
||||||
|
EmulatedUnscopedShadowDomStrategy
|
||||||
|
} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||||
|
import {XHR} from 'angular2/src/services/xhr';
|
||||||
|
import {XHRImpl} from 'angular2/src/services/xhr_impl';
|
||||||
|
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
|
||||||
|
import {KeyEventsPlugin} from 'angular2/src/render/dom/events/key_events';
|
||||||
|
import {HammerGesturesPlugin} from 'angular2/src/render/dom/events/hammer_gestures';
|
||||||
|
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
|
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||||
|
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||||
|
import {
|
||||||
|
ComponentRef,
|
||||||
|
DynamicComponentLoader
|
||||||
|
} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
|
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
|
||||||
|
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||||
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
|
import {Renderer, RenderCompiler} from 'angular2/src/render/api';
|
||||||
|
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||||
|
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
||||||
|
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||||
|
import {internalView} from 'angular2/src/core/compiler/view_ref';
|
||||||
|
|
||||||
|
import {appComponentRefToken, appComponentTypeToken} from './application_tokens';
|
||||||
|
|
||||||
|
var _rootInjector: Injector;
|
||||||
|
|
||||||
|
// Contains everything that is safe to share between applications.
|
||||||
|
var _rootBindings = [bind(Reflector).toValue(reflector), TestabilityRegistry];
|
||||||
|
|
||||||
|
function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
||||||
|
return [
|
||||||
|
bind(DOCUMENT_TOKEN)
|
||||||
|
.toValue(DOM.defaultDoc()),
|
||||||
|
bind(appComponentTypeToken).toValue(appComponentType),
|
||||||
|
bind(appComponentRefToken)
|
||||||
|
.toAsyncFactory((dynamicComponentLoader, injector, testability, registry) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO(rado): investigate whether to support bindings on root component.
|
||||||
|
return dynamicComponentLoader.loadAsRoot(appComponentType, null, injector)
|
||||||
|
.then((componentRef) => {
|
||||||
|
var domView = resolveInternalDomView(componentRef.hostView.render);
|
||||||
|
// We need to do this here to ensure that we create Testability and
|
||||||
|
// it's ready on the window for users.
|
||||||
|
registry.registerApplication(domView.boundElements[0], testability);
|
||||||
|
|
||||||
|
return componentRef;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[DynamicComponentLoader, Injector, Testability, TestabilityRegistry]),
|
||||||
|
|
||||||
|
bind(appComponentType).toFactory((ref) => ref.instance, [appComponentRefToken]),
|
||||||
|
bind(LifeCycle)
|
||||||
|
.toFactory((exceptionHandler) => new LifeCycle(exceptionHandler, null, assertionsEnabled()),
|
||||||
|
[ExceptionHandler]),
|
||||||
|
bind(EventManager)
|
||||||
|
.toFactory(
|
||||||
|
(ngZone) =>
|
||||||
|
{
|
||||||
|
var plugins =
|
||||||
|
[new HammerGesturesPlugin(), new KeyEventsPlugin(), new DomEventsPlugin()];
|
||||||
|
return new EventManager(plugins, ngZone);
|
||||||
|
},
|
||||||
|
[NgZone]),
|
||||||
|
bind(ShadowDomStrategy)
|
||||||
|
.toFactory((styleUrlResolver, doc) =>
|
||||||
|
new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||||
|
[StyleUrlResolver, DOCUMENT_TOKEN]),
|
||||||
|
// TODO(tbosch): We need an explicit factory here, as
|
||||||
|
// we are getting errors in dart2js with mirrors...
|
||||||
|
bind(DomRenderer)
|
||||||
|
.toFactory((eventManager, shadowDomStrategy, doc) =>
|
||||||
|
new DomRenderer(eventManager, shadowDomStrategy, doc),
|
||||||
|
[EventManager, ShadowDomStrategy, DOCUMENT_TOKEN]),
|
||||||
|
DefaultDomCompiler,
|
||||||
|
bind(Renderer).toAlias(DomRenderer),
|
||||||
|
bind(RenderCompiler).toAlias(DefaultDomCompiler),
|
||||||
|
ProtoViewFactory,
|
||||||
|
// TODO(tbosch): We need an explicit factory here, as
|
||||||
|
// we are getting errors in dart2js with mirrors...
|
||||||
|
bind(AppViewPool).toFactory((capacity) => new AppViewPool(capacity), [APP_VIEW_POOL_CAPACITY]),
|
||||||
|
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||||
|
AppViewManager,
|
||||||
|
AppViewManagerUtils,
|
||||||
|
Compiler,
|
||||||
|
CompilerCache,
|
||||||
|
TemplateResolver,
|
||||||
|
bind(PipeRegistry).toValue(defaultPipeRegistry),
|
||||||
|
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
||||||
|
TemplateLoader,
|
||||||
|
DirectiveResolver,
|
||||||
|
Parser,
|
||||||
|
Lexer,
|
||||||
|
ExceptionHandler,
|
||||||
|
bind(XHR).toValue(new XHRImpl()),
|
||||||
|
ComponentUrlMapper,
|
||||||
|
UrlResolver,
|
||||||
|
StyleUrlResolver,
|
||||||
|
StyleInliner,
|
||||||
|
DynamicComponentLoader,
|
||||||
|
Testability
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createNgZone(givenReporter: Function): NgZone {
|
||||||
|
var defaultErrorReporter = (exception, stackTrace) => {
|
||||||
|
var longStackTrace = ListWrapper.join(stackTrace, "\n\n-----async gap-----\n");
|
||||||
|
DOM.logError(`${exception}\n\n${longStackTrace}`);
|
||||||
|
throw exception;
|
||||||
|
};
|
||||||
|
|
||||||
|
var reporter = isPresent(givenReporter) ? givenReporter : defaultErrorReporter;
|
||||||
|
|
||||||
|
var zone = new NgZone({enableLongStackTrace: assertionsEnabled()});
|
||||||
|
zone.initCallbacks({onErrorHandler: reporter});
|
||||||
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrapping for Angular applications.
|
||||||
|
*
|
||||||
|
* You instantiate an Angular application by explicitly specifying a component to use as the root
|
||||||
|
* component for your
|
||||||
|
* application via the `bootstrap()` method.
|
||||||
|
*
|
||||||
|
* ## Simple Example
|
||||||
|
*
|
||||||
|
* Assuming this `index.html`:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <html>
|
||||||
|
* <!-- load Angular script tags here. -->
|
||||||
|
* <body>
|
||||||
|
* <my-app>loading...</my-app>
|
||||||
|
* </body>
|
||||||
|
* </html>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* An application is bootstrapped inside an existing browser DOM, typically `index.html`. Unlike
|
||||||
|
* Angular 1, Angular 2
|
||||||
|
* does not compile/process bindings in `index.html`. This is mainly for security reasons, as well
|
||||||
|
* as architectural
|
||||||
|
* changes in Angular 2. This means that `index.html` can safely be processed using server-side
|
||||||
|
* technologies such as
|
||||||
|
* bindings. Bindings can thus use double-curly `{{ syntax }}` without collision from Angular 2
|
||||||
|
* component double-curly
|
||||||
|
* `{{ syntax }}`.
|
||||||
|
*
|
||||||
|
* We can use this script code:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Component({
|
||||||
|
* selector: 'my-app'
|
||||||
|
* })
|
||||||
|
* @View({
|
||||||
|
* template: 'Hello {{ name }}!'
|
||||||
|
* })
|
||||||
|
* class MyApp {
|
||||||
|
* name:string;
|
||||||
|
*
|
||||||
|
* constructor() {
|
||||||
|
* this.name = 'World';
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* main() {
|
||||||
|
* return bootstrap(MyApp);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* When the app developer invokes `bootstrap()` with the root component `MyApp` as its argument,
|
||||||
|
* Angular performs the
|
||||||
|
* following tasks:
|
||||||
|
*
|
||||||
|
* 1. It uses the component's `selector` property to locate the DOM element which needs to be
|
||||||
|
* upgraded into
|
||||||
|
* the angular component.
|
||||||
|
* 2. It creates a new child injector (from the platform injector) and configures the injector with
|
||||||
|
* the component's
|
||||||
|
* `appInjector`. Optionally, you can also override the injector configuration for an app by
|
||||||
|
* invoking
|
||||||
|
* `bootstrap` with the `componentInjectableBindings` argument.
|
||||||
|
* 3. It creates a new `Zone` and connects it to the angular application's change detection domain
|
||||||
|
* instance.
|
||||||
|
* 4. It creates a shadow DOM on the selected component's host element and loads the template into
|
||||||
|
* it.
|
||||||
|
* 5. It instantiates the specified component.
|
||||||
|
* 6. Finally, Angular performs change detection to apply the initial data bindings for the
|
||||||
|
* application.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Instantiating Multiple Applications on a Single Page
|
||||||
|
*
|
||||||
|
* There are two ways to do this.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ### Isolated Applications
|
||||||
|
*
|
||||||
|
* Angular creates a new application each time that the `bootstrap()` method is invoked. When
|
||||||
|
* multiple applications
|
||||||
|
* are created for a page, Angular treats each application as independent within an isolated change
|
||||||
|
* detection and
|
||||||
|
* `Zone` domain. If you need to share data between applications, use the strategy described in the
|
||||||
|
* next
|
||||||
|
* section, "Applications That Share Change Detection."
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ### Applications That Share Change Detection
|
||||||
|
*
|
||||||
|
* If you need to bootstrap multiple applications that share common data, the applications must
|
||||||
|
* share a common
|
||||||
|
* change detection and zone. To do that, create a meta-component that lists the application
|
||||||
|
* components in its template.
|
||||||
|
* By only invoking the `bootstrap()` method once, with the meta-component as its argument, you
|
||||||
|
* ensure that only a
|
||||||
|
* single change detection zone is created and therefore data can be shared across the applications.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Platform Injector
|
||||||
|
*
|
||||||
|
* When working within a browser window, there are many singleton resources: cookies, title,
|
||||||
|
* location, and others.
|
||||||
|
* Angular services that represent these resources must likewise be shared across all Angular
|
||||||
|
* applications that
|
||||||
|
* occupy the same browser window. For this reason, Angular creates exactly one global platform
|
||||||
|
* injector which stores
|
||||||
|
* all shared services, and each angular application injector has the platform injector as its
|
||||||
|
* parent.
|
||||||
|
*
|
||||||
|
* Each application has its own private injector as well. When there are multiple applications on a
|
||||||
|
* page, Angular treats
|
||||||
|
* each application injector's services as private to that application.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* # API
|
||||||
|
* - `appComponentType`: The root component which should act as the application. This is a reference
|
||||||
|
* to a `Type`
|
||||||
|
* which is annotated with `@Component(...)`.
|
||||||
|
* - `componentInjectableBindings`: An additional set of bindings that can be added to `appInjector`
|
||||||
|
* for the
|
||||||
|
* {@link Component} to override default injection behavior.
|
||||||
|
* - `errorReporter`: `function(exception:any, stackTrace:string)` a default error reporter for
|
||||||
|
* unhandled exceptions.
|
||||||
|
*
|
||||||
|
* Returns a `Promise` with the application`s private {@link Injector}.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/core
|
||||||
|
*/
|
||||||
|
export function bootstrap(appComponentType: Type,
|
||||||
|
componentInjectableBindings: List<Type | Binding | List<any>> = null,
|
||||||
|
errorReporter: Function = null): Promise<ApplicationRef> {
|
||||||
|
BrowserDomAdapter.makeCurrent();
|
||||||
|
var bootstrapProcess = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
var zone = _createNgZone(errorReporter);
|
||||||
|
zone.run(() => {
|
||||||
|
// TODO(rado): prepopulate template cache, so applications with only
|
||||||
|
// index.html and main.js are possible.
|
||||||
|
|
||||||
|
var appInjector = _createAppInjector(appComponentType, componentInjectableBindings, zone);
|
||||||
|
|
||||||
|
PromiseWrapper.then(
|
||||||
|
appInjector.asyncGet(appComponentRefToken),
|
||||||
|
(componentRef) =>
|
||||||
|
{
|
||||||
|
var appChangeDetector = internalView(componentRef.hostView).changeDetector;
|
||||||
|
// retrieve life cycle: may have already been created if injected in root component
|
||||||
|
var lc = appInjector.get(LifeCycle);
|
||||||
|
lc.registerWith(zone, appChangeDetector);
|
||||||
|
lc.tick(); // the first tick that will bootstrap the app
|
||||||
|
|
||||||
|
bootstrapProcess.resolve(new ApplicationRef(componentRef, appComponentType, appInjector));
|
||||||
|
},
|
||||||
|
|
||||||
|
(err, stackTrace) => {bootstrapProcess.reject(err, stackTrace)});
|
||||||
|
});
|
||||||
|
|
||||||
|
return bootstrapProcess.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApplicationRef {
|
||||||
|
_hostComponent: ComponentRef;
|
||||||
|
_injector: Injector;
|
||||||
|
_hostComponentType: Type;
|
||||||
|
constructor(hostComponent: ComponentRef, hostComponentType: Type, injector: Injector) {
|
||||||
|
this._hostComponent = hostComponent;
|
||||||
|
this._injector = injector;
|
||||||
|
this._hostComponentType = hostComponentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hostComponentType() { return this._hostComponentType; }
|
||||||
|
|
||||||
|
get hostComponent() { return this._hostComponent.instance; }
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
// TODO: We also need to clean up the Zone, ... here!
|
||||||
|
return this._hostComponent.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
get injector() { return this._injector; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createAppInjector(appComponentType: Type, bindings: List<Type | Binding | List<any>>,
|
||||||
|
zone: NgZone): Injector {
|
||||||
|
if (isBlank(_rootInjector)) _rootInjector = Injector.resolveAndCreate(_rootBindings);
|
||||||
|
var mergedBindings = isPresent(bindings) ?
|
||||||
|
ListWrapper.concat(_injectorBindings(appComponentType), bindings) :
|
||||||
|
_injectorBindings(appComponentType);
|
||||||
|
ListWrapper.push(mergedBindings, bind(NgZone).toValue(zone));
|
||||||
|
return _rootInjector.resolveAndCreateChild(mergedBindings);
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
import {OpaqueToken} from 'angular2/di';
|
|
||||||
|
|
||||||
export var appComponentRefToken:OpaqueToken = new OpaqueToken('ComponentRef');
|
|
||||||
export var appComponentTypeToken:OpaqueToken = new OpaqueToken('RootComponent');
|
|
4
modules/angular2/src/core/application_tokens.ts
Normal file
4
modules/angular2/src/core/application_tokens.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import {OpaqueToken} from 'angular2/di';
|
||||||
|
|
||||||
|
export var appComponentRefToken: OpaqueToken = new OpaqueToken('ComponentRef');
|
||||||
|
export var appComponentTypeToken: OpaqueToken = new OpaqueToken('RootComponent');
|
@ -21,9 +21,7 @@ export class BaseQueryList {
|
|||||||
this._dirty = false;
|
this._dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.iterator]() {
|
[Symbol.iterator]() { return this._results[Symbol.iterator](); }
|
||||||
return this._results[Symbol.iterator]();
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(newList) {
|
reset(newList) {
|
||||||
this._results = newList;
|
this._results = newList;
|
||||||
@ -43,11 +41,7 @@ export class BaseQueryList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(callback) {
|
onChange(callback) { ListWrapper.push(this._callbacks, callback); }
|
||||||
ListWrapper.push(this._callbacks, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCallback(callback) {
|
removeCallback(callback) { ListWrapper.remove(this._callbacks, callback); }
|
||||||
ListWrapper.remove(this._callbacks, callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
258
modules/angular2/src/core/compiler/compiler.js
vendored
258
modules/angular2/src/core/compiler/compiler.js
vendored
@ -1,258 +0,0 @@
|
|||||||
import {Binding, resolveForwardRef} from 'angular2/di';
|
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {Type, isBlank, isPresent, BaseException, normalizeBlank, stringify} from 'angular2/src/facade/lang';
|
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
|
||||||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
|
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
|
||||||
|
|
||||||
import {AppProtoView} from './view';
|
|
||||||
import {ElementBinder} from './element_binder';
|
|
||||||
import {ProtoViewRef} from './view_ref';
|
|
||||||
import {DirectiveBinding} from './element_injector';
|
|
||||||
import {TemplateResolver} from './template_resolver';
|
|
||||||
import {View} from '../annotations_impl/view';
|
|
||||||
import {ComponentUrlMapper} from './component_url_mapper';
|
|
||||||
import {ProtoViewFactory} from './proto_view_factory';
|
|
||||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
|
||||||
|
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache that stores the AppProtoView of the template of a component.
|
|
||||||
* Used to prevent duplicate work and resolve cyclic dependencies.
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class CompilerCache {
|
|
||||||
_cache:Map;
|
|
||||||
constructor() {
|
|
||||||
this._cache = MapWrapper.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
set(component:Type, protoView:AppProtoView):void {
|
|
||||||
MapWrapper.set(this._cache, component, protoView);
|
|
||||||
}
|
|
||||||
|
|
||||||
get(component:Type):AppProtoView {
|
|
||||||
var result = MapWrapper.get(this._cache, component);
|
|
||||||
return normalizeBlank(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear():void {
|
|
||||||
MapWrapper.clear(this._cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @exportedAs angular2/view
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class Compiler {
|
|
||||||
_reader: DirectiveResolver;
|
|
||||||
_compilerCache:CompilerCache;
|
|
||||||
_compiling:Map<Type, Promise>;
|
|
||||||
_templateResolver: TemplateResolver;
|
|
||||||
_componentUrlMapper: ComponentUrlMapper;
|
|
||||||
_urlResolver: UrlResolver;
|
|
||||||
_appUrl: string;
|
|
||||||
_render: renderApi.RenderCompiler;
|
|
||||||
_protoViewFactory:ProtoViewFactory;
|
|
||||||
|
|
||||||
constructor(reader: DirectiveResolver,
|
|
||||||
cache:CompilerCache,
|
|
||||||
templateResolver: TemplateResolver,
|
|
||||||
componentUrlMapper: ComponentUrlMapper,
|
|
||||||
urlResolver: UrlResolver,
|
|
||||||
render: renderApi.RenderCompiler,
|
|
||||||
protoViewFactory: ProtoViewFactory) {
|
|
||||||
this._reader = reader;
|
|
||||||
this._compilerCache = cache;
|
|
||||||
this._compiling = MapWrapper.create();
|
|
||||||
this._templateResolver = templateResolver;
|
|
||||||
this._componentUrlMapper = componentUrlMapper;
|
|
||||||
this._urlResolver = urlResolver;
|
|
||||||
this._appUrl = urlResolver.resolve(null, './');
|
|
||||||
this._render = render;
|
|
||||||
this._protoViewFactory = protoViewFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
_bindDirective(directiveTypeOrBinding):DirectiveBinding {
|
|
||||||
if (directiveTypeOrBinding instanceof DirectiveBinding) {
|
|
||||||
return directiveTypeOrBinding;
|
|
||||||
} else if (directiveTypeOrBinding instanceof Binding) {
|
|
||||||
let annotation = this._reader.resolve(directiveTypeOrBinding.token);
|
|
||||||
return DirectiveBinding.createFromBinding(directiveTypeOrBinding, annotation);
|
|
||||||
} else {
|
|
||||||
let annotation = this._reader.resolve(directiveTypeOrBinding);
|
|
||||||
return DirectiveBinding.createFromType(directiveTypeOrBinding, annotation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a hostView as if the compiler encountered <hostcmp></hostcmp>.
|
|
||||||
// Used for bootstrapping.
|
|
||||||
compileInHost(componentTypeOrBinding:any):Promise<ProtoViewRef> {
|
|
||||||
var componentBinding = this._bindDirective(componentTypeOrBinding);
|
|
||||||
Compiler._assertTypeIsComponent(componentBinding);
|
|
||||||
|
|
||||||
var directiveMetadata = componentBinding.metadata;
|
|
||||||
return this._render.compileHost(directiveMetadata).then( (hostRenderPv) => {
|
|
||||||
return this._compileNestedProtoViews(componentBinding, hostRenderPv, [componentBinding]);
|
|
||||||
}).then( (appProtoView) => {
|
|
||||||
return new ProtoViewRef(appProtoView);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compile(component: Type):Promise<ProtoViewRef> {
|
|
||||||
var componentBinding = this._bindDirective(component);
|
|
||||||
Compiler._assertTypeIsComponent(componentBinding);
|
|
||||||
var protoView = this._compile(componentBinding);
|
|
||||||
var pvPromise = PromiseWrapper.isPromise(protoView) ? protoView : PromiseWrapper.resolve(protoView);
|
|
||||||
return pvPromise.then( (appProtoView) => {
|
|
||||||
return new ProtoViewRef(appProtoView);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vicb): union type return AppProtoView or Promise<AppProtoView>
|
|
||||||
_compile(componentBinding: DirectiveBinding) {
|
|
||||||
var component = componentBinding.key.token;
|
|
||||||
var protoView = this._compilerCache.get(component);
|
|
||||||
if (isPresent(protoView)) {
|
|
||||||
// The component has already been compiled into an AppProtoView,
|
|
||||||
// returns a plain AppProtoView, not wrapped inside of a Promise.
|
|
||||||
// Needed for recursive components.
|
|
||||||
return protoView;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pvPromise = MapWrapper.get(this._compiling, component);
|
|
||||||
if (isPresent(pvPromise)) {
|
|
||||||
// The component is already being compiled, attach to the existing Promise
|
|
||||||
// instead of re-compiling the component.
|
|
||||||
// It happens when a template references a component multiple times.
|
|
||||||
return pvPromise;
|
|
||||||
}
|
|
||||||
var template = this._templateResolver.resolve(component);
|
|
||||||
if (isBlank(template)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var directives = this._flattenDirectives(template);
|
|
||||||
|
|
||||||
for (var i = 0; i < directives.length; i++) {
|
|
||||||
if (!Compiler._isValidDirective(directives[i])) {
|
|
||||||
throw new BaseException(
|
|
||||||
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var boundDirectives = ListWrapper.map(directives, (directive) => this._bindDirective(directive));
|
|
||||||
|
|
||||||
var renderTemplate = this._buildRenderTemplate(component, template, boundDirectives);
|
|
||||||
pvPromise = this._render.compile(renderTemplate).then( (renderPv) => {
|
|
||||||
return this._compileNestedProtoViews(componentBinding, renderPv, boundDirectives);
|
|
||||||
});
|
|
||||||
|
|
||||||
MapWrapper.set(this._compiling, component, pvPromise);
|
|
||||||
return pvPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(tbosch): union type return AppProtoView or Promise<AppProtoView>
|
|
||||||
_compileNestedProtoViews(componentBinding, renderPv, directives) {
|
|
||||||
var protoViews = this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
|
|
||||||
var protoView = protoViews[0];
|
|
||||||
// TODO(tbosch): we should be caching host protoViews as well!
|
|
||||||
// -> need a separate cache for this...
|
|
||||||
if (renderPv.type === renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE && isPresent(componentBinding)) {
|
|
||||||
// Populate the cache before compiling the nested components,
|
|
||||||
// so that components can reference themselves in their template.
|
|
||||||
var component = componentBinding.key.token;
|
|
||||||
this._compilerCache.set(component, protoView);
|
|
||||||
MapWrapper.delete(this._compiling, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
var nestedPVPromises = [];
|
|
||||||
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
|
|
||||||
var nestedComponent = elementBinder.componentDirective;
|
|
||||||
var elementBinderDone = (nestedPv) => {
|
|
||||||
elementBinder.nestedProtoView = nestedPv;
|
|
||||||
};
|
|
||||||
var nestedCall = this._compile(nestedComponent);
|
|
||||||
if (PromiseWrapper.isPromise(nestedCall)) {
|
|
||||||
ListWrapper.push(nestedPVPromises, nestedCall.then(elementBinderDone));
|
|
||||||
} else if (isPresent(nestedCall)) {
|
|
||||||
elementBinderDone(nestedCall);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var protoViewDone = (_) => {
|
|
||||||
return protoView;
|
|
||||||
};
|
|
||||||
if (nestedPVPromises.length > 0) {
|
|
||||||
return PromiseWrapper.all(nestedPVPromises).then(protoViewDone);
|
|
||||||
} else {
|
|
||||||
return protoViewDone(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_collectComponentElementBinders(protoViews:List<AppProtoView>):List<ElementBinder> {
|
|
||||||
var componentElementBinders = [];
|
|
||||||
ListWrapper.forEach(protoViews, (protoView) => {
|
|
||||||
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
|
||||||
if (isPresent(elementBinder.componentDirective)) {
|
|
||||||
ListWrapper.push(componentElementBinders, elementBinder);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return componentElementBinders;
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildRenderTemplate(component, view, directives): renderApi.ViewDefinition {
|
|
||||||
var componentUrl = this._urlResolver.resolve(
|
|
||||||
this._appUrl, this._componentUrlMapper.getUrl(component)
|
|
||||||
);
|
|
||||||
var templateAbsUrl = null;
|
|
||||||
if (isPresent(view.templateUrl)) {
|
|
||||||
templateAbsUrl = this._urlResolver.resolve(componentUrl, view.templateUrl);
|
|
||||||
} else if (isPresent(view.template)) {
|
|
||||||
// Note: If we have an inline template, we also need to send
|
|
||||||
// the url for the component to the render so that it
|
|
||||||
// is able to resolve urls in stylesheets.
|
|
||||||
templateAbsUrl = componentUrl;
|
|
||||||
}
|
|
||||||
return new renderApi.ViewDefinition({
|
|
||||||
componentId: stringify(component),
|
|
||||||
absUrl: templateAbsUrl,
|
|
||||||
template: view.template,
|
|
||||||
directives: ListWrapper.map(directives, directiveBinding => directiveBinding.metadata )
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_flattenDirectives(template: View):List<Type> {
|
|
||||||
if (isBlank(template.directives)) return [];
|
|
||||||
|
|
||||||
var directives = [];
|
|
||||||
this._flattenList(template.directives, directives);
|
|
||||||
|
|
||||||
return directives;
|
|
||||||
}
|
|
||||||
|
|
||||||
_flattenList(tree:List<any>, out:List<any> /*<Type|Binding>*/):void {
|
|
||||||
for (var i = 0; i < tree.length; i++) {
|
|
||||||
var item = resolveForwardRef(tree[i]);
|
|
||||||
if (ListWrapper.isList(item)) {
|
|
||||||
this._flattenList(item, out);
|
|
||||||
} else {
|
|
||||||
ListWrapper.push(out, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static _isValidDirective(value: any): boolean {
|
|
||||||
return isPresent(value) && (value instanceof Type || value instanceof Binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
static _assertTypeIsComponent(directiveBinding:DirectiveBinding):void {
|
|
||||||
if (directiveBinding.metadata.type !== renderApi.DirectiveMetadata.COMPONENT_TYPE) {
|
|
||||||
throw new BaseException(`Could not load '${stringify(directiveBinding.key.token)}' because it is not a component.`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
258
modules/angular2/src/core/compiler/compiler.ts
Normal file
258
modules/angular2/src/core/compiler/compiler.ts
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
import {Binding, resolveForwardRef, Injectable} from 'angular2/di';
|
||||||
|
import {
|
||||||
|
Type,
|
||||||
|
isBlank,
|
||||||
|
isPresent,
|
||||||
|
BaseException,
|
||||||
|
normalizeBlank,
|
||||||
|
stringify
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
|
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
|
|
||||||
|
import {AppProtoView} from './view';
|
||||||
|
import {ElementBinder} from './element_binder';
|
||||||
|
import {ProtoViewRef} from './view_ref';
|
||||||
|
import {DirectiveBinding} from './element_injector';
|
||||||
|
import {TemplateResolver} from './template_resolver';
|
||||||
|
import {View} from '../annotations_impl/view';
|
||||||
|
import {ComponentUrlMapper} from './component_url_mapper';
|
||||||
|
import {ProtoViewFactory} from './proto_view_factory';
|
||||||
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
|
|
||||||
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache that stores the AppProtoView of the template of a component.
|
||||||
|
* Used to prevent duplicate work and resolve cyclic dependencies.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CompilerCache {
|
||||||
|
_cache: Map<Type, AppProtoView>;
|
||||||
|
constructor() { this._cache = MapWrapper.create(); }
|
||||||
|
|
||||||
|
set(component: Type, protoView: AppProtoView): void {
|
||||||
|
MapWrapper.set(this._cache, component, protoView);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(component: Type): AppProtoView {
|
||||||
|
var result = MapWrapper.get(this._cache, component);
|
||||||
|
return normalizeBlank(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void { MapWrapper.clear(this._cache); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @exportedAs angular2/view
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class Compiler {
|
||||||
|
private _reader: DirectiveResolver;
|
||||||
|
private _compilerCache: CompilerCache;
|
||||||
|
private _compiling: Map<Type, Promise<AppProtoView>>;
|
||||||
|
private _templateResolver: TemplateResolver;
|
||||||
|
private _componentUrlMapper: ComponentUrlMapper;
|
||||||
|
private _urlResolver: UrlResolver;
|
||||||
|
private _appUrl: string;
|
||||||
|
private _render: renderApi.RenderCompiler;
|
||||||
|
private _protoViewFactory: ProtoViewFactory;
|
||||||
|
|
||||||
|
constructor(reader: DirectiveResolver, cache: CompilerCache, templateResolver: TemplateResolver,
|
||||||
|
componentUrlMapper: ComponentUrlMapper, urlResolver: UrlResolver,
|
||||||
|
render: renderApi.RenderCompiler, protoViewFactory: ProtoViewFactory) {
|
||||||
|
this._reader = reader;
|
||||||
|
this._compilerCache = cache;
|
||||||
|
this._compiling = MapWrapper.create();
|
||||||
|
this._templateResolver = templateResolver;
|
||||||
|
this._componentUrlMapper = componentUrlMapper;
|
||||||
|
this._urlResolver = urlResolver;
|
||||||
|
this._appUrl = urlResolver.resolve(null, './');
|
||||||
|
this._render = render;
|
||||||
|
this._protoViewFactory = protoViewFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _bindDirective(directiveTypeOrBinding): DirectiveBinding {
|
||||||
|
if (directiveTypeOrBinding instanceof DirectiveBinding) {
|
||||||
|
return directiveTypeOrBinding;
|
||||||
|
} else if (directiveTypeOrBinding instanceof Binding) {
|
||||||
|
let annotation = this._reader.resolve(directiveTypeOrBinding.token);
|
||||||
|
return DirectiveBinding.createFromBinding(directiveTypeOrBinding, annotation);
|
||||||
|
} else {
|
||||||
|
let annotation = this._reader.resolve(directiveTypeOrBinding);
|
||||||
|
return DirectiveBinding.createFromType(directiveTypeOrBinding, annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a hostView as if the compiler encountered <hostcmp></hostcmp>.
|
||||||
|
// Used for bootstrapping.
|
||||||
|
compileInHost(componentTypeOrBinding: Type | Binding): Promise<ProtoViewRef> {
|
||||||
|
var componentBinding = this._bindDirective(componentTypeOrBinding);
|
||||||
|
Compiler._assertTypeIsComponent(componentBinding);
|
||||||
|
|
||||||
|
var directiveMetadata = componentBinding.metadata;
|
||||||
|
return this._render.compileHost(directiveMetadata)
|
||||||
|
.then((hostRenderPv) =>
|
||||||
|
{
|
||||||
|
return this._compileNestedProtoViews(componentBinding, hostRenderPv,
|
||||||
|
[componentBinding]);
|
||||||
|
})
|
||||||
|
.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
|
||||||
|
}
|
||||||
|
|
||||||
|
compile(component: Type): Promise<ProtoViewRef> {
|
||||||
|
var componentBinding = this._bindDirective(component);
|
||||||
|
Compiler._assertTypeIsComponent(componentBinding);
|
||||||
|
var pvOrPromise = this._compile(componentBinding);
|
||||||
|
var pvPromise = PromiseWrapper.isPromise(pvOrPromise) ? <Promise<AppProtoView>>pvOrPromise :
|
||||||
|
PromiseWrapper.resolve(pvOrPromise);
|
||||||
|
return pvPromise.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
|
||||||
|
var component = <Type>componentBinding.key.token;
|
||||||
|
var protoView = this._compilerCache.get(component);
|
||||||
|
if (isPresent(protoView)) {
|
||||||
|
// The component has already been compiled into an AppProtoView,
|
||||||
|
// returns a plain AppProtoView, not wrapped inside of a Promise.
|
||||||
|
// Needed for recursive components.
|
||||||
|
return protoView;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pvPromise = MapWrapper.get(this._compiling, component);
|
||||||
|
if (isPresent(pvPromise)) {
|
||||||
|
// The component is already being compiled, attach to the existing Promise
|
||||||
|
// instead of re-compiling the component.
|
||||||
|
// It happens when a template references a component multiple times.
|
||||||
|
return pvPromise;
|
||||||
|
}
|
||||||
|
var template = this._templateResolver.resolve(component);
|
||||||
|
if (isBlank(template)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var directives = this._flattenDirectives(template);
|
||||||
|
|
||||||
|
for (var i = 0; i < directives.length; i++) {
|
||||||
|
if (!Compiler._isValidDirective(directives[i])) {
|
||||||
|
throw new BaseException(
|
||||||
|
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var boundDirectives =
|
||||||
|
ListWrapper.map(directives, (directive) => this._bindDirective(directive));
|
||||||
|
|
||||||
|
var renderTemplate = this._buildRenderTemplate(component, template, boundDirectives);
|
||||||
|
pvPromise =
|
||||||
|
this._render.compile(renderTemplate)
|
||||||
|
.then((renderPv) => {
|
||||||
|
return this._compileNestedProtoViews(componentBinding, renderPv, boundDirectives);
|
||||||
|
});
|
||||||
|
|
||||||
|
MapWrapper.set(this._compiling, component, pvPromise);
|
||||||
|
return pvPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _compileNestedProtoViews(componentBinding, renderPv, directives): Promise<AppProtoView>|
|
||||||
|
AppProtoView {
|
||||||
|
var protoViews =
|
||||||
|
this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
|
||||||
|
var protoView = protoViews[0];
|
||||||
|
// TODO(tbosch): we should be caching host protoViews as well!
|
||||||
|
// -> need a separate cache for this...
|
||||||
|
if (renderPv.type === renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE &&
|
||||||
|
isPresent(componentBinding)) {
|
||||||
|
// Populate the cache before compiling the nested components,
|
||||||
|
// so that components can reference themselves in their template.
|
||||||
|
var component = componentBinding.key.token;
|
||||||
|
this._compilerCache.set(component, protoView);
|
||||||
|
MapWrapper.delete(this._compiling, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nestedPVPromises = [];
|
||||||
|
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
|
||||||
|
var nestedComponent = elementBinder.componentDirective;
|
||||||
|
var elementBinderDone = (nestedPv: AppProtoView) => {
|
||||||
|
elementBinder.nestedProtoView = nestedPv;
|
||||||
|
};
|
||||||
|
var nestedCall = this._compile(nestedComponent);
|
||||||
|
if (PromiseWrapper.isPromise(nestedCall)) {
|
||||||
|
ListWrapper.push(nestedPVPromises,
|
||||||
|
(<Promise<AppProtoView>>nestedCall).then(elementBinderDone));
|
||||||
|
} else if (isPresent(nestedCall)) {
|
||||||
|
elementBinderDone(<AppProtoView>nestedCall);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (nestedPVPromises.length > 0) {
|
||||||
|
return PromiseWrapper.all(nestedPVPromises).then((_) => protoView);
|
||||||
|
} else {
|
||||||
|
return protoView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _collectComponentElementBinders(protoViews: List<AppProtoView>): List<ElementBinder> {
|
||||||
|
var componentElementBinders = [];
|
||||||
|
ListWrapper.forEach(protoViews, (protoView) => {
|
||||||
|
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
||||||
|
if (isPresent(elementBinder.componentDirective)) {
|
||||||
|
ListWrapper.push(componentElementBinders, elementBinder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return componentElementBinders;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _buildRenderTemplate(component, view, directives): renderApi.ViewDefinition {
|
||||||
|
var componentUrl =
|
||||||
|
this._urlResolver.resolve(this._appUrl, this._componentUrlMapper.getUrl(component));
|
||||||
|
var templateAbsUrl = null;
|
||||||
|
if (isPresent(view.templateUrl)) {
|
||||||
|
templateAbsUrl = this._urlResolver.resolve(componentUrl, view.templateUrl);
|
||||||
|
} else if (isPresent(view.template)) {
|
||||||
|
// Note: If we have an inline template, we also need to send
|
||||||
|
// the url for the component to the render so that it
|
||||||
|
// is able to resolve urls in stylesheets.
|
||||||
|
templateAbsUrl = componentUrl;
|
||||||
|
}
|
||||||
|
return new renderApi.ViewDefinition({
|
||||||
|
componentId: stringify(component),
|
||||||
|
absUrl: templateAbsUrl, template: view.template,
|
||||||
|
directives: ListWrapper.map(directives, directiveBinding => directiveBinding.metadata)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _flattenDirectives(template: View): List<Type> {
|
||||||
|
if (isBlank(template.directives)) return [];
|
||||||
|
|
||||||
|
var directives = [];
|
||||||
|
this._flattenList(template.directives, directives);
|
||||||
|
|
||||||
|
return directives;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _flattenList(tree: List<any>, out: List<Type | Binding | List<any>>): void {
|
||||||
|
for (var i = 0; i < tree.length; i++) {
|
||||||
|
var item = resolveForwardRef(tree[i]);
|
||||||
|
if (ListWrapper.isList(item)) {
|
||||||
|
this._flattenList(item, out);
|
||||||
|
} else {
|
||||||
|
ListWrapper.push(out, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _isValidDirective(value: Type | Binding): boolean {
|
||||||
|
return isPresent(value) && (value instanceof Type || value instanceof Binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _assertTypeIsComponent(directiveBinding: DirectiveBinding): void {
|
||||||
|
if (directiveBinding.metadata.type !== renderApi.DirectiveMetadata.COMPONENT_TYPE) {
|
||||||
|
throw new BaseException(
|
||||||
|
`Could not load '${stringify(directiveBinding.key.token)}' because it is not a component.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/di';
|
||||||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
import {Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
@ -8,13 +8,11 @@ export class ComponentUrlMapper {
|
|||||||
// The returned URL could be:
|
// The returned URL could be:
|
||||||
// - an absolute URL,
|
// - an absolute URL,
|
||||||
// - a path relative to the application
|
// - a path relative to the application
|
||||||
getUrl(component: Type): string {
|
getUrl(component: Type): string { return './'; }
|
||||||
return './';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RuntimeComponentUrlMapper extends ComponentUrlMapper {
|
export class RuntimeComponentUrlMapper extends ComponentUrlMapper {
|
||||||
_componentUrls: Map;
|
_componentUrls: Map<Type, string>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
@ -1,15 +1,14 @@
|
|||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {resolveForwardRef, Injectable} from 'angular2/di';
|
||||||
import {resolveForwardRef} from 'angular2/di';
|
|
||||||
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
import {Type, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||||
import {Directive} from '../annotations_impl/annotations';
|
import {Directive} from '../annotations_impl/annotations';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DirectiveResolver {
|
export class DirectiveResolver {
|
||||||
resolve(type:Type):Directive {
|
resolve(type: Type): Directive {
|
||||||
var annotations = reflector.annotations(resolveForwardRef(type));
|
var annotations = reflector.annotations(resolveForwardRef(type));
|
||||||
if (isPresent(annotations)) {
|
if (isPresent(annotations)) {
|
||||||
for (var i=0; i<annotations.length; i++) {
|
for (var i = 0; i < annotations.length; i++) {
|
||||||
var annotation = annotations[i];
|
var annotation = annotations[i];
|
||||||
|
|
||||||
if (annotation instanceof Directive) {
|
if (annotation instanceof Directive) {
|
||||||
@ -19,5 +18,4 @@ export class DirectiveResolver {
|
|||||||
}
|
}
|
||||||
throw new BaseException(`No Directive annotation found on ${stringify(type)}`);
|
throw new BaseException(`No Directive annotation found on ${stringify(type)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,132 +0,0 @@
|
|||||||
import {Key, Injector, ResolvedBinding, Binding, bind} from 'angular2/di'
|
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {Compiler} from './compiler';
|
|
||||||
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
|
|
||||||
import {Promise} from 'angular2/src/facade/async';
|
|
||||||
import {AppViewManager, ComponentCreateResult} from 'angular2/src/core/compiler/view_manager';
|
|
||||||
import {ElementRef} from './element_ref';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @exportedAs angular2/view
|
|
||||||
*/
|
|
||||||
export class ComponentRef {
|
|
||||||
location:ElementRef;
|
|
||||||
instance:any;
|
|
||||||
_dispose:Function;
|
|
||||||
|
|
||||||
constructor(location:ElementRef, instance:any, dispose:Function) {
|
|
||||||
this.location = location;
|
|
||||||
this.instance = instance;
|
|
||||||
this._dispose = dispose;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hostView() {
|
|
||||||
return this.location.parentView;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this._dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Service for dynamically loading a Component into an arbitrary position in the internal Angular
|
|
||||||
* application tree.
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/view
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class DynamicComponentLoader {
|
|
||||||
_compiler:Compiler;
|
|
||||||
_viewManager:AppViewManager;
|
|
||||||
|
|
||||||
constructor(compiler:Compiler,
|
|
||||||
viewManager: AppViewManager) {
|
|
||||||
this._compiler = compiler;
|
|
||||||
this._viewManager = viewManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a component into the location given by the provided ElementRef. The loaded component
|
|
||||||
* receives injection as if it in the place of the provided ElementRef.
|
|
||||||
*/
|
|
||||||
loadIntoExistingLocation(typeOrBinding, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
|
||||||
var binding = this._getBinding(typeOrBinding);
|
|
||||||
return this._compiler.compile(binding.token).then(componentProtoViewRef => {
|
|
||||||
this._viewManager.createDynamicComponentView(
|
|
||||||
location, componentProtoViewRef, binding, injector);
|
|
||||||
var component = this._viewManager.getComponent(location);
|
|
||||||
var dispose = () => {throw new BaseException("Not implemented");};
|
|
||||||
return new ComponentRef(location, component, dispose);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a root component that is placed at the first element that matches the
|
|
||||||
* component's selector.
|
|
||||||
* The loaded component receives injection normally as a hosted view.
|
|
||||||
*/
|
|
||||||
loadAsRoot(typeOrBinding, overrideSelector = null, injector:Injector = null):Promise<ComponentRef> {
|
|
||||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
|
||||||
var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
|
||||||
var newLocation = new ElementRef(hostViewRef, 0);
|
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
|
||||||
|
|
||||||
var dispose = () => {
|
|
||||||
this._viewManager.destroyRootHostView(hostViewRef);
|
|
||||||
};
|
|
||||||
return new ComponentRef(newLocation, component, dispose);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a component into a free host view that is not yet attached to
|
|
||||||
* a parent on the render side, although it is attached to a parent in the injector hierarchy.
|
|
||||||
* The loaded component receives injection normally as a hosted view.
|
|
||||||
*/
|
|
||||||
loadIntoNewLocation(typeOrBinding, parentComponentLocation:ElementRef,
|
|
||||||
injector:Injector = null):Promise<ComponentRef> {
|
|
||||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
|
||||||
var hostViewRef = this._viewManager.createFreeHostView(
|
|
||||||
parentComponentLocation, hostProtoViewRef, injector);
|
|
||||||
var newLocation = new ElementRef(hostViewRef, 0);
|
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
|
||||||
|
|
||||||
var dispose = () => {
|
|
||||||
this._viewManager.destroyFreeHostView(parentComponentLocation, hostViewRef);
|
|
||||||
};
|
|
||||||
return new ComponentRef(newLocation, component, dispose);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a component next to the provided ElementRef. The loaded component receives
|
|
||||||
* injection normally as a hosted view.
|
|
||||||
*/
|
|
||||||
loadNextToExistingLocation(typeOrBinding, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
|
||||||
var binding = this._getBinding(typeOrBinding);
|
|
||||||
return this._compiler.compileInHost(binding).then(hostProtoViewRef => {
|
|
||||||
var viewContainer = this._viewManager.getViewContainer(location);
|
|
||||||
var hostViewRef = viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
|
|
||||||
var newLocation = new ElementRef(hostViewRef, 0);
|
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
|
||||||
|
|
||||||
var dispose = () => {
|
|
||||||
var index = viewContainer.indexOf(hostViewRef);
|
|
||||||
viewContainer.remove(index);
|
|
||||||
};
|
|
||||||
return new ComponentRef(newLocation, component, dispose);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_getBinding(typeOrBinding) {
|
|
||||||
var binding;
|
|
||||||
if (typeOrBinding instanceof Binding) {
|
|
||||||
binding = typeOrBinding;
|
|
||||||
} else {
|
|
||||||
binding = bind(typeOrBinding).toClass(typeOrBinding);
|
|
||||||
}
|
|
||||||
return binding;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
118
modules/angular2/src/core/compiler/dynamic_component_loader.ts
Normal file
118
modules/angular2/src/core/compiler/dynamic_component_loader.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import {Key, Injector, ResolvedBinding, Binding, bind, Injectable} from 'angular2/di';
|
||||||
|
import {Compiler} from './compiler';
|
||||||
|
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {Promise} from 'angular2/src/facade/async';
|
||||||
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
|
import {ElementRef} from './element_ref';
|
||||||
|
import {ViewRef} from './view_ref';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @exportedAs angular2/view
|
||||||
|
*/
|
||||||
|
export class ComponentRef {
|
||||||
|
constructor(public location: ElementRef, public instance: any, public dispose: Function) {}
|
||||||
|
|
||||||
|
get hostView(): ViewRef { return this.location.parentView; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for dynamically loading a Component into an arbitrary position in the internal Angular
|
||||||
|
* application tree.
|
||||||
|
*
|
||||||
|
* @exportedAs angular2/view
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class DynamicComponentLoader {
|
||||||
|
private _compiler: Compiler;
|
||||||
|
private _viewManager: AppViewManager;
|
||||||
|
|
||||||
|
constructor(compiler: Compiler, viewManager: AppViewManager) {
|
||||||
|
this._compiler = compiler;
|
||||||
|
this._viewManager = viewManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a component into the location given by the provided ElementRef. The loaded component
|
||||||
|
* receives injection as if it in the place of the provided ElementRef.
|
||||||
|
*/
|
||||||
|
loadIntoExistingLocation(typeOrBinding, location: ElementRef,
|
||||||
|
injector: Injector = null): Promise<ComponentRef> {
|
||||||
|
var binding = this._getBinding(typeOrBinding);
|
||||||
|
return this._compiler.compile(binding.token).then(componentProtoViewRef => {
|
||||||
|
this._viewManager.createDynamicComponentView(location, componentProtoViewRef, binding,
|
||||||
|
injector);
|
||||||
|
var component = this._viewManager.getComponent(location);
|
||||||
|
var dispose = () => { throw new BaseException("Not implemented");};
|
||||||
|
return new ComponentRef(location, component, dispose);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a root component that is placed at the first element that matches the
|
||||||
|
* component's selector.
|
||||||
|
* The loaded component receives injection normally as a hosted view.
|
||||||
|
*/
|
||||||
|
loadAsRoot(typeOrBinding, overrideSelector = null,
|
||||||
|
injector: Injector = null): Promise<ComponentRef> {
|
||||||
|
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
||||||
|
var hostViewRef =
|
||||||
|
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
||||||
|
var newLocation = new ElementRef(hostViewRef, 0);
|
||||||
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
|
var dispose = () => { this._viewManager.destroyRootHostView(hostViewRef);
|
||||||
|
};
|
||||||
|
return new ComponentRef(newLocation, component, dispose);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a component into a free host view that is not yet attached to
|
||||||
|
* a parent on the render side, although it is attached to a parent in the injector hierarchy.
|
||||||
|
* The loaded component receives injection normally as a hosted view.
|
||||||
|
*/
|
||||||
|
loadIntoNewLocation(typeOrBinding, parentComponentLocation: ElementRef,
|
||||||
|
injector: Injector = null): Promise<ComponentRef> {
|
||||||
|
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoViewRef => {
|
||||||
|
var hostViewRef =
|
||||||
|
this._viewManager.createFreeHostView(parentComponentLocation, hostProtoViewRef, injector);
|
||||||
|
var newLocation = new ElementRef(hostViewRef, 0);
|
||||||
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
|
var dispose = () => {
|
||||||
|
this._viewManager.destroyFreeHostView(parentComponentLocation, hostViewRef);
|
||||||
|
};
|
||||||
|
return new ComponentRef(newLocation, component, dispose);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a component next to the provided ElementRef. The loaded component receives
|
||||||
|
* injection normally as a hosted view.
|
||||||
|
*/
|
||||||
|
loadNextToExistingLocation(typeOrBinding, location: ElementRef,
|
||||||
|
injector: Injector = null): Promise<ComponentRef> {
|
||||||
|
var binding = this._getBinding(typeOrBinding);
|
||||||
|
return this._compiler.compileInHost(binding).then(hostProtoViewRef => {
|
||||||
|
var viewContainer = this._viewManager.getViewContainer(location);
|
||||||
|
var hostViewRef = viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
|
||||||
|
var newLocation = new ElementRef(hostViewRef, 0);
|
||||||
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
|
var dispose = () => { var index = viewContainer.indexOf(hostViewRef);
|
||||||
|
viewContainer.remove(index);
|
||||||
|
};
|
||||||
|
return new ComponentRef(newLocation, component, dispose);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getBinding(typeOrBinding): Binding {
|
||||||
|
var binding;
|
||||||
|
if (typeOrBinding instanceof Binding) {
|
||||||
|
binding = typeOrBinding;
|
||||||
|
} else {
|
||||||
|
binding = bind(typeOrBinding).toClass(typeOrBinding);
|
||||||
|
}
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import {AST} from 'angular2/change_detection';
|
||||||
import {int, isBlank, isPresent, BaseException} from 'angular2/src/facade/lang';
|
import {int, isBlank, isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||||
import * as eiModule from './element_injector';
|
import * as eiModule from './element_injector';
|
||||||
import {DirectiveBinding} from './element_injector';
|
import {DirectiveBinding} from './element_injector';
|
||||||
@ -5,25 +6,14 @@ import {List, StringMap} from 'angular2/src/facade/collection';
|
|||||||
import * as viewModule from './view';
|
import * as viewModule from './view';
|
||||||
|
|
||||||
export class ElementBinder {
|
export class ElementBinder {
|
||||||
protoElementInjector:eiModule.ProtoElementInjector;
|
|
||||||
componentDirective:DirectiveBinding;
|
|
||||||
nestedProtoView: viewModule.AppProtoView;
|
nestedProtoView: viewModule.AppProtoView;
|
||||||
hostListeners:StringMap;
|
hostListeners: StringMap<string, Map<number, AST>>;
|
||||||
parent:ElementBinder;
|
constructor(public index: int, public parent: ElementBinder, public distanceToParent: int,
|
||||||
index:int;
|
public protoElementInjector: eiModule.ProtoElementInjector,
|
||||||
distanceToParent:int;
|
public componentDirective: DirectiveBinding) {
|
||||||
constructor(
|
|
||||||
index:int, parent:ElementBinder, distanceToParent: int,
|
|
||||||
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveBinding) {
|
|
||||||
if (isBlank(index)) {
|
if (isBlank(index)) {
|
||||||
throw new BaseException('null index not allowed.');
|
throw new BaseException('null index not allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.protoElementInjector = protoElementInjector;
|
|
||||||
this.componentDirective = componentDirective;
|
|
||||||
this.parent = parent;
|
|
||||||
this.index = index;
|
|
||||||
this.distanceToParent = distanceToParent;
|
|
||||||
// updated later when events are bound
|
// updated later when events are bound
|
||||||
this.hostListeners = null;
|
this.hostListeners = null;
|
||||||
// updated later, so we are able to resolve cycles
|
// updated later, so we are able to resolve cycles
|
File diff suppressed because it is too large
Load Diff
@ -7,10 +7,10 @@ import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
|||||||
* @exportedAs angular2/view
|
* @exportedAs angular2/view
|
||||||
*/
|
*/
|
||||||
export class ElementRef {
|
export class ElementRef {
|
||||||
parentView:ViewRef;
|
parentView: ViewRef;
|
||||||
boundElementIndex:number;
|
boundElementIndex: number;
|
||||||
|
|
||||||
constructor(parentView:ViewRef, boundElementIndex:number) {
|
constructor(parentView: ViewRef, boundElementIndex: number) {
|
||||||
this.parentView = parentView;
|
this.parentView = parentView;
|
||||||
this.boundElementIndex = boundElementIndex;
|
this.boundElementIndex = boundElementIndex;
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ export class ElementRef {
|
|||||||
// TODO(tbosch): Here we expose the real DOM element.
|
// TODO(tbosch): Here we expose the real DOM element.
|
||||||
// We need a more general way to read/write to the DOM element
|
// We need a more general way to read/write to the DOM element
|
||||||
// via a proper abstraction in the render layer
|
// via a proper abstraction in the render layer
|
||||||
getAttribute(name:string):string {
|
getAttribute(name: string): string {
|
||||||
return normalizeBlank(DOM.getAttribute(this.domElement, name));
|
return normalizeBlank(DOM.getAttribute(this.domElement, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +0,0 @@
|
|||||||
/**
|
|
||||||
* @exportedAs angular2/angular2
|
|
||||||
*/
|
|
||||||
export class OnChange {
|
|
||||||
onChange(changes) {
|
|
||||||
throw "OnChange.onChange is not implemented";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +1,61 @@
|
|||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/di';
|
||||||
|
|
||||||
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeDetection, DirectiveIndex, BindingRecord, DirectiveRecord,
|
ChangeDetection,
|
||||||
ProtoChangeDetector, DEFAULT, ChangeDetectorDefinition
|
DirectiveIndex,
|
||||||
|
BindingRecord,
|
||||||
|
DirectiveRecord,
|
||||||
|
ProtoChangeDetector,
|
||||||
|
DEFAULT,
|
||||||
|
ChangeDetectorDefinition
|
||||||
} from 'angular2/change_detection';
|
} from 'angular2/change_detection';
|
||||||
|
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
import {AppProtoView} from './view';
|
import {AppProtoView} from './view';
|
||||||
|
import {ElementBinder} from './element_binder';
|
||||||
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
|
import {ProtoElementInjector, DirectiveBinding} from './element_injector';
|
||||||
|
|
||||||
class BindingRecordsCreator {
|
class BindingRecordsCreator {
|
||||||
_directiveRecordsMap;
|
_directiveRecordsMap: Map<number, DirectiveRecord>;
|
||||||
_textNodeIndex:number;
|
_textNodeIndex: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._directiveRecordsMap = MapWrapper.create();
|
this._directiveRecordsMap = MapWrapper.create();
|
||||||
this._textNodeIndex = 0;
|
this._textNodeIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBindingRecords(elementBinders:List<renderApi.ElementBinder>,
|
getBindingRecords(elementBinders: List<renderApi.ElementBinder>,
|
||||||
allDirectiveMetadatas:List<renderApi.DirectiveMetadata>
|
allDirectiveMetadatas: List<renderApi.DirectiveMetadata>): List<BindingRecord> {
|
||||||
):List<BindingRecord> {
|
|
||||||
var bindings = [];
|
var bindings = [];
|
||||||
|
|
||||||
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; boundElementIndex++) {
|
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length;
|
||||||
|
boundElementIndex++) {
|
||||||
var renderElementBinder = elementBinders[boundElementIndex];
|
var renderElementBinder = elementBinders[boundElementIndex];
|
||||||
this._createTextNodeRecords(bindings, renderElementBinder);
|
this._createTextNodeRecords(bindings, renderElementBinder);
|
||||||
this._createElementPropertyRecords(bindings, boundElementIndex, renderElementBinder);
|
this._createElementPropertyRecords(bindings, boundElementIndex, renderElementBinder);
|
||||||
this._createDirectiveRecords(bindings, boundElementIndex,
|
this._createDirectiveRecords(bindings, boundElementIndex, renderElementBinder.directives,
|
||||||
renderElementBinder.directives, allDirectiveMetadatas);
|
allDirectiveMetadatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveRecords(
|
getDirectiveRecords(
|
||||||
elementBinders:List<renderApi.ElementBinder>,
|
elementBinders: List<renderApi.ElementBinder>,
|
||||||
allDirectiveMetadatas:List<renderApi.DirectiveMetadata>): List<DirectiveRecord> {
|
allDirectiveMetadatas: List<renderApi.DirectiveMetadata>): List<DirectiveRecord> {
|
||||||
var directiveRecords = [];
|
var directiveRecords = [];
|
||||||
|
|
||||||
for (var elementIndex = 0; elementIndex < elementBinders.length; ++elementIndex) {
|
for (var elementIndex = 0; elementIndex < elementBinders.length; ++elementIndex) {
|
||||||
var dirs = elementBinders[elementIndex].directives;
|
var dirs = elementBinders[elementIndex].directives;
|
||||||
for (var dirIndex = 0; dirIndex < dirs.length; ++dirIndex) {
|
for (var dirIndex = 0; dirIndex < dirs.length; ++dirIndex) {
|
||||||
ListWrapper.push(directiveRecords,
|
ListWrapper.push(
|
||||||
this._getDirectiveRecord(
|
directiveRecords,
|
||||||
elementIndex, dirIndex, allDirectiveMetadatas[dirs[dirIndex].directiveIndex]));
|
this._getDirectiveRecord(elementIndex, dirIndex,
|
||||||
|
allDirectiveMetadatas[dirs[dirIndex].directiveIndex]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +63,7 @@ class BindingRecordsCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_createTextNodeRecords(bindings: List<BindingRecord>,
|
_createTextNodeRecords(bindings: List<BindingRecord>,
|
||||||
renderElementBinder: renderApi.ElementBinder) {
|
renderElementBinder: renderApi.ElementBinder) {
|
||||||
if (isBlank(renderElementBinder.textBindings)) return;
|
if (isBlank(renderElementBinder.textBindings)) return;
|
||||||
|
|
||||||
ListWrapper.forEach(renderElementBinder.textBindings, (b) => {
|
ListWrapper.forEach(renderElementBinder.textBindings, (b) => {
|
||||||
@ -64,17 +71,17 @@ class BindingRecordsCreator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_createElementPropertyRecords(bindings: List<BindingRecord>,
|
_createElementPropertyRecords(bindings: List<BindingRecord>, boundElementIndex: number,
|
||||||
boundElementIndex:number, renderElementBinder:renderApi.ElementBinder) {
|
renderElementBinder: renderApi.ElementBinder) {
|
||||||
MapWrapper.forEach(renderElementBinder.propertyBindings, (astWithSource, propertyName) => {
|
MapWrapper.forEach(renderElementBinder.propertyBindings, (astWithSource, propertyName) => {
|
||||||
ListWrapper.push(bindings,
|
ListWrapper.push(
|
||||||
BindingRecord.createForElement(astWithSource, boundElementIndex, propertyName));
|
bindings, BindingRecord.createForElement(astWithSource, boundElementIndex, propertyName));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_createDirectiveRecords(bindings: List<BindingRecord>,
|
_createDirectiveRecords(bindings: List<BindingRecord>, boundElementIndex: number,
|
||||||
boundElementIndex:number, directiveBinders:List<renderApi.DirectiveBinder>,
|
directiveBinders: List<renderApi.DirectiveBinder>,
|
||||||
allDirectiveMetadatas:List<renderApi.DirectiveMetadata>) {
|
allDirectiveMetadatas: List<renderApi.DirectiveMetadata>) {
|
||||||
for (var i = 0; i < directiveBinders.length; i++) {
|
for (var i = 0; i < directiveBinders.length; i++) {
|
||||||
var directiveBinder = directiveBinders[i];
|
var directiveBinder = directiveBinders[i];
|
||||||
var directiveMetadata = allDirectiveMetadatas[directiveBinder.directiveIndex];
|
var directiveMetadata = allDirectiveMetadatas[directiveBinder.directiveIndex];
|
||||||
@ -85,28 +92,30 @@ class BindingRecordsCreator {
|
|||||||
// it monomorphic!
|
// it monomorphic!
|
||||||
var setter = reflector.setter(propertyName);
|
var setter = reflector.setter(propertyName);
|
||||||
var directiveRecord = this._getDirectiveRecord(boundElementIndex, i, directiveMetadata);
|
var directiveRecord = this._getDirectiveRecord(boundElementIndex, i, directiveMetadata);
|
||||||
ListWrapper.push(bindings,
|
ListWrapper.push(bindings, BindingRecord.createForDirective(astWithSource, propertyName,
|
||||||
BindingRecord.createForDirective(astWithSource, propertyName, setter, directiveRecord));
|
setter, directiveRecord));
|
||||||
});
|
});
|
||||||
|
|
||||||
// host properties
|
// host properties
|
||||||
MapWrapper.forEach(directiveBinder.hostPropertyBindings, (astWithSource, propertyName) => {
|
MapWrapper.forEach(directiveBinder.hostPropertyBindings, (astWithSource, propertyName) => {
|
||||||
var dirIndex = new DirectiveIndex(boundElementIndex, i);
|
var dirIndex = new DirectiveIndex(boundElementIndex, i);
|
||||||
ListWrapper.push(bindings,
|
ListWrapper.push(
|
||||||
BindingRecord.createForHostProperty(dirIndex, astWithSource, propertyName));
|
bindings, BindingRecord.createForHostProperty(dirIndex, astWithSource, propertyName));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDirectiveRecord(boundElementIndex:number, directiveIndex:number, directiveMetadata:renderApi.DirectiveMetadata): DirectiveRecord {
|
_getDirectiveRecord(boundElementIndex: number, directiveIndex: number,
|
||||||
|
directiveMetadata: renderApi.DirectiveMetadata): DirectiveRecord {
|
||||||
var id = boundElementIndex * 100 + directiveIndex;
|
var id = boundElementIndex * 100 + directiveIndex;
|
||||||
|
|
||||||
if (!MapWrapper.contains(this._directiveRecordsMap, id)) {
|
if (!MapWrapper.contains(this._directiveRecordsMap, id)) {
|
||||||
var changeDetection = directiveMetadata.changeDetection;
|
var changeDetection = directiveMetadata.changeDetection;
|
||||||
|
|
||||||
MapWrapper.set(this._directiveRecordsMap, id,
|
MapWrapper.set(this._directiveRecordsMap, id,
|
||||||
new DirectiveRecord(new DirectiveIndex(boundElementIndex, directiveIndex),
|
new DirectiveRecord(new DirectiveIndex(boundElementIndex, directiveIndex),
|
||||||
directiveMetadata.callOnAllChangesDone, directiveMetadata.callOnChange, changeDetection));
|
directiveMetadata.callOnAllChangesDone,
|
||||||
|
directiveMetadata.callOnChange, changeDetection));
|
||||||
}
|
}
|
||||||
|
|
||||||
return MapWrapper.get(this._directiveRecordsMap, id);
|
return MapWrapper.get(this._directiveRecordsMap, id);
|
||||||
@ -117,30 +126,28 @@ class BindingRecordsCreator {
|
|||||||
export class ProtoViewFactory {
|
export class ProtoViewFactory {
|
||||||
_changeDetection: ChangeDetection;
|
_changeDetection: ChangeDetection;
|
||||||
|
|
||||||
constructor(changeDetection: ChangeDetection) {
|
constructor(changeDetection: ChangeDetection) { this._changeDetection = changeDetection; }
|
||||||
this._changeDetection = changeDetection;
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppProtoViews(hostComponentBinding:DirectiveBinding,
|
createAppProtoViews(hostComponentBinding: DirectiveBinding,
|
||||||
rootRenderProtoView: renderApi.ProtoViewDto, allDirectives:List<DirectiveBinding>):List<AppProtoView> {
|
rootRenderProtoView: renderApi.ProtoViewDto,
|
||||||
var allRenderDirectiveMetadata = ListWrapper.map(allDirectives, directiveBinding => directiveBinding.metadata);
|
allDirectives: List<DirectiveBinding>): List<AppProtoView> {
|
||||||
|
var allRenderDirectiveMetadata =
|
||||||
|
ListWrapper.map(allDirectives, directiveBinding => directiveBinding.metadata);
|
||||||
var nestedPvsWithIndex = _collectNestedProtoViews(rootRenderProtoView);
|
var nestedPvsWithIndex = _collectNestedProtoViews(rootRenderProtoView);
|
||||||
var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
|
var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
|
||||||
var nestedPvVariableNames = _collectNestedProtoViewsVariableNames(nestedPvsWithIndex, nestedPvVariableBindings);
|
var nestedPvVariableNames =
|
||||||
var changeDetectorDefs = _getChangeDetectorDefinitions(
|
_collectNestedProtoViewsVariableNames(nestedPvsWithIndex, nestedPvVariableBindings);
|
||||||
hostComponentBinding.metadata, nestedPvsWithIndex, nestedPvVariableNames, allRenderDirectiveMetadata
|
var changeDetectorDefs =
|
||||||
);
|
_getChangeDetectorDefinitions(hostComponentBinding.metadata, nestedPvsWithIndex,
|
||||||
|
nestedPvVariableNames, allRenderDirectiveMetadata);
|
||||||
var protoChangeDetectors = ListWrapper.map(
|
var protoChangeDetectors = ListWrapper.map(
|
||||||
changeDetectorDefs, changeDetectorDef => this._changeDetection.createProtoChangeDetector(changeDetectorDef)
|
changeDetectorDefs,
|
||||||
);
|
changeDetectorDef => this._changeDetection.createProtoChangeDetector(changeDetectorDef));
|
||||||
var appProtoViews = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
|
var appProtoViews = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
|
||||||
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex) => {
|
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
var appProtoView = _createAppProtoView(
|
var appProtoView =
|
||||||
pvWithIndex.renderProtoView,
|
_createAppProtoView(pvWithIndex.renderProtoView, protoChangeDetectors[pvWithIndex.index],
|
||||||
protoChangeDetectors[pvWithIndex.index],
|
nestedPvVariableBindings[pvWithIndex.index], allDirectives);
|
||||||
nestedPvVariableBindings[pvWithIndex.index],
|
|
||||||
allDirectives
|
|
||||||
);
|
|
||||||
if (isPresent(pvWithIndex.parentIndex)) {
|
if (isPresent(pvWithIndex.parentIndex)) {
|
||||||
var parentView = appProtoViews[pvWithIndex.parentIndex];
|
var parentView = appProtoViews[pvWithIndex.parentIndex];
|
||||||
parentView.elementBinders[pvWithIndex.boundElementIndex].nestedProtoView = appProtoView;
|
parentView.elementBinders[pvWithIndex.boundElementIndex].nestedProtoView = appProtoView;
|
||||||
@ -156,34 +163,31 @@ export class ProtoViewFactory {
|
|||||||
* for the given ProtoView and all nested ProtoViews.
|
* for the given ProtoView and all nested ProtoViews.
|
||||||
*/
|
*/
|
||||||
export function getChangeDetectorDefinitions(
|
export function getChangeDetectorDefinitions(
|
||||||
hostComponentMetadata:renderApi.DirectiveMetadata,
|
hostComponentMetadata: renderApi.DirectiveMetadata, rootRenderProtoView: renderApi.ProtoViewDto,
|
||||||
rootRenderProtoView: renderApi.ProtoViewDto,
|
allRenderDirectiveMetadata: List<renderApi.DirectiveMetadata>): List<ChangeDetectorDefinition> {
|
||||||
allRenderDirectiveMetadata:List<renderApi.DirectiveMetadata>): List<ChangeDetectorDefinition> {
|
|
||||||
var nestedPvsWithIndex = _collectNestedProtoViews(rootRenderProtoView);
|
var nestedPvsWithIndex = _collectNestedProtoViews(rootRenderProtoView);
|
||||||
var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
|
var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
|
||||||
var nestedPvVariableNames = _collectNestedProtoViewsVariableNames(nestedPvsWithIndex, nestedPvVariableBindings);
|
var nestedPvVariableNames =
|
||||||
|
_collectNestedProtoViewsVariableNames(nestedPvsWithIndex, nestedPvVariableBindings);
|
||||||
|
|
||||||
return _getChangeDetectorDefinitions(
|
return _getChangeDetectorDefinitions(hostComponentMetadata, nestedPvsWithIndex,
|
||||||
hostComponentMetadata,
|
nestedPvVariableNames, allRenderDirectiveMetadata);
|
||||||
nestedPvsWithIndex,
|
|
||||||
nestedPvVariableNames,
|
|
||||||
allRenderDirectiveMetadata
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _collectNestedProtoViews(renderProtoView:renderApi.ProtoViewDto,
|
function _collectNestedProtoViews(
|
||||||
parentIndex:number = null,
|
renderProtoView: renderApi.ProtoViewDto, parentIndex: number = null, boundElementIndex = null,
|
||||||
boundElementIndex = null,
|
result: List<RenderProtoViewWithIndex> = null): List<RenderProtoViewWithIndex> {
|
||||||
result:List<RenderProtoViewWithIndex> = null): List<RenderProtoViewWithIndex> {
|
|
||||||
if (isBlank(result)) {
|
if (isBlank(result)) {
|
||||||
result = [];
|
result = [];
|
||||||
}
|
}
|
||||||
ListWrapper.push(result, new RenderProtoViewWithIndex(renderProtoView, result.length, parentIndex, boundElementIndex));
|
ListWrapper.push(result, new RenderProtoViewWithIndex(renderProtoView, result.length, parentIndex,
|
||||||
|
boundElementIndex));
|
||||||
var currentIndex = result.length - 1;
|
var currentIndex = result.length - 1;
|
||||||
var childBoundElementIndex = 0;
|
var childBoundElementIndex = 0;
|
||||||
ListWrapper.forEach(renderProtoView.elementBinders, (elementBinder) => {
|
ListWrapper.forEach(renderProtoView.elementBinders, (elementBinder) => {
|
||||||
if (isPresent(elementBinder.nestedProtoView)) {
|
if (isPresent(elementBinder.nestedProtoView)) {
|
||||||
_collectNestedProtoViews(elementBinder.nestedProtoView, currentIndex, childBoundElementIndex, result);
|
_collectNestedProtoViews(elementBinder.nestedProtoView, currentIndex, childBoundElementIndex,
|
||||||
|
result);
|
||||||
}
|
}
|
||||||
childBoundElementIndex++;
|
childBoundElementIndex++;
|
||||||
});
|
});
|
||||||
@ -191,15 +195,16 @@ function _collectNestedProtoViews(renderProtoView:renderApi.ProtoViewDto,
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _getChangeDetectorDefinitions(
|
function _getChangeDetectorDefinitions(
|
||||||
hostComponentMetadata:renderApi.DirectiveMetadata,
|
hostComponentMetadata: renderApi.DirectiveMetadata,
|
||||||
nestedPvsWithIndex: List<RenderProtoViewWithIndex>,
|
nestedPvsWithIndex: List<RenderProtoViewWithIndex>, nestedPvVariableNames: List<List<string>>,
|
||||||
nestedPvVariableNames: List<List<string>>,
|
allRenderDirectiveMetadata: List<renderApi.DirectiveMetadata>): List<ChangeDetectorDefinition> {
|
||||||
allRenderDirectiveMetadata:List<renderApi.DirectiveMetadata>):List<ChangeDetectorDefinition> {
|
|
||||||
return ListWrapper.map(nestedPvsWithIndex, (pvWithIndex) => {
|
return ListWrapper.map(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
var elementBinders = pvWithIndex.renderProtoView.elementBinders;
|
var elementBinders = pvWithIndex.renderProtoView.elementBinders;
|
||||||
var bindingRecordsCreator = new BindingRecordsCreator();
|
var bindingRecordsCreator = new BindingRecordsCreator();
|
||||||
var bindingRecords = bindingRecordsCreator.getBindingRecords(elementBinders, allRenderDirectiveMetadata);
|
var bindingRecords =
|
||||||
var directiveRecords = bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata);
|
bindingRecordsCreator.getBindingRecords(elementBinders, allRenderDirectiveMetadata);
|
||||||
|
var directiveRecords =
|
||||||
|
bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata);
|
||||||
var strategyName = DEFAULT;
|
var strategyName = DEFAULT;
|
||||||
var typeString;
|
var typeString;
|
||||||
if (pvWithIndex.renderProtoView.type === renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE) {
|
if (pvWithIndex.renderProtoView.type === renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE) {
|
||||||
@ -212,16 +217,14 @@ function _getChangeDetectorDefinitions(
|
|||||||
}
|
}
|
||||||
var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
|
var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
|
||||||
var variableNames = nestedPvVariableNames[pvWithIndex.index];
|
var variableNames = nestedPvVariableNames[pvWithIndex.index];
|
||||||
return new ChangeDetectorDefinition(id, strategyName, variableNames, bindingRecords, directiveRecords);
|
return new ChangeDetectorDefinition(id, strategyName, variableNames, bindingRecords,
|
||||||
|
directiveRecords);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createAppProtoView(
|
function _createAppProtoView(
|
||||||
renderProtoView: renderApi.ProtoViewDto,
|
renderProtoView: renderApi.ProtoViewDto, protoChangeDetector: ProtoChangeDetector,
|
||||||
protoChangeDetector: ProtoChangeDetector,
|
variableBindings: Map<string, string>, allDirectives: List<DirectiveBinding>): AppProtoView {
|
||||||
variableBindings: Map<string, string>,
|
|
||||||
allDirectives:List<DirectiveBinding>
|
|
||||||
):AppProtoView {
|
|
||||||
var elementBinders = renderProtoView.elementBinders;
|
var elementBinders = renderProtoView.elementBinders;
|
||||||
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector, variableBindings);
|
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector, variableBindings);
|
||||||
|
|
||||||
@ -233,14 +236,13 @@ function _createAppProtoView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _collectNestedProtoViewsVariableBindings(
|
function _collectNestedProtoViewsVariableBindings(
|
||||||
nestedPvsWithIndex: List<RenderProtoViewWithIndex>
|
nestedPvsWithIndex: List<RenderProtoViewWithIndex>): List<Map<string, string>> {
|
||||||
):List<Map<string, string>> {
|
|
||||||
return ListWrapper.map(nestedPvsWithIndex, (pvWithIndex) => {
|
return ListWrapper.map(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
return _createVariableBindings(pvWithIndex.renderProtoView);
|
return _createVariableBindings(pvWithIndex.renderProtoView);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createVariableBindings(renderProtoView):Map {
|
function _createVariableBindings(renderProtoView): Map<string, string> {
|
||||||
var variableBindings = MapWrapper.create();
|
var variableBindings = MapWrapper.create();
|
||||||
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
|
MapWrapper.forEach(renderProtoView.variableBindings, (mappedName, varName) => {
|
||||||
MapWrapper.set(variableBindings, varName, mappedName);
|
MapWrapper.set(variableBindings, varName, mappedName);
|
||||||
@ -255,48 +257,49 @@ function _createVariableBindings(renderProtoView):Map {
|
|||||||
|
|
||||||
function _collectNestedProtoViewsVariableNames(
|
function _collectNestedProtoViewsVariableNames(
|
||||||
nestedPvsWithIndex: List<RenderProtoViewWithIndex>,
|
nestedPvsWithIndex: List<RenderProtoViewWithIndex>,
|
||||||
nestedPvVariableBindings:List<Map<string, string>>
|
nestedPvVariableBindings: List<Map<string, string>>): List<List<string>> {
|
||||||
):List<List<string>> {
|
|
||||||
var nestedPvVariableNames = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
|
var nestedPvVariableNames = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
|
||||||
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex) => {
|
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex) => {
|
||||||
var parentVariableNames = isPresent(pvWithIndex.parentIndex) ? nestedPvVariableNames[pvWithIndex.parentIndex] : null;
|
var parentVariableNames =
|
||||||
nestedPvVariableNames[pvWithIndex.index] = _createVariableNames(
|
isPresent(pvWithIndex.parentIndex) ? nestedPvVariableNames[pvWithIndex.parentIndex] : null;
|
||||||
parentVariableNames, nestedPvVariableBindings[pvWithIndex.index]
|
nestedPvVariableNames[pvWithIndex.index] =
|
||||||
);
|
_createVariableNames(parentVariableNames, nestedPvVariableBindings[pvWithIndex.index]);
|
||||||
});
|
});
|
||||||
return nestedPvVariableNames;
|
return nestedPvVariableNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createVariableNames(parentVariableNames, variableBindings):List {
|
function _createVariableNames(parentVariableNames, variableBindings): List<string> {
|
||||||
var variableNames = isPresent(parentVariableNames) ? ListWrapper.clone(parentVariableNames) : [];
|
var variableNames = isPresent(parentVariableNames) ? ListWrapper.clone(parentVariableNames) : [];
|
||||||
MapWrapper.forEach(variableBindings, (local, v) => {
|
MapWrapper.forEach(variableBindings, (local, v) => { ListWrapper.push(variableNames, local); });
|
||||||
ListWrapper.push(variableNames, local);
|
|
||||||
});
|
|
||||||
return variableNames;
|
return variableNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createElementBinders(protoView, elementBinders, allDirectiveBindings) {
|
function _createElementBinders(protoView, elementBinders, allDirectiveBindings) {
|
||||||
for (var i=0; i<elementBinders.length; i++) {
|
for (var i = 0; i < elementBinders.length; i++) {
|
||||||
var renderElementBinder = elementBinders[i];
|
var renderElementBinder = elementBinders[i];
|
||||||
var dirs = elementBinders[i].directives;
|
var dirs = elementBinders[i].directives;
|
||||||
|
|
||||||
var parentPeiWithDistance = _findParentProtoElementInjectorWithDistance(
|
var parentPeiWithDistance =
|
||||||
i, protoView.elementBinders, elementBinders);
|
_findParentProtoElementInjectorWithDistance(i, protoView.elementBinders, elementBinders);
|
||||||
var directiveBindings = ListWrapper.map(dirs, (dir) => allDirectiveBindings[dir.directiveIndex] );
|
var directiveBindings =
|
||||||
|
ListWrapper.map(dirs, (dir) => allDirectiveBindings[dir.directiveIndex]);
|
||||||
var componentDirectiveBinding = null;
|
var componentDirectiveBinding = null;
|
||||||
if (directiveBindings.length > 0) {
|
if (directiveBindings.length > 0) {
|
||||||
if (directiveBindings[0].metadata.type === renderApi.DirectiveMetadata.COMPONENT_TYPE) {
|
if (directiveBindings[0].metadata.type === renderApi.DirectiveMetadata.COMPONENT_TYPE) {
|
||||||
componentDirectiveBinding = directiveBindings[0];
|
componentDirectiveBinding = directiveBindings[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var protoElementInjector = _createProtoElementInjector(
|
var protoElementInjector =
|
||||||
i, parentPeiWithDistance, renderElementBinder, componentDirectiveBinding, directiveBindings);
|
_createProtoElementInjector(i, parentPeiWithDistance, renderElementBinder,
|
||||||
|
componentDirectiveBinding, directiveBindings);
|
||||||
|
|
||||||
_createElementBinder(protoView, i, renderElementBinder, protoElementInjector, componentDirectiveBinding);
|
_createElementBinder(protoView, i, renderElementBinder, protoElementInjector,
|
||||||
|
componentDirectiveBinding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _findParentProtoElementInjectorWithDistance(binderIndex, elementBinders, renderElementBinders) {
|
function _findParentProtoElementInjectorWithDistance(
|
||||||
|
binderIndex, elementBinders, renderElementBinders): ParentProtoElementInjectorWithDistance {
|
||||||
var distance = 0;
|
var distance = 0;
|
||||||
do {
|
do {
|
||||||
var renderElementBinder = renderElementBinders[binderIndex];
|
var renderElementBinder = renderElementBinders[binderIndex];
|
||||||
@ -305,14 +308,16 @@ function _findParentProtoElementInjectorWithDistance(binderIndex, elementBinders
|
|||||||
distance += renderElementBinder.distanceToParent;
|
distance += renderElementBinder.distanceToParent;
|
||||||
var elementBinder = elementBinders[binderIndex];
|
var elementBinder = elementBinders[binderIndex];
|
||||||
if (isPresent(elementBinder.protoElementInjector)) {
|
if (isPresent(elementBinder.protoElementInjector)) {
|
||||||
return new ParentProtoElementInjectorWithDistance(elementBinder.protoElementInjector, distance);
|
return new ParentProtoElementInjectorWithDistance(elementBinder.protoElementInjector,
|
||||||
|
distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (binderIndex !== -1);
|
} while (binderIndex !== -1);
|
||||||
return new ParentProtoElementInjectorWithDistance(null, -1);
|
return new ParentProtoElementInjectorWithDistance(null, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createProtoElementInjector(binderIndex, parentPeiWithDistance, renderElementBinder, componentDirectiveBinding, directiveBindings) {
|
function _createProtoElementInjector(binderIndex, parentPeiWithDistance, renderElementBinder,
|
||||||
|
componentDirectiveBinding, directiveBindings) {
|
||||||
var protoElementInjector = null;
|
var protoElementInjector = null;
|
||||||
// Create a protoElementInjector for any element that either has bindings *or* has one
|
// Create a protoElementInjector for any element that either has bindings *or* has one
|
||||||
// or more var- defined. Elements with a var- defined need a their own element injector
|
// or more var- defined. Elements with a var- defined need a their own element injector
|
||||||
@ -320,10 +325,8 @@ function _createProtoElementInjector(binderIndex, parentPeiWithDistance, renderE
|
|||||||
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
|
var hasVariables = MapWrapper.size(renderElementBinder.variableBindings) > 0;
|
||||||
if (directiveBindings.length > 0 || hasVariables) {
|
if (directiveBindings.length > 0 || hasVariables) {
|
||||||
protoElementInjector = ProtoElementInjector.create(
|
protoElementInjector = ProtoElementInjector.create(
|
||||||
parentPeiWithDistance.protoElementInjector, binderIndex,
|
parentPeiWithDistance.protoElementInjector, binderIndex, directiveBindings,
|
||||||
directiveBindings,
|
isPresent(componentDirectiveBinding), parentPeiWithDistance.distance);
|
||||||
isPresent(componentDirectiveBinding), parentPeiWithDistance.distance
|
|
||||||
);
|
|
||||||
protoElementInjector.attributes = renderElementBinder.readAttributes;
|
protoElementInjector.attributes = renderElementBinder.readAttributes;
|
||||||
if (hasVariables) {
|
if (hasVariables) {
|
||||||
protoElementInjector.exportComponent = isPresent(componentDirectiveBinding);
|
protoElementInjector.exportComponent = isPresent(componentDirectiveBinding);
|
||||||
@ -339,17 +342,14 @@ function _createProtoElementInjector(binderIndex, parentPeiWithDistance, renderE
|
|||||||
return protoElementInjector;
|
return protoElementInjector;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createElementBinder(protoView, boundElementIndex, renderElementBinder, protoElementInjector, componentDirectiveBinding) {
|
function _createElementBinder(protoView, boundElementIndex, renderElementBinder,
|
||||||
|
protoElementInjector, componentDirectiveBinding): ElementBinder {
|
||||||
var parent = null;
|
var parent = null;
|
||||||
if (renderElementBinder.parentIndex !== -1) {
|
if (renderElementBinder.parentIndex !== -1) {
|
||||||
parent = protoView.elementBinders[renderElementBinder.parentIndex];
|
parent = protoView.elementBinders[renderElementBinder.parentIndex];
|
||||||
}
|
}
|
||||||
var elBinder = protoView.bindElement(
|
var elBinder = protoView.bindElement(parent, renderElementBinder.distanceToParent,
|
||||||
parent,
|
protoElementInjector, componentDirectiveBinding);
|
||||||
renderElementBinder.distanceToParent,
|
|
||||||
protoElementInjector,
|
|
||||||
componentDirectiveBinding
|
|
||||||
);
|
|
||||||
protoView.bindEvent(renderElementBinder.eventBindings, boundElementIndex, -1);
|
protoView.bindEvent(renderElementBinder.eventBindings, boundElementIndex, -1);
|
||||||
// variables
|
// variables
|
||||||
// The view's locals needs to have a full set of variable names at construction time
|
// The view's locals needs to have a full set of variable names at construction time
|
||||||
@ -362,7 +362,7 @@ function _createElementBinder(protoView, boundElementIndex, renderElementBinder,
|
|||||||
return elBinder;
|
return elBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _bindDirectiveEvents(protoView, elementBinders:List<renderApi.ElementBinder>) {
|
function _bindDirectiveEvents(protoView, elementBinders: List<renderApi.ElementBinder>) {
|
||||||
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; ++boundElementIndex) {
|
for (var boundElementIndex = 0; boundElementIndex < elementBinders.length; ++boundElementIndex) {
|
||||||
var dirs = elementBinders[boundElementIndex].directives;
|
var dirs = elementBinders[boundElementIndex].directives;
|
||||||
for (var i = 0; i < dirs.length; i++) {
|
for (var i = 0; i < dirs.length; i++) {
|
||||||
@ -376,11 +376,12 @@ function _bindDirectiveEvents(protoView, elementBinders:List<renderApi.ElementBi
|
|||||||
|
|
||||||
|
|
||||||
class RenderProtoViewWithIndex {
|
class RenderProtoViewWithIndex {
|
||||||
renderProtoView:renderApi.ProtoViewDto;
|
renderProtoView: renderApi.ProtoViewDto;
|
||||||
index:number;
|
index: number;
|
||||||
parentIndex:number;
|
parentIndex: number;
|
||||||
boundElementIndex:number;
|
boundElementIndex: number;
|
||||||
constructor(renderProtoView:renderApi.ProtoViewDto, index:number, parentIndex:number, boundElementIndex:number) {
|
constructor(renderProtoView: renderApi.ProtoViewDto, index: number, parentIndex: number,
|
||||||
|
boundElementIndex: number) {
|
||||||
this.renderProtoView = renderProtoView;
|
this.renderProtoView = renderProtoView;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.parentIndex = parentIndex;
|
this.parentIndex = parentIndex;
|
||||||
@ -389,9 +390,9 @@ class RenderProtoViewWithIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ParentProtoElementInjectorWithDistance {
|
class ParentProtoElementInjectorWithDistance {
|
||||||
protoElementInjector:ProtoElementInjector;
|
protoElementInjector: ProtoElementInjector;
|
||||||
distance:number;
|
distance: number;
|
||||||
constructor(protoElementInjector:ProtoElementInjector, distance:number) {
|
constructor(protoElementInjector: ProtoElementInjector, distance: number) {
|
||||||
this.protoElementInjector = protoElementInjector;
|
this.protoElementInjector = protoElementInjector;
|
||||||
this.distance = distance;
|
this.distance = distance;
|
||||||
}
|
}
|
@ -6,15 +6,18 @@ import {BaseQueryList} from './base_query_list';
|
|||||||
* Injectable Objects that contains a live list of child directives in the light DOM of a directive.
|
* Injectable Objects that contains a live list of child directives in the light DOM of a directive.
|
||||||
* The directives are kept in depth-first pre-order traversal of the DOM.
|
* The directives are kept in depth-first pre-order traversal of the DOM.
|
||||||
*
|
*
|
||||||
* The `QueryList` is iterable, therefore it can be used in both javascript code with `for..of` loop as well as in
|
* The `QueryList` is iterable, therefore it can be used in both javascript code with `for..of` loop
|
||||||
|
* as well as in
|
||||||
* template with `*ng-for="of"` directive.
|
* template with `*ng-for="of"` directive.
|
||||||
*
|
*
|
||||||
* NOTE: In the future this class will implement an `Observable` interface. For now it uses a plain list of observable
|
* NOTE: In the future this class will implement an `Observable` interface. For now it uses a plain
|
||||||
|
* list of observable
|
||||||
* callbacks.
|
* callbacks.
|
||||||
*
|
*
|
||||||
* # Example:
|
* # Example:
|
||||||
*
|
*
|
||||||
* Assume that `<tabs>` component would like to get a list its children which are `<pane>` components as shown in this
|
* Assume that `<tabs>` component would like to get a list its children which are `<pane>`
|
||||||
|
* components as shown in this
|
||||||
* example:
|
* example:
|
||||||
*
|
*
|
||||||
* ```html
|
* ```html
|
||||||
@ -24,15 +27,21 @@ import {BaseQueryList} from './base_query_list';
|
|||||||
* </tabs>
|
* </tabs>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* In the above example the list of `<tabs>` elements needs to get a list of `<pane>` elements so that it could render
|
* In the above example the list of `<tabs>` elements needs to get a list of `<pane>` elements so
|
||||||
|
* that it could render
|
||||||
* tabs with the correct titles and in the correct order.
|
* tabs with the correct titles and in the correct order.
|
||||||
*
|
*
|
||||||
* A possible solution would be for a `<pane>` to inject `<tabs>` component and then register itself with `<tabs>`
|
* A possible solution would be for a `<pane>` to inject `<tabs>` component and then register itself
|
||||||
* component's on `hydrate` and deregister on `dehydrate` event. While a reasonable approach, this would only work
|
* with `<tabs>`
|
||||||
* partialy since `*ng-for` could rearange the list of `<pane>` components which would not be reported to `<tabs>`
|
* component's on `hydrate` and deregister on `dehydrate` event. While a reasonable approach, this
|
||||||
* component and thus the list of `<pane>` componets would be out of sync with respect to the list of `<pane>` elements.
|
* would only work
|
||||||
|
* partialy since `*ng-for` could rearange the list of `<pane>` components which would not be
|
||||||
|
* reported to `<tabs>`
|
||||||
|
* component and thus the list of `<pane>` componets would be out of sync with respect to the list
|
||||||
|
* of `<pane>` elements.
|
||||||
*
|
*
|
||||||
* A preferred solution is to inject a `QueryList` which is a live list of directives in the component`s light DOM.
|
* A preferred solution is to inject a `QueryList` which is a live list of directives in the
|
||||||
|
* component`s light DOM.
|
||||||
*
|
*
|
||||||
* ```javascript
|
* ```javascript
|
||||||
* @Component({
|
* @Component({
|
||||||
@ -67,17 +76,11 @@ import {BaseQueryList} from './base_query_list';
|
|||||||
* @exportedAs angular2/view
|
* @exportedAs angular2/view
|
||||||
*/
|
*/
|
||||||
export class QueryList extends BaseQueryList {
|
export class QueryList extends BaseQueryList {
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
onChange(callback) { return super.onChange(callback); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
onChange(callback) {
|
removeCallback(callback) { return super.removeCallback(callback); }
|
||||||
return super.onChange(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
removeCallback(callback) {
|
|
||||||
return super.removeCallback(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/di';
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||||
|
|
||||||
import {Type, stringify, isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {Type, stringify, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
@ -9,11 +9,9 @@ import {reflector} from 'angular2/src/reflection/reflection';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TemplateResolver {
|
export class TemplateResolver {
|
||||||
_cache: Map;
|
_cache: Map<Type, /*node*/ any>;
|
||||||
|
|
||||||
constructor() {
|
constructor() { this._cache = MapWrapper.create(); }
|
||||||
this._cache = MapWrapper.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(component: Type): View {
|
resolve(component: Type): View {
|
||||||
var view = MapWrapper.get(this._cache, component);
|
var view = MapWrapper.get(this._cache, component);
|
@ -1,10 +1,24 @@
|
|||||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||||
import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
|
import {
|
||||||
ChangeRecord, BindingRecord, DirectiveRecord, DirectiveIndex, ChangeDetectorRef} from 'angular2/change_detection';
|
AST,
|
||||||
|
Locals,
|
||||||
|
ChangeDispatcher,
|
||||||
|
ProtoChangeDetector,
|
||||||
|
ChangeDetector,
|
||||||
|
BindingRecord,
|
||||||
|
DirectiveRecord,
|
||||||
|
DirectiveIndex,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from 'angular2/change_detection';
|
||||||
|
|
||||||
import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding} from './element_injector';
|
import {
|
||||||
|
ProtoElementInjector,
|
||||||
|
ElementInjector,
|
||||||
|
PreBuiltObjects,
|
||||||
|
DirectiveBinding
|
||||||
|
} from './element_injector';
|
||||||
import {ElementBinder} from './element_binder';
|
import {ElementBinder} from './element_binder';
|
||||||
import {IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
import {EventDispatcher} from 'angular2/src/render/api';
|
import {EventDispatcher} from 'angular2/src/render/api';
|
||||||
|
|
||||||
@ -21,22 +35,18 @@ export class AppViewContainer {
|
|||||||
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@IMPLEMENTS(ChangeDispatcher)
|
export class AppView implements ChangeDispatcher, EventDispatcher {
|
||||||
@IMPLEMENTS(EventDispatcher)
|
render: renderApi.RenderViewRef;
|
||||||
export class AppView {
|
|
||||||
render:renderApi.RenderViewRef;
|
|
||||||
/// This list matches the _nodes list. It is sparse, since only Elements have ElementInjector
|
/// This list matches the _nodes list. It is sparse, since only Elements have ElementInjector
|
||||||
rootElementInjectors:List<ElementInjector>;
|
rootElementInjectors: List<ElementInjector>;
|
||||||
elementInjectors:List<ElementInjector>;
|
elementInjectors: List<ElementInjector>;
|
||||||
changeDetector:ChangeDetector;
|
changeDetector: ChangeDetector;
|
||||||
componentChildViews: List<AppView>;
|
componentChildViews: List<AppView>;
|
||||||
/// Host views that were added by an imperative view.
|
/// Host views that were added by an imperative view.
|
||||||
/// This is a dynamically growing / shrinking array.
|
/// This is a dynamically growing / shrinking array.
|
||||||
freeHostViews: List<AppView>;
|
freeHostViews: List<AppView>;
|
||||||
viewContainers: List<AppViewContainer>;
|
viewContainers: List<AppViewContainer>;
|
||||||
preBuiltObjects: List<PreBuiltObjects>;
|
preBuiltObjects: List<PreBuiltObjects>;
|
||||||
proto: AppProtoView;
|
|
||||||
renderer: renderApi.Renderer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The context against which data-binding expressions in this view are evaluated against.
|
* The context against which data-binding expressions in this view are evaluated against.
|
||||||
@ -50,11 +60,11 @@ export class AppView {
|
|||||||
* context). This is used for thing like `<video #player>` or
|
* context). This is used for thing like `<video #player>` or
|
||||||
* `<li template="for #item of items">`, where "player" and "item" are locals, respectively.
|
* `<li template="for #item of items">`, where "player" and "item" are locals, respectively.
|
||||||
*/
|
*/
|
||||||
locals:Locals;
|
locals: Locals;
|
||||||
|
|
||||||
constructor(renderer:renderApi.Renderer, proto:AppProtoView, protoLocals:Map) {
|
constructor(public renderer: renderApi.Renderer, public proto: AppProtoView,
|
||||||
|
protoLocals: Map<string, any>) {
|
||||||
this.render = null;
|
this.render = null;
|
||||||
this.proto = proto;
|
|
||||||
this.changeDetector = null;
|
this.changeDetector = null;
|
||||||
this.elementInjectors = null;
|
this.elementInjectors = null;
|
||||||
this.rootElementInjectors = null;
|
this.rootElementInjectors = null;
|
||||||
@ -62,13 +72,13 @@ export class AppView {
|
|||||||
this.viewContainers = ListWrapper.createFixedSize(this.proto.elementBinders.length);
|
this.viewContainers = ListWrapper.createFixedSize(this.proto.elementBinders.length);
|
||||||
this.preBuiltObjects = null;
|
this.preBuiltObjects = null;
|
||||||
this.context = null;
|
this.context = null;
|
||||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); // TODO optimize this
|
||||||
this.renderer = renderer;
|
|
||||||
this.freeHostViews = [];
|
this.freeHostViews = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
|
init(changeDetector: ChangeDetector, elementInjectors: List<ElementInjector>,
|
||||||
preBuiltObjects:List, componentChildViews:List) {
|
rootElementInjectors: List<ElementInjector>, preBuiltObjects: List<PreBuiltObjects>,
|
||||||
|
componentChildViews: List<AppView>) {
|
||||||
this.changeDetector = changeDetector;
|
this.changeDetector = changeDetector;
|
||||||
this.elementInjectors = elementInjectors;
|
this.elementInjectors = elementInjectors;
|
||||||
this.rootElementInjectors = rootElementInjectors;
|
this.rootElementInjectors = rootElementInjectors;
|
||||||
@ -76,7 +86,7 @@ export class AppView {
|
|||||||
this.componentChildViews = componentChildViews;
|
this.componentChildViews = componentChildViews;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLocal(contextName: string, value):void {
|
setLocal(contextName: string, value): void {
|
||||||
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
|
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
|
||||||
if (!MapWrapper.contains(this.proto.variableBindings, contextName)) {
|
if (!MapWrapper.contains(this.proto.variableBindings, contextName)) {
|
||||||
return;
|
return;
|
||||||
@ -85,9 +95,7 @@ export class AppView {
|
|||||||
this.locals.set(templateName, value);
|
this.locals.set(templateName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrated():boolean {
|
hydrated(): boolean { return isPresent(this.context); }
|
||||||
return isPresent(this.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers the event handlers for the element and the directives.
|
* Triggers the event handlers for the element and the directives.
|
||||||
@ -105,34 +113,32 @@ export class AppView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dispatch to element injector or text nodes based on context
|
// dispatch to element injector or text nodes based on context
|
||||||
notifyOnBinding(b:BindingRecord, currentValue:any): void {
|
notifyOnBinding(b: BindingRecord, currentValue: any): void {
|
||||||
if (b.isElement()) {
|
if (b.isElement()) {
|
||||||
this.renderer.setElementProperty(
|
this.renderer.setElementProperty(this.render, b.elementIndex, b.propertyName, currentValue);
|
||||||
this.render, b.elementIndex, b.propertyName, currentValue
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// we know it refers to _textNodes.
|
// we know it refers to _textNodes.
|
||||||
this.renderer.setText(this.render, b.elementIndex, currentValue);
|
this.renderer.setText(this.render, b.elementIndex, currentValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveFor(directive:DirectiveIndex) {
|
getDirectiveFor(directive: DirectiveIndex) {
|
||||||
var elementInjector = this.elementInjectors[directive.elementIndex];
|
var elementInjector = this.elementInjectors[directive.elementIndex];
|
||||||
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
|
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDetectorFor(directive:DirectiveIndex) {
|
getDetectorFor(directive: DirectiveIndex) {
|
||||||
var childView = this.componentChildViews[directive.elementIndex];
|
var childView = this.componentChildViews[directive.elementIndex];
|
||||||
return isPresent(childView) ? childView.changeDetector : null;
|
return isPresent(childView) ? childView.changeDetector : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
callAction(elementIndex:number, actionExpression:string, action:Object) {
|
callAction(elementIndex: number, actionExpression: string, action: Object) {
|
||||||
this.renderer.callAction(this.render, elementIndex, actionExpression, action);
|
this.renderer.callAction(this.render, elementIndex, actionExpression, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
// implementation of EventDispatcher#dispatchEvent
|
// implementation of EventDispatcher#dispatchEvent
|
||||||
// returns false if preventDefault must be applied to the DOM event
|
// returns false if preventDefault must be applied to the DOM event
|
||||||
dispatchEvent(elementIndex:number, eventName:string, locals:Map<string, any>): boolean {
|
dispatchEvent(elementIndex: number, eventName: string, locals: Map<string, any>): boolean {
|
||||||
// Most of the time the event will be fired only when the view is in the live document.
|
// Most of the time the event will be fired only when the view is in the live document.
|
||||||
// However, in a rare circumstance the view might get dehydrated, in between the event
|
// However, in a rare circumstance the view might get dehydrated, in between the event
|
||||||
// queuing up and firing.
|
// queuing up and firing.
|
||||||
@ -163,33 +169,26 @@ export class AppView {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export class AppProtoView {
|
export class AppProtoView {
|
||||||
elementBinders:List<ElementBinder>;
|
elementBinders: List<ElementBinder>;
|
||||||
protoChangeDetector:ProtoChangeDetector;
|
protoLocals: Map<string, any>;
|
||||||
variableBindings: Map;
|
|
||||||
protoLocals:Map;
|
|
||||||
bindings:List;
|
|
||||||
render:renderApi.RenderProtoViewRef;
|
|
||||||
|
|
||||||
constructor(
|
constructor(public render: renderApi.RenderProtoViewRef,
|
||||||
render:renderApi.RenderProtoViewRef,
|
public protoChangeDetector: ProtoChangeDetector,
|
||||||
protoChangeDetector:ProtoChangeDetector,
|
public variableBindings: Map<string, string>) {
|
||||||
variableBindings:Map) {
|
|
||||||
this.render = render;
|
|
||||||
this.elementBinders = [];
|
this.elementBinders = [];
|
||||||
this.variableBindings = variableBindings;
|
|
||||||
this.protoLocals = MapWrapper.create();
|
this.protoLocals = MapWrapper.create();
|
||||||
if (isPresent(variableBindings)) {
|
if (isPresent(variableBindings)) {
|
||||||
MapWrapper.forEach(variableBindings, (templateName, _) => {
|
MapWrapper.forEach(variableBindings, (templateName, _) => {
|
||||||
MapWrapper.set(this.protoLocals, templateName, null);
|
MapWrapper.set(this.protoLocals, templateName, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.protoChangeDetector = protoChangeDetector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bindElement(parent:ElementBinder, distanceToParent:int, protoElementInjector:ProtoElementInjector,
|
bindElement(parent: ElementBinder, distanceToParent: int,
|
||||||
componentDirective:DirectiveBinding = null):ElementBinder {
|
protoElementInjector: ProtoElementInjector,
|
||||||
|
componentDirective: DirectiveBinding = null): ElementBinder {
|
||||||
var elBinder = new ElementBinder(this.elementBinders.length, parent, distanceToParent,
|
var elBinder = new ElementBinder(this.elementBinders.length, parent, distanceToParent,
|
||||||
protoElementInjector, componentDirective);
|
protoElementInjector, componentDirective);
|
||||||
ListWrapper.push(this.elementBinders, elBinder);
|
ListWrapper.push(this.elementBinders, elBinder);
|
||||||
return elBinder;
|
return elBinder;
|
||||||
}
|
}
|
||||||
@ -207,7 +206,8 @@ export class AppProtoView {
|
|||||||
* @param {int} directiveIndex The directive index in the binder or -1 when the event is not bound
|
* @param {int} directiveIndex The directive index in the binder or -1 when the event is not bound
|
||||||
* to a directive
|
* to a directive
|
||||||
*/
|
*/
|
||||||
bindEvent(eventBindings: List<renderApi.EventBinding>, boundElementIndex:number, directiveIndex: int = -1): void {
|
bindEvent(eventBindings: List<renderApi.EventBinding>, boundElementIndex: number,
|
||||||
|
directiveIndex: int = -1): void {
|
||||||
var elBinder = this.elementBinders[boundElementIndex];
|
var elBinder = this.elementBinders[boundElementIndex];
|
||||||
var events = elBinder.hostListeners;
|
var events = elBinder.hostListeners;
|
||||||
if (isBlank(events)) {
|
if (isBlank(events)) {
|
@ -1,71 +0,0 @@
|
|||||||
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
|
||||||
import {Injector} from 'angular2/di';
|
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
import * as avmModule from './view_manager';
|
|
||||||
|
|
||||||
import {ElementRef} from './element_ref';
|
|
||||||
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
|
||||||
/**
|
|
||||||
* @exportedAs angular2/core
|
|
||||||
*/
|
|
||||||
export class ViewContainerRef {
|
|
||||||
_viewManager: avmModule.AppViewManager;
|
|
||||||
_element: ElementRef;
|
|
||||||
|
|
||||||
constructor(viewManager: avmModule.AppViewManager,
|
|
||||||
element: ElementRef) {
|
|
||||||
this._viewManager = viewManager;
|
|
||||||
this._element = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getViews() {
|
|
||||||
var vc = internalView(this._element.parentView).viewContainers[this._element.boundElementIndex];
|
|
||||||
return isPresent(vc) ? vc.views : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
clear():void {
|
|
||||||
for (var i = this.length - 1; i >= 0; i--) {
|
|
||||||
this.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get(index: number): ViewRef {
|
|
||||||
return new ViewRef(this._getViews()[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
get length() /* :int */ {
|
|
||||||
return this._getViews().length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(rado): profile and decide whether bounds checks should be added
|
|
||||||
// to the methods below.
|
|
||||||
create(protoViewRef:ProtoViewRef = null, atIndex:number=-1, context:ElementRef, injector:Injector = null): ViewRef {
|
|
||||||
if (atIndex == -1) atIndex = this.length;
|
|
||||||
return this._viewManager.createViewInContainer(this._element, atIndex, protoViewRef, context, injector);
|
|
||||||
}
|
|
||||||
|
|
||||||
insert(viewRef:ViewRef, atIndex:number=-1): ViewRef {
|
|
||||||
if (atIndex == -1) atIndex = this.length;
|
|
||||||
return this._viewManager.attachViewInContainer(this._element, atIndex, viewRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
indexOf(viewRef:ViewRef) {
|
|
||||||
return ListWrapper.indexOf(this._getViews(), internalView(viewRef));
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(atIndex:number=-1):void {
|
|
||||||
if (atIndex == -1) atIndex = this.length - 1;
|
|
||||||
this._viewManager.destroyViewInContainer(this._element, atIndex);
|
|
||||||
// view is intentionally not returned to the client.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The method can be used together with insert to implement a view move, i.e.
|
|
||||||
* moving the dom nodes while the directives in the view stay intact.
|
|
||||||
*/
|
|
||||||
detach(atIndex:number=-1): ViewRef {
|
|
||||||
if (atIndex == -1) atIndex = this.length - 1;
|
|
||||||
return this._viewManager.detachViewInContainer(this._element, atIndex);
|
|
||||||
}
|
|
||||||
}
|
|
61
modules/angular2/src/core/compiler/view_container_ref.ts
Normal file
61
modules/angular2/src/core/compiler/view_container_ref.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {ListWrapper, List} from 'angular2/src/facade/collection';
|
||||||
|
import {Injector} from 'angular2/di';
|
||||||
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
import * as avmModule from './view_manager';
|
||||||
|
import * as viewModule from './view';
|
||||||
|
|
||||||
|
import {ElementRef} from './element_ref';
|
||||||
|
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
||||||
|
/**
|
||||||
|
* @exportedAs angular2/core
|
||||||
|
*/
|
||||||
|
export class ViewContainerRef {
|
||||||
|
constructor(public viewManager: avmModule.AppViewManager, public element: ElementRef) {}
|
||||||
|
|
||||||
|
private _getViews(): List<viewModule.AppView> {
|
||||||
|
var vc = internalView(this.element.parentView).viewContainers[this.element.boundElementIndex];
|
||||||
|
return isPresent(vc) ? vc.views : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
for (var i = this.length - 1; i >= 0; i--) {
|
||||||
|
this.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(index: number): ViewRef { return new ViewRef(this._getViews()[index]); }
|
||||||
|
|
||||||
|
get length() /* :int */ { return this._getViews().length; }
|
||||||
|
|
||||||
|
// TODO(rado): profile and decide whether bounds checks should be added
|
||||||
|
// to the methods below.
|
||||||
|
create(protoViewRef: ProtoViewRef = null, atIndex: number = -1, context: ElementRef = null,
|
||||||
|
injector: Injector = null): ViewRef {
|
||||||
|
if (atIndex == -1) atIndex = this.length;
|
||||||
|
return this.viewManager.createViewInContainer(this.element, atIndex, protoViewRef, context,
|
||||||
|
injector);
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(viewRef: ViewRef, atIndex: number = -1): ViewRef {
|
||||||
|
if (atIndex == -1) atIndex = this.length;
|
||||||
|
return this.viewManager.attachViewInContainer(this.element, atIndex, viewRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
indexOf(viewRef: ViewRef) { return ListWrapper.indexOf(this._getViews(), internalView(viewRef)); }
|
||||||
|
|
||||||
|
remove(atIndex: number = -1): void {
|
||||||
|
if (atIndex == -1) atIndex = this.length - 1;
|
||||||
|
this.viewManager.destroyViewInContainer(this.element, atIndex);
|
||||||
|
// view is intentionally not returned to the client.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method can be used together with insert to implement a view move, i.e.
|
||||||
|
* moving the dom nodes while the directives in the view stay intact.
|
||||||
|
*/
|
||||||
|
detach(atIndex: number = -1): ViewRef {
|
||||||
|
if (atIndex == -1) atIndex = this.length - 1;
|
||||||
|
return this.viewManager.detachViewInContainer(this.element, atIndex);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import {Injector, Binding} from 'angular2/di';
|
import {Injector, Binding, Injectable} from 'angular2/di';
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
import * as viewModule from './view';
|
import * as viewModule from './view';
|
||||||
import {ElementRef} from './element_ref';
|
import {ElementRef} from './element_ref';
|
||||||
@ -16,53 +15,56 @@ import {AppViewPool} from './view_pool';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppViewManager {
|
export class AppViewManager {
|
||||||
_viewPool:AppViewPool;
|
_viewPool: AppViewPool;
|
||||||
_utils:AppViewManagerUtils;
|
_utils: AppViewManagerUtils;
|
||||||
_renderer:Renderer;
|
_renderer: Renderer;
|
||||||
|
|
||||||
constructor(viewPool:AppViewPool, utils:AppViewManagerUtils, renderer:Renderer) {
|
constructor(viewPool: AppViewPool, utils: AppViewManagerUtils, renderer: Renderer) {
|
||||||
this._renderer = renderer;
|
this._renderer = renderer;
|
||||||
this._viewPool = viewPool;
|
this._viewPool = viewPool;
|
||||||
this._utils = utils;
|
this._utils = utils;
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponentView(hostLocation:ElementRef):ViewRef {
|
getComponentView(hostLocation: ElementRef): ViewRef {
|
||||||
var hostView = internalView(hostLocation.parentView);
|
var hostView = internalView(hostLocation.parentView);
|
||||||
var boundElementIndex = hostLocation.boundElementIndex;
|
var boundElementIndex = hostLocation.boundElementIndex;
|
||||||
return new ViewRef(hostView.componentChildViews[boundElementIndex]);
|
return new ViewRef(hostView.componentChildViews[boundElementIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewContainer(location:ElementRef):ViewContainerRef {
|
getViewContainer(location: ElementRef): ViewContainerRef {
|
||||||
var hostView = internalView(location.parentView);
|
var hostView = internalView(location.parentView);
|
||||||
return hostView.elementInjectors[location.boundElementIndex].getViewContainerRef();
|
return hostView.elementInjectors[location.boundElementIndex].getViewContainerRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponent(hostLocation:ElementRef):any {
|
getComponent(hostLocation: ElementRef): any {
|
||||||
var hostView = internalView(hostLocation.parentView);
|
var hostView = internalView(hostLocation.parentView);
|
||||||
var boundElementIndex = hostLocation.boundElementIndex;
|
var boundElementIndex = hostLocation.boundElementIndex;
|
||||||
return this._utils.getComponentInstance(hostView, boundElementIndex);
|
return this._utils.getComponentInstance(hostView, boundElementIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
createDynamicComponentView(hostLocation:ElementRef,
|
createDynamicComponentView(hostLocation: ElementRef, componentProtoViewRef: ProtoViewRef,
|
||||||
componentProtoViewRef:ProtoViewRef, componentBinding:Binding, injector:Injector):ViewRef {
|
componentBinding: Binding, injector: Injector): ViewRef {
|
||||||
var componentProtoView = internalProtoView(componentProtoViewRef);
|
var componentProtoView = internalProtoView(componentProtoViewRef);
|
||||||
var hostView = internalView(hostLocation.parentView);
|
var hostView = internalView(hostLocation.parentView);
|
||||||
var boundElementIndex = hostLocation.boundElementIndex;
|
var boundElementIndex = hostLocation.boundElementIndex;
|
||||||
var binder = hostView.proto.elementBinders[boundElementIndex];
|
var binder = hostView.proto.elementBinders[boundElementIndex];
|
||||||
if (!binder.hasDynamicComponent()) {
|
if (!binder.hasDynamicComponent()) {
|
||||||
throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`)
|
throw new BaseException(
|
||||||
|
`There is no dynamic component directive at element ${boundElementIndex}`)
|
||||||
}
|
}
|
||||||
var componentView = this._createPooledView(componentProtoView);
|
var componentView = this._createPooledView(componentProtoView);
|
||||||
this._renderer.attachComponentView(hostView.render, boundElementIndex, componentView.render);
|
this._renderer.attachComponentView(hostView.render, boundElementIndex, componentView.render);
|
||||||
this._utils.attachComponentView(hostView, boundElementIndex, componentView);
|
this._utils.attachComponentView(hostView, boundElementIndex, componentView);
|
||||||
this._utils.hydrateDynamicComponentInElementInjector(hostView, boundElementIndex, componentBinding, injector);
|
this._utils.hydrateDynamicComponentInElementInjector(hostView, boundElementIndex,
|
||||||
|
componentBinding, injector);
|
||||||
this._utils.hydrateComponentView(hostView, boundElementIndex);
|
this._utils.hydrateComponentView(hostView, boundElementIndex);
|
||||||
this._viewHydrateRecurse(componentView);
|
this._viewHydrateRecurse(componentView);
|
||||||
|
|
||||||
return new ViewRef(componentView);
|
return new ViewRef(componentView);
|
||||||
}
|
}
|
||||||
|
|
||||||
createRootHostView(hostProtoViewRef:ProtoViewRef, overrideSelector:string, injector:Injector):ViewRef {
|
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
||||||
|
injector: Injector): ViewRef {
|
||||||
var hostProtoView = internalProtoView(hostProtoViewRef);
|
var hostProtoView = internalProtoView(hostProtoViewRef);
|
||||||
var hostElementSelector = overrideSelector;
|
var hostElementSelector = overrideSelector;
|
||||||
if (isBlank(hostElementSelector)) {
|
if (isBlank(hostElementSelector)) {
|
||||||
@ -78,7 +80,7 @@ export class AppViewManager {
|
|||||||
return new ViewRef(hostView);
|
return new ViewRef(hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyRootHostView(hostViewRef:ViewRef) {
|
destroyRootHostView(hostViewRef: ViewRef) {
|
||||||
// Note: Don't detach the hostView as we want to leave the
|
// Note: Don't detach the hostView as we want to leave the
|
||||||
// root element in place. Also don't put the hostView into the view pool
|
// root element in place. Also don't put the hostView into the view pool
|
||||||
// as it is depending on the element for which it was created.
|
// as it is depending on the element for which it was created.
|
||||||
@ -88,24 +90,28 @@ export class AppViewManager {
|
|||||||
this._renderer.destroyView(hostView.render);
|
this._renderer.destroyView(hostView.render);
|
||||||
}
|
}
|
||||||
|
|
||||||
createFreeHostView(parentComponentLocation:ElementRef, hostProtoViewRef:ProtoViewRef, injector:Injector):ViewRef {
|
createFreeHostView(parentComponentLocation: ElementRef, hostProtoViewRef: ProtoViewRef,
|
||||||
|
injector: Injector): ViewRef {
|
||||||
var hostProtoView = internalProtoView(hostProtoViewRef);
|
var hostProtoView = internalProtoView(hostProtoViewRef);
|
||||||
var hostView = this._createPooledView(hostProtoView);
|
var hostView = this._createPooledView(hostProtoView);
|
||||||
var parentComponentHostView = internalView(parentComponentLocation.parentView);
|
var parentComponentHostView = internalView(parentComponentLocation.parentView);
|
||||||
var parentComponentBoundElementIndex = parentComponentLocation.boundElementIndex;
|
var parentComponentBoundElementIndex = parentComponentLocation.boundElementIndex;
|
||||||
this._utils.attachAndHydrateFreeHostView(parentComponentHostView, parentComponentBoundElementIndex, hostView, injector);
|
this._utils.attachAndHydrateFreeHostView(parentComponentHostView,
|
||||||
|
parentComponentBoundElementIndex, hostView, injector);
|
||||||
this._viewHydrateRecurse(hostView);
|
this._viewHydrateRecurse(hostView);
|
||||||
return new ViewRef(hostView);
|
return new ViewRef(hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyFreeHostView(parentComponentLocation:ElementRef, hostViewRef:ViewRef) {
|
destroyFreeHostView(parentComponentLocation: ElementRef, hostViewRef: ViewRef) {
|
||||||
var hostView = internalView(hostViewRef);
|
var hostView = internalView(hostViewRef);
|
||||||
var parentView = internalView(parentComponentLocation.parentView).componentChildViews[parentComponentLocation.boundElementIndex];
|
var parentView = internalView(parentComponentLocation.parentView)
|
||||||
|
.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||||
this._destroyFreeHostView(parentView, hostView);
|
this._destroyFreeHostView(parentView, hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
createViewInContainer(viewContainerLocation:ElementRef,
|
createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
||||||
atIndex:number, protoViewRef:ProtoViewRef, context:ElementRef = null, injector:Injector = null):ViewRef {
|
protoViewRef: ProtoViewRef, context: ElementRef = null,
|
||||||
|
injector: Injector = null): ViewRef {
|
||||||
var protoView = internalProtoView(protoViewRef);
|
var protoView = internalProtoView(protoViewRef);
|
||||||
var parentView = internalView(viewContainerLocation.parentView);
|
var parentView = internalView(viewContainerLocation.parentView);
|
||||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||||
@ -118,20 +124,24 @@ export class AppViewManager {
|
|||||||
|
|
||||||
var view = this._createPooledView(protoView);
|
var view = this._createPooledView(protoView);
|
||||||
|
|
||||||
this._renderer.attachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
this._renderer.attachViewInContainer(parentView.render, boundElementIndex, atIndex,
|
||||||
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView, contextBoundElementIndex, atIndex, view);
|
view.render);
|
||||||
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView, contextBoundElementIndex, atIndex, injector);
|
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
|
||||||
|
contextBoundElementIndex, atIndex, view);
|
||||||
|
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
|
||||||
|
contextBoundElementIndex, atIndex, injector);
|
||||||
this._viewHydrateRecurse(view);
|
this._viewHydrateRecurse(view);
|
||||||
return new ViewRef(view);
|
return new ViewRef(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyViewInContainer(viewContainerLocation:ElementRef, atIndex:number) {
|
destroyViewInContainer(viewContainerLocation: ElementRef, atIndex: number) {
|
||||||
var parentView = internalView(viewContainerLocation.parentView);
|
var parentView = internalView(viewContainerLocation.parentView);
|
||||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||||
this._destroyViewInContainer(parentView, boundElementIndex, atIndex);
|
this._destroyViewInContainer(parentView, boundElementIndex, atIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachViewInContainer(viewContainerLocation:ElementRef, atIndex:number, viewRef:ViewRef):ViewRef {
|
attachViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
||||||
|
viewRef: ViewRef): ViewRef {
|
||||||
var view = internalView(viewRef);
|
var view = internalView(viewRef);
|
||||||
var parentView = internalView(viewContainerLocation.parentView);
|
var parentView = internalView(viewContainerLocation.parentView);
|
||||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||||
@ -142,31 +152,34 @@ export class AppViewManager {
|
|||||||
// Right now we are destroying any special
|
// Right now we are destroying any special
|
||||||
// context view that might have been used.
|
// context view that might have been used.
|
||||||
this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, atIndex, view);
|
this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, atIndex, view);
|
||||||
this._renderer.attachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
this._renderer.attachViewInContainer(parentView.render, boundElementIndex, atIndex,
|
||||||
|
view.render);
|
||||||
return viewRef;
|
return viewRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
detachViewInContainer(viewContainerLocation:ElementRef, atIndex:number):ViewRef {
|
detachViewInContainer(viewContainerLocation: ElementRef, atIndex: number): ViewRef {
|
||||||
var parentView = internalView(viewContainerLocation.parentView);
|
var parentView = internalView(viewContainerLocation.parentView);
|
||||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||||
var view = viewContainer.views[atIndex];
|
var view = viewContainer.views[atIndex];
|
||||||
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
||||||
this._renderer.detachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
this._renderer.detachViewInContainer(parentView.render, boundElementIndex, atIndex,
|
||||||
|
view.render);
|
||||||
return new ViewRef(view);
|
return new ViewRef(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createPooledView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
_createPooledView(protoView: viewModule.AppProtoView): viewModule.AppView {
|
||||||
var view = this._viewPool.getView(protoView);
|
var view = this._viewPool.getView(protoView);
|
||||||
if (isBlank(view)) {
|
if (isBlank(view)) {
|
||||||
view = this._utils.createView(protoView, this._renderer.createView(protoView.render), this, this._renderer);
|
view = this._utils.createView(protoView, this._renderer.createView(protoView.render), this,
|
||||||
|
this._renderer);
|
||||||
this._renderer.setEventDispatcher(view.render, view);
|
this._renderer.setEventDispatcher(view.render, view);
|
||||||
this._createViewRecurse(view);
|
this._createViewRecurse(view);
|
||||||
}
|
}
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createViewRecurse(view:viewModule.AppView) {
|
_createViewRecurse(view: viewModule.AppView) {
|
||||||
var binders = view.proto.elementBinders;
|
var binders = view.proto.elementBinders;
|
||||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||||
var binder = binders[binderIdx];
|
var binder = binders[binderIdx];
|
||||||
@ -178,17 +191,18 @@ export class AppViewManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroyPooledView(view:viewModule.AppView) {
|
_destroyPooledView(view: viewModule.AppView) {
|
||||||
// TODO: if the pool is full, call renderer.destroyView as well!
|
// TODO: if the pool is full, call renderer.destroyView as well!
|
||||||
this._viewPool.returnView(view);
|
this._viewPool.returnView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroyViewInContainer(parentView, boundElementIndex, atIndex:number) {
|
_destroyViewInContainer(parentView, boundElementIndex, atIndex: number) {
|
||||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||||
var view = viewContainer.views[atIndex];
|
var view = viewContainer.views[atIndex];
|
||||||
this._viewDehydrateRecurse(view, false);
|
this._viewDehydrateRecurse(view, false);
|
||||||
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
||||||
this._renderer.detachViewInContainer(parentView.render, boundElementIndex, atIndex, view.render);
|
this._renderer.detachViewInContainer(parentView.render, boundElementIndex, atIndex,
|
||||||
|
view.render);
|
||||||
this._destroyPooledView(view);
|
this._destroyPooledView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,22 +220,19 @@ export class AppViewManager {
|
|||||||
this._destroyPooledView(hostView);
|
this._destroyPooledView(hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewHydrateRecurse(
|
_viewHydrateRecurse(view: viewModule.AppView) {
|
||||||
view:viewModule.AppView) {
|
|
||||||
this._renderer.hydrateView(view.render);
|
this._renderer.hydrateView(view.render);
|
||||||
|
|
||||||
var binders = view.proto.elementBinders;
|
var binders = view.proto.elementBinders;
|
||||||
for (var i = 0; i < binders.length; ++i) {
|
for (var i = 0; i < binders.length; ++i) {
|
||||||
if (binders[i].hasStaticComponent()) {
|
if (binders[i].hasStaticComponent()) {
|
||||||
this._utils.hydrateComponentView(view, i);
|
this._utils.hydrateComponentView(view, i);
|
||||||
this._viewHydrateRecurse(
|
this._viewHydrateRecurse(view.componentChildViews[i]);
|
||||||
view.componentChildViews[i]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewDehydrateRecurse(view:viewModule.AppView, forceDestroyComponents) {
|
_viewDehydrateRecurse(view: viewModule.AppView, forceDestroyComponents) {
|
||||||
this._utils.dehydrateView(view);
|
this._utils.dehydrateView(view);
|
||||||
this._renderer.dehydrateView(view.render);
|
this._renderer.dehydrateView(view.render);
|
||||||
var binders = view.proto.elementBinders;
|
var binders = view.proto.elementBinders;
|
||||||
@ -243,7 +254,7 @@ export class AppViewManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// freeHostViews
|
// freeHostViews
|
||||||
for (var i = view.freeHostViews.length-1; i>=0; i--) {
|
for (var i = view.freeHostViews.length - 1; i >= 0; i--) {
|
||||||
var hostView = view.freeHostViews[i];
|
var hostView = view.freeHostViews[i];
|
||||||
this._destroyFreeHostView(view, hostView);
|
this._destroyFreeHostView(view, hostView);
|
||||||
}
|
}
|
@ -1,24 +1,21 @@
|
|||||||
import {Injector, Binding} from 'angular2/di';
|
import {Injector, Binding, Injectable} from 'angular2/di';
|
||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||||
import * as eli from './element_injector';
|
import * as eli from './element_injector';
|
||||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
import * as viewModule from './view';
|
import * as viewModule from './view';
|
||||||
import * as avmModule from './view_manager';
|
import * as avmModule from './view_manager';
|
||||||
import {Renderer} from 'angular2/src/render/api';
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
import {Locals} from 'angular2/change_detection';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {RenderViewRef} from 'angular2/src/render/api';
|
import {RenderViewRef} from 'angular2/src/render/api';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppViewManagerUtils {
|
export class AppViewManagerUtils {
|
||||||
_directiveResolver:DirectiveResolver;
|
_directiveResolver: DirectiveResolver;
|
||||||
|
|
||||||
constructor(metadataReader:DirectiveResolver) {
|
constructor(metadataReader: DirectiveResolver) { this._directiveResolver = metadataReader; }
|
||||||
this._directiveResolver = metadataReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
getComponentInstance(parentView:viewModule.AppView, boundElementIndex:number):any {
|
getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
|
||||||
var binder = parentView.proto.elementBinders[boundElementIndex];
|
var binder = parentView.proto.elementBinders[boundElementIndex];
|
||||||
var eli = parentView.elementInjectors[boundElementIndex];
|
var eli = parentView.elementInjectors[boundElementIndex];
|
||||||
if (binder.hasDynamicComponent()) {
|
if (binder.hasDynamicComponent()) {
|
||||||
@ -28,7 +25,8 @@ export class AppViewManagerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createView(protoView:viewModule.AppProtoView, renderView:RenderViewRef, viewManager:avmModule.AppViewManager, renderer:Renderer): viewModule.AppView {
|
createView(protoView: viewModule.AppProtoView, renderView: RenderViewRef,
|
||||||
|
viewManager: avmModule.AppViewManager, renderer: Renderer): viewModule.AppView {
|
||||||
var view = new viewModule.AppView(renderer, protoView, protoView.protoLocals);
|
var view = new viewModule.AppView(renderer, protoView, protoView.protoLocals);
|
||||||
// TODO(tbosch): pass RenderViewRef as argument to AppView!
|
// TODO(tbosch): pass RenderViewRef as argument to AppView!
|
||||||
view.render = renderView;
|
view.render = renderView;
|
||||||
@ -65,56 +63,56 @@ export class AppViewManagerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.init(changeDetector, elementInjectors, rootElementInjectors,
|
view.init(changeDetector, elementInjectors, rootElementInjectors, preBuiltObjects,
|
||||||
preBuiltObjects, componentChildViews);
|
componentChildViews);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
attachComponentView(hostView:viewModule.AppView, boundElementIndex:number,
|
attachComponentView(hostView: viewModule.AppView, boundElementIndex: number,
|
||||||
componentView:viewModule.AppView) {
|
componentView: viewModule.AppView) {
|
||||||
var childChangeDetector = componentView.changeDetector;
|
var childChangeDetector = componentView.changeDetector;
|
||||||
hostView.changeDetector.addShadowDomChild(childChangeDetector);
|
hostView.changeDetector.addShadowDomChild(childChangeDetector);
|
||||||
hostView.componentChildViews[boundElementIndex] = componentView;
|
hostView.componentChildViews[boundElementIndex] = componentView;
|
||||||
}
|
}
|
||||||
|
|
||||||
detachComponentView(hostView:viewModule.AppView, boundElementIndex:number) {
|
detachComponentView(hostView: viewModule.AppView, boundElementIndex: number) {
|
||||||
var componentView = hostView.componentChildViews[boundElementIndex];
|
var componentView = hostView.componentChildViews[boundElementIndex];
|
||||||
hostView.changeDetector.removeShadowDomChild(componentView.changeDetector);
|
hostView.changeDetector.removeShadowDomChild(componentView.changeDetector);
|
||||||
hostView.componentChildViews[boundElementIndex] = null;
|
hostView.componentChildViews[boundElementIndex] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrateComponentView(hostView:viewModule.AppView, boundElementIndex:number, injector:Injector = null) {
|
hydrateComponentView(hostView: viewModule.AppView, boundElementIndex: number,
|
||||||
|
injector: Injector = null) {
|
||||||
var elementInjector = hostView.elementInjectors[boundElementIndex];
|
var elementInjector = hostView.elementInjectors[boundElementIndex];
|
||||||
var componentView = hostView.componentChildViews[boundElementIndex];
|
var componentView = hostView.componentChildViews[boundElementIndex];
|
||||||
var component = this.getComponentInstance(hostView, boundElementIndex);
|
var component = this.getComponentInstance(hostView, boundElementIndex);
|
||||||
this._hydrateView(
|
this._hydrateView(componentView, injector, elementInjector, component, null);
|
||||||
componentView, injector, elementInjector, component, null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrateRootHostView(hostView:viewModule.AppView, injector:Injector = null) {
|
hydrateRootHostView(hostView: viewModule.AppView, injector: Injector = null) {
|
||||||
this._hydrateView(hostView, injector, null, new Object(), null);
|
this._hydrateView(hostView, injector, null, new Object(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachAndHydrateFreeHostView(parentComponentHostView:viewModule.AppView, parentComponentBoundElementIndex:number,
|
attachAndHydrateFreeHostView(parentComponentHostView: viewModule.AppView,
|
||||||
hostView:viewModule.AppView, injector:Injector = null) {
|
parentComponentBoundElementIndex: number,
|
||||||
var hostElementInjector = parentComponentHostView.elementInjectors[parentComponentBoundElementIndex];
|
hostView: viewModule.AppView, injector: Injector = null) {
|
||||||
|
var hostElementInjector =
|
||||||
|
parentComponentHostView.elementInjectors[parentComponentBoundElementIndex];
|
||||||
var parentView = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex];
|
var parentView = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex];
|
||||||
parentView.changeDetector.addChild(hostView.changeDetector);
|
parentView.changeDetector.addChild(hostView.changeDetector);
|
||||||
ListWrapper.push(parentView.freeHostViews, hostView);
|
ListWrapper.push(parentView.freeHostViews, hostView);
|
||||||
this._hydrateView(hostView, injector, hostElementInjector, new Object(), null);
|
this._hydrateView(hostView, injector, hostElementInjector, new Object(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
detachFreeHostView(parentView:viewModule.AppView,
|
detachFreeHostView(parentView: viewModule.AppView, hostView: viewModule.AppView) {
|
||||||
hostView:viewModule.AppView) {
|
|
||||||
parentView.changeDetector.removeChild(hostView.changeDetector);
|
parentView.changeDetector.removeChild(hostView.changeDetector);
|
||||||
ListWrapper.remove(parentView.freeHostViews, hostView);
|
ListWrapper.remove(parentView.freeHostViews, hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachViewInContainer(parentView:viewModule.AppView, boundElementIndex:number,
|
attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||||
contextView:viewModule.AppView, contextBoundElementIndex:number,
|
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||||
atIndex:number, view:viewModule.AppView) {
|
atIndex: number, view: viewModule.AppView) {
|
||||||
if (isBlank(contextView)) {
|
if (isBlank(contextView)) {
|
||||||
contextView = parentView;
|
contextView = parentView;
|
||||||
contextBoundElementIndex = boundElementIndex;
|
contextBoundElementIndex = boundElementIndex;
|
||||||
@ -138,7 +136,8 @@ export class AppViewManagerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
detachViewInContainer(parentView:viewModule.AppView, boundElementIndex:number, atIndex:number) {
|
detachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||||
|
atIndex: number) {
|
||||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||||
var view = viewContainer.views[atIndex];
|
var view = viewContainer.views[atIndex];
|
||||||
view.changeDetector.remove();
|
view.changeDetector.remove();
|
||||||
@ -148,9 +147,9 @@ export class AppViewManagerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrateViewInContainer(parentView:viewModule.AppView, boundElementIndex:number,
|
hydrateViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||||
contextView:viewModule.AppView, contextBoundElementIndex:number,
|
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||||
atIndex:number, injector:Injector) {
|
atIndex: number, injector: Injector) {
|
||||||
if (isBlank(contextView)) {
|
if (isBlank(contextView)) {
|
||||||
contextView = parentView;
|
contextView = parentView;
|
||||||
contextBoundElementIndex = boundElementIndex;
|
contextBoundElementIndex = boundElementIndex;
|
||||||
@ -161,11 +160,12 @@ export class AppViewManagerUtils {
|
|||||||
this._hydrateView(view, injector, elementInjector, contextView.context, contextView.locals);
|
this._hydrateView(view, injector, elementInjector, contextView.context, contextView.locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrateDynamicComponentInElementInjector(hostView:viewModule.AppView, boundElementIndex:number,
|
hydrateDynamicComponentInElementInjector(hostView: viewModule.AppView, boundElementIndex: number,
|
||||||
componentBinding:Binding, injector:Injector = null) {
|
componentBinding: Binding, injector: Injector = null) {
|
||||||
var elementInjector = hostView.elementInjectors[boundElementIndex];
|
var elementInjector = hostView.elementInjectors[boundElementIndex];
|
||||||
if (isPresent(elementInjector.getDynamicallyLoadedComponent())) {
|
if (isPresent(elementInjector.getDynamicallyLoadedComponent())) {
|
||||||
throw new BaseException(`There already is a dynamic component loaded at element ${boundElementIndex}`);
|
throw new BaseException(
|
||||||
|
`There already is a dynamic component loaded at element ${boundElementIndex}`);
|
||||||
}
|
}
|
||||||
if (isBlank(injector)) {
|
if (isBlank(injector)) {
|
||||||
injector = elementInjector.getLightDomAppInjector();
|
injector = elementInjector.getLightDomAppInjector();
|
||||||
@ -175,7 +175,8 @@ export class AppViewManagerUtils {
|
|||||||
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
_hydrateView(view:viewModule.AppView, appInjector:Injector, hostElementInjector:eli.ElementInjector, context: Object, parentLocals:Locals) {
|
_hydrateView(view: viewModule.AppView, appInjector: Injector,
|
||||||
|
hostElementInjector: eli.ElementInjector, context: Object, parentLocals: Locals) {
|
||||||
if (isBlank(appInjector)) {
|
if (isBlank(appInjector)) {
|
||||||
appInjector = hostElementInjector.getShadowDomAppInjector();
|
appInjector = hostElementInjector.getShadowDomAppInjector();
|
||||||
}
|
}
|
||||||
@ -207,7 +208,8 @@ export class AppViewManagerUtils {
|
|||||||
view.changeDetector.hydrate(view.context, view.locals, view);
|
view.changeDetector.hydrate(view.context, view.locals, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setUpEventEmitters(view:viewModule.AppView, elementInjector:eli.ElementInjector, boundElementIndex:number) {
|
_setUpEventEmitters(view: viewModule.AppView, elementInjector: eli.ElementInjector,
|
||||||
|
boundElementIndex: number) {
|
||||||
var emitters = elementInjector.getEventEmitterAccessors();
|
var emitters = elementInjector.getEventEmitterAccessors();
|
||||||
for (var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
|
for (var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
|
||||||
var directiveEmitters = emitters[directiveIndex];
|
var directiveEmitters = emitters[directiveIndex];
|
||||||
@ -220,7 +222,8 @@ export class AppViewManagerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setUpHostActions(view:viewModule.AppView, elementInjector:eli.ElementInjector, boundElementIndex:number) {
|
_setUpHostActions(view: viewModule.AppView, elementInjector: eli.ElementInjector,
|
||||||
|
boundElementIndex: number) {
|
||||||
var hostActions = elementInjector.getHostActionAccessors();
|
var hostActions = elementInjector.getHostActionAccessors();
|
||||||
for (var directiveIndex = 0; directiveIndex < hostActions.length; ++directiveIndex) {
|
for (var directiveIndex = 0; directiveIndex < hostActions.length; ++directiveIndex) {
|
||||||
var directiveHostActions = hostActions[directiveIndex];
|
var directiveHostActions = hostActions[directiveIndex];
|
||||||
@ -233,7 +236,7 @@ export class AppViewManagerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dehydrateView(view:viewModule.AppView) {
|
dehydrateView(view: viewModule.AppView) {
|
||||||
var binders = view.proto.elementBinders;
|
var binders = view.proto.elementBinders;
|
||||||
for (var i = 0; i < binders.length; ++i) {
|
for (var i = 0; i < binders.length; ++i) {
|
||||||
var elementInjector = view.elementInjectors[i];
|
var elementInjector = view.elementInjectors[i];
|
||||||
@ -247,5 +250,4 @@ export class AppViewManagerUtils {
|
|||||||
view.context = null;
|
view.context = null;
|
||||||
view.changeDetector.dehydrate();
|
view.changeDetector.dehydrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {Inject} from 'angular2/src/di/annotations_impl';
|
import {Inject} from 'angular2/di';
|
||||||
|
|
||||||
import {ListWrapper, MapWrapper, Map, List} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper, Map, List} from 'angular2/src/facade/collection';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
@ -10,15 +10,15 @@ import * as viewModule from './view';
|
|||||||
export const APP_VIEW_POOL_CAPACITY = 'AppViewPool.viewPoolCapacity';
|
export const APP_VIEW_POOL_CAPACITY = 'AppViewPool.viewPoolCapacity';
|
||||||
|
|
||||||
export class AppViewPool {
|
export class AppViewPool {
|
||||||
_poolCapacityPerProtoView:number;
|
_poolCapacityPerProtoView: number;
|
||||||
_pooledViewsPerProtoView:Map<viewModule.AppProtoView, List<viewModule.AppView>>;
|
_pooledViewsPerProtoView: Map<viewModule.AppProtoView, List<viewModule.AppView>>;
|
||||||
|
|
||||||
constructor(@Inject(APP_VIEW_POOL_CAPACITY) poolCapacityPerProtoView) {
|
constructor(@Inject(APP_VIEW_POOL_CAPACITY) poolCapacityPerProtoView) {
|
||||||
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
||||||
this._pooledViewsPerProtoView = MapWrapper.create();
|
this._pooledViewsPerProtoView = MapWrapper.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
getView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
getView(protoView: viewModule.AppProtoView): viewModule.AppView {
|
||||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||||
if (isPresent(pooledViews) && pooledViews.length > 0) {
|
if (isPresent(pooledViews) && pooledViews.length > 0) {
|
||||||
return ListWrapper.removeLast(pooledViews);
|
return ListWrapper.removeLast(pooledViews);
|
||||||
@ -26,7 +26,7 @@ export class AppViewPool {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
returnView(view:viewModule.AppView) {
|
returnView(view: viewModule.AppView) {
|
||||||
var protoView = view.proto;
|
var protoView = view.proto;
|
||||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||||
if (isBlank(pooledViews)) {
|
if (isBlank(pooledViews)) {
|
||||||
@ -37,5 +37,4 @@ export class AppViewPool {
|
|||||||
ListWrapper.push(pooledViews, view);
|
ListWrapper.push(pooledViews, view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -3,12 +3,12 @@ import * as viewModule from './view';
|
|||||||
import {RenderViewRef} from 'angular2/src/render/api';
|
import {RenderViewRef} from 'angular2/src/render/api';
|
||||||
|
|
||||||
// This is a workaround for privacy in Dart as we don't have library parts
|
// This is a workaround for privacy in Dart as we don't have library parts
|
||||||
export function internalView(viewRef:ViewRef):viewModule.AppView {
|
export function internalView(viewRef: ViewRef): viewModule.AppView {
|
||||||
return viewRef._view;
|
return viewRef._view;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a workaround for privacy in Dart as we don't have library parts
|
// This is a workaround for privacy in Dart as we don't have library parts
|
||||||
export function internalProtoView(protoViewRef:ProtoViewRef):viewModule.AppProtoView {
|
export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppProtoView {
|
||||||
return isPresent(protoViewRef) ? protoViewRef._protoView : null;
|
return isPresent(protoViewRef) ? protoViewRef._protoView : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,28 +16,20 @@ export function internalProtoView(protoViewRef:ProtoViewRef):viewModule.AppProto
|
|||||||
* @exportedAs angular2/view
|
* @exportedAs angular2/view
|
||||||
*/
|
*/
|
||||||
export class ViewRef {
|
export class ViewRef {
|
||||||
_view:viewModule.AppView;
|
_view: viewModule.AppView;
|
||||||
|
|
||||||
constructor(view:viewModule.AppView) {
|
constructor(view: viewModule.AppView) { this._view = view; }
|
||||||
this._view = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
get render():RenderViewRef {
|
get render(): RenderViewRef { return this._view.render; }
|
||||||
return this._view.render;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLocal(contextName:string, value:any):void {
|
setLocal(contextName: string, value: any): void { this._view.setLocal(contextName, value); }
|
||||||
this._view.setLocal(contextName, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @exportedAs angular2/view
|
* @exportedAs angular2/view
|
||||||
*/
|
*/
|
||||||
export class ProtoViewRef {
|
export class ProtoViewRef {
|
||||||
_protoView:viewModule.AppProtoView;
|
_protoView: viewModule.AppProtoView;
|
||||||
|
|
||||||
constructor(protoView) {
|
constructor(protoView) { this._protoView = protoView; }
|
||||||
this._protoView = protoView;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/di';
|
||||||
import {isPresent, print} from 'angular2/src/facade/lang';
|
import {isPresent, print} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
import {ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
@ -6,7 +6,8 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||||||
/**
|
/**
|
||||||
* Provides a hook for centralized exception handling.
|
* Provides a hook for centralized exception handling.
|
||||||
*
|
*
|
||||||
* The default implementation of `ExceptionHandler` prints error messages to the `Console`. To intercept error handling,
|
* The default implementation of `ExceptionHandler` prints error messages to the `Console`. To
|
||||||
|
* intercept error handling,
|
||||||
* write a custom exception handler that replaces this default as appropriate for your app.
|
* write a custom exception handler that replaces this default as appropriate for your app.
|
||||||
*
|
*
|
||||||
* # Example
|
* # Example
|
||||||
@ -35,7 +36,8 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExceptionHandler {
|
export class ExceptionHandler {
|
||||||
call(error, stackTrace = null, reason = null) {
|
call(error, stackTrace = null, reason = null) {
|
||||||
var longStackTrace = isListLikeIterable(stackTrace) ? ListWrapper.join(stackTrace, "\n\n") : stackTrace;
|
var longStackTrace =
|
||||||
|
isListLikeIterable(stackTrace) ? ListWrapper.join(stackTrace, "\n\n") : stackTrace;
|
||||||
var reasonStr = isPresent(reason) ? `\n${reason}` : '';
|
var reasonStr = isPresent(reason) ? `\n${reason}` : '';
|
||||||
DOM.logError(`${error}${reasonStr}\nSTACKTRACE:\n${longStackTrace}`);
|
DOM.logError(`${error}${reasonStr}\nSTACKTRACE:\n${longStackTrace}`);
|
||||||
}
|
}
|
@ -1,76 +0,0 @@
|
|||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {ChangeDetector} from 'angular2/change_detection';
|
|
||||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
|
||||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
|
||||||
import {isPresent} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to explicitly trigger change detection in an application.
|
|
||||||
*
|
|
||||||
* By default, `Zone` triggers change detection in Angular on each virtual machine (VM) turn. When testing, or in some
|
|
||||||
* limited application use cases, a developer can also trigger change detection with the `lifecycle.tick()` method.
|
|
||||||
*
|
|
||||||
* Each Angular application has a single `LifeCycle` instance.
|
|
||||||
*
|
|
||||||
* # Example
|
|
||||||
*
|
|
||||||
* This is a contrived example, since the bootstrap automatically runs inside of the `Zone`, which invokes
|
|
||||||
* `lifecycle.tick()` on your behalf.
|
|
||||||
*
|
|
||||||
* ```javascript
|
|
||||||
* bootstrap(MyApp).then((ref:ComponentRef) => {
|
|
||||||
* var lifeCycle = ref.injector.get(LifeCycle);
|
|
||||||
* var myApp = ref.instance;
|
|
||||||
*
|
|
||||||
* ref.doSomething();
|
|
||||||
* lifecycle.tick();
|
|
||||||
* });
|
|
||||||
* ```
|
|
||||||
* @exportedAs angular2/change_detection
|
|
||||||
*/
|
|
||||||
@Injectable()
|
|
||||||
export class LifeCycle {
|
|
||||||
_errorHandler;
|
|
||||||
_changeDetector:ChangeDetector;
|
|
||||||
_enforceNoNewChanges:boolean;
|
|
||||||
|
|
||||||
constructor(exceptionHandler:ExceptionHandler, changeDetector:ChangeDetector = null, enforceNoNewChanges:boolean = false) {
|
|
||||||
this._errorHandler = (exception, stackTrace) => {
|
|
||||||
exceptionHandler.call(exception, stackTrace);
|
|
||||||
throw exception;
|
|
||||||
};
|
|
||||||
this._changeDetector = changeDetector; // may be null when instantiated from application bootstrap
|
|
||||||
this._enforceNoNewChanges = enforceNoNewChanges;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
registerWith(zone:NgZone, changeDetector:ChangeDetector = null) {
|
|
||||||
if (isPresent(changeDetector)) {
|
|
||||||
this._changeDetector=changeDetector;
|
|
||||||
}
|
|
||||||
|
|
||||||
zone.initCallbacks({
|
|
||||||
onErrorHandler: this._errorHandler,
|
|
||||||
onTurnDone: () => this.tick()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoke this method to explicitly process change detection and its side-effects.
|
|
||||||
*
|
|
||||||
* In development mode, `tick()` also performs a second change detection cycle to ensure that no further
|
|
||||||
* changes are detected. If additional changes are picked up during this second cycle, bindings in the app have
|
|
||||||
* side-effects that cannot be resolved in a single change detection pass. In this case, Angular throws an error,
|
|
||||||
* since an Angular application can only have one change detection pass during which all change detection must
|
|
||||||
* complete.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
tick() {
|
|
||||||
this._changeDetector.detectChanges();
|
|
||||||
if (this._enforceNoNewChanges) {
|
|
||||||
this._changeDetector.checkNoChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
81
modules/angular2/src/core/life_cycle/life_cycle.ts
Normal file
81
modules/angular2/src/core/life_cycle/life_cycle.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import {Injectable} from 'angular2/di';
|
||||||
|
import {ChangeDetector} from 'angular2/change_detection';
|
||||||
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to explicitly trigger change detection in an application.
|
||||||
|
*
|
||||||
|
* By default, `Zone` triggers change detection in Angular on each virtual machine (VM) turn. When
|
||||||
|
* testing, or in some
|
||||||
|
* limited application use cases, a developer can also trigger change detection with the
|
||||||
|
* `lifecycle.tick()` method.
|
||||||
|
*
|
||||||
|
* Each Angular application has a single `LifeCycle` instance.
|
||||||
|
*
|
||||||
|
* # Example
|
||||||
|
*
|
||||||
|
* This is a contrived example, since the bootstrap automatically runs inside of the `Zone`, which
|
||||||
|
* invokes
|
||||||
|
* `lifecycle.tick()` on your behalf.
|
||||||
|
*
|
||||||
|
* ```javascript
|
||||||
|
* bootstrap(MyApp).then((ref:ComponentRef) => {
|
||||||
|
* var lifeCycle = ref.injector.get(LifeCycle);
|
||||||
|
* var myApp = ref.instance;
|
||||||
|
*
|
||||||
|
* ref.doSomething();
|
||||||
|
* lifecycle.tick();
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
* @exportedAs angular2/change_detection
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class LifeCycle {
|
||||||
|
_errorHandler;
|
||||||
|
_changeDetector: ChangeDetector;
|
||||||
|
_enforceNoNewChanges: boolean;
|
||||||
|
|
||||||
|
constructor(exceptionHandler: ExceptionHandler, changeDetector: ChangeDetector = null,
|
||||||
|
enforceNoNewChanges: boolean = false) {
|
||||||
|
this._errorHandler = (exception, stackTrace) => { exceptionHandler.call(exception, stackTrace);
|
||||||
|
throw exception;
|
||||||
|
};
|
||||||
|
this._changeDetector =
|
||||||
|
changeDetector; // may be null when instantiated from application bootstrap
|
||||||
|
this._enforceNoNewChanges = enforceNoNewChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
registerWith(zone: NgZone, changeDetector: ChangeDetector = null) {
|
||||||
|
if (isPresent(changeDetector)) {
|
||||||
|
this._changeDetector = changeDetector;
|
||||||
|
}
|
||||||
|
|
||||||
|
zone.initCallbacks({onErrorHandler: this._errorHandler, onTurnDone: () => this.tick()});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke this method to explicitly process change detection and its side-effects.
|
||||||
|
*
|
||||||
|
* In development mode, `tick()` also performs a second change detection cycle to ensure that no
|
||||||
|
* further
|
||||||
|
* changes are detected. If additional changes are picked up during this second cycle, bindings in
|
||||||
|
* the app have
|
||||||
|
* side-effects that cannot be resolved in a single change detection pass. In this case, Angular
|
||||||
|
* throws an error,
|
||||||
|
* since an Angular application can only have one change detection pass during which all change
|
||||||
|
* detection must
|
||||||
|
* complete.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
tick() {
|
||||||
|
this._changeDetector.detectChanges();
|
||||||
|
if (this._enforceNoNewChanges) {
|
||||||
|
this._changeDetector.checkNoChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,11 @@ import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/te
|
|||||||
import {global} from 'angular2/src/facade/lang';
|
import {global} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
class PublicTestability {
|
class PublicTestability {
|
||||||
_testabililty: Testability;
|
_testability: Testability;
|
||||||
|
|
||||||
constructor(testability: Testability) {
|
constructor(testability: Testability) { this._testability = testability; }
|
||||||
this._testability = testability;
|
|
||||||
}
|
|
||||||
|
|
||||||
whenStable(callback: Function) {
|
whenStable(callback: Function) { this._testability.whenStable(callback); }
|
||||||
this._testability.whenStable(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
findBindings(using, binding: string, exactMatch: boolean) {
|
findBindings(using, binding: string, exactMatch: boolean) {
|
||||||
return this._testability.findBindings(using, binding, exactMatch);
|
return this._testability.findBindings(using, binding, exactMatch);
|
@ -1,8 +1,8 @@
|
|||||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
import {Injectable} from 'angular2/di';
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {Map, MapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
|
import {Map, MapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
import {StringWrapper, isBlank, BaseException} from 'angular2/src/facade/lang';
|
import {StringWrapper, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||||
import * as getTestabilityModule from 'angular2/src/core/testability/get_testability';
|
import * as getTestabilityModule from './get_testability';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,7 +13,7 @@ import * as getTestabilityModule from 'angular2/src/core/testability/get_testabi
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class Testability {
|
export class Testability {
|
||||||
_pendingCount: number;
|
_pendingCount: number;
|
||||||
_callbacks: List;
|
_callbacks: List<Function>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._pendingCount = 0;
|
this._pendingCount = 0;
|
||||||
@ -45,11 +45,9 @@ export class Testability {
|
|||||||
// TODO(juliemr) - hook into the zone api.
|
// TODO(juliemr) - hook into the zone api.
|
||||||
}
|
}
|
||||||
|
|
||||||
getPendingCount(): number {
|
getPendingCount(): number { return this._pendingCount; }
|
||||||
return this._pendingCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
findBindings(using, binding: string, exactMatch: boolean): List {
|
findBindings(using, binding: string, exactMatch: boolean): List<any> {
|
||||||
// TODO(juliemr): implement.
|
// TODO(juliemr): implement.
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@ -57,7 +55,7 @@ export class Testability {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TestabilityRegistry {
|
export class TestabilityRegistry {
|
||||||
_applications: Map;
|
_applications: Map<any, Testability>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._applications = MapWrapper.create();
|
this._applications = MapWrapper.create();
|
||||||
@ -69,7 +67,7 @@ export class TestabilityRegistry {
|
|||||||
MapWrapper.set(this._applications, token, testability);
|
MapWrapper.set(this._applications, token, testability);
|
||||||
}
|
}
|
||||||
|
|
||||||
findTestabilityInTree(elem) : Testability {
|
findTestabilityInTree(elem): Testability {
|
||||||
if (elem == null) {
|
if (elem == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
@ -75,9 +75,9 @@ export class NgZone {
|
|||||||
* micro task
|
* micro task
|
||||||
*/
|
*/
|
||||||
initCallbacks({onTurnStart, onTurnDone, onErrorHandler}: {
|
initCallbacks({onTurnStart, onTurnDone, onErrorHandler}: {
|
||||||
onTurnStart?: () => void,
|
onTurnStart?: /*() => void*/ Function,
|
||||||
onTurnDone?: () => void,
|
onTurnDone?: /*() => void*/ Function,
|
||||||
onErrorHandler?: (error, stack) => void
|
onErrorHandler?: /*(error, stack) => void*/ Function
|
||||||
} = {}) {
|
} = {}) {
|
||||||
this._onTurnStart = normalizeBlank(onTurnStart);
|
this._onTurnStart = normalizeBlank(onTurnStart);
|
||||||
this._onTurnDone = normalizeBlank(onTurnDone);
|
this._onTurnDone = normalizeBlank(onTurnDone);
|
||||||
|
@ -67,7 +67,7 @@ function _isWaiting(obj): boolean {
|
|||||||
* @exportedAs angular2/di
|
* @exportedAs angular2/di
|
||||||
*/
|
*/
|
||||||
export class Injector {
|
export class Injector {
|
||||||
private _bindings: List<any>;
|
private _bindings: List<ResolvedBinding>;
|
||||||
private _instances: List<any>;
|
private _instances: List<any>;
|
||||||
private _parent: Injector;
|
private _parent: Injector;
|
||||||
private _defaultBindings: boolean;
|
private _defaultBindings: boolean;
|
||||||
@ -90,7 +90,7 @@ export class Injector {
|
|||||||
*such as
|
*such as
|
||||||
* `fromResolvedBindings` and `createChildFromResolved`.
|
* `fromResolvedBindings` and `createChildFromResolved`.
|
||||||
*/
|
*/
|
||||||
static resolve(bindings: List<any>): List<ResolvedBinding> {
|
static resolve(bindings: List<Type | Binding | List<any>>): List<ResolvedBinding> {
|
||||||
var resolvedBindings = resolveBindings(bindings);
|
var resolvedBindings = resolveBindings(bindings);
|
||||||
var flatten = _flattenBindings(resolvedBindings, MapWrapper.create());
|
var flatten = _flattenBindings(resolvedBindings, MapWrapper.create());
|
||||||
return _createListOfBindings(flatten);
|
return _createListOfBindings(flatten);
|
||||||
@ -109,7 +109,8 @@ export class Injector {
|
|||||||
* bindings.
|
* bindings.
|
||||||
* @param `defaultBindings` Setting to true will auto-create bindings.
|
* @param `defaultBindings` Setting to true will auto-create bindings.
|
||||||
*/
|
*/
|
||||||
static resolveAndCreate(bindings: List<any>, {defaultBindings = false}: any = {}): Injector {
|
static resolveAndCreate(bindings: List<Type | Binding | List<any>>,
|
||||||
|
{defaultBindings = false}: any = {}): Injector {
|
||||||
return new Injector(Injector.resolve(bindings), null, defaultBindings);
|
return new Injector(Injector.resolve(bindings), null, defaultBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +185,7 @@ export class Injector {
|
|||||||
* recursive list of more bindings.
|
* recursive list of more bindings.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
resolveAndCreateChild(bindings: List<any>): Injector {
|
resolveAndCreateChild(bindings: List<Type | Binding | List<any>>): Injector {
|
||||||
return new Injector(Injector.resolve(bindings), this, false);
|
return new Injector(Injector.resolve(bindings), this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,7 +369,7 @@ class _AsyncInjectorStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveBindings(bindings: List<any>): List<ResolvedBinding> {
|
export function resolveBindings(bindings: List<Type | Binding | List<any>>): List<ResolvedBinding> {
|
||||||
var resolvedList = ListWrapper.createFixedSize(bindings.length);
|
var resolvedList = ListWrapper.createFixedSize(bindings.length);
|
||||||
for (var i = 0; i < bindings.length; i++) {
|
for (var i = 0; i < bindings.length; i++) {
|
||||||
var unresolved = resolveForwardRef(bindings[i]);
|
var unresolved = resolveForwardRef(bindings[i]);
|
||||||
@ -398,13 +399,14 @@ function flattenBindings(bindings: List<ResolvedBinding>): List<ResolvedBinding>
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createListOfBindings(flattenedBindings): List<any> {
|
function _createListOfBindings(
|
||||||
|
flattenedBindings: Map<number, ResolvedBinding>): List<ResolvedBinding> {
|
||||||
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
|
var bindings = ListWrapper.createFixedSize(Key.numberOfKeys + 1);
|
||||||
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
|
MapWrapper.forEach(flattenedBindings, (v, keyId) => bindings[keyId] = v);
|
||||||
return bindings;
|
return bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _flattenBindings(bindings: List<ResolvedBinding /* | List<any>*/>,
|
function _flattenBindings(bindings: List<ResolvedBinding | List<any>>,
|
||||||
res: Map<number, ResolvedBinding>): Map<number, ResolvedBinding> {
|
res: Map<number, ResolvedBinding>): Map<number, ResolvedBinding> {
|
||||||
ListWrapper.forEach(bindings, function(b) {
|
ListWrapper.forEach(bindings, function(b) {
|
||||||
if (b instanceof ResolvedBinding) {
|
if (b instanceof ResolvedBinding) {
|
||||||
|
@ -22,7 +22,7 @@ export class DomAdapter {
|
|||||||
* Maps attribute names to their corresponding property names for cases
|
* Maps attribute names to their corresponding property names for cases
|
||||||
* where attribute name doesn't match property name.
|
* where attribute name doesn't match property name.
|
||||||
*/
|
*/
|
||||||
get attrToPropMap(): any { throw _abstract(); }
|
get attrToPropMap(): StringMap<string, string> { throw _abstract(); }
|
||||||
|
|
||||||
parse(templateHtml: string) { throw _abstract(); }
|
parse(templateHtml: string) { throw _abstract(); }
|
||||||
query(selector: string): any { throw _abstract(); }
|
query(selector: string): any { throw _abstract(); }
|
||||||
|
@ -37,47 +37,49 @@ export class MapWrapper {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
static createFromPairs(pairs: List<any>): Map<any, any> { return createMapFromPairs(pairs); }
|
static createFromPairs(pairs: List<any>): Map<any, any> { return createMapFromPairs(pairs); }
|
||||||
static get(m, k) { return m.get(k); }
|
static get<K, V>(m: Map<K, V>, k: K): V { return m.get(k); }
|
||||||
static set(m, k, v) { m.set(k, v); }
|
static set<K, V>(m: Map<K, V>, k: K, v: V) { m.set(k, v); }
|
||||||
static contains(m, k) { return m.has(k); }
|
static contains<K>(m: Map<K, any>, k: K) { return m.has(k); }
|
||||||
static forEach(m, fn) { m.forEach(fn); }
|
static forEach<K, V>(m: Map<K, V>, fn: /*(V, K) => void*/ Function) { m.forEach(<any>fn); }
|
||||||
static size(m) { return m.size; }
|
static size(m: Map<any, any>) { return m.size; }
|
||||||
static delete (m, k) { m.delete(k); }
|
static delete<K>(m: Map<K, any>, k: K) { m.delete(k); }
|
||||||
static clear(m) { m.clear(); }
|
static clear(m: Map<any, any>) { m.clear(); }
|
||||||
static clearValues(m) {
|
static clearValues(m: Map<any, any>) {
|
||||||
var keyIterator = m.keys();
|
var keyIterator = m.keys();
|
||||||
var k;
|
var k;
|
||||||
while (!((k = keyIterator.next()).done)) {
|
while (!((k = (<any>keyIterator).next()).done)) {
|
||||||
m.set(k.value, null);
|
m.set(k.value, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static iterable(m) { return m; }
|
static iterable(m) { return m; }
|
||||||
static keys(m) { return m.keys(); }
|
static keys<K>(m: Map<K, any>): List<K> { return m.keys(); }
|
||||||
static values(m) { return m.values(); }
|
static values<V>(m: Map<any, V>): List<V> { return m.values(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps Javascript Objects
|
* Wraps Javascript Objects
|
||||||
*/
|
*/
|
||||||
export class StringMapWrapper {
|
export class StringMapWrapper {
|
||||||
static create(): Object {
|
static create(): StringMap<any, any> {
|
||||||
// Note: We are not using Object.create(null) here due to
|
// Note: We are not using Object.create(null) here due to
|
||||||
// performance!
|
// performance!
|
||||||
// http://jsperf.com/ng2-object-create-null
|
// http://jsperf.com/ng2-object-create-null
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
static contains(map, key) { return map.hasOwnProperty(key); }
|
static contains(map: StringMap<string, any>, key: string) { return map.hasOwnProperty(key); }
|
||||||
static get(map, key) { return map.hasOwnProperty(key) ? map[key] : undefined; }
|
static get<V>(map: StringMap<string, V>, key: string): V {
|
||||||
static set(map, key, value) { map[key] = value; }
|
return map.hasOwnProperty(key) ? map[key] : undefined;
|
||||||
static keys(map) { return Object.keys(map); }
|
}
|
||||||
static isEmpty(map) {
|
static set<V>(map: StringMap<string, V>, key: string, value: V) { map[key] = value; }
|
||||||
|
static keys(map: StringMap<string, any>): List<string> { return Object.keys(map); }
|
||||||
|
static isEmpty(map: StringMap<string, any>) {
|
||||||
for (var prop in map) {
|
for (var prop in map) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
static delete (map, key) { delete map[key]; }
|
static delete (map: StringMap<string, any>, key: string) { delete map[key]; }
|
||||||
static forEach(map, callback) {
|
static forEach<K, V>(map: StringMap<string, V>, callback: /*(V, K) => void*/ Function) {
|
||||||
for (var prop in map) {
|
for (var prop in map) {
|
||||||
if (map.hasOwnProperty(prop)) {
|
if (map.hasOwnProperty(prop)) {
|
||||||
callback(map[prop], prop);
|
callback(map[prop], prop);
|
||||||
@ -85,7 +87,7 @@ export class StringMapWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static merge(m1, m2) {
|
static merge<V>(m1: StringMap<string, V>, m2: StringMap<string, V>): StringMap<string, V> {
|
||||||
var m = {};
|
var m = {};
|
||||||
|
|
||||||
for (var attr in m1) {
|
for (var attr in m1) {
|
||||||
@ -103,7 +105,7 @@ export class StringMapWrapper {
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
static equals(m1, m2) {
|
static equals<V>(m1: StringMap<string, V>, m2: StringMap<string, V>): boolean {
|
||||||
var k1 = Object.keys(m1);
|
var k1 = Object.keys(m1);
|
||||||
var k2 = Object.keys(m2);
|
var k2 = Object.keys(m2);
|
||||||
if (k1.length != k2.length) {
|
if (k1.length != k2.length) {
|
||||||
|
@ -2,7 +2,7 @@ var _global = typeof window === 'undefined' ? global : window;
|
|||||||
export {_global as global};
|
export {_global as global};
|
||||||
|
|
||||||
export var Type = Function;
|
export var Type = Function;
|
||||||
export type Type = typeof Function;
|
export type Type = new (... args: any[]) => any;
|
||||||
|
|
||||||
export class BaseException extends Error {
|
export class BaseException extends Error {
|
||||||
message;
|
message;
|
||||||
|
@ -18,12 +18,15 @@ import {CompileControl} from './compile_control';
|
|||||||
|
|
||||||
import {DirectiveMetadata} from '../../api';
|
import {DirectiveMetadata} from '../../api';
|
||||||
import {dashCaseToCamelCase, camelCaseToDashCase, EVENT_TARGET_SEPARATOR} from '../util';
|
import {dashCaseToCamelCase, camelCaseToDashCase, EVENT_TARGET_SEPARATOR} from '../util';
|
||||||
|
import {
|
||||||
|
DirectiveBuilder
|
||||||
|
} from '../view/proto_view_builder'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the directives on a single element. Assumes ViewSplitter has already created
|
* Parses the directives on a single element. Assumes ViewSplitter has already created
|
||||||
* <template> elements for template directives.
|
* <template> elements for template directives.
|
||||||
*/
|
*/
|
||||||
export class DirectiveParser implements CompileStep {
|
export class DirectiveParser implements CompileStep {
|
||||||
_selectorMatcher: SelectorMatcher;
|
_selectorMatcher: SelectorMatcher;
|
||||||
_directives: List<DirectiveMetadata>;
|
_directives: List<DirectiveMetadata>;
|
||||||
_parser: Parser;
|
_parser: Parser;
|
||||||
@ -118,7 +121,8 @@ export class DirectiveParser implements CompileStep {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_bindDirectiveProperty(dirProperty, bindConfig, compileElement, directiveBinderBuilder) {
|
_bindDirectiveProperty(dirProperty: string, bindConfig: string, compileElement: CompileElement,
|
||||||
|
directiveBinderBuilder: DirectiveBuilder) {
|
||||||
var pipes = this._splitBindConfig(bindConfig);
|
var pipes = this._splitBindConfig(bindConfig);
|
||||||
var elProp = ListWrapper.removeAt(pipes, 0);
|
var elProp = ListWrapper.removeAt(pipes, 0);
|
||||||
|
|
||||||
|
@ -140,12 +140,12 @@ export class SelectorMatcher {
|
|||||||
return notMatcher;
|
return notMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _elementMap: Map<string, string>;
|
private _elementMap: Map<string, List<string>>;
|
||||||
private _elementPartialMap: Map<string, string>;
|
private _elementPartialMap: Map<string, SelectorMatcher>;
|
||||||
private _classMap: Map<string, string>;
|
private _classMap: Map<string, List<string>>;
|
||||||
private _classPartialMap: Map<string, string>;
|
private _classPartialMap: Map<string, SelectorMatcher>;
|
||||||
private _attrValueMap: Map<string, string>;
|
private _attrValueMap: Map<string, Map<string, List<string>>>;
|
||||||
private _attrValuePartialMap: Map<string, string>;
|
private _attrValuePartialMap: Map<string, Map<string, SelectorMatcher>>;
|
||||||
private _listContexts: List<SelectorListContext>;
|
private _listContexts: List<SelectorListContext>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -212,22 +212,28 @@ export class SelectorMatcher {
|
|||||||
var isTerminal = index === attrs.length - 2;
|
var isTerminal = index === attrs.length - 2;
|
||||||
var attrName = attrs[index++];
|
var attrName = attrs[index++];
|
||||||
var attrValue = attrs[index++];
|
var attrValue = attrs[index++];
|
||||||
var map = isTerminal ? matcher._attrValueMap : matcher._attrValuePartialMap;
|
|
||||||
var valuesMap = MapWrapper.get(map, attrName);
|
|
||||||
if (isBlank(valuesMap)) {
|
|
||||||
valuesMap = MapWrapper.create();
|
|
||||||
MapWrapper.set(map, attrName, valuesMap);
|
|
||||||
}
|
|
||||||
if (isTerminal) {
|
if (isTerminal) {
|
||||||
this._addTerminal(valuesMap, attrValue, selectable);
|
var terminalMap = matcher._attrValueMap;
|
||||||
|
var terminalValuesMap = MapWrapper.get(terminalMap, attrName);
|
||||||
|
if (isBlank(terminalValuesMap)) {
|
||||||
|
terminalValuesMap = MapWrapper.create();
|
||||||
|
MapWrapper.set(terminalMap, attrName, terminalValuesMap);
|
||||||
|
}
|
||||||
|
this._addTerminal(terminalValuesMap, attrValue, selectable);
|
||||||
} else {
|
} else {
|
||||||
matcher = this._addPartial(valuesMap, attrValue);
|
var parttialMap = matcher._attrValuePartialMap;
|
||||||
|
var partialValuesMap = MapWrapper.get(parttialMap, attrName);
|
||||||
|
if (isBlank(partialValuesMap)) {
|
||||||
|
partialValuesMap = MapWrapper.create();
|
||||||
|
MapWrapper.set(parttialMap, attrName, partialValuesMap);
|
||||||
|
}
|
||||||
|
matcher = this._addPartial(partialValuesMap, attrValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addTerminal(map: Map<string, string>, name: string, selectable: SelectorContext) {
|
private _addTerminal(map: Map<string, List<string>>, name: string, selectable: SelectorContext) {
|
||||||
var terminalList = MapWrapper.get(map, name);
|
var terminalList = MapWrapper.get(map, name);
|
||||||
if (isBlank(terminalList)) {
|
if (isBlank(terminalList)) {
|
||||||
terminalList = ListWrapper.create();
|
terminalList = ListWrapper.create();
|
||||||
@ -236,7 +242,7 @@ export class SelectorMatcher {
|
|||||||
ListWrapper.push(terminalList, selectable);
|
ListWrapper.push(terminalList, selectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addPartial(map: Map<string, string>, name: string) {
|
private _addPartial(map: Map<string, SelectorMatcher>, name: string): SelectorMatcher {
|
||||||
var matcher = MapWrapper.get(map, name);
|
var matcher = MapWrapper.get(map, name);
|
||||||
if (isBlank(matcher)) {
|
if (isBlank(matcher)) {
|
||||||
matcher = new SelectorMatcher();
|
matcher = new SelectorMatcher();
|
||||||
@ -282,22 +288,24 @@ export class SelectorMatcher {
|
|||||||
var attrName = attrs[index++];
|
var attrName = attrs[index++];
|
||||||
var attrValue = attrs[index++];
|
var attrValue = attrs[index++];
|
||||||
|
|
||||||
var valuesMap = MapWrapper.get(this._attrValueMap, attrName);
|
var terminalValuesMap = MapWrapper.get(this._attrValueMap, attrName);
|
||||||
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
||||||
result =
|
result = this._matchTerminal(terminalValuesMap, _EMPTY_ATTR_VALUE, cssSelector,
|
||||||
this._matchTerminal(valuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) ||
|
matchedCallback) ||
|
||||||
result;
|
result;
|
||||||
}
|
}
|
||||||
result = this._matchTerminal(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
result = this._matchTerminal(terminalValuesMap, attrValue, cssSelector, matchedCallback) ||
|
||||||
|
result;
|
||||||
|
|
||||||
valuesMap = MapWrapper.get(this._attrValuePartialMap, attrName);
|
var partialValuesMap = MapWrapper.get(this._attrValuePartialMap, attrName);
|
||||||
result = this._matchPartial(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
result =
|
||||||
|
this._matchPartial(partialValuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_matchTerminal(map: Map<string, string>, name, cssSelector: CssSelector,
|
_matchTerminal(map: Map<string, List<string>>, name, cssSelector: CssSelector,
|
||||||
matchedCallback /*: (CssSelector, any) => void*/): boolean {
|
matchedCallback /*: (CssSelector, any) => void*/): boolean {
|
||||||
if (isBlank(map) || isBlank(name)) {
|
if (isBlank(map) || isBlank(name)) {
|
||||||
return false;
|
return false;
|
||||||
@ -320,7 +328,7 @@ export class SelectorMatcher {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_matchPartial(map: Map<string, string>, name, cssSelector: CssSelector,
|
_matchPartial(map: Map<string, SelectorMatcher>, name, cssSelector: CssSelector,
|
||||||
matchedCallback /*: (CssSelector, any) => void*/): boolean {
|
matchedCallback /*: (CssSelector, any) => void*/): boolean {
|
||||||
if (isBlank(map) || isBlank(name)) {
|
if (isBlank(map) || isBlank(name)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -11,7 +11,7 @@ import {StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
|||||||
import {EventManagerPlugin} from './event_manager';
|
import {EventManagerPlugin} from './event_manager';
|
||||||
|
|
||||||
var modifierKeys = ['alt', 'control', 'meta', 'shift'];
|
var modifierKeys = ['alt', 'control', 'meta', 'shift'];
|
||||||
var modifierKeyGetters =
|
var modifierKeyGetters: StringMap<string, Function> =
|
||||||
{
|
{
|
||||||
'alt': (event) => event.altKey,
|
'alt': (event) => event.altKey,
|
||||||
'control': (event) => event.ctrlKey,
|
'control': (event) => event.ctrlKey,
|
||||||
|
@ -40,14 +40,15 @@ export class EmulatedScopedShadowDomStrategy extends EmulatedUnscopedShadowDomSt
|
|||||||
cssText = this.styleUrlResolver.resolveUrls(cssText, templateUrl);
|
cssText = this.styleUrlResolver.resolveUrls(cssText, templateUrl);
|
||||||
var inlinedCss = this.styleInliner.inlineImports(cssText, templateUrl);
|
var inlinedCss = this.styleInliner.inlineImports(cssText, templateUrl);
|
||||||
|
|
||||||
if (isPresent(inlinedCss.asyncResult)) {
|
if (PromiseWrapper.isPromise(inlinedCss)) {
|
||||||
DOM.setText(styleEl, '');
|
DOM.setText(styleEl, '');
|
||||||
return inlinedCss.asyncResult.then((css) => {
|
return (<Promise<string>>inlinedCss)
|
||||||
css = shimCssForComponent(css, hostComponentId);
|
.then((css) => {
|
||||||
DOM.setText(styleEl, css);
|
css = shimCssForComponent(css, hostComponentId);
|
||||||
});
|
DOM.setText(styleEl, css);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
var css = shimCssForComponent(inlinedCss.syncResult, hostComponentId);
|
var css = shimCssForComponent(<string>inlinedCss, hostComponentId);
|
||||||
DOM.setText(styleEl, css);
|
DOM.setText(styleEl, css);
|
||||||
DOM.remove(styleEl);
|
DOM.remove(styleEl);
|
||||||
insertStyleElement(this.styleHost, styleEl);
|
insertStyleElement(this.styleHost, styleEl);
|
||||||
|
@ -18,10 +18,6 @@ import {
|
|||||||
PromiseWrapper,
|
PromiseWrapper,
|
||||||
} from 'angular2/src/facade/async';
|
} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
export class SyncAsyncResult<T> {
|
|
||||||
constructor(public syncResult: T, public asyncResult: Promise<T>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline @import rules in the given CSS.
|
* Inline @import rules in the given CSS.
|
||||||
*
|
*
|
||||||
@ -48,18 +44,18 @@ export class StyleInliner {
|
|||||||
* @param {string} baseUrl
|
* @param {string} baseUrl
|
||||||
* @returns {*} a Promise<string> when @import rules are present, a string otherwise
|
* @returns {*} a Promise<string> when @import rules are present, a string otherwise
|
||||||
*/
|
*/
|
||||||
inlineImports(cssText: string, baseUrl: string): SyncAsyncResult<string> {
|
inlineImports(cssText: string, baseUrl: string): Promise<string>| string {
|
||||||
return this._inlineImports(cssText, baseUrl, []);
|
return this._inlineImports(cssText, baseUrl, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
_inlineImports(cssText: string, baseUrl: string,
|
_inlineImports(cssText: string, baseUrl: string, inlinedUrls: List<string>): Promise<string>|
|
||||||
inlinedUrls: List<string>): SyncAsyncResult<string> {
|
string {
|
||||||
var partIndex = 0;
|
var partIndex = 0;
|
||||||
var parts = StringWrapper.split(cssText, _importRe);
|
var parts = StringWrapper.split(cssText, _importRe);
|
||||||
|
|
||||||
if (parts.length === 1) {
|
if (parts.length === 1) {
|
||||||
// no @import rule found, return the original css
|
// no @import rule found, return the original css
|
||||||
return new SyncAsyncResult(cssText, null);
|
return cssText;
|
||||||
}
|
}
|
||||||
|
|
||||||
var promises = [];
|
var promises = [];
|
||||||
@ -87,14 +83,14 @@ export class StyleInliner {
|
|||||||
promise = PromiseWrapper.then(this._xhr.get(url), (rawCss) => {
|
promise = PromiseWrapper.then(this._xhr.get(url), (rawCss) => {
|
||||||
// resolve nested @import rules
|
// resolve nested @import rules
|
||||||
var inlinedCss = this._inlineImports(rawCss, url, inlinedUrls);
|
var inlinedCss = this._inlineImports(rawCss, url, inlinedUrls);
|
||||||
if (isPresent(inlinedCss.asyncResult)) {
|
if (PromiseWrapper.isPromise(inlinedCss)) {
|
||||||
// wait until nested @import are inlined
|
// wait until nested @import are inlined
|
||||||
return inlinedCss.asyncResult.then(
|
return (<Promise<string>>inlinedCss)
|
||||||
(css) => {return prefix + this._transformImportedCss(css, mediaQuery, url) + '\n'});
|
.then((css) => {return prefix + this._transformImportedCss(css, mediaQuery, url) +
|
||||||
|
'\n'});
|
||||||
} else {
|
} else {
|
||||||
// there are no nested @import, return the css
|
// there are no nested @import, return the css
|
||||||
return prefix + this._transformImportedCss(inlinedCss.syncResult, mediaQuery, url) +
|
return prefix + this._transformImportedCss(<string>inlinedCss, mediaQuery, url) + '\n';
|
||||||
'\n';
|
|
||||||
}
|
}
|
||||||
}, (error) => `/* failed to import ${url} */\n`);
|
}, (error) => `/* failed to import ${url} */\n`);
|
||||||
}
|
}
|
||||||
@ -102,14 +98,14 @@ export class StyleInliner {
|
|||||||
partIndex += 2;
|
partIndex += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SyncAsyncResult(null, PromiseWrapper.all(promises).then(function(cssParts) {
|
return PromiseWrapper.all(promises).then(function(cssParts) {
|
||||||
var cssText = cssParts.join('');
|
var cssText = cssParts.join('');
|
||||||
if (partIndex < parts.length) {
|
if (partIndex < parts.length) {
|
||||||
// append then content located after the last @import rule
|
// append then content located after the last @import rule
|
||||||
cssText += parts[partIndex];
|
cssText += parts[partIndex];
|
||||||
}
|
}
|
||||||
return cssText;
|
return cssText;
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_transformImportedCss(css: string, mediaQuery: string, url: string): string {
|
_transformImportedCss(css: string, mediaQuery: string, url: string): string {
|
||||||
|
@ -21,14 +21,14 @@ export class Rectangle {
|
|||||||
|
|
||||||
export class Ruler {
|
export class Ruler {
|
||||||
domAdapter: DomAdapter;
|
domAdapter: DomAdapter;
|
||||||
constructor(domAdapter: DomAdapter) {
|
constructor(domAdapter: DomAdapter) { this.domAdapter = domAdapter; }
|
||||||
this.domAdapter = domAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
measure(el:ElementRef): Promise<Rectangle> {
|
measure(el: ElementRef): Promise<Rectangle> {
|
||||||
var clntRect = this.domAdapter.getBoundingClientRect(el.domElement);
|
var clntRect = <any>this.domAdapter.getBoundingClientRect(el.domElement);
|
||||||
|
|
||||||
//even if getBoundingClientRect is synchronous we use async API in preparation for further changes
|
// even if getBoundingClientRect is synchronous we use async API in preparation for further
|
||||||
return PromiseWrapper.resolve(new Rectangle(clntRect.left, clntRect.top, clntRect.width, clntRect.height));
|
// changes
|
||||||
|
return PromiseWrapper.resolve(
|
||||||
|
new Rectangle(clntRect.left, clntRect.top, clntRect.width, clntRect.height));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,7 +32,7 @@ export function main() {
|
|||||||
it('should return a string when there is no import statement', inject([StyleInliner], (inliner) => {
|
it('should return a string when there is no import statement', inject([StyleInliner], (inliner) => {
|
||||||
var css = '.main {}';
|
var css = '.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
var loadedCss = inliner.inlineImports(css, 'http://base');
|
||||||
expect(loadedCss.syncResult).toEqual(css);
|
expect(loadedCss).toEqual(css);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should inline @import rules',
|
it('should inline @import rules',
|
||||||
@ -40,9 +40,9 @@ export function main() {
|
|||||||
xhr.reply('http://base/one.css', '.one {}');
|
xhr.reply('http://base/one.css', '.one {}');
|
||||||
var css = '@import url("one.css");.main {}';
|
var css = '@import url("one.css");.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
var loadedCss = inliner.inlineImports(css, 'http://base');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('.one {}\n.main {}');
|
expect(css).toEqual('.one {}\n.main {}');
|
||||||
async.done();
|
async.done();
|
||||||
@ -58,9 +58,9 @@ export function main() {
|
|||||||
xhr.reply('http://base/one.css', '.one {}');
|
xhr.reply('http://base/one.css', '.one {}');
|
||||||
var css = '@import url(one.css);.main {}';
|
var css = '@import url(one.css);.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
var loadedCss = inliner.inlineImports(css, 'http://base');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('.one {}\n.main {}');
|
expect(css).toEqual('.one {}\n.main {}');
|
||||||
async.done();
|
async.done();
|
||||||
@ -75,9 +75,9 @@ export function main() {
|
|||||||
inject([StyleInliner, AsyncTestCompleter], (inliner, async) => {
|
inject([StyleInliner, AsyncTestCompleter], (inliner, async) => {
|
||||||
var css = '@import "one.css";.main {}';
|
var css = '@import "one.css";.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
var loadedCss = inliner.inlineImports(css, 'http://base');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('/* failed to import http://base/one.css */\n.main {}');
|
expect(css).toEqual('/* failed to import http://base/one.css */\n.main {}');
|
||||||
async.done();
|
async.done();
|
||||||
@ -94,9 +94,9 @@ export function main() {
|
|||||||
xhr.reply('http://base/two.css', '.two {}');
|
xhr.reply('http://base/two.css', '.two {}');
|
||||||
var css = '@import "one.css";@import "two.css";.main {}';
|
var css = '@import "one.css";@import "two.css";.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base');
|
var loadedCss = inliner.inlineImports(css, 'http://base');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('.one {}\n.two {}\n.main {}');
|
expect(css).toEqual('.one {}\n.two {}\n.main {}');
|
||||||
async.done();
|
async.done();
|
||||||
@ -113,9 +113,9 @@ export function main() {
|
|||||||
xhr.reply('http://base/two.css', '.two {}');
|
xhr.reply('http://base/two.css', '.two {}');
|
||||||
var css = '@import "one.css";.main {}';
|
var css = '@import "one.css";.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('.two {}\n.one {}\n.main {}');
|
expect(css).toEqual('.two {}\n.one {}\n.main {}');
|
||||||
async.done();
|
async.done();
|
||||||
@ -132,9 +132,9 @@ export function main() {
|
|||||||
xhr.reply('http://base/two.css', '@import "one.css";.two {}');
|
xhr.reply('http://base/two.css', '@import "one.css";.two {}');
|
||||||
var css = '@import "one.css";.main {}';
|
var css = '@import "one.css";.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('.two {}\n.one {}\n.main {}');
|
expect(css).toEqual('.two {}\n.one {}\n.main {}');
|
||||||
async.done();
|
async.done();
|
||||||
@ -150,9 +150,9 @@ export function main() {
|
|||||||
// Invalid rule: the url is not quoted
|
// Invalid rule: the url is not quoted
|
||||||
var css = '@import one.css;.main {}';
|
var css = '@import one.css;.main {}';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('/* Invalid import rule: "@import one.css;" */.main {}');
|
expect(css).toEqual('/* Invalid import rule: "@import one.css;" */.main {}');
|
||||||
async.done();
|
async.done();
|
||||||
@ -170,9 +170,9 @@ export function main() {
|
|||||||
xhr.reply('http://base/one.css', '.one {}');
|
xhr.reply('http://base/one.css', '.one {}');
|
||||||
var css = '@import "one.css" (min-width: 700px) and (orientation: landscape);';
|
var css = '@import "one.css" (min-width: 700px) and (orientation: landscape);';
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual('@media (min-width: 700px) and (orientation: landscape) {\n.one {}\n}\n');
|
expect(css).toEqual('@media (min-width: 700px) and (orientation: landscape) {\n.one {}\n}\n');
|
||||||
async.done();
|
async.done();
|
||||||
@ -192,9 +192,9 @@ export function main() {
|
|||||||
xhr.reply('http://base/nested/two.css', '.two {background-image: url("../img/two.jpg");}');
|
xhr.reply('http://base/nested/two.css', '.two {background-image: url("../img/two.jpg");}');
|
||||||
var css = '@import "one.css";'
|
var css = '@import "one.css";'
|
||||||
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
var loadedCss = inliner.inlineImports(css, 'http://base/');
|
||||||
expect(loadedCss.asyncResult).toBePromise();
|
expect(loadedCss).toBePromise();
|
||||||
PromiseWrapper.then(
|
PromiseWrapper.then(
|
||||||
loadedCss.asyncResult,
|
loadedCss,
|
||||||
function(css) {
|
function(css) {
|
||||||
expect(css).toEqual(
|
expect(css).toEqual(
|
||||||
".two {background-image: url('http://base/img/two.jpg');}\n" +
|
".two {background-image: url('http://base/img/two.jpg');}\n" +
|
||||||
|
2
modules/angular2/traceur-runtime.d.ts
vendored
2
modules/angular2/traceur-runtime.d.ts
vendored
@ -37,6 +37,8 @@ interface Map<K, V> {
|
|||||||
clear(): void;
|
clear(): void;
|
||||||
delete (key: K): boolean;
|
delete (key: K): boolean;
|
||||||
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
|
forEach(callbackfn: (value: V, index: K, map: Map<K, V>) => void, thisArg?: any): void;
|
||||||
|
keys(): List<K>;
|
||||||
|
values(): List<V>;
|
||||||
get(key: K): V;
|
get(key: K): V;
|
||||||
has(key: K): boolean;
|
has(key: K): boolean;
|
||||||
set(key: K, value: V): Map<K, V>;
|
set(key: K, value: V): Map<K, V>;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user