feat(change_detector): split light dom and shadow dom children

This commit is contained in:
vsavkin 2015-03-27 11:02:55 -07:00
parent 723e8fde93
commit e92918bbfe
4 changed files with 86 additions and 36 deletions

View File

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

View File

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

View File

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

View File

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