Skip to content

Commit 6335fc4

Browse files
committed
design: add changed detection API
1 parent 7e3005e commit 6335fc4

File tree

10 files changed

+195
-71
lines changed

10 files changed

+195
-71
lines changed
Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
1+
import {WatchGroup} from './watch_group';
2+
import {Record} from './record';
3+
14
export class ChangeDetection {
5+
6+
@FIELD('final _rootWatchGroup:WatchGroup')
7+
constructor(watchGroup:WatchGroup) {
8+
this._rootWatchGroup = watchGroup;
9+
}
10+
11+
detectChanges():int {
12+
var current:Record = _rootWatchGroup._headRecord;
13+
var count:number = 0;
14+
while(current != null) {
15+
if(current.check()) {
16+
count++;
17+
}
18+
}
19+
return count;
20+
}
221

3-
detectChanges():int {}
4-
5-
}
22+
}

modules/change_detection/src/proto_record.js

Lines changed: 0 additions & 3 deletions
This file was deleted.

modules/change_detection/src/proto_watch_group.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

modules/change_detection/src/record.js

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,54 @@
1-
import {WatchGroup} from './watch_group';
1+
import {ProtoWatchGroup, WatchGroup} from './watch_group';
2+
3+
export class ProtoRecord {
4+
5+
@FIELD('final watchGroup:ProtoWatchGroup')
6+
@FIELD('final fieldName:String')
7+
/// order list of all records. Including head/tail markers
8+
@FIELD('next:ProtoRecord')
9+
@FIELD('prev:ProtoRecord')
10+
/// next record to dirty check
11+
@FIELD('_checkNext:ProtoRecord')
12+
@FIELD('_checkPrev:ProtoRecord')
13+
// next notifier
14+
@FIELD('_notifierNext:ProtoRecord')
15+
// Opeque data which will be presented to WatchGroupDispatcher
16+
@FIELD('dispatcherContext')
17+
// IF we detect change, we have to update the _context of the
18+
// next record.
19+
@FIELD('_updateContext:ProtoRecord')
20+
// May be removed if we don't support coelsence.
21+
@FIELD('_updateContextNext:ProtoRecord')
22+
@FIELD('_clone')
23+
constructor(watchGroup:ProtoWatchGroup, fieldName:String) {
24+
this.watchGroup = watchGroup;
25+
this.fieldName = fieldName;
26+
this._next = null;
27+
this._prev = null;
28+
this._checkNext = null;
29+
this._checkPrev = null;
30+
this._notifierNext = null;
31+
this.dispatcherContext = null;
32+
this._updateContext = null;
33+
this._updateContextNext = null;
34+
this._clone = null;
35+
}
36+
37+
instantiate(watchGroup:WatchGroup):Record {
38+
var record = this._clone = new Record(watchGroup, this);
39+
record._prev = this._prev._clone;
40+
record._checkPrev = this._checkPrev._clone;
41+
return _clone;
42+
}
43+
44+
instantiateComplete():Record {
45+
var record = this._clone;
46+
record._next = this._next._clone;
47+
record._checkNext = this._checkNext._clone;
48+
this._clone = null;
49+
return this._next;
50+
}
51+
}
252

353

454
/**
@@ -19,13 +69,8 @@ import {WatchGroup} from './watch_group';
1969
*/
2070
export class Record {
2171

22-
@FIELD('final _watchGroup:WatchGroup')
23-
@FIELD('final _protoRecord:ProtoRecord')
24-
@FIELD('_context')
25-
@FIELD('_getter')
26-
@FIELD('_arguments')
27-
@FIELD('_previousValue')
28-
@FIELD('_mode:int')
72+
@FIELD('final watchGroup:WatchGroup')
73+
@FIELD('final protoRecord:ProtoRecord')
2974
/// order list of all records. Including head/tail markers
3075
@FIELD('_next:Record')
3176
@FIELD('_prev:Record')
@@ -37,18 +82,40 @@ export class Record {
3782
// notifier context will be present to the notifier to release
3883
// the object from notification/watching.
3984
@FIELD('_notifierContext')
40-
// Opeque data which will be presented to WatchGroupDispatcher
41-
@FIELD('_watchContext')
4285
// IF we detect change, we have to update the _context of the
4386
// next record.
4487
@FIELD('_updateContext:Record')
4588
// May be removed if we don't support coelsence.
4689
@FIELD('_updateContextNext:Record')
47-
constructor() {
90+
91+
@FIELD('_mode:int')
92+
@FIELD('_context')
93+
@FIELD('_getter')
94+
@FIELD('_arguments')
95+
@FIELD('currentValue')
96+
@FIELD('previousValue')
97+
constructor(watchGroup:WatchGroup, protoRecord:ProtoRecord) {
98+
this.protoRecord = protoRecord;
99+
this.watchGroup = watchGroup;
100+
this._next = null;
101+
this._prev = null;
102+
this._checkNext = null;
103+
this._checkPrev = null;
104+
this._notifierNext = null;
105+
this._notifierContext = null;
106+
this._updateContext = null;
107+
this._updateContextNext = null;
108+
109+
this._mode = MODE_STATE_MARKER;
110+
this._context = null;
111+
this._getter = null;
112+
this._arguments = null;
113+
this.currentValue = null;
114+
this.previousValue = null;
48115
}
49116

50117
check():bool {
51-
var mode = this.mode;
118+
var mode = this._mode;
52119
var state = mode & MODE_MASK_STATE;
53120
var notify = mode & MODE_MASK_NOTIFY;
54121
var currentValue;
@@ -67,14 +134,15 @@ export class Record {
67134
case MODE_STATE_MAP:
68135
case MODE_STATE_LIST:
69136
}
70-
var previousValue = this._previousValue;
137+
var previousValue = this.previousValue;
71138
if (isSame(previousValue, currentValue)) return false;
72139
if (previousValue instanceof String && currentValue instanceof String
73140
&& previousValue == currentValue) {
74-
this._previousValue = currentValue;
141+
this.previousValue = currentValue;
75142
return false
76143
}
77-
this.previousValue = previousValue;
144+
this.previousValue = currentValue;
145+
this.watchGroup.dispatcher.onRecordChange(this, this.protoRecord.dispatcherContext);
78146
return true;
79147
}
80148
}
@@ -83,24 +151,24 @@ export class Record {
83151
// to use and which dereference mode to execute.
84152

85153
// We use dirty checking aka no notification
86-
var MODE_MASK_NOTIFY:number = 0xFF00;
154+
const MODE_MASK_NOTIFY = 0xFF00;
87155
// Encodes the state of dereference
88-
var MODE_MASK_STATE:int = 0x00FF;
156+
const MODE_MASK_STATE = 0x00FF;
89157

90-
var MODE_PLUGIN_DIRTY_CHECK:int = 0x0000;
91-
var MODE_STATE_MARKER:int = 0x0000;
158+
const MODE_PLUGIN_DIRTY_CHECK = 0x0000;
159+
const MODE_STATE_MARKER = 0x0000;
92160

93161
/// _context[_protoRecord.propname] => _getter(_context)
94-
var MODE_STATE_PROPERTY:int = 0x0001;
162+
const MODE_STATE_PROPERTY = 0x0001;
95163
/// _context(_arguments)
96-
var MODE_STATE_INVOKE_CLOSURE:int = 0x0002;
164+
const MODE_STATE_INVOKE_CLOSURE = 0x0002;
97165
/// _getter(_context, _arguments)
98-
var MODE_STATE_INVOKE_METHOD:int = 0x0003;
166+
const MODE_STATE_INVOKE_METHOD = 0x0003;
99167

100168
/// _context is Map => _previousValue is MapChangeRecord
101-
var MODE_STATE_MAP:int = 0x0004;
169+
const MODE_STATE_MAP = 0x0004;
102170
/// _context is Array/List/Iterable => _previousValue = ListChangeRecord
103-
var MODE_STATE_LIST:int = 0x0005;
171+
const MODE_STATE_LIST = 0x0005;
104172

105173
function isSame(a, b) {
106174
if (a === b) {
Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,63 @@
1+
import {ProtoRecord, Record} from './record';
2+
import {WatchGroupDispatcher} from './watch_group_dispatcher';
3+
4+
export class ProtoWatchGroup {
5+
@FIELD('final _headRecord:ProtoRecord')
6+
@FIELD('final _tailRecord:ProtoRecord')
7+
constructor() {
8+
this._headRecord = null;
9+
this._tailRecord = null;
10+
}
11+
12+
watch(
13+
expression:String,
14+
context,
15+
{isCollection})
16+
{
17+
/// IMPREMENT
18+
}
19+
20+
instantiate(dispatcher:WatchGroupDispatcher):WatchGroup {
21+
var watchGroup:WatchGroup = new WatchGroup(this, dispatcher);
22+
var head:Record = null;
23+
var tail:Record = null;
24+
var proto:ProtoRecord = this._headRecord;
25+
26+
while(proto != null) {
27+
tail = proto.instantiate(watchGroup);
28+
if (head == null) head = tail;
29+
proto = proto.next;
30+
}
31+
32+
proto = this._headRecord;
33+
while(proto != null) {
34+
proto = proto.instantiateComplete();
35+
}
36+
37+
watchGroup._headRecord = head;
38+
watchGroup._tailRecord = tail;
39+
return watchGroup;
40+
}
41+
}
42+
143
export class WatchGroup {
44+
@FIELD('final protoWatchGroup:ProtoWatchGroup')
245
@FIELD('final dispatcher:WatchGroupDispatcher')
3-
constructor() {}
4-
}
46+
@FIELD('final _headRecord:Record')
47+
@FIELD('final _tailRecord:Record')
48+
constructor(protoWatchGroup:ProtoWatchGroup, dispatcher:WatchGroupDispatcher) {
49+
this.protoWatchGroup = protoWatchGroup;
50+
this.dispatcher = dispatcher;
51+
this._headRecord = null;
52+
this._tailRecord = null;
53+
}
54+
55+
insertChildGroup(newChild:WatchGroup, insertAfter:WatchGroup) {
56+
/// IMPLEMENT
57+
}
58+
59+
remove() {
60+
/// IMPLEMENT
61+
}
62+
63+
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1+
import {Record} from './record';
12

23
export class WatchGroupDispatcher {
3-
notify(record:Record, context) {}
4-
}
4+
onRecordChange(record:Record, context) {}
5+
}

modules/core/src/core.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './annotations/component';
66
export * from './annotations/template_config';
77

88
export * from 'change_detection/change_detection';
9+
export * from 'change_detection/watch_group';
910
export * from 'change_detection/record';
1011

1112
export * from './compiler/compiler';

modules/core/src/view/view.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {Node, DocumentFragment} from 'facade/dom';
22
import {ListWrapper wraps List} from 'facade/collection';
3+
import {WatchGroupDispatcher} from 'change_detection/watch_group_dispatcher';
34
import {Record} from 'change_detection/record';
45

56
@IMPLEMENTS(WatchGroupDispatcher)
@@ -18,17 +19,17 @@ export class View {
1819
this._nodes = ListWrapper.clone(fragment.childNodes);
1920
}
2021

21-
notify(record:Record, target) {
22-
/*
22+
onRecordChange(record:Record, target) {
2323
// dispatch to element injector or text nodes based on context
24-
if (Number.is(target)) {
25-
// we know it refferst to _textNodes.
26-
} else {
24+
if (target is ElementInjectorTarge) {
2725
// we know that it is ElementInjectorTarge
2826
var eTarget:ElementInjectorTarget = target;
2927
onChangeDispatcher.notify(this, eTarget);
3028
eTarget.invoke(record, _elementInjectors);
29+
} else {
30+
// we know it refferst to _textNodes.
31+
var textNodeIndex:number = target;
32+
DOM.setText(this._textNodes[textNodeIndex], record.currentValue);
3133
}
32-
*/
3334
}
3435
}

modules/facade/src/dom.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ library angular.core.facade.dom;
22

33
import 'dart:html';
44

5-
export 'dart:html' show DocumentFragment, Node, Element, TemplateElement;
5+
export 'dart:html' show DocumentFragment, Node, Element, TemplateElement, Text;
66

77
class DOM {
88
static query(selector) {
@@ -14,7 +14,10 @@ class DOM {
1414
static getInnerHTML(el) {
1515
return el.innerHtml;
1616
}
17-
static setInnerHTML(el, value) {
17+
static setInnerHTML(el:, value) {
1818
el.innerHtml = value;
1919
}
20-
}
20+
static setText(Text text, String value) {
21+
text.text = value;
22+
}
23+
}

modules/facade/src/dom.es6

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export var DocumentFragment = window.DocumentFragment;
22
export var Node = window.Node;
3+
export var Text = window.Text;
34
export var Element = window.HTMLElement;
45
export var TemplateElement = window.HTMLTemplateElement;
56

@@ -16,4 +17,7 @@ export class DOM {
1617
static setInnerHTML(el, value) {
1718
el.innerHTML = value;
1819
}
19-
}
20+
static setText(text:Text, value:String) {
21+
text.nodeValue = value;
22+
}
23+
}

0 commit comments

Comments
 (0)