feat(change_detection): added a directive lifecycle hook that is called after children are checked
This commit is contained in:
@ -44,7 +44,8 @@ export function main() {
|
||||
var dispatcher = new TestDispatcher();
|
||||
|
||||
var variableBindings = convertLocalsToVariableBindings(locals);
|
||||
var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast(exp), memo, memo)], variableBindings);
|
||||
var records = [new BindingRecord(ast(exp), memo, new FakeDirectiveMemento(memo, false))];
|
||||
var cd = pcd.instantiate(dispatcher, records, variableBindings, []);
|
||||
cd.hydrate(context, locals);
|
||||
|
||||
return {"changeDetector" : cd, "dispatcher" : dispatcher};
|
||||
@ -56,6 +57,10 @@ export function main() {
|
||||
return res["dispatcher"].log;
|
||||
}
|
||||
|
||||
function instantiate(protoChangeDetector, dispatcher, bindings) {
|
||||
return protoChangeDetector.instantiate(dispatcher, bindings, null, []);
|
||||
}
|
||||
|
||||
describe(`${name} change detection`, () => {
|
||||
it('should do simple watching', () => {
|
||||
var person = new Person("misko");
|
||||
@ -193,7 +198,7 @@ export function main() {
|
||||
var ast = parser.parseInterpolation("B{{a}}A", "location");
|
||||
|
||||
var dispatcher = new TestDispatcher();
|
||||
var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast, "memo", "memo")], null);
|
||||
var cd = instantiate(pcd, dispatcher, [new BindingRecord(ast, "memo", null)]);
|
||||
cd.hydrate(new TestData("value"), null);
|
||||
|
||||
cd.detectChanges();
|
||||
@ -239,15 +244,18 @@ export function main() {
|
||||
});
|
||||
|
||||
describe("group changes", () => {
|
||||
var dirMemento1 = new FakeDirectiveMemento(1);
|
||||
var dirMemento2 = new FakeDirectiveMemento(2);
|
||||
|
||||
it("should notify the dispatcher when a group of records changes", () => {
|
||||
var pcd = createProtoChangeDetector();
|
||||
|
||||
var dispatcher = new TestDispatcher();
|
||||
var cd = pcd.instantiate(dispatcher, [
|
||||
new BindingRecord(ast("1 + 2"), "memo", "1"),
|
||||
new BindingRecord(ast("10 + 20"), "memo", "1"),
|
||||
new BindingRecord(ast("100 + 200"), "memo", "2")
|
||||
], null);
|
||||
var cd = instantiate(pcd, dispatcher, [
|
||||
new BindingRecord(ast("1 + 2"), "memo", dirMemento1),
|
||||
new BindingRecord(ast("10 + 20"), "memo", dirMemento1),
|
||||
new BindingRecord(ast("100 + 200"), "memo", dirMemento2)
|
||||
]);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
@ -257,11 +265,11 @@ export function main() {
|
||||
it("should notify the dispatcher before switching to the next group", () => {
|
||||
var pcd = createProtoChangeDetector();
|
||||
var dispatcher = new TestDispatcher();
|
||||
var cd = pcd.instantiate(dispatcher, [
|
||||
new BindingRecord(ast("a()"), "a", "1"),
|
||||
new BindingRecord(ast("b()"), "b", "2"),
|
||||
new BindingRecord(ast("c()"), "c", "2")
|
||||
], null);
|
||||
var cd = instantiate(pcd, dispatcher, [
|
||||
new BindingRecord(ast("a()"), "a", dirMemento1),
|
||||
new BindingRecord(ast("b()"), "b", dirMemento2),
|
||||
new BindingRecord(ast("c()"), "c", dirMemento2)
|
||||
]);
|
||||
|
||||
var tr = new TestRecord();
|
||||
tr.a = () => {
|
||||
@ -283,6 +291,46 @@ export function main() {
|
||||
expect(dispatcher.loggedValues).toEqual(['InvokeA', ['a'], 'InvokeB', 'InvokeC', ['b', 'c']]);
|
||||
});
|
||||
});
|
||||
|
||||
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]);
|
||||
|
||||
cd.hydrate(null, null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
expect(dispatcher.loggedOnAllChangesDone).toEqual([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]);
|
||||
|
||||
cd.hydrate(null, null);
|
||||
|
||||
cd.detectChanges();
|
||||
|
||||
expect(dispatcher.loggedOnAllChangesDone).toEqual([memento2, memento1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("enforce no new changes", () => {
|
||||
@ -291,9 +339,9 @@ export function main() {
|
||||
pcd.addAst(ast("a"), "a", 1);
|
||||
|
||||
var dispatcher = new TestDispatcher();
|
||||
var cd = pcd.instantiate(dispatcher, [
|
||||
var cd = instantiate(pcd, dispatcher, [
|
||||
new BindingRecord(ast("a"), "a", 1)
|
||||
], null);
|
||||
]);
|
||||
cd.hydrate(new TestData('value'), null);
|
||||
|
||||
expect(() => {
|
||||
@ -308,7 +356,7 @@ export function main() {
|
||||
var pcd = createProtoChangeDetector();
|
||||
var cd = pcd.instantiate(new TestDispatcher(), [
|
||||
new BindingRecord(ast("invalidProp", "someComponent"), "a", 1)
|
||||
], null);
|
||||
], null, []);
|
||||
cd.hydrate(null, null);
|
||||
|
||||
try {
|
||||
@ -363,10 +411,10 @@ export function main() {
|
||||
|
||||
beforeEach(() => {
|
||||
var protoParent = createProtoChangeDetector();
|
||||
parent = protoParent.instantiate(null, [], null);
|
||||
parent = instantiate(protoParent, null, []);
|
||||
|
||||
var protoChild = createProtoChangeDetector();
|
||||
child = protoChild.instantiate(null, [], null);
|
||||
child = instantiate(protoChild, null, []);
|
||||
});
|
||||
|
||||
it("should add children", () => {
|
||||
@ -409,7 +457,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should change CHECK_ONCE to CHECKED", () => {
|
||||
var cd = createProtoChangeDetector().instantiate(null, [], null);
|
||||
var cd = instantiate(createProtoChangeDetector(), null, []);
|
||||
cd.mode = CHECK_ONCE;
|
||||
|
||||
cd.detectChanges();
|
||||
@ -418,7 +466,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should not change the CHECK_ALWAYS", () => {
|
||||
var cd = createProtoChangeDetector().instantiate(null, [], null);
|
||||
var cd = instantiate(createProtoChangeDetector(), null, []);
|
||||
cd.mode = CHECK_ALWAYS;
|
||||
|
||||
cd.detectChanges();
|
||||
@ -429,7 +477,7 @@ export function main() {
|
||||
|
||||
describe("markPathToRootAsCheckOnce", () => {
|
||||
function changeDetector(mode, parent) {
|
||||
var cd = createProtoChangeDetector().instantiate(null, [], null);
|
||||
var cd = instantiate(createProtoChangeDetector(), null, []);
|
||||
cd.mode = mode;
|
||||
if (isPresent(parent)) parent.addChild(cd);
|
||||
return cd;
|
||||
@ -700,16 +748,28 @@ class TestData {
|
||||
}
|
||||
}
|
||||
|
||||
class FakeDirectiveMemento {
|
||||
value:any;
|
||||
notifyOnAllChangesDone:boolean;
|
||||
|
||||
constructor(value, notifyOnAllChangesDone:boolean = false) {
|
||||
this.value = value;
|
||||
this.notifyOnAllChangesDone = notifyOnAllChangesDone;
|
||||
}
|
||||
}
|
||||
|
||||
class TestDispatcher extends ChangeDispatcher {
|
||||
log:List;
|
||||
loggedValues:List;
|
||||
changeRecords:List;
|
||||
loggedOnAllChangesDone:List;
|
||||
onChange:Function;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.log = null;
|
||||
this.loggedValues = null;
|
||||
this.loggedOnAllChangesDone = null;
|
||||
this.onChange = (_, __) => {};
|
||||
this.clear();
|
||||
}
|
||||
@ -717,6 +777,7 @@ class TestDispatcher extends ChangeDispatcher {
|
||||
clear() {
|
||||
this.log = ListWrapper.create();
|
||||
this.loggedValues = ListWrapper.create();
|
||||
this.loggedOnAllChangesDone = ListWrapper.create();
|
||||
this.changeRecords = ListWrapper.create();
|
||||
}
|
||||
|
||||
@ -724,7 +785,7 @@ class TestDispatcher extends ChangeDispatcher {
|
||||
ListWrapper.push(this.loggedValues, value);
|
||||
}
|
||||
|
||||
onRecordChange(group, changeRecords:List) {
|
||||
onRecordChange(directiveMemento, changeRecords:List) {
|
||||
var value = changeRecords[0].change.currentValue;
|
||||
var memento = changeRecords[0].bindingMemento;
|
||||
ListWrapper.push(this.log, memento + '=' + this._asString(value));
|
||||
@ -734,9 +795,12 @@ class TestDispatcher extends ChangeDispatcher {
|
||||
|
||||
ListWrapper.push(this.changeRecords, changeRecords);
|
||||
|
||||
this.onChange(group, changeRecords);
|
||||
this.onChange(directiveMemento, changeRecords);
|
||||
}
|
||||
|
||||
onAllChangesDone(directiveMemento) {
|
||||
ListWrapper.push(this.loggedOnAllChangesDone, directiveMemento);
|
||||
}
|
||||
|
||||
_asString(value) {
|
||||
return (isBlank(value) ? 'null' : value.toString());
|
||||
|
Reference in New Issue
Block a user