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:
Tobias Bosch
2015-04-29 15:07:55 -07:00
parent fb67e37339
commit 3aac2fefd7
35 changed files with 280 additions and 366 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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.

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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);
}
});
}

View File

@ -32,7 +32,7 @@ export class RouterOutlet {
]);
this._viewContainer.clear();
this._viewContainer.create(0, pv, outletInjector);
this._viewContainer.create(pv, 0, outletInjector);
});
}

View File

@ -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;
}