fix(shadowdom): allow conditional content tags.

Distribution is triggered on the host element injector after each new
view creation.
This commit is contained in:
Rado Kirov 2015-03-10 19:35:49 -07:00
parent a82e20889d
commit f7963e1ea6
5 changed files with 53 additions and 46 deletions

View File

@ -5,7 +5,7 @@ import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility'; import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
import {EventEmitter, PropertySetter} from 'angular2/src/core/annotations/di'; 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 {LightDom, SourceLightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
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'
@ -25,7 +25,7 @@ class StaticKeys {
ngElementId:number; ngElementId:number;
viewContainerId:number; viewContainerId:number;
destinationLightDomId:number; destinationLightDomId:number;
sourceLightDomId:number; lightDomId:number;
bindingPropagationConfigId:number; bindingPropagationConfigId:number;
constructor() { constructor() {
@ -34,7 +34,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.destinationLightDomId = Key.get(DestinationLightDom).id; this.destinationLightDomId = Key.get(DestinationLightDom).id;
this.sourceLightDomId = Key.get(SourceLightDom).id; this.lightDomId = Key.get(LightDom).id;
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id; this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
} }
@ -581,8 +581,8 @@ export class ElementInjector extends TreeNode {
var p:ElementInjector = this.directParent(); var p:ElementInjector = this.directParent();
return isPresent(p) ? p._preBuiltObjects.lightDom : null; return isPresent(p) ? p._preBuiltObjects.lightDom : null;
} }
if (keyId === staticKeys.sourceLightDomId) { if (keyId === staticKeys.lightDomId) {
return this._host._preBuiltObjects.lightDom; return this._preBuiltObjects.lightDom;
} }
//TODO add other objects as needed //TODO add other objects as needed

View File

@ -7,7 +7,6 @@ import {ElementInjector} from '../element_injector';
import {ViewContainer} from '../view_container'; import {ViewContainer} from '../view_container';
import {Content} from './content_tag'; import {Content} from './content_tag';
export class SourceLightDom {}
export class DestinationLightDom {} export class DestinationLightDom {}
@ -21,7 +20,7 @@ class _Root {
} }
} }
// TODO: LightDom should implement SourceLightDom and DestinationLightDom // TODO: LightDom should implement DestinationLightDom
// once interfaces are supported // once interfaces are supported
export class LightDom { export class LightDom {
// The light DOM of the element is enclosed inside the lightDomView // The light DOM of the element is enclosed inside the lightDomView

View File

@ -6,17 +6,19 @@ import {Injector} from 'angular2/di';
import * as eiModule from 'angular2/src/core/compiler/element_injector'; import * as eiModule from 'angular2/src/core/compiler/element_injector';
import {isPresent, isBlank} from 'angular2/src/facade/lang'; import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {EventManager} from 'angular2/src/core/events/event_manager'; import {EventManager} from 'angular2/src/core/events/event_manager';
import * as ldModule from './shadow_dom_emulation/light_dom';
export class ViewContainer { export class ViewContainer {
parentView: viewModule.View; parentView: viewModule.View;
templateElement; templateElement;
defaultProtoView: viewModule.ProtoView; defaultProtoView: viewModule.ProtoView;
_views: List<viewModule.View>; _views: List<viewModule.View>;
_lightDom: any; _lightDom: ldModule.LightDom;
_eventManager: EventManager; _eventManager: EventManager;
elementInjector: eiModule.ElementInjector; elementInjector: eiModule.ElementInjector;
appInjector: Injector; appInjector: Injector;
hostElementInjector: eiModule.ElementInjector; hostElementInjector: eiModule.ElementInjector;
hostLightDom: ldModule.LightDom;
constructor(parentView: viewModule.View, constructor(parentView: viewModule.View,
templateElement, templateElement,
@ -34,17 +36,20 @@ export class ViewContainer {
this._views = []; this._views = [];
this.appInjector = null; this.appInjector = null;
this.hostElementInjector = null; this.hostElementInjector = null;
this.hostLightDom = null;
this._eventManager = eventManager; this._eventManager = eventManager;
} }
hydrate(appInjector: Injector, hostElementInjector: eiModule.ElementInjector) { hydrate(appInjector: Injector, hostElementInjector: eiModule.ElementInjector) {
this.appInjector = appInjector; this.appInjector = appInjector;
this.hostElementInjector = hostElementInjector; this.hostElementInjector = hostElementInjector;
this.hostLightDom = isPresent(hostElementInjector) ? hostElementInjector.get(ldModule.LightDom) : null;
} }
dehydrate() { dehydrate() {
this.appInjector = null; this.appInjector = null;
this.hostElementInjector = null; this.hostElementInjector = null;
this.hostLightDom = null;
this.clear(); this.clear();
} }
@ -81,6 +86,11 @@ export class ViewContainer {
// insertion must come before hydration so that element injector trees are attached. // insertion must come before hydration so that element injector trees are attached.
this.insert(newView, atIndex); this.insert(newView, atIndex);
newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context); newView.hydrate(this.appInjector, this.hostElementInjector, this.parentView.context);
// new content tags might have appeared, we need to redistrubute.
if (isPresent(this.hostLightDom)) {
this.hostLightDom.redistribute();
}
return newView; return newView;
} }
@ -94,6 +104,7 @@ export class ViewContainer {
} }
this.parentView.changeDetector.addChild(view.changeDetector); this.parentView.changeDetector.addChild(view.changeDetector);
this._linkElementInjectors(view); this._linkElementInjectors(view);
return view; return view;
} }
@ -119,6 +130,10 @@ export class ViewContainer {
} else { } else {
this._lightDom.redistribute(); this._lightDom.redistribute();
} }
// content tags might have disappeared we need to do redistribution.
if (isPresent(this.hostLightDom)) {
this.hostLightDom.redistribute();
}
detachedView.changeDetector.remove(); detachedView.changeDetector.remove();
this._unlinkElementInjectors(detachedView); this._unlinkElementInjectors(detachedView);
return detachedView; return detachedView;

View File

@ -9,7 +9,7 @@ import {Optional, Injector, Inject, bind} from 'angular2/di';
import {ProtoView, View} from 'angular2/src/core/compiler/view'; import {ProtoView, View} 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 {LightDom, SourceLightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
import {Directive} from 'angular2/src/core/annotations/annotations'; import {Directive} 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 {DynamicProtoChangeDetector} from 'angular2/change_detection'; import {DynamicProtoChangeDetector} from 'angular2/change_detection';
@ -455,10 +455,10 @@ export function main() {
parentPreBuiltObjects = new PreBuiltObjects(null, null, null, lightDom, null); parentPreBuiltObjects = new PreBuiltObjects(null, null, null, lightDom, null);
}); });
it("should return destination light DOM from the parent's injector", function () { it("should return light DOM from the current injector", function () {
var child = parentChildInjectors([], [], parentPreBuiltObjects); var inj = injector([], null, null, parentPreBuiltObjects);
expect(child.get(DestinationLightDom)).toEqual(lightDom); expect(inj.get(LightDom)).toEqual(lightDom);
}); });
it("should return null when parent's injector is a component boundary", function () { it("should return null when parent's injector is a component boundary", function () {
@ -466,12 +466,6 @@ export function main() {
expect(child.get(DestinationLightDom)).toBeNull(); expect(child.get(DestinationLightDom)).toBeNull();
}); });
it("should return source light DOM from the closest component boundary", function () {
var child = hostShadowInjectors([], [], parentPreBuiltObjects);
expect(child.get(SourceLightDom)).toEqual(lightDom);
});
}); });
}); });

View File

@ -188,33 +188,32 @@ export function main() {
}); });
}); });
// Enable once dom-write queue is implemented and onDehydrate is implemented it('should redistribute when the shadow dom changes', (done) => {
//it('should redistribute when the shadow dom changes', (done) => { var temp = '<conditional-content>' +
// var temp = '<conditional-content>' + '<div class="left">A</div>' +
// '<div class="left">A</div>' + '<div>B</div>' +
// '<div>B</div>' + '<div>C</div>' +
// '<div>C</div>' + '</conditional-content>';
// '</conditional-content>';
//
// compile(temp, [ConditionalContentComponent, AutoViewportDirective], (view, lc) => {
// compile(temp, (view, lc) => { var cmp = view.elementInjectors[0].get(ConditionalContentComponent);
// var cmp = view.elementInjectors[0].get(ConditionalContentComponent);
// expect(view.nodes).toHaveText('(, ABC)');
// expect(view.nodes).toHaveText('(, ABC)');
// cmp.showLeft();
// cmp.showLeft(); lc.tick();
// lc.tick();
// expect(view.nodes).toHaveText('(A, BC)');
// expect(view.nodes).toHaveText('(A, BC)');
// cmp.hideLeft();
// cmp.hideLeft() lc.tick();
// lc.tick();
// expect(view.nodes).toHaveText('(, ABC)');
// expect(view.nodes).toHaveText('(, ABC)');
// done();
// done(); });
// }); });
//});
//Implement once NgElement support changing a class //Implement once NgElement support changing a class
//it("should redistribute when a class has been added or removed"); //it("should redistribute when a class has been added or removed");
@ -300,7 +299,7 @@ class MultipleContentTagsComponent {
@Component({selector: 'conditional-content'}) @Component({selector: 'conditional-content'})
@Template({ @Template({
inline: '<div>(<div template="auto: cond"><content select=".left"></content></div>, <content></content>)</div>', inline: '<div>(<div *auto="cond"><content select=".left"></content></div>, <content></content>)</div>',
directives: [AutoViewportDirective] directives: [AutoViewportDirective]
}) })
class ConditionalContentComponent { class ConditionalContentComponent {