refactor(compiler): remove `Viewport` directives, use `Decorator` instead
BREAKING_CHANGE: - The special type of `Viewport` directives is removed in favor of a more general `Decorator` directive - `ViewContainerRef` now no more has a default `ProtoViewRef` but requires an explicit one when creating views. Closes #1536
This commit is contained in:
parent
fb67e37339
commit
3aac2fefd7
|
@ -5,7 +5,7 @@ projected to DOM as well as which DOM events should invoke which methods on the
|
|||
syntax which is core to Angular and allows for data-binding, event-binding, template-instantiation.
|
||||
|
||||
The design of the template syntax has these properties:
|
||||
|
||||
|
||||
|
||||
* All data-binding expressions are easily identifiable. (i.e. there is never an ambiguity whether the value should be
|
||||
interpreted as string literal or as an expression.)
|
||||
|
@ -372,8 +372,8 @@ Where:
|
|||
inserted. The template can be defined implicitly with `template` attribute, which turns the current element into
|
||||
a template, or explicitly with `<template>` element. Explicit declaration is longer, but it allows for having
|
||||
templates which have more than one root DOM node.
|
||||
* `viewport` is required for templates. The Viewport directive is responsible for deciding when
|
||||
and in which order should child views be inserted into this location. An Viewport directive usually has one or
|
||||
* `viewport` is required for templates. The directive is responsible for deciding when
|
||||
and in which order should child views be inserted into this location. Such a directive usually has one or
|
||||
more bindings and can be represented as either `viewport-directive-bindings` or
|
||||
`viewport-directive-microsyntax` on `template` element or attribute. See template microsyntax for more details.
|
||||
|
||||
|
@ -387,7 +387,7 @@ Hello {{user}}!
|
|||
</div>
|
||||
```
|
||||
|
||||
In the above example the `if` Viewport determines whether the child view (an instance of the child template) should be
|
||||
In the above example the `if` directive determines whether the child view (an instance of the child template) should be
|
||||
inserted into the root view. The `if` makes this decision based on if the `isAdministrator` binding is true.
|
||||
|
||||
The above example is in the short form, for better clarity let's rewrite it in the canonical form, which is functionally
|
||||
|
@ -402,8 +402,6 @@ Hello {{user}}!
|
|||
</template>
|
||||
```
|
||||
|
||||
NOTE: Only Viewport directives can be placed on the template element. (Decorators and Components are not allowed.)
|
||||
|
||||
|
||||
### Template Microsyntax
|
||||
|
||||
|
@ -514,7 +512,7 @@ Where:
|
|||
* `some-element` Any element which can generate DOM events (or has an angular directive which generates the event).
|
||||
* `some-event` (escaped with `()` or `on-`) is the name of the event `some-event`. In this case the
|
||||
dash-case is converted into camel-case `someEvent`.
|
||||
* `statement` is a valid statement (as defined in section below).
|
||||
* `statement` is a valid statement (as defined in section below).
|
||||
If the execution of the statement returns `false`, then `preventDefault`is applied on the DOM event.
|
||||
|
||||
By default, angular only listens to the element on the event, and ignores events which bubble. To listen to bubbled
|
||||
|
|
|
@ -10,7 +10,6 @@ There are three different kinds of directives (described in more detail in later
|
|||
|
||||
1. *Decorators*: can be placed on any DOM element and can be combined with other directives.
|
||||
2. *Components*: Components have an encapsulated view and can configure injectors.
|
||||
3. *Viewport*: is responsible for adding or removing child views in a parent view. (i.e. for, if)
|
||||
|
||||
|
||||
|
||||
|
@ -163,21 +162,19 @@ Example of usage:
|
|||
|
||||
|
||||
|
||||
## Viewport
|
||||
## Directives that use a ViewContainer
|
||||
|
||||
Viewport is a directive which can control instantiation of child views which are then inserted into the DOM. (Examples are `if` and `for`.)
|
||||
Directives that use a ViewContainer can control instantiation of child views which are then inserted into the DOM. (Examples are `if` and `for`.)
|
||||
|
||||
* Viewports can only be placed on `<template>` elements (or the short hand version which uses `<element template>` attribute.)
|
||||
* Only one viewport can be present per DOM template element.
|
||||
* The viewport is created over the `template` element. This is known as the `ViewContainerRef`.
|
||||
* Viewport can insert child views into the `ViewContainerRef`. The child views show up as siblings of the `Viewport` in the DOM.
|
||||
* Every `template` element creates a `ProtoView` which can be used to create Views via the ViewContainer.
|
||||
* The child views show up as siblings of the directive in the DOM.
|
||||
|
||||
>> TODO(misko): Relationship with Injection
|
||||
>> TODO(misko): Instantiator can not be injected into child Views
|
||||
|
||||
|
||||
```
|
||||
@Viewport({
|
||||
@Directive({
|
||||
selector: '[if]',
|
||||
properties: {
|
||||
'condition': 'if'
|
||||
|
@ -185,17 +182,19 @@ Viewport is a directive which can control instantiation of child views which are
|
|||
})
|
||||
export class If {
|
||||
viewContainer: ViewContainerRef;
|
||||
protoViewRef: ProtoViewRef;
|
||||
view: View;
|
||||
|
||||
constructor(viewContainer: ViewContainerRef) {
|
||||
constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
this.viewContainer = viewContainer;
|
||||
this.protoViewRef = protoViewRef;
|
||||
this.view = null;
|
||||
}
|
||||
|
||||
set condition(value) {
|
||||
if (value) {
|
||||
if (this.view === null) {
|
||||
this.view = this.viewContainer.create();
|
||||
this.view = this.viewContainer.create(protoViewRef);
|
||||
}
|
||||
} else {
|
||||
if (this.view !== null) {
|
||||
|
@ -342,7 +341,7 @@ Shadow DOM provides an encapsulation for components, so as a general rule it doe
|
|||
})
|
||||
class Kid {
|
||||
constructor(
|
||||
@Parent() dad:Dad,
|
||||
@Parent() dad:Dad,
|
||||
@Optional() grandpa:Grandpa
|
||||
) {
|
||||
this.name = 'Billy';
|
||||
|
@ -365,7 +364,7 @@ class Dad {
|
|||
this.dad = dad.name;
|
||||
console.log(dad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: '[grandpa]',
|
||||
|
@ -379,17 +378,17 @@ class Grandpa {
|
|||
constructor() {
|
||||
this.name = 'Joe';
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `grandpa.html`: The Dad has access to the Grandpa.
|
||||
```
|
||||
Name: {{name}}: <br> Children: <div dad></div>
|
||||
Name: {{name}}: <br> Children: <div dad></div>
|
||||
```
|
||||
|
||||
Assume the following DOM structure for `dad.html`: Here the rendered Kid will also have access to Grandpa.
|
||||
```
|
||||
Name: {{name}}: <br> Dad: {{dad}} <br> Children: <div kid></div>
|
||||
Name: {{name}}: <br> Dad: {{dad}} <br> Children: <div kid></div>
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
This document explains the concept of a View.
|
||||
A View is a core primitive used by angular to render the DOM tree.
|
||||
A ViewPort is location in a View which can accept child Views.
|
||||
Every ViewPort has an associated ViewContainerRef than can contain any number of child Views.
|
||||
A ViewContainer is location in a View which can accept child Views.
|
||||
Every ViewContainer has an associated ViewContainerRef than can contain any number of child Views.
|
||||
Views form a tree structure which mimics the DOM tree.
|
||||
|
||||
* View is a core rendering construct. A running application is just a collection of Views which are
|
||||
|
@ -15,7 +15,7 @@ Views form a tree structure which mimics the DOM tree.
|
|||
* Views represent a running instance of a DOM View. This implies that while elements in a View
|
||||
can change properties, they can not change structurally. (Structural changes such as, adding or
|
||||
removing elements requires adding or removing child Views into ViewContainers).
|
||||
* View can have zero or more ViewPorts. A ViewPort is a marker in the DOM which allows
|
||||
* View can have zero or more ViewContainers. A ViewContainer is a marker in the DOM which allows
|
||||
the insertion of child Views.
|
||||
* Views are created from a ProtoView. A ProtoView is a compiled DOM View which is efficient at
|
||||
creating Views.
|
||||
|
@ -88,7 +88,7 @@ Note:
|
|||
## Composed View
|
||||
|
||||
An important part of an application is to be able to change the DOM structure to render data for the
|
||||
user. In Angular this is done by inserting child views into the ViewPort.
|
||||
user. In Angular this is done by inserting child views into the ViewContainer.
|
||||
|
||||
Let's start with a View such as:
|
||||
|
||||
|
@ -108,7 +108,7 @@ and
|
|||
|
||||
```
|
||||
<ul> | protoViewA(someContext)
|
||||
<template></template> | protoViewA(someContext): new ProtoViewPort(protoViewB)
|
||||
<template></template> | protoViewA(someContext): protoViewB
|
||||
</ul> | protoViewA(someContext)
|
||||
```
|
||||
|
||||
|
@ -119,7 +119,7 @@ The next step is to compose these two ProtoViews into an actual view which is re
|
|||
|
||||
```
|
||||
<ul> | viewA(someContext)
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewPort(protoViewB))
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewContainer(protoViewB))
|
||||
</ul> | viewA(someContext)
|
||||
```
|
||||
|
||||
|
@ -128,11 +128,11 @@ has a reference to `protoViewA`).
|
|||
|
||||
|
||||
*Step3:* As the `Foreach` directive unrolls it asks the `ViewContainerRef` to instantiate `protoViewB` and insert
|
||||
it after the `ViewPort` anchor. This is repeated for each `person` in `people`. Notice that
|
||||
it after the `ViewContainer` anchor. This is repeated for each `person` in `people`. Notice that
|
||||
|
||||
```
|
||||
<ul> | viewA(someContext)
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewPort(protoViewB))
|
||||
<template></template> | viewA(someContext): new Foreach(new ViewContainer(protoViewB))
|
||||
<li>{{person}}</li> | viewB0(locals0(someContext))
|
||||
<li>{{person}}</li> | viewB1(locals0(someContext))
|
||||
</ul> | viewA(someContext)
|
||||
|
@ -145,13 +145,13 @@ delegate any unknown references to the parent context.
|
|||
|
||||
```
|
||||
<ul> | viewA
|
||||
<template></template> | viewA: new Foreach(new ViewPort(protoViewB))
|
||||
<template></template> | viewA: new Foreach(new ViewContainer(protoViewB))
|
||||
<li>Alice</li> | viewB0
|
||||
<li>Bob</li> | viewB1
|
||||
</ul> | viewA
|
||||
```
|
||||
|
||||
Each View can have zero or more ViewPorts. By inserting and removing child Views to and from the
|
||||
Each View can have zero or more ViewContainers. By inserting and removing child Views to and from the
|
||||
ViewContainers, the application can mutate the DOM structure to any desirable state. A View may contain
|
||||
individual nodes or a complex DOM structure. The insertion points for the child Views, known as
|
||||
ViewContainers, contain a DOM element which acts as an anchor. The anchor is either a `template` or
|
||||
|
@ -161,7 +161,7 @@ inserted.
|
|||
## Component Views
|
||||
|
||||
A View can also contain Components. Components contain Shadow DOM for encapsulating their internal
|
||||
rendering state. Unlike ViewPorts which can contain zero or more Views, the Component always contains
|
||||
rendering state. Unlike ViewContainers which can contain zero or more Views, the Component always contains
|
||||
exactly one Shadow View.
|
||||
|
||||
```
|
||||
|
|
|
@ -8,8 +8,7 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||
/**
|
||||
* Directives allow you to attach behavior to elements in the DOM.
|
||||
*
|
||||
* Directive is an abstract concept, instead use concrete directives: {@link Component}, {@link DynamicComponent}, {@link Decorator}
|
||||
* or {@link Viewport}.
|
||||
* Directive is an abstract concept, instead use concrete directives: {@link Component}, {@link DynamicComponent}, {@link Decorator}.
|
||||
*
|
||||
* 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:
|
||||
|
@ -55,7 +54,7 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||
*
|
||||
* To inject element-specific special objects, declare the constructor parameter as:
|
||||
* - `element: ElementRef` to obtain a reference to logical element in the view.
|
||||
* - `viewContainer: ViewContainerRef` to control child template instantiation, for {@link Viewport} directives only
|
||||
* - `viewContainer: ViewContainerRef` to control child template instantiation, for {@link Decorator} directives only
|
||||
* - `bindingPropagation: BindingPropagation` to control change detection in a more granular way.
|
||||
*
|
||||
* ## Example
|
||||
|
@ -188,8 +187,8 @@ import {DEFAULT} from 'angular2/change_detection';
|
|||
*
|
||||
* 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
|
||||
* injects a {@link QueryList}, which updates its contents as children are added, removed, or moved by any
|
||||
* {@link Viewport} directive such as a `for`, an `if`, or a `switch`.
|
||||
* 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`.
|
||||
*
|
||||
* ```
|
||||
* @Decorator({ selector: '[my-directive]' })
|
||||
|
@ -783,6 +782,98 @@ export class DynamicComponent extends Directive {
|
|||
* <div tooltip="some text here"></div>
|
||||
* ```
|
||||
*
|
||||
* Decorators 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.
|
||||
* The {@link ViewContainerRef} is created as a result of `<template>` element, and represents a location in the current view
|
||||
* 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
|
||||
* 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
|
||||
* also supports a shorthand notation: `<li *foo="bar">` and `<li template="foo: bar">` are equivalent.
|
||||
*
|
||||
* Thus,
|
||||
*
|
||||
* ```
|
||||
* <ul>
|
||||
* <li *foo="bar" title="text"></li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Expands in use to:
|
||||
*
|
||||
* ```
|
||||
* <ul>
|
||||
* <template [foo]="bar">
|
||||
* <li title="text"></li>
|
||||
* </template>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Let's suppose we want to implement the `unless` behavior, to conditionally include a template.
|
||||
*
|
||||
* Here is a simple directive that triggers on an `unless` selector:
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: '[unless]',
|
||||
* properties: {
|
||||
* 'unless': 'unless'
|
||||
* }
|
||||
* })
|
||||
* export class Unless {
|
||||
* viewContainer: ViewContainerRef;
|
||||
* protoViewRef: ProtoViewRef;
|
||||
* prevCondition: boolean;
|
||||
*
|
||||
* constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
* this.viewContainer = viewContainer;
|
||||
* this.protoViewRef = protoViewRef;
|
||||
* this.prevCondition = null;
|
||||
* }
|
||||
*
|
||||
* set unless(newCondition) {
|
||||
* if (newCondition && (isBlank(this.prevCondition) || !this.prevCondition)) {
|
||||
* this.prevCondition = true;
|
||||
* this.viewContainer.clear();
|
||||
* } else if (!newCondition && (isBlank(this.prevCondition) || this.prevCondition)) {
|
||||
* this.prevCondition = false;
|
||||
* this.viewContainer.create(this.protoViewRef);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We can then use this `unless` selector in a template:
|
||||
* ```
|
||||
* <ul>
|
||||
* <li *unless="expr"></li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Once the directive instantiates the child view, the shorthand notation for the template expands and the result is:
|
||||
*
|
||||
* ```
|
||||
* <ul>
|
||||
* <template [unless]="exp">
|
||||
* <li></li>
|
||||
* </template>
|
||||
* <li></li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class Decorator extends Directive {
|
||||
|
@ -824,127 +915,6 @@ export class Decorator extends Directive {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directive that controls the instantiation, destruction, and positioning of inline template elements.
|
||||
*
|
||||
* A viewport directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at 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.
|
||||
*
|
||||
* 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 viewport directive that created it.
|
||||
*
|
||||
* Since viewport directives are common in Angular, and using the full `<template>` element syntax is wordy, Angular
|
||||
* also supports a shorthand notation: `<li *foo="bar">` and `<li template="foo: bar">` are equivalent.
|
||||
*
|
||||
* Thus,
|
||||
*
|
||||
* ```
|
||||
* <ul>
|
||||
* <li *foo="bar" title="text"></li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Expands in use to:
|
||||
*
|
||||
* ```
|
||||
* <ul>
|
||||
* <template [foo]="bar">
|
||||
* <li title="text"></li>
|
||||
* </template>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Notice that although the shorthand places `*foo="bar"` within the `<li>` element, the binding for the `Viewport`
|
||||
* controller is correctly instantiated on the `<template>` element rather than the `<li>` element.
|
||||
*
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* Let's suppose we want to implement the `unless` behavior, to conditionally include a template.
|
||||
*
|
||||
* Here is a simple viewport directive that triggers on an `unless` selector:
|
||||
*
|
||||
* ```
|
||||
* @Viewport({
|
||||
* selector: '[unless]',
|
||||
* properties: {
|
||||
* 'unless': 'unless'
|
||||
* }
|
||||
* })
|
||||
* export class Unless {
|
||||
* viewContainer: ViewContainerRef;
|
||||
* prevCondition: boolean;
|
||||
*
|
||||
* constructor(viewContainer: ViewContainerRef) {
|
||||
* this.viewContainer = viewContainer;
|
||||
* this.prevCondition = null;
|
||||
* }
|
||||
*
|
||||
* set unless(newCondition) {
|
||||
* if (newCondition && (isBlank(this.prevCondition) || !this.prevCondition)) {
|
||||
* this.prevCondition = true;
|
||||
* this.viewContainer.clear();
|
||||
* } else if (!newCondition && (isBlank(this.prevCondition) || this.prevCondition)) {
|
||||
* this.prevCondition = false;
|
||||
* this.viewContainer.create();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* We can then use this `unless` selector in a template:
|
||||
* ```
|
||||
* <ul>
|
||||
* <li *unless="expr"></li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Once the viewport instantiates the child view, the shorthand notation for the template expands and the result is:
|
||||
*
|
||||
* ```
|
||||
* <ul>
|
||||
* <template [unless]="exp">
|
||||
* <li></li>
|
||||
* </template>
|
||||
* <li></li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* @exportedAs angular2/annotations
|
||||
*/
|
||||
export class Viewport extends Directive {
|
||||
@CONST()
|
||||
constructor({
|
||||
selector,
|
||||
properties,
|
||||
events,
|
||||
hostListeners,
|
||||
hostProperties,
|
||||
lifecycle
|
||||
}:{
|
||||
selector:string,
|
||||
properties:any,
|
||||
hostListeners:any,
|
||||
hostProperties:any,
|
||||
events:List,
|
||||
lifecycle:List
|
||||
}={})
|
||||
{
|
||||
super({
|
||||
selector: selector,
|
||||
properties: properties,
|
||||
events: events,
|
||||
hostListeners: hostListeners,
|
||||
hostProperties: hostProperties,
|
||||
lifecycle: lifecycle
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//TODO(misko): turn into LifecycleEvent class once we switch to TypeScript;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
|||
import {List, ListWrapper, Map, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
import {Component, Viewport, DynamicComponent, Decorator} from '../annotations_impl/annotations';
|
||||
import {Component, DynamicComponent, Decorator} from '../annotations_impl/annotations';
|
||||
import {AppProtoView} from './view';
|
||||
import {ProtoViewRef} from './view_ref';
|
||||
import {DirectiveBinding} from './element_injector';
|
||||
|
@ -233,8 +233,6 @@ export class Compiler {
|
|||
var compileChildren = true;
|
||||
if ((ann instanceof Component) || (ann instanceof DynamicComponent)) {
|
||||
renderType = renderApi.DirectiveMetadata.COMPONENT_TYPE;
|
||||
} else if (ann instanceof Viewport) {
|
||||
renderType = renderApi.DirectiveMetadata.VIEWPORT_TYPE;
|
||||
} else if (ann instanceof Decorator) {
|
||||
renderType = renderApi.DirectiveMetadata.DECORATOR_TYPE;
|
||||
compileChildren = ann.compileChildren;
|
||||
|
|
|
@ -87,7 +87,7 @@ export class DynamicComponentLoader {
|
|||
var binding = this._getBinding(typeOrBinding);
|
||||
return this._compiler.compileInHost(binding).then(hostProtoViewRef => {
|
||||
var viewContainer = this._viewManager.getViewContainer(location);
|
||||
var hostViewRef = viewContainer.create(-1, hostProtoViewRef, injector);
|
||||
var hostViewRef = viewContainer.create(hostProtoViewRef, viewContainer.length, injector);
|
||||
var newLocation = new ElementRef(hostViewRef, 0);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import * as viewModule from './view';
|
|||
export class ElementBinder {
|
||||
protoElementInjector:eiModule.ProtoElementInjector;
|
||||
componentDirective:DirectiveBinding;
|
||||
viewportDirective:DirectiveBinding;
|
||||
nestedProtoView: viewModule.AppProtoView;
|
||||
hostListeners:StringMap;
|
||||
parent:ElementBinder;
|
||||
|
@ -15,15 +14,13 @@ export class ElementBinder {
|
|||
distanceToParent:int;
|
||||
constructor(
|
||||
index:int, parent:ElementBinder, distanceToParent: int,
|
||||
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveBinding,
|
||||
viewportDirective:DirectiveBinding) {
|
||||
protoElementInjector: eiModule.ProtoElementInjector, componentDirective:DirectiveBinding) {
|
||||
if (isBlank(index)) {
|
||||
throw new BaseException('null index not allowed.');
|
||||
}
|
||||
|
||||
this.protoElementInjector = protoElementInjector;
|
||||
this.componentDirective = componentDirective;
|
||||
this.viewportDirective = viewportDirective;
|
||||
this.parent = parent;
|
||||
this.index = index;
|
||||
this.distanceToParent = distanceToParent;
|
||||
|
@ -40,4 +37,8 @@ export class ElementBinder {
|
|||
hasDynamicComponent() {
|
||||
return isPresent(this.componentDirective) && isBlank(this.nestedProtoView);
|
||||
}
|
||||
|
||||
hasEmbeddedProtoView() {
|
||||
return !isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -638,7 +638,7 @@ export class ElementInjector extends TreeNode {
|
|||
}
|
||||
|
||||
getViewContainerRef() {
|
||||
return new ViewContainerRef(this._preBuiltObjects.viewManager, this.getElementRef(), new ProtoViewRef(this._preBuiltObjects.protoView));
|
||||
return new ViewContainerRef(this._preBuiltObjects.viewManager, this.getElementRef());
|
||||
}
|
||||
|
||||
getDynamicallyLoadedComponent() {
|
||||
|
@ -719,6 +719,9 @@ export class ElementInjector extends TreeNode {
|
|||
return this.getViewContainerRef();
|
||||
}
|
||||
if (dep.key.id === StaticKeys.instance().protoViewId) {
|
||||
if (isBlank(this._preBuiltObjects.protoView)) {
|
||||
throw new NoBindingError(dep.key);
|
||||
}
|
||||
return new ProtoViewRef(this._preBuiltObjects.protoView);
|
||||
}
|
||||
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
||||
|
|
|
@ -4,7 +4,7 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
|||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
import {ChangeDetection, DirectiveIndex} from 'angular2/change_detection';
|
||||
import {Component, Viewport, DynamicComponent} from '../annotations_impl/annotations';
|
||||
import {Component, DynamicComponent} from '../annotations_impl/annotations';
|
||||
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
import {AppProtoView} from './view';
|
||||
|
@ -81,8 +81,7 @@ export class ProtoViewFactory {
|
|||
isPresent(sortedDirectives.componentDirective), parentPeiWithDistance.distance
|
||||
);
|
||||
protoElementInjector.attributes = renderElementBinder.readAttributes;
|
||||
// Viewport directives are treated differently than other element with var- definitions.
|
||||
if (hasVariables && !isPresent(sortedDirectives.viewportDirective)) {
|
||||
if (hasVariables) {
|
||||
protoElementInjector.exportComponent = isPresent(sortedDirectives.componentDirective);
|
||||
protoElementInjector.exportElement = isBlank(sortedDirectives.componentDirective);
|
||||
|
||||
|
@ -105,8 +104,7 @@ export class ProtoViewFactory {
|
|||
parent,
|
||||
renderElementBinder.distanceToParent,
|
||||
protoElementInjector,
|
||||
sortedDirectives.componentDirective,
|
||||
sortedDirectives.viewportDirective
|
||||
sortedDirectives.componentDirective
|
||||
);
|
||||
// text nodes
|
||||
for (var i=0; i<renderElementBinder.textBindings.length; i++) {
|
||||
|
@ -155,14 +153,12 @@ export class ProtoViewFactory {
|
|||
|
||||
class SortedDirectives {
|
||||
componentDirective: DirectiveBinding;
|
||||
viewportDirective: DirectiveBinding;
|
||||
renderDirectives: List<renderApi.DirectiveBinder>;
|
||||
directives: List<DirectiveBinding>;
|
||||
|
||||
constructor(renderDirectives, allDirectives) {
|
||||
this.renderDirectives = [];
|
||||
this.directives = [];
|
||||
this.viewportDirective = null;
|
||||
this.componentDirective = null;
|
||||
ListWrapper.forEach(renderDirectives, (renderDirectiveBinder) => {
|
||||
var directiveBinding = allDirectives[renderDirectiveBinder.directiveIndex];
|
||||
|
@ -172,9 +168,6 @@ class SortedDirectives {
|
|||
ListWrapper.insert(this.renderDirectives, 0, renderDirectiveBinder);
|
||||
ListWrapper.insert(this.directives, 0, directiveBinding);
|
||||
} else {
|
||||
if (directiveBinding.annotation instanceof Viewport) {
|
||||
this.viewportDirective = directiveBinding;
|
||||
}
|
||||
ListWrapper.push(this.renderDirectives, renderDirectiveBinder);
|
||||
ListWrapper.push(this.directives, directiveBinding);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import {BaseQueryList} from './base_query_list';
|
|||
* 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
|
||||
* template with `*for="of"` viewport.
|
||||
* template with `*for="of"` directive.
|
||||
*
|
||||
* NOTE: In the future this class will implement an `Observable` interface. For now it uses a plain list of observable
|
||||
* callbacks.
|
||||
|
|
|
@ -235,9 +235,9 @@ export class AppProtoView {
|
|||
}
|
||||
|
||||
bindElement(parent:ElementBinder, distanceToParent:int, protoElementInjector:ProtoElementInjector,
|
||||
componentDirective:DirectiveBinding = null, viewportDirective:DirectiveBinding = null):ElementBinder {
|
||||
componentDirective:DirectiveBinding = null):ElementBinder {
|
||||
var elBinder = new ElementBinder(this.elementBinders.length, parent, distanceToParent,
|
||||
protoElementInjector, componentDirective, viewportDirective);
|
||||
protoElementInjector, componentDirective);
|
||||
ListWrapper.push(this.elementBinders, elBinder);
|
||||
return elBinder;
|
||||
}
|
||||
|
|
|
@ -10,14 +10,11 @@ import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
|||
export class ViewContainerRef {
|
||||
_viewManager: avmModule.AppViewManager;
|
||||
_element: ElementRef;
|
||||
_defaultProtoViewRef: ProtoViewRef;
|
||||
|
||||
constructor(viewManager: avmModule.AppViewManager,
|
||||
element: ElementRef,
|
||||
defaultProtoViewRef: ProtoViewRef) {
|
||||
element: ElementRef) {
|
||||
this._viewManager = viewManager;
|
||||
this._element = element;
|
||||
this._defaultProtoViewRef = defaultProtoViewRef;
|
||||
}
|
||||
|
||||
_getViews() {
|
||||
|
@ -41,11 +38,8 @@ export class ViewContainerRef {
|
|||
|
||||
// TODO(rado): profile and decide whether bounds checks should be added
|
||||
// to the methods below.
|
||||
create(atIndex:number=-1, protoViewRef:ProtoViewRef = null, injector:Injector = null): ViewRef {
|
||||
create(protoViewRef:ProtoViewRef = null, atIndex:number=-1, injector:Injector = null): ViewRef {
|
||||
if (atIndex == -1) atIndex = this.length;
|
||||
if (isBlank(protoViewRef)) {
|
||||
protoViewRef = this._defaultProtoViewRef;
|
||||
}
|
||||
return this._viewManager.createViewInContainer(this._element, atIndex, protoViewRef, injector);
|
||||
}
|
||||
|
||||
|
|
|
@ -56,8 +56,8 @@ export class AppViewManagerUtils {
|
|||
|
||||
// preBuiltObjects
|
||||
if (isPresent(elementInjector)) {
|
||||
var defaultProtoView = isPresent(binder.viewportDirective) ? binder.nestedProtoView : null;
|
||||
preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(viewManager, view, defaultProtoView);
|
||||
var embeddedProtoView = binder.hasEmbeddedProtoView() ? binder.nestedProtoView : null;
|
||||
preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(viewManager, view, embeddedProtoView);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Viewport} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Decorator} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
|
||||
import {ViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
import {ViewRef, ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
|
@ -36,7 +36,7 @@ import {ListWrapper} from 'angular2/src/facade/collection';
|
|||
*
|
||||
* @exportedAs angular2/directives
|
||||
*/
|
||||
@Viewport({
|
||||
@Decorator({
|
||||
selector: '[for][of]',
|
||||
properties: {
|
||||
'iterableChanges': 'of | iterableDiff'
|
||||
|
@ -44,8 +44,10 @@ import {ListWrapper} from 'angular2/src/facade/collection';
|
|||
})
|
||||
export class For {
|
||||
viewContainer: ViewContainerRef;
|
||||
constructor(viewContainer:ViewContainerRef) {
|
||||
protoViewRef: ProtoViewRef;
|
||||
constructor(viewContainer:ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
this.viewContainer = viewContainer;
|
||||
this.protoViewRef = protoViewRef;
|
||||
}
|
||||
|
||||
set iterableChanges(changes) {
|
||||
|
@ -71,7 +73,7 @@ export class For {
|
|||
(addedRecord) => ListWrapper.push(insertTuples, new RecordViewTuple(addedRecord, null))
|
||||
);
|
||||
|
||||
For.bulkInsert(insertTuples, this.viewContainer);
|
||||
For.bulkInsert(insertTuples, this.viewContainer, this.protoViewRef);
|
||||
|
||||
for (var i = 0; i < insertTuples.length; i++) {
|
||||
this.perViewChange(insertTuples[i].view, insertTuples[i].record);
|
||||
|
@ -99,14 +101,14 @@ export class For {
|
|||
return movedTuples;
|
||||
}
|
||||
|
||||
static bulkInsert(tuples, viewContainer) {
|
||||
static bulkInsert(tuples, viewContainer, protoViewRef) {
|
||||
tuples.sort((a, b) => a.record.currentIndex - b.record.currentIndex);
|
||||
for (var i = 0; i < tuples.length; i++) {
|
||||
var tuple = tuples[i];
|
||||
if (isPresent(tuple.view)) {
|
||||
viewContainer.insert(tuple.view, tuple.record.currentIndex);
|
||||
} else {
|
||||
tuple.view = viewContainer.create(tuple.record.currentIndex);
|
||||
tuple.view = viewContainer.create(protoViewRef, tuple.record.currentIndex);
|
||||
}
|
||||
}
|
||||
return tuples;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {Viewport} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Decorator} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
|
||||
import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
|
@ -25,7 +26,7 @@ import {isBlank} from 'angular2/src/facade/lang';
|
|||
*
|
||||
* @exportedAs angular2/directives
|
||||
*/
|
||||
@Viewport({
|
||||
@Decorator({
|
||||
selector: '[if]',
|
||||
properties: {
|
||||
'condition': 'if'
|
||||
|
@ -33,17 +34,19 @@ import {isBlank} from 'angular2/src/facade/lang';
|
|||
})
|
||||
export class If {
|
||||
viewContainer: ViewContainerRef;
|
||||
protoViewRef: ProtoViewRef;
|
||||
prevCondition: boolean;
|
||||
|
||||
constructor(viewContainer: ViewContainerRef) {
|
||||
constructor(viewContainer: ViewContainerRef, protoViewRef:ProtoViewRef) {
|
||||
this.viewContainer = viewContainer;
|
||||
this.prevCondition = null;
|
||||
this.protoViewRef = protoViewRef;
|
||||
}
|
||||
|
||||
set condition(newCondition /* boolean */) {
|
||||
if (newCondition && (isBlank(this.prevCondition) || !this.prevCondition)) {
|
||||
this.prevCondition = true;
|
||||
this.viewContainer.create();
|
||||
this.viewContainer.create(this.protoViewRef);
|
||||
} else if (!newCondition && (isBlank(this.prevCondition) || this.prevCondition)) {
|
||||
this.prevCondition = false;
|
||||
this.viewContainer.clear();
|
||||
|
|
|
@ -1,9 +1,28 @@
|
|||
import {Decorator, Viewport} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Decorator} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
|
||||
import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
import {isPresent, isBlank, normalizeBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, List, MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
import {Parent} from 'angular2/src/core/annotations_impl/visibility';
|
||||
|
||||
class SwitchView {
|
||||
_viewContainerRef: ViewContainerRef;
|
||||
_protoViewRef: ProtoViewRef;
|
||||
|
||||
constructor(viewContainerRef: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
this._protoViewRef = protoViewRef;
|
||||
this._viewContainerRef = viewContainerRef;
|
||||
}
|
||||
|
||||
create() {
|
||||
this._viewContainerRef.create(this._protoViewRef);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._viewContainerRef.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Switch` directive is used to conditionally swap DOM structure on your template based on a
|
||||
* scope expression.
|
||||
|
@ -40,93 +59,94 @@ import {Parent} from 'angular2/src/core/annotations_impl/visibility';
|
|||
export class Switch {
|
||||
_switchValue: any;
|
||||
_useDefault: boolean;
|
||||
_valueViewContainers: Map;
|
||||
_activeViewContainers: List<ViewContainerRef>;
|
||||
_valueViews: Map;
|
||||
_activeViews: List<SwitchView>;
|
||||
|
||||
constructor() {
|
||||
this._valueViewContainers = MapWrapper.create();
|
||||
this._activeViewContainers = ListWrapper.create();
|
||||
this._valueViews = MapWrapper.create();
|
||||
this._activeViews = ListWrapper.create();
|
||||
this._useDefault = false;
|
||||
}
|
||||
|
||||
set value(value) {
|
||||
// Empty the currently active ViewContainers
|
||||
this._emptyAllActiveViewContainers();
|
||||
this._emptyAllActiveViews();
|
||||
|
||||
// Add the ViewContainers matching the value (with a fallback to default)
|
||||
this._useDefault = false;
|
||||
var containers = MapWrapper.get(this._valueViewContainers, value);
|
||||
if (isBlank(containers)) {
|
||||
var views = MapWrapper.get(this._valueViews, value);
|
||||
if (isBlank(views)) {
|
||||
this._useDefault = true;
|
||||
containers = normalizeBlank(MapWrapper.get(this._valueViewContainers, _whenDefault));
|
||||
views = normalizeBlank(MapWrapper.get(this._valueViews, _whenDefault));
|
||||
}
|
||||
this._activateViewContainers(containers);
|
||||
this._activateViews(views);
|
||||
|
||||
this._switchValue = value;
|
||||
}
|
||||
|
||||
_onWhenValueChanged(oldWhen, newWhen, viewContainer: ViewContainerRef):void {
|
||||
this._deregisterViewContainer(oldWhen, viewContainer);
|
||||
this._registerViewContainer(newWhen, viewContainer);
|
||||
_onWhenValueChanged(oldWhen, newWhen, view: SwitchView):void {
|
||||
this._deregisterView(oldWhen, view);
|
||||
this._registerView(newWhen, view);
|
||||
|
||||
if (oldWhen === this._switchValue) {
|
||||
viewContainer.remove();
|
||||
ListWrapper.remove(this._activeViewContainers, viewContainer);
|
||||
view.destroy();
|
||||
ListWrapper.remove(this._activeViews, view);
|
||||
} else if (newWhen === this._switchValue) {
|
||||
if (this._useDefault) {
|
||||
this._useDefault = false;
|
||||
this._emptyAllActiveViewContainers();
|
||||
this._emptyAllActiveViews();
|
||||
}
|
||||
viewContainer.create();
|
||||
ListWrapper.push(this._activeViewContainers, viewContainer);
|
||||
view.create();
|
||||
ListWrapper.push(this._activeViews, view);
|
||||
}
|
||||
|
||||
// Switch to default when there is no more active ViewContainers
|
||||
if (this._activeViewContainers.length === 0 && !this._useDefault) {
|
||||
if (this._activeViews.length === 0 && !this._useDefault) {
|
||||
this._useDefault = true;
|
||||
this._activateViewContainers(MapWrapper.get(this._valueViewContainers, _whenDefault));
|
||||
this._activateViews(MapWrapper.get(this._valueViews, _whenDefault));
|
||||
}
|
||||
}
|
||||
|
||||
_emptyAllActiveViewContainers():void {
|
||||
var activeContainers = this._activeViewContainers;
|
||||
_emptyAllActiveViews():void {
|
||||
var activeContainers = this._activeViews;
|
||||
for (var i = 0; i < activeContainers.length; i++) {
|
||||
activeContainers[i].remove();
|
||||
activeContainers[i].destroy();
|
||||
}
|
||||
this._activeViewContainers = ListWrapper.create();
|
||||
this._activeViews = ListWrapper.create();
|
||||
}
|
||||
|
||||
_activateViewContainers(containers: List<ViewContainerRef>):void {
|
||||
// TODO(vicb): assert(this._activeViewContainers.length === 0);
|
||||
if (isPresent(containers)) {
|
||||
for (var i = 0; i < containers.length; i++) {
|
||||
containers[i].create();
|
||||
_activateViews(views: List<SwitchView>):void {
|
||||
// TODO(vicb): assert(this._activeViews.length === 0);
|
||||
if (isPresent(views)) {
|
||||
for (var i = 0; i < views.length; i++) {
|
||||
views[i].create();
|
||||
}
|
||||
this._activeViewContainers = containers;
|
||||
this._activeViews = views;
|
||||
}
|
||||
}
|
||||
|
||||
_registerViewContainer(value, container: ViewContainerRef): void {
|
||||
var containers = MapWrapper.get(this._valueViewContainers, value);
|
||||
if (isBlank(containers)) {
|
||||
containers = ListWrapper.create();
|
||||
MapWrapper.set(this._valueViewContainers, value, containers);
|
||||
_registerView(value, view: SwitchView): void {
|
||||
var views = MapWrapper.get(this._valueViews, value);
|
||||
if (isBlank(views)) {
|
||||
views = ListWrapper.create();
|
||||
MapWrapper.set(this._valueViews, value, views);
|
||||
}
|
||||
ListWrapper.push(containers, container);
|
||||
ListWrapper.push(views, view);
|
||||
}
|
||||
|
||||
_deregisterViewContainer(value, container: ViewContainerRef):void {
|
||||
_deregisterView(value, view: SwitchView):void {
|
||||
// `_whenDefault` is used a marker for non-registered whens
|
||||
if (value == _whenDefault) return;
|
||||
var containers = MapWrapper.get(this._valueViewContainers, value);
|
||||
if (containers.length == 1) {
|
||||
MapWrapper.delete(this._valueViewContainers, value);
|
||||
var views = MapWrapper.get(this._valueViews, value);
|
||||
if (views.length == 1) {
|
||||
MapWrapper.delete(this._valueViews, value);
|
||||
} else {
|
||||
ListWrapper.remove(containers, container);
|
||||
ListWrapper.remove(views, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines a case statement as an expression.
|
||||
*
|
||||
|
@ -144,7 +164,7 @@ export class Switch {
|
|||
*
|
||||
* @exportedAs angular2/directives
|
||||
*/
|
||||
@Viewport({
|
||||
@Decorator({
|
||||
selector: '[switch-when]',
|
||||
properties: {
|
||||
'when' : 'switch-when'
|
||||
|
@ -153,17 +173,21 @@ export class Switch {
|
|||
export class SwitchWhen {
|
||||
_value: any;
|
||||
_switch: Switch;
|
||||
_viewContainer: ViewContainerRef;
|
||||
_view: SwitchView;
|
||||
|
||||
constructor(viewContainer: ViewContainerRef, @Parent() sswitch: Switch) {
|
||||
constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef, @Parent() sswitch: Switch) {
|
||||
// `_whenDefault` is used as a marker for a not yet initialized value
|
||||
this._value = _whenDefault;
|
||||
this._switch = sswitch;
|
||||
this._viewContainer = viewContainer;
|
||||
this._view = new SwitchView(viewContainer, protoViewRef);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this._switch
|
||||
}
|
||||
|
||||
set when(value) {
|
||||
this._switch._onWhenValueChanged(this._value, value, this._viewContainer);
|
||||
this._switch._onWhenValueChanged(this._value, value, this._view);
|
||||
this._value = value;
|
||||
}
|
||||
}
|
||||
|
@ -182,12 +206,12 @@ export class SwitchWhen {
|
|||
*
|
||||
* @exportedAs angular2/directives
|
||||
*/
|
||||
@Viewport({
|
||||
@Decorator({
|
||||
selector: '[switch-default]'
|
||||
})
|
||||
export class SwitchDefault {
|
||||
constructor(viewContainer: ViewContainerRef, @Parent() sswitch: Switch) {
|
||||
sswitch._registerViewContainer(_whenDefault, viewContainer);
|
||||
constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef, @Parent() sswitch: Switch) {
|
||||
sswitch._registerView(_whenDefault, new SwitchView(viewContainer, protoViewRef));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ export class ProtoViewDto {
|
|||
// The view of the component
|
||||
// Can contain 0 to n views of type #EMBEDDED_VIEW_TYPE
|
||||
static get COMPONENT_VIEW_TYPE() { return 1; }
|
||||
// A view that is included via a Viewport directive
|
||||
// A view that is embedded into another View via a <template> element
|
||||
// inside of a component view
|
||||
static get EMBEDDED_VIEW_TYPE() { return 1; }
|
||||
|
||||
|
@ -111,7 +111,6 @@ export class ProtoViewDto {
|
|||
export class DirectiveMetadata {
|
||||
static get DECORATOR_TYPE() { return 0; }
|
||||
static get COMPONENT_TYPE() { return 1; }
|
||||
static get VIEWPORT_TYPE() { return 2; }
|
||||
id:any;
|
||||
selector:string;
|
||||
compileChildren:boolean;
|
||||
|
|
|
@ -56,11 +56,7 @@ export class DirectiveParser extends CompileStep {
|
|||
cssSelector.addAttribute(attrName, attrValue);
|
||||
});
|
||||
|
||||
var viewportDirective;
|
||||
var componentDirective;
|
||||
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
|
||||
// only be present on <template> elements!
|
||||
var isTemplateElement = DOM.isTemplateElement(current.element);
|
||||
|
||||
this._selectorMatcher.match(cssSelector, (selector, directiveIndex) => {
|
||||
var elementBinder = current.bindElement();
|
||||
|
@ -87,26 +83,12 @@ export class DirectiveParser extends CompileStep {
|
|||
elementBinder.readAttribute(attrName);
|
||||
});
|
||||
}
|
||||
if (directive.type === DirectiveMetadata.VIEWPORT_TYPE) {
|
||||
if (!isTemplateElement) {
|
||||
throw new BaseException(`Viewport directives need to be placed on <template> elements or elements ` +
|
||||
`with template attribute - check ${current.elementDescription}`);
|
||||
}
|
||||
if (isPresent(viewportDirective)) {
|
||||
throw new BaseException(`Only one viewport directive is allowed per element - check ${current.elementDescription}`);
|
||||
}
|
||||
viewportDirective = directive;
|
||||
} else {
|
||||
if (isTemplateElement) {
|
||||
throw new BaseException(`Only template directives are allowed on template elements - check ${current.elementDescription}`);
|
||||
}
|
||||
if (directive.type === DirectiveMetadata.COMPONENT_TYPE) {
|
||||
if (isPresent(componentDirective)) {
|
||||
throw new BaseException(`Only one component directive is allowed per element - check ${current.elementDescription}`);
|
||||
}
|
||||
componentDirective = directive;
|
||||
elementBinder.setComponentId(directive.id);
|
||||
if (directive.type === DirectiveMetadata.COMPONENT_TYPE) {
|
||||
if (isPresent(componentDirective)) {
|
||||
throw new BaseException(`Only one component directive is allowed per element - check ${current.elementDescription}`);
|
||||
}
|
||||
componentDirective = directive;
|
||||
elementBinder.setComponentId(directive.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ export class RouterOutlet {
|
|||
]);
|
||||
|
||||
this._viewContainer.clear();
|
||||
this._viewContainer.create(0, pv, outletInjector);
|
||||
this._viewContainer.create(pv, 0, outletInjector);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,6 @@ num _getDirectiveType(String annotationName) {
|
|||
return DirectiveMetadata.DECORATOR_TYPE;
|
||||
case 'Component':
|
||||
return DirectiveMetadata.COMPONENT_TYPE;
|
||||
case 'Viewport':
|
||||
return DirectiveMetadata.VIEWPORT_TYPE;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
|||
import {AppProtoView} from 'angular2/src/core/compiler/view';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Component, DynamicComponent, Viewport, Decorator} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Component, DynamicComponent, Decorator} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Attribute} from 'angular2/src/core/annotations_impl/di';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {internalProtoView} from 'angular2/src/core/compiler/view_ref';
|
||||
|
@ -150,13 +150,6 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should fill directive.type for viewport directives', inject([AsyncTestCompleter], (async) => {
|
||||
captureDirective(SomeViewportDirective).then( (renderDir) => {
|
||||
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.VIEWPORT_TYPE);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should fill directive.type for decorator directives', inject([AsyncTestCompleter], (async) => {
|
||||
captureDirective(SomeDecoratorDirective).then( (renderDir) => {
|
||||
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.DECORATOR_TYPE);
|
||||
|
@ -297,7 +290,7 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should load nested components in viewport', inject([AsyncTestCompleter], (async) => {
|
||||
it('should load nested components in viewcontainers', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
||||
tplResolver.setView(NestedComponent, new View({template: '<div></div>'}));
|
||||
var mainProtoView = createProtoView([
|
||||
|
@ -451,16 +444,14 @@ function createComponentElementBinder(reader, type) {
|
|||
var binding = createDirectiveBinding(reader, type);
|
||||
return new ElementBinder(
|
||||
0, null, 0,
|
||||
null, binding,
|
||||
null
|
||||
null, binding
|
||||
);
|
||||
}
|
||||
|
||||
function createViewportElementBinder(nestedProtoView) {
|
||||
var elBinder = new ElementBinder(
|
||||
0, null, 0,
|
||||
null, null,
|
||||
null
|
||||
null, null
|
||||
);
|
||||
elBinder.nestedProtoView = nestedProtoView;
|
||||
return elBinder;
|
||||
|
@ -502,9 +493,6 @@ class RecursiveComponent {}
|
|||
@DynamicComponent()
|
||||
class SomeDynamicComponentDirective {}
|
||||
|
||||
@Viewport()
|
||||
class SomeViewportDirective {}
|
||||
|
||||
@Decorator()
|
||||
class SomeDecoratorDirective {}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import {isPresent} from 'angular2/src/facade/lang';
|
|||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Decorator, Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {Injectable, Injector} from 'angular2/di';
|
||||
|
||||
|
@ -15,9 +15,6 @@ class SomeDecorator {}
|
|||
@Component({selector: 'someComponent', injectables: [SomeInjectable]})
|
||||
class SomeComponent {}
|
||||
|
||||
@Viewport({selector: 'someViewport'})
|
||||
class SomeViewport {}
|
||||
|
||||
class SomeDirectiveWithoutAnnotation {
|
||||
}
|
||||
|
||||
|
@ -35,12 +32,6 @@ export function main() {
|
|||
new DirectiveMetadata(SomeDecorator, new Decorator({selector: 'someDecorator'}), null));
|
||||
});
|
||||
|
||||
it('should read out the Viewport annotation', () => {
|
||||
var directiveMetadata = reader.read(SomeViewport);
|
||||
expect(directiveMetadata).toEqual(
|
||||
new DirectiveMetadata(SomeViewport, new Viewport({selector: 'someViewport'}), null));
|
||||
});
|
||||
|
||||
it('should read out the Component annotation', () => {
|
||||
var m = reader.read(SomeComponent);
|
||||
// For some reason `toEqual` fails to compare ResolvedBinding objects.
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
|
||||
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
||||
|
||||
import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Decorator, Component, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
|
@ -61,7 +61,7 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should allow to destroy and create them via viewport directives',
|
||||
it('should allow to destroy and create them via viewcontainer directives',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: '<div><dynamic-comp #dynamic template="if: ctxBoolProp"></dynamic-comp></div>',
|
||||
|
|
|
@ -705,6 +705,12 @@ export function main() {
|
|||
|
||||
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
||||
});
|
||||
|
||||
it("should throw if there is no ProtoViewRef", function () {
|
||||
expect(
|
||||
() => injector([NeedsProtoViewRef])
|
||||
).toThrowError('No provider for ProtoViewRef! (NeedsProtoViewRef -> ProtoViewRef)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('directive queries', () => {
|
||||
|
|
|
@ -24,7 +24,7 @@ import {Injector, bind} from 'angular2/di';
|
|||
import {PipeRegistry, defaultPipeRegistry,
|
||||
ChangeDetection, DynamicChangeDetection, Pipe, ChangeDetectorRef, ON_PUSH} from 'angular2/change_detection';
|
||||
|
||||
import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Decorator, Component, DynamicComponent} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations_impl/visibility';
|
||||
import {Attribute} from 'angular2/src/core/annotations_impl/di';
|
||||
|
@ -32,6 +32,7 @@ import {Attribute} from 'angular2/src/core/annotations_impl/di';
|
|||
import {If} from 'angular2/src/directives/if';
|
||||
|
||||
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
|
||||
import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
import {Compiler} from 'angular2/src/core/compiler/compiler';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
|
||||
|
@ -522,7 +523,7 @@ export function main() {
|
|||
})
|
||||
}));
|
||||
|
||||
it('should create a component that injects an @Ancestor through viewport directive', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
it('should create a component that injects an @Ancestor through viewcontainer directive', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new View({
|
||||
template: `
|
||||
<some-directive>
|
||||
|
@ -865,7 +866,7 @@ class DynamicViewport {
|
|||
var myService = new MyService();
|
||||
myService.greeting = 'dynamic greet';
|
||||
this.done = compiler.compileInHost(ChildCompUsingService).then( (hostPv) => {
|
||||
vc.create(0, hostPv, inj.createChildFromResolved(Injector.resolve([bind(MyService).toValue(myService)])))
|
||||
vc.create(hostPv, 0, inj.createChildFromResolved(Injector.resolve([bind(MyService).toValue(myService)])))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1037,13 +1038,13 @@ class ChildComp2 {
|
|||
}
|
||||
}
|
||||
|
||||
@Viewport({
|
||||
@Decorator({
|
||||
selector: '[some-viewport]'
|
||||
})
|
||||
class SomeViewport {
|
||||
constructor(container: ViewContainerRef) {
|
||||
container.create().setLocal('some-tmpl', 'hello');
|
||||
container.create().setLocal('some-tmpl', 'again');
|
||||
constructor(container: ViewContainerRef, protoView:ProtoViewRef) {
|
||||
container.create(protoView).setLocal('some-tmpl', 'hello');
|
||||
container.create(protoView).setLocal('some-tmpl', 'again');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ export function main() {
|
|||
return new AppView(null, createProtoView(), MapWrapper.create());
|
||||
}
|
||||
|
||||
function createViewContainer(defaultProtoView = null) {
|
||||
return new ViewContainerRef(viewManager, location, defaultProtoView);
|
||||
function createViewContainer() {
|
||||
return new ViewContainerRef(viewManager, location);
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
|
|
|
@ -60,12 +60,12 @@ export function main() {
|
|||
}
|
||||
|
||||
function createEmptyElBinder() {
|
||||
return new ElementBinder(0, null, 0, null, null, null);
|
||||
return new ElementBinder(0, null, 0, null, null);
|
||||
}
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding, null);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
|
|
@ -45,12 +45,12 @@ export function main() {
|
|||
}
|
||||
|
||||
function createEmptyElBinder() {
|
||||
return new ElementBinder(0, null, 0, null, null, null);
|
||||
return new ElementBinder(0, null, 0, null, null);
|
||||
}
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding, null);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@ export function main() {
|
|||
annotatedDirectives = [
|
||||
someComponent,
|
||||
someComponent2,
|
||||
someViewport,
|
||||
someViewport2,
|
||||
someDecorator,
|
||||
someDecoratorIgnoringChildren,
|
||||
decoratorWithMultipleAttrs,
|
||||
|
@ -153,27 +151,6 @@ export function main() {
|
|||
expect(eventBinding.source.source).toEqual('doItGlobal()');
|
||||
});
|
||||
|
||||
//TODO: assertions should be enabled when running tests: https://github.com/angular/angular/issues/1340
|
||||
describe('viewport directives', () => {
|
||||
it('should not allow multiple viewport directives on the same element', () => {
|
||||
expect( () => {
|
||||
process(
|
||||
el('<template some-vp some-vp2></template>')
|
||||
);
|
||||
}).toThrowError('Only one viewport directive is allowed per element - check '
|
||||
+ (assertionsEnabled() ? '<template some-vp some-vp2>' : 'null'));
|
||||
});
|
||||
|
||||
it('should not allow viewport directives on non <template> elements', () => {
|
||||
expect( () => {
|
||||
process(
|
||||
el('<div some-vp></div>')
|
||||
);
|
||||
}).toThrowError('Viewport directives need to be placed on <template> elements or elements with template attribute - check '
|
||||
+ (assertionsEnabled() ? '<div some-vp>' : 'null'));
|
||||
});
|
||||
});
|
||||
|
||||
//TODO: assertions should be enabled when running tests: https://github.com/angular/angular/issues/1340
|
||||
describe('component directives', () => {
|
||||
it('should save the component id', () => {
|
||||
|
@ -231,18 +208,6 @@ var someComponent2 = new DirectiveMetadata({
|
|||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var someViewport = new DirectiveMetadata({
|
||||
selector: '[some-vp]',
|
||||
id: 'someViewport',
|
||||
type: DirectiveMetadata.VIEWPORT_TYPE
|
||||
});
|
||||
|
||||
var someViewport2 = new DirectiveMetadata({
|
||||
selector: '[some-vp2]',
|
||||
id: 'someViewport2',
|
||||
type: DirectiveMetadata.VIEWPORT_TYPE
|
||||
});
|
||||
|
||||
var someDecorator = new DirectiveMetadata({
|
||||
selector: '[some-decor]',
|
||||
type: DirectiveMetadata.DECORATOR_TYPE
|
||||
|
|
|
@ -43,7 +43,7 @@ export function main() {
|
|||
['properties', MapWrapper.createFromPairs([['propKey', 'propVal']])],
|
||||
['readAttributes', ['readTest1', 'readTest2']],
|
||||
['selector', 'testSelector'],
|
||||
['type', DirectiveMetadata.VIEWPORT_TYPE]
|
||||
['type', DirectiveMetadata.DECORATOR_TYPE]
|
||||
]);
|
||||
var meta = directiveMetadataFromMap(map);
|
||||
expect(meta.compileChildren).toEqual(false);
|
||||
|
@ -56,7 +56,7 @@ export function main() {
|
|||
MapWrapper.createFromPairs([['propKey', 'propVal']]));
|
||||
expect(meta.readAttributes).toEqual(['readTest1', 'readTest2']);
|
||||
expect(meta.selector).toEqual('testSelector');
|
||||
expect(meta.type).toEqual(DirectiveMetadata.VIEWPORT_TYPE);
|
||||
expect(meta.type).toEqual(DirectiveMetadata.DECORATOR_TYPE);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -405,7 +405,7 @@ var multipleContentTagsComponent = new DirectiveMetadata({
|
|||
var manualViewportDirective = new DirectiveMetadata({
|
||||
selector: '[manual]',
|
||||
id: 'manual',
|
||||
type: DirectiveMetadata.VIEWPORT_TYPE
|
||||
type: DirectiveMetadata.DECORATOR_TYPE
|
||||
});
|
||||
|
||||
var outerWithIndirectNestedComponent = new DirectiveMetadata({
|
||||
|
@ -441,7 +441,7 @@ var conditionalContentComponent = new DirectiveMetadata({
|
|||
var autoViewportDirective = new DirectiveMetadata({
|
||||
selector: '[auto]',
|
||||
id: '[auto]',
|
||||
type: DirectiveMetadata.VIEWPORT_TYPE
|
||||
type: DirectiveMetadata.DECORATOR_TYPE
|
||||
});
|
||||
|
||||
var tabGroupComponent = new DirectiveMetadata({
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import {TestBed} from 'angular2/test';
|
||||
|
||||
import {Injector, bind} from 'angular2/di';
|
||||
import {Component, Viewport} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
|
||||
import {RootRouter} from 'angular2/src/router/router';
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {RootRouter, Viewport} from 'angular2/src/router/router';
|
||||
import {RootRouter} from 'angular2/src/router/router';
|
||||
import {Pipeline} from 'angular2/src/router/pipeline';
|
||||
import {RouterOutlet} from 'angular2/src/router/router_outlet';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {bootstrap, Component, Viewport, View, ViewContainerRef, Compiler, Decorator} from 'angular2/angular2';
|
||||
import {bootstrap, Component, View, ViewContainerRef, Compiler, Decorator} from 'angular2/angular2';
|
||||
|
||||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
|
|
@ -7,11 +7,10 @@ import {Component, Decorator} from 'angular2/src/core/annotations_impl/annotatio
|
|||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
|
||||
|
||||
// Angular 2.0 supports 3 basic types of directives:
|
||||
// Angular 2.0 supports 2 basic types of directives:
|
||||
// - Component - the basic building blocks of Angular 2.0 apps. Backed by
|
||||
// ShadowDom.(http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/)
|
||||
// - Decorator - add behavior to existing elements.
|
||||
// - Viewport - allow for stamping out of a html template (not in this demo).
|
||||
|
||||
// @Component is AtScript syntax to annotate the HelloCmp class as an Angular
|
||||
// 2.0 component.
|
||||
|
@ -46,8 +45,8 @@ export class HelloCmp {
|
|||
}
|
||||
}
|
||||
|
||||
// Decorators are light-weight. They don't allow for templates, or new
|
||||
// expression contexts (use @Component or @Viewport for those needs).
|
||||
// Decorators are light-weight. They don't allow new
|
||||
// expression contexts (use @Component for those needs).
|
||||
@Decorator({
|
||||
selector: '[red]'
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue