diff --git a/modules/angular2/src/change_detection/abstract_change_detector.js b/modules/angular2/src/change_detection/abstract_change_detector.js index 746ad700d4..3f17f2fbef 100644 --- a/modules/angular2/src/change_detection/abstract_change_detector.js +++ b/modules/angular2/src/change_detection/abstract_change_detector.js @@ -4,25 +4,32 @@ import {BindingPropagationConfig} from './binding_propagation_config'; import {ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces'; export class AbstractChangeDetector extends ChangeDetector { - children:List; + lightDomChildren:List; + shadowDomChildren:List; parent:ChangeDetector; mode:string; bindingPropagationConfig:BindingPropagationConfig; constructor() { super(); - this.children = []; + this.lightDomChildren = []; + this.shadowDomChildren = []; this.bindingPropagationConfig = new BindingPropagationConfig(this); this.mode = CHECK_ALWAYS; } addChild(cd:ChangeDetector) { - ListWrapper.push(this.children, cd); + ListWrapper.push(this.lightDomChildren, cd); cd.parent = this; } removeChild(cd:ChangeDetector) { - ListWrapper.remove(this.children, cd); + ListWrapper.remove(this.lightDomChildren, cd); + } + + addShadowDomChild(cd:ChangeDetector) { + ListWrapper.push(this.shadowDomChildren, cd); + cd.parent = this; } remove() { @@ -41,19 +48,30 @@ export class AbstractChangeDetector extends ChangeDetector { if (this.mode === DETACHED || this.mode === CHECKED) return; this.detectChangesInRecords(throwOnChange); - this._detectChangesInChildren(throwOnChange); + + this._detectChangesInLightDomChildren(throwOnChange); + this.notifyOnAllChangesDone(); + this._detectChangesInShadowDomChildren(throwOnChange); + if (this.mode === CHECK_ONCE) this.mode = CHECKED; } detectChangesInRecords(throwOnChange:boolean){} notifyOnAllChangesDone(){} - _detectChangesInChildren(throwOnChange:boolean) { - var children = this.children; - for(var i = 0; i < children.length; ++i) { - children[i]._detectChanges(throwOnChange); + _detectChangesInLightDomChildren(throwOnChange:boolean) { + var c = this.lightDomChildren; + for(var i = 0; i < c.length; ++i) { + c[i]._detectChanges(throwOnChange); + } + } + + _detectChangesInShadowDomChildren(throwOnChange:boolean) { + var c = this.shadowDomChildren; + for(var i = 0; i < c.length; ++i) { + c[i]._detectChanges(throwOnChange); } } diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index c433ba59f5..bea832b7f7 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -350,6 +350,7 @@ export class ProtoView { } } + //TODO: Tobias or Victor. Moving it into the constructor. // this work should be done the constructor of ProtoView once we separate // ProtoView and ProtoViewBuilder _getVariableBindings() { @@ -367,6 +368,7 @@ export class ProtoView { return this._variableBindings; } + //TODO: Tobias or Victor. Moving it into the constructor. // this work should be done the constructor of ProtoView once we separate // ProtoView and ProtoViewBuilder _getDirectiveMementos() { diff --git a/modules/angular2/test/change_detection/change_detection_spec.js b/modules/angular2/test/change_detection/change_detection_spec.js index 4ea2b061cb..946d396451 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.js +++ b/modules/angular2/test/change_detection/change_detection_spec.js @@ -62,6 +62,12 @@ export function main() { } describe(`${name} change detection`, () => { + var dispatcher; + + beforeEach(() => { + dispatcher = new TestDispatcher(); + }); + it('should do simple watching', () => { var person = new Person("misko"); var c = createChangeDetector('name', 'name', person); @@ -197,7 +203,6 @@ export function main() { var pcd = createProtoChangeDetector(); var ast = parser.parseInterpolation("B{{a}}A", "location"); - var dispatcher = new TestDispatcher(); var cd = instantiate(pcd, dispatcher, [new BindingRecord(ast, "memo", null)]); cd.hydrate(new TestData("value"), null); @@ -250,7 +255,6 @@ export function main() { it("should notify the dispatcher when a group of records changes", () => { var pcd = createProtoChangeDetector(); - var dispatcher = new TestDispatcher(); var cd = instantiate(pcd, dispatcher, [ new BindingRecord(ast("1 + 2"), "memo", dirMemento1), new BindingRecord(ast("10 + 20"), "memo", dirMemento1), @@ -264,7 +268,7 @@ export function main() { it("should notify the dispatcher before switching to the next group", () => { var pcd = createProtoChangeDetector(); - var dispatcher = new TestDispatcher(); + var cd = instantiate(pcd, dispatcher, [ new BindingRecord(ast("a()"), "a", dirMemento1), new BindingRecord(ast("b()"), "b", dirMemento2), @@ -294,41 +298,60 @@ export function main() { describe("onAllChangesDone", () => { it("should notify the dispatcher about processing all the children", () => { - var pcd = createProtoChangeDetector(); - var dispatcher = new TestDispatcher(); - var memento1 = new FakeDirectiveMemento(1, false); var memento2 = new FakeDirectiveMemento(2, true); - var cd = pcd.instantiate(dispatcher, [ - new BindingRecord(ast("1"), "a", memento1), - new BindingRecord(ast("2"), "b", memento2) - ], null, [memento1, memento2]); + var pcd = createProtoChangeDetector(); + var cd = pcd.instantiate(dispatcher, [], null, [memento1, memento2]); cd.hydrate(null, null); cd.detectChanges(); - expect(dispatcher.loggedOnAllChangesDone).toEqual([memento2]); + expect(dispatcher.loggedValues).toEqual([ + ["onAllChangesDone", memento2] + ]); }); it("should notify in reverse order so the child is always notified before the parent", () => { - var pcd = createProtoChangeDetector(); - var dispatcher = new TestDispatcher(); - var memento1 = new FakeDirectiveMemento(1, true); var memento2 = new FakeDirectiveMemento(2, true); - var cd = pcd.instantiate(dispatcher, [ - new BindingRecord(ast("1"), "a", memento1), - new BindingRecord(ast("2"), "b", memento2) - ], null, [memento1, memento2]); + var pcd = createProtoChangeDetector(); + var cd = pcd.instantiate(dispatcher, [], null, [memento1, memento2]); cd.hydrate(null, null); cd.detectChanges(); - expect(dispatcher.loggedOnAllChangesDone).toEqual([memento2, memento1]); + expect(dispatcher.loggedValues).toEqual([ + ["onAllChangesDone", memento2], + ["onAllChangesDone", memento1] + ]); + }); + + it("should notify the dispatcher before processing shadow dom children", () => { + var memento = new FakeDirectiveMemento(1, true); + + var pcd = createProtoChangeDetector(); + var shadowDomChildPCD = createProtoChangeDetector(); + + var parent = pcd.instantiate(dispatcher, [], null, [memento]); + var child = shadowDomChildPCD.instantiate(dispatcher, [ + new BindingRecord(ast("1"), "a", memento)], null, []); + parent.addShadowDomChild(child); + + parent.hydrate(null, null); + child.hydrate(null, null); + + parent.detectChanges(); + + // loggedValues contains everything that the dispatcher received + // the first value is the directive memento passed into onAllChangesDone + expect(dispatcher.loggedValues).toEqual([ + ["onAllChangesDone", memento], + [1] + ]); }); }); }); @@ -417,18 +440,25 @@ export function main() { child = instantiate(protoChild, null, []); }); - it("should add children", () => { + it("should add light dom children", () => { parent.addChild(child); - expect(parent.children.length).toEqual(1); - expect(parent.children[0]).toBe(child); + expect(parent.lightDomChildren.length).toEqual(1); + expect(parent.lightDomChildren[0]).toBe(child); }); - it("should remove children", () => { + it("should add shadow dom children", () => { + parent.addShadowDomChild(child); + + expect(parent.shadowDomChildren.length).toEqual(1); + expect(parent.shadowDomChildren[0]).toBe(child); + }); + + it("should remove light dom children", () => { parent.addChild(child); parent.removeChild(child); - expect(parent.children).toEqual([]); + expect(parent.lightDomChildren).toEqual([]); }); }); }); @@ -799,7 +829,7 @@ class TestDispatcher extends ChangeDispatcher { } onAllChangesDone(directiveMemento) { - ListWrapper.push(this.loggedOnAllChangesDone, directiveMemento); + ListWrapper.push(this.loggedValues, ["onAllChangesDone", directiveMemento]); } _asString(value) { diff --git a/modules/angular2/test/core/compiler/view_container_spec.js b/modules/angular2/test/core/compiler/view_container_spec.js index a1b2b2cda5..33d0070a12 100644 --- a/modules/angular2/test/core/compiler/view_container_spec.js +++ b/modules/angular2/test/core/compiler/view_container_spec.js @@ -228,7 +228,7 @@ export function main() { ListWrapper.forEach(fancyView.rootElementInjectors, (inj) => expect(inj.parent).toBe(elementInjector)); - expect(parentView.changeDetector.children.length).toBe(1); + expect(parentView.changeDetector.lightDomChildren.length).toBe(1); }); it('dehydrating should update rootElementInjectors and parent change detector', () => { @@ -236,7 +236,7 @@ export function main() { viewContainer.remove(); ListWrapper.forEach(fancyView.rootElementInjectors, (inj) => expect(inj.parent).toBe(null)); - expect(parentView.changeDetector.children.length).toBe(0); + expect(parentView.changeDetector.lightDomChildren.length).toBe(0); expect(viewContainer.length).toBe(0); }); });