Skip to content

Commit 7f941eb

Browse files
committed
fix(change_detector): adding new ranges when disabling the current enabled record
1 parent b4772fc commit 7f941eb

File tree

4 files changed

+109
-60
lines changed

4 files changed

+109
-60
lines changed

modules/change_detection/src/change_detector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class ChangeDetector {
4040
}
4141
}
4242

43-
record = record.nextEnabled;
43+
record = record.findNextEnabled();
4444
}
4545

4646
return count;

modules/change_detection/src/record.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ export class Record {
173173
return (this._mode & RECORD_FLAG_DISABLED) === RECORD_FLAG_DISABLED;
174174
}
175175

176+
isEnabled():boolean {
177+
return ! this.disabled;
178+
}
179+
176180
set disabled(value:boolean) {
177181
if (value) {
178182
this._mode |= RECORD_FLAG_DISABLED;
@@ -316,6 +320,49 @@ export class Record {
316320
groupMemento() {
317321
return isPresent(this.protoRecord) ? this.protoRecord.groupMemento : null;
318322
}
323+
324+
325+
/**
326+
* Returns the next enabled record. This search is not limited to the current range.
327+
*
328+
* [H ER1 T] [H ER2 T] _nextEnable(ER1) will return ER2
329+
*
330+
* The function skips disabled sub ranges.
331+
*/
332+
findNextEnabled() {
333+
if (this.isEnabled()) return this.nextEnabled;
334+
335+
var record = this.next;
336+
while (isPresent(record) && record.disabled) {
337+
if (record.isMarkerRecord && record.recordRange.disabled) {
338+
record = record.recordRange.tailRecord.next;
339+
} else {
340+
record = record.next;
341+
}
342+
}
343+
return record;
344+
}
345+
346+
/**
347+
* Returns the prev enabled record. This search is not limited to the current range.
348+
*
349+
* [H ER1 T] [H ER2 T] _nextEnable(ER2) will return ER1
350+
*
351+
* The function skips disabled sub ranges.
352+
*/
353+
findPrevEnabled() {
354+
if (this.isEnabled()) return this.prevEnabled;
355+
356+
var record = this.prev;
357+
while (isPresent(record) && record.disabled) {
358+
if (record.isMarkerRecord && record.recordRange.disabled) {
359+
record = record.recordRange.headRecord.prev;
360+
} else {
361+
record = record.prev;
362+
}
363+
}
364+
return record;
365+
}
319366
}
320367

321368
function isSame(a, b) {

modules/change_detection/src/record_range.js

Lines changed: 7 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ export class RecordRange {
129129

130130
addRange(child:RecordRange) {
131131
var lastRecord = this.tailRecord.prev;
132-
var prevEnabledRecord = RecordRange._prevEnabled(this.tailRecord);
133-
var nextEnabledRerord = RecordRange._nextEnabled(this.tailRecord);
132+
var prevEnabledRecord = this.tailRecord.findPrevEnabled();
133+
var nextEnabledRerord = this.tailRecord.findNextEnabled();
134134

135135
var firstEnabledChildRecord = child.findFirstEnabledRecord();
136136
var lastEnabledChildRecord = child.findLastEnabledRecord();
@@ -174,10 +174,10 @@ export class RecordRange {
174174
}
175175

176176
enableRecord(record:Record) {
177-
if (!record.disabled) return;
177+
if (record.isEnabled()) return;
178178

179-
var prevEnabled = RecordRange._prevEnabled(record);
180-
var nextEnabled = RecordRange._nextEnabled(record);
179+
var prevEnabled = record.findPrevEnabled();
180+
var nextEnabled = record.findNextEnabled();
181181

182182
record.prevEnabled = prevEnabled;
183183
record.nextEnabled = nextEnabled;
@@ -203,8 +203,8 @@ export class RecordRange {
203203
}
204204

205205
enable() {
206-
var prevEnabledRecord = RecordRange._prevEnabled(this.headRecord);
207-
var nextEnabledRecord = RecordRange._nextEnabled(this.tailRecord);
206+
var prevEnabledRecord = this.headRecord.findPrevEnabled();
207+
var nextEnabledRecord = this.tailRecord.findNextEnabled();
208208

209209
var firstEnabledthisRecord = this.findFirstEnabledRecord();
210210
var lastEnabledthisRecord = this.findLastEnabledRecord();
@@ -268,44 +268,6 @@ export class RecordRange {
268268
return record === this.headRecord ? null : record;
269269
}
270270

271-
/**
272-
* Returns the next enabled record. This search is not limited to the current range.
273-
*
274-
* [H ER1 T] [H ER2 T] _nextEnable(ER1) will return ER2
275-
*
276-
* The function skips disabled sub ranges.
277-
*/
278-
static _nextEnabled(record:Record) {
279-
record = record.next;
280-
while (isPresent(record) && record.disabled) {
281-
if (record.isMarkerRecord && record.recordRange.disabled) {
282-
record = record.recordRange.tailRecord.next;
283-
} else {
284-
record = record.next;
285-
}
286-
}
287-
return record;
288-
}
289-
290-
/**
291-
* Returns the prev enabled record. This search is not limited to the current range.
292-
*
293-
* [H ER1 T] [H ER2 T] _nextEnable(ER2) will return ER1
294-
*
295-
* The function skips disabled sub ranges.
296-
*/
297-
static _prevEnabled(record:Record) {
298-
record = record.prev;
299-
while (isPresent(record) && record.disabled) {
300-
if (record.isMarkerRecord && record.recordRange.disabled) {
301-
record = record.recordRange.headRecord.prev;
302-
} else {
303-
record = record.prev;
304-
}
305-
}
306-
return record;
307-
}
308-
309271
/**
310272
* Sets the context (the object) on which the change detection expressions will
311273
* dereference themselves on. Since the RecordRange can be reused the context

modules/change_detection/test/change_detector_spec.js

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ddescribe, describe, it, iit, xit, expect} from 'test_lib/test_lib';
1+
import {ddescribe, describe, it, iit, xit, expect, beforeEach} from 'test_lib/test_lib';
22

33
import {isPresent, isBlank, isJsObject} from 'facade/lang';
44
import {List, ListWrapper, MapWrapper} from 'facade/collection';
@@ -28,7 +28,7 @@ export function main() {
2828
var prr = new ProtoRecordRange();
2929
prr.addRecordsFromAST(ast(exp), memo, memo, content);
3030

31-
var dispatcher = new LoggingDispatcher();
31+
var dispatcher = new TestDispatcher();
3232
var rr = prr.instantiate(dispatcher, formatters);
3333
rr.setContext(context);
3434

@@ -46,6 +46,17 @@ export function main() {
4646

4747
describe('change_detection', () => {
4848
describe('ChangeDetection', () => {
49+
function createRange(dispatcher, ast, group) {
50+
var prr = new ProtoRecordRange();
51+
prr.addRecordsFromAST(ast, "memo", group);
52+
return prr.instantiate(dispatcher, null);
53+
}
54+
55+
function detectChangesInRange(recordRange) {
56+
var cd = new ChangeDetector(recordRange);
57+
cd.detectChanges();
58+
}
59+
4960
it('should do simple watching', () => {
5061
var person = new Person("misko");
5162

@@ -345,18 +356,45 @@ export function main() {
345356
}
346357
});
347358

348-
describe("onGroupChange", () => {
359+
describe("adding new ranges", () => {
360+
var dispatcher;
361+
362+
beforeEach(() => {
363+
dispatcher = new TestDispatcher();
364+
});
365+
366+
/**
367+
* Tests that we can add a new range after the current
368+
* record has been disabled. The new range must be processed
369+
* during the same change detection run.
370+
*/
371+
it("should work when disabling the last enabled record", () => {
372+
var rr = createRange(dispatcher, ast("1"), 1);
373+
374+
dispatcher.onChange = (group, _) => {
375+
if (group === 1) { // to prevent infinite loop
376+
var rangeToAppend = createRange(dispatcher, ast("2"), 2);
377+
rr.addRange(rangeToAppend);
378+
}
379+
};
380+
381+
detectChangesInRange(rr);
382+
383+
expect(dispatcher.loggedValues).toEqual([[1], [2]]);
384+
});
385+
});
386+
387+
describe("group changes", () => {
349388
it("should notify the dispatcher when a group of records changes", () => {
350389
var prr = new ProtoRecordRange();
351390
prr.addRecordsFromAST(ast("1 + 2"), "memo", 1);
352391
prr.addRecordsFromAST(ast("10 + 20"), "memo", 1);
353392
prr.addRecordsFromAST(ast("100 + 200"), "memo2", 2);
354393

355-
var dispatcher = new LoggingDispatcher();
394+
var dispatcher = new TestDispatcher();
356395
var rr = prr.instantiate(dispatcher, null);
357396

358-
var cd = new ChangeDetector(rr);
359-
cd.detectChanges();
397+
detectChangesInRange(rr);
360398

361399
expect(dispatcher.loggedValues).toEqual([[3, 30], [300]]);
362400
});
@@ -365,13 +403,12 @@ export function main() {
365403
var prr = new ProtoRecordRange();
366404
prr.addRecordsFromAST(ast("1 + 2"), "memo", "memo");
367405

368-
var dispatcher = new LoggingDispatcher();
406+
var dispatcher = new TestDispatcher();
369407
var rr = new RecordRange(null, dispatcher);
370408
rr.addRange(prr.instantiate(dispatcher, null));
371409
rr.addRange(prr.instantiate(dispatcher, null));
372410

373-
var cd = new ChangeDetector(rr);
374-
cd.detectChanges();
411+
detectChangesInRange(rr);
375412

376413
expect(dispatcher.loggedValues).toEqual([[3], [3]]);
377414
});
@@ -382,7 +419,7 @@ export function main() {
382419
prr.addRecordsFromAST(ast("b()"), "b", 2);
383420
prr.addRecordsFromAST(ast("c()"), "b", 2);
384421

385-
var dispatcher = new LoggingDispatcher();
422+
var dispatcher = new TestDispatcher();
386423
var rr = prr.instantiate(dispatcher, null);
387424

388425
var tr = new TestRecord();
@@ -391,8 +428,7 @@ export function main() {
391428
tr.c = () => {dispatcher.logValue('InvokeC'); return 'c'};
392429
rr.setContext(tr);
393430

394-
var cd = new ChangeDetector(rr);
395-
cd.detectChanges();
431+
detectChangesInRange(rr);
396432

397433
expect(dispatcher.loggedValues).toEqual(['InvokeA', ['a'], 'InvokeB', 'InvokeC', ['b', 'c']]);
398434
});
@@ -444,21 +480,23 @@ class TestData {
444480
}
445481
}
446482

447-
class LoggingDispatcher extends WatchGroupDispatcher {
483+
class TestDispatcher extends WatchGroupDispatcher {
448484
log:List;
449485
loggedValues:List;
486+
onChange:Function;
450487

451488
constructor() {
452489
this.log = null;
453490
this.loggedValues = null;
491+
this.onChange = (_, __) => {};
454492
this.clear();
455493
}
456494

457495
clear() {
458496
this.log = ListWrapper.create();
459497
this.loggedValues = ListWrapper.create();
460498
}
461-
499+
462500
logValue(value) {
463501
ListWrapper.push(this.loggedValues, value);
464502
}
@@ -470,6 +508,8 @@ class LoggingDispatcher extends WatchGroupDispatcher {
470508

471509
var values = ListWrapper.map(records, (r) => r.currentValue);
472510
ListWrapper.push(this.loggedValues, values);
511+
512+
this.onChange(group, records);
473513
}
474514

475515
_asString(value) {

0 commit comments

Comments
 (0)