feat(element_injector): added PrivateComponentLocation
This commit is contained in:
parent
b69f3043e0
commit
7488456d68
|
@ -7,8 +7,9 @@ import {EventEmitter, PropertySetter} from 'angular2/src/core/annotations/di';
|
||||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||||
import {NgElement} from 'angular2/src/core/dom/element';
|
import {NgElement} from 'angular2/src/core/dom/element';
|
||||||
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations'
|
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations';
|
||||||
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'
|
import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config';
|
||||||
|
import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location';
|
||||||
import {reflector} from 'angular2/src/reflection/reflection';
|
import {reflector} from 'angular2/src/reflection/reflection';
|
||||||
|
|
||||||
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
|
||||||
|
@ -24,6 +25,7 @@ class StaticKeys {
|
||||||
ngElementId:number;
|
ngElementId:number;
|
||||||
viewContainerId:number;
|
viewContainerId:number;
|
||||||
bindingPropagationConfigId:number;
|
bindingPropagationConfigId:number;
|
||||||
|
privateComponentLocationId:number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
||||||
|
@ -31,6 +33,7 @@ class StaticKeys {
|
||||||
this.ngElementId = Key.get(NgElement).id;
|
this.ngElementId = Key.get(NgElement).id;
|
||||||
this.viewContainerId = Key.get(ViewContainer).id;
|
this.viewContainerId = Key.get(ViewContainer).id;
|
||||||
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
|
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
|
||||||
|
this.privateComponentLocationId = Key.get(PrivateComponentLocation).id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static instance() {
|
static instance() {
|
||||||
|
@ -155,7 +158,6 @@ export class DirectiveBinding extends Binding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||||
export class PreBuiltObjects {
|
export class PreBuiltObjects {
|
||||||
view:viewModule.View;
|
view:viewModule.View;
|
||||||
|
@ -317,6 +319,8 @@ export class ElementInjector extends TreeNode {
|
||||||
_obj9:any;
|
_obj9:any;
|
||||||
_preBuiltObjects;
|
_preBuiltObjects;
|
||||||
_constructionCounter;
|
_constructionCounter;
|
||||||
|
_privateComponent;
|
||||||
|
_privateComponentBinding:DirectiveBinding;
|
||||||
|
|
||||||
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector) {
|
constructor(proto:ProtoElementInjector, parent:ElementInjector, host:ElementInjector) {
|
||||||
super(parent);
|
super(parent);
|
||||||
|
@ -366,6 +370,9 @@ export class ElementInjector extends TreeNode {
|
||||||
if (isPresent(p._binding7) && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
|
if (isPresent(p._binding7) && p._binding7.callOnDestroy) {this._obj7.onDestroy();}
|
||||||
if (isPresent(p._binding8) && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
|
if (isPresent(p._binding8) && p._binding8.callOnDestroy) {this._obj8.onDestroy();}
|
||||||
if (isPresent(p._binding9) && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
|
if (isPresent(p._binding9) && p._binding9.callOnDestroy) {this._obj9.onDestroy();}
|
||||||
|
if (isPresent(this._privateComponentBinding) && this._privateComponentBinding.callOnDestroy) {
|
||||||
|
this._privateComponent.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
this._obj0 = null;
|
this._obj0 = null;
|
||||||
this._obj1 = null;
|
this._obj1 = null;
|
||||||
|
@ -377,6 +384,7 @@ export class ElementInjector extends TreeNode {
|
||||||
this._obj7 = null;
|
this._obj7 = null;
|
||||||
this._obj8 = null;
|
this._obj8 = null;
|
||||||
this._obj9 = null;
|
this._obj9 = null;
|
||||||
|
this._privateComponent = null;
|
||||||
|
|
||||||
this._constructionCounter = 0;
|
this._constructionCounter = 0;
|
||||||
}
|
}
|
||||||
|
@ -399,6 +407,15 @@ export class ElementInjector extends TreeNode {
|
||||||
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
|
if (isPresent(p._keyId7)) this._getDirectiveByKeyId(p._keyId7);
|
||||||
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
|
if (isPresent(p._keyId8)) this._getDirectiveByKeyId(p._keyId8);
|
||||||
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
|
if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9);
|
||||||
|
if (isPresent(this._privateComponentBinding)) {
|
||||||
|
this._privateComponent = this._new(this._privateComponentBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createPrivateComponent(componentType:Type, annotation:Directive) {
|
||||||
|
this._privateComponentBinding = DirectiveBinding.createFromType(componentType, annotation);
|
||||||
|
this._privateComponent = this._new(this._privateComponentBinding);
|
||||||
|
return this._privateComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkShadowDomAppInjector(shadowDomAppInjector:Injector) {
|
_checkShadowDomAppInjector(shadowDomAppInjector:Injector) {
|
||||||
|
@ -439,6 +456,14 @@ export class ElementInjector extends TreeNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPrivateComponent() {
|
||||||
|
return this._privateComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
getShadowDomAppInjector() {
|
||||||
|
return this._shadowDomAppInjector;
|
||||||
|
}
|
||||||
|
|
||||||
directParent(): ElementInjector {
|
directParent(): ElementInjector {
|
||||||
return this._proto.distanceToParent < 2 ? this.parent : null;
|
return this._proto.distanceToParent < 2 ? this.parent : null;
|
||||||
}
|
}
|
||||||
|
@ -447,6 +472,10 @@ export class ElementInjector extends TreeNode {
|
||||||
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
|
return this._proto._binding0IsComponent && key.id === this._proto._keyId0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isPrivateComponentKey(key:Key) {
|
||||||
|
return isPresent(this._privateComponentBinding) && key.id === this._privateComponentBinding.key.id;
|
||||||
|
}
|
||||||
|
|
||||||
_new(binding:Binding) {
|
_new(binding:Binding) {
|
||||||
if (this._constructionCounter++ > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER) {
|
if (this._constructionCounter++ > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER) {
|
||||||
throw new CyclicDependencyError(binding.key);
|
throw new CyclicDependencyError(binding.key);
|
||||||
|
@ -545,6 +574,8 @@ export class ElementInjector extends TreeNode {
|
||||||
|
|
||||||
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
||||||
return this._host.getComponent();
|
return this._host.getComponent();
|
||||||
|
} else if (isPresent(this._host) && this._host._isPrivateComponentKey(key)) {
|
||||||
|
return this._host.getPrivateComponent();
|
||||||
} else if (optional) {
|
} else if (optional) {
|
||||||
return this._appInjector(requestor).getOptional(key);
|
return this._appInjector(requestor).getOptional(key);
|
||||||
} else {
|
} else {
|
||||||
|
@ -571,6 +602,10 @@ export class ElementInjector extends TreeNode {
|
||||||
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
|
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer;
|
||||||
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
|
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
|
||||||
|
|
||||||
|
if (keyId === staticKeys.privateComponentLocationId) {
|
||||||
|
return new PrivateComponentLocation(this, this._preBuiltObjects.element, this._preBuiltObjects.view);
|
||||||
|
}
|
||||||
|
|
||||||
//TODO add other objects as needed
|
//TODO add other objects as needed
|
||||||
return _undefined;
|
return _undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {Directive} from 'angular2/src/core/annotations/annotations'
|
||||||
|
import {NgElement} from 'angular2/src/core/dom/element';
|
||||||
|
import {ElementInjector} from './element_injector';
|
||||||
|
import {ProtoView, View} from './view';
|
||||||
|
import {ShadowDomStrategy} from './shadow_dom_strategy';
|
||||||
|
import {EventManager} from 'angular2/src/core/events/event_manager';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
export class PrivateComponentLocation {
|
||||||
|
_elementInjector:ElementInjector;
|
||||||
|
_elt:NgElement;
|
||||||
|
_view:View;
|
||||||
|
|
||||||
|
constructor(elementInjector:ElementInjector, elt:NgElement, view:View){
|
||||||
|
this._elementInjector = elementInjector;
|
||||||
|
this._elt = elt;
|
||||||
|
this._view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
createComponent(type:Type, annotation:Directive, componentProtoView:ProtoView,
|
||||||
|
eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) {
|
||||||
|
var context = this._elementInjector.createPrivateComponent(type, annotation);
|
||||||
|
|
||||||
|
var view = componentProtoView.instantiate(this._elementInjector, eventManager);
|
||||||
|
view.hydrate(this._elementInjector.getShadowDomAppInjector(), this._elementInjector, context);
|
||||||
|
|
||||||
|
shadowDomStrategy.attachTemplate(this._elt.domElement, view);
|
||||||
|
|
||||||
|
ListWrapper.push(this._view.componentChildViews, view);
|
||||||
|
this._view.changeDetector.addChild(view.changeDetector);
|
||||||
|
}
|
||||||
|
}
|
|
@ -114,6 +114,7 @@ class DirectiveWithDestroy {
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null, null);
|
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null, null);
|
||||||
|
var appInjector = new Injector([]);
|
||||||
|
|
||||||
function humanize(tree, names:List) {
|
function humanize(tree, names:List) {
|
||||||
var lookupName = (item) =>
|
var lookupName = (item) =>
|
||||||
|
@ -126,7 +127,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function injector(bindings, lightDomAppInjector = null, shadowDomAppInjector = null, preBuiltObjects = null) {
|
function injector(bindings, lightDomAppInjector = null, shadowDomAppInjector = null, preBuiltObjects = null) {
|
||||||
if (isBlank(lightDomAppInjector)) lightDomAppInjector = new Injector([]);
|
if (isBlank(lightDomAppInjector)) lightDomAppInjector = appInjector;
|
||||||
|
|
||||||
var proto = new ProtoElementInjector(null, 0, bindings, isPresent(shadowDomAppInjector));
|
var proto = new ProtoElementInjector(null, 0, bindings, isPresent(shadowDomAppInjector));
|
||||||
var inj = proto.instantiate(null, null);
|
var inj = proto.instantiate(null, null);
|
||||||
|
@ -447,6 +448,59 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("createPrivateComponent", () => {
|
||||||
|
it("should create a private component", () => {
|
||||||
|
var inj = injector([]);
|
||||||
|
inj.createPrivateComponent(SimpleDirective, null);
|
||||||
|
expect(inj.getPrivateComponent()).toBeAnInstanceOf(SimpleDirective);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should inject parent dependencies into the private component", () => {
|
||||||
|
var inj = parentChildInjectors([SimpleDirective], []);
|
||||||
|
inj.createPrivateComponent(NeedDirectiveFromAncestor, null);
|
||||||
|
expect(inj.getPrivateComponent()).toBeAnInstanceOf(NeedDirectiveFromAncestor);
|
||||||
|
expect(inj.getPrivateComponent().dependency).toBeAnInstanceOf(SimpleDirective);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not inject the proxy component into the children of the private component", () => {
|
||||||
|
var injWithPrivateComponent = injector([SimpleDirective]);
|
||||||
|
injWithPrivateComponent.createPrivateComponent(SomeOtherDirective, null);
|
||||||
|
|
||||||
|
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
|
||||||
|
var shadowDomInj = shadowDomProtoInjector.instantiate(null, injWithPrivateComponent);
|
||||||
|
|
||||||
|
expect(() => shadowDomInj.instantiateDirectives(appInjector, null, defaultPreBuiltObjects)).
|
||||||
|
toThrowError(new RegExp("No provider for SimpleDirective"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should inject the private component into the children of the private component", () => {
|
||||||
|
var injWithPrivateComponent = injector([]);
|
||||||
|
injWithPrivateComponent.createPrivateComponent(SimpleDirective, null);
|
||||||
|
|
||||||
|
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
|
||||||
|
var shadowDomInjector = shadowDomProtoInjector.instantiate(null, injWithPrivateComponent);
|
||||||
|
shadowDomInjector.instantiateDirectives(appInjector, null, defaultPreBuiltObjects);
|
||||||
|
|
||||||
|
expect(shadowDomInjector.get(NeedDirectiveFromAncestor)).toBeAnInstanceOf(NeedDirectiveFromAncestor);
|
||||||
|
expect(shadowDomInjector.get(NeedDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should support rehydrating the private component", () => {
|
||||||
|
var inj = injector([]);
|
||||||
|
inj.createPrivateComponent(DirectiveWithDestroy, new Directive({lifecycle: [onDestroy]}));
|
||||||
|
var dir = inj.getPrivateComponent();
|
||||||
|
|
||||||
|
inj.clearDirectives();
|
||||||
|
|
||||||
|
expect(inj.getPrivateComponent()).toBe(null);
|
||||||
|
expect(dir.onDestroyCounter).toBe(1);
|
||||||
|
|
||||||
|
inj.instantiateDirectives(null, null, null);
|
||||||
|
|
||||||
|
expect(inj.getPrivateComponent()).not.toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('event emitters', () => {
|
describe('event emitters', () => {
|
||||||
it('should be injectable and callable', () => {
|
it('should be injectable and callable', () => {
|
||||||
var called = false;
|
var called = false;
|
||||||
|
|
Loading…
Reference in New Issue