Skip to content

Commit 695b4eb

Browse files
committed
feat(change_detection): add support for pipes
1 parent fa25965 commit 695b4eb

20 files changed

+436
-257
lines changed

modules/angular2/change_detection.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export {Lexer} from './src/change_detection/parser/lexer';
33
export {Parser} from './src/change_detection/parser/parser';
44
export {ContextWithVariableBindings}
55
from './src/change_detection/parser/context_with_variable_bindings';
6-
76
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
87
from './src/change_detection/exceptions';
98
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
@@ -12,9 +11,15 @@ export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
1211
from './src/change_detection/proto_change_detector';
1312
export {DynamicChangeDetector}
1413
from './src/change_detection/dynamic_change_detector';
14+
export * from './src/change_detection/pipes/pipe_registry';
15+
export * from './src/change_detection/pipes/pipe';
16+
1517

1618
import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
1719
from './src/change_detection/proto_change_detector';
20+
import {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
21+
import {ArrayChanges} from './src/change_detection/pipes/array_changes';
22+
import {NullPipe} from './src/change_detection/pipes/null_pipe';
1823

1924
export class ChangeDetection {
2025
createProtoChangeDetector(name:string):ProtoChangeDetector{
@@ -23,15 +28,30 @@ export class ChangeDetection {
2328
}
2429
}
2530

31+
export var defaultPipes = {
32+
"[]" : [
33+
{
34+
"supports" : ArrayChanges.supportsObj,
35+
"pipe" : () => new ArrayChanges()
36+
},
37+
{
38+
"supports" : NullPipe.supportsObj,
39+
"pipe" : () => new NullPipe()
40+
}
41+
]
42+
};
43+
44+
var _registry = new PipeRegistry(defaultPipes);
45+
2646
export class DynamicChangeDetection extends ChangeDetection {
2747
createProtoChangeDetector(name:string):ProtoChangeDetector{
28-
return new DynamicProtoChangeDetector();
48+
return new DynamicProtoChangeDetector(_registry);
2949
}
3050
}
3151

3252
export class JitChangeDetection extends ChangeDetection {
3353
createProtoChangeDetector(name:string):ProtoChangeDetector{
34-
return new JitProtoChangeDetector();
54+
return new JitProtoChangeDetector(_registry);
3555
}
3656
}
3757

modules/angular2/src/change_detection/change_detection_jit_generator.es6

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -95,25 +95,27 @@ var PROTOS_ACCESSOR = "this.protos";
9595
var CHANGE_LOCAL = "change";
9696
var CHANGES_LOCAL = "changes";
9797
var TEMP_LOCAL = "temp";
98+
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
9899

99100
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
100101
return `
101102
${cons}
102103
${detectChanges}
103104
${setContext};
104105
105-
return function(dispatcher, formatters) {
106-
return new ${type}(dispatcher, formatters, protos);
106+
return function(dispatcher, formatters, pipeRegistry) {
107+
return new ${type}(dispatcher, formatters, pipeRegistry, protos);
107108
}
108109
`;
109110
}
110111

111112
function constructorTemplate(type:string, fieldsDefinitions:string):string {
112113
return `
113-
var ${type} = function ${type}(dispatcher, formatters, protos) {
114+
var ${type} = function ${type}(dispatcher, formatters, pipeRegistry, protos) {
114115
${ABSTRACT_CHANGE_DETECTOR}.call(this);
115116
${DISPATCHER_ACCESSOR} = dispatcher;
116117
${FORMATTERS_ACCESSOR} = formatters;
118+
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
117119
${PROTOS_ACCESSOR} = protos;
118120
${fieldsDefinitions}
119121
}
@@ -162,14 +164,18 @@ if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) {
162164
`;
163165
}
164166

165-
166-
function structuralCheckTemplate(selfIndex:number, field:string, context:string, notify:string):string{
167+
function pipeCheckTemplate(context:string, pipe:string,
168+
value:string, change:string, addRecord:string, notify:string):string{
167169
return `
168-
${CHANGE_LOCAL} = ${UTIL}.structuralCheck(${field}, ${context});
169-
if (${CHANGE_LOCAL}) {
170-
${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL},
171-
${UTIL}.changeRecord(${PROTOS_ACCESSOR}[${selfIndex}].bindingMemento, ${CHANGE_LOCAL}));
172-
${field} = ${CHANGE_LOCAL}.currentValue;
170+
if (${pipe} === ${UTIL}.unitialized() || !${pipe}.supports(${context})) {
171+
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('[]', ${context});
172+
}
173+
174+
${CHANGE_LOCAL} = ${pipe}.transform(${context});
175+
if (! ${UTIL}.noChangeMarker(${CHANGE_LOCAL})) {
176+
${value} = ${CHANGE_LOCAL};
177+
${change} = true;
178+
${addRecord}
173179
}
174180
${notify}
175181
`;
@@ -235,6 +241,7 @@ export class ChangeDetectorJITGenerator {
235241
localNames:List<String>;
236242
changeNames:List<String>;
237243
fieldNames:List<String>;
244+
pipeNames:List<String>;
238245

239246
constructor(typeName:string, records:List<ProtoRecord>) {
240247
this.typeName = typeName;
@@ -243,6 +250,7 @@ export class ChangeDetectorJITGenerator {
243250
this.localNames = this.getLocalNames(records);
244251
this.changeNames = this.getChangeNames(this.localNames);
245252
this.fieldNames = this.getFieldNames(this.localNames);
253+
this.pipeNames = this.getPipeNames(this.localNames);
246254
}
247255

248256
getLocalNames(records:List<ProtoRecord>):List<String> {
@@ -262,14 +270,26 @@ export class ChangeDetectorJITGenerator {
262270
return localNames.map((n) => `this.${n}`);
263271
}
264272

273+
getPipeNames(localNames:List<String>):List<String> {
274+
return localNames.map((n) => `this.${n}_pipe`);
275+
}
265276

266277
generate():Function {
267278
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genSetContext());
268279
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'ContextWithVariableBindings', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, ContextWithVariableBindings, this.records);
269280
}
270281

271282
genConstructor():string {
272-
return constructorTemplate(this.typeName, fieldDefinitionsTemplate(this.fieldNames));
283+
var fields = [];
284+
fields = fields.concat(this.fieldNames);
285+
286+
this.records.forEach((r) => {
287+
if (r.mode === RECORD_TYPE_STRUCTURAL_CHECK) {
288+
fields.push(this.pipeNames[r.selfIndex]);
289+
}
290+
});
291+
292+
return constructorTemplate(this.typeName, fieldDefinitionsTemplate(fields));
273293
}
274294

275295
genSetContext():string {
@@ -295,17 +315,24 @@ export class ChangeDetectorJITGenerator {
295315
}
296316

297317
genRecord(r:ProtoRecord):string {
298-
if (r.mode == RECORD_TYPE_STRUCTURAL_CHECK) {
299-
return this.getStructuralCheck(r);
318+
if (r.mode === RECORD_TYPE_STRUCTURAL_CHECK) {
319+
return this.genPipeCheck (r);
300320
} else {
301321
return this.genReferenceCheck(r);
302322
}
303323
}
304324

305-
getStructuralCheck(r:ProtoRecord):string {
306-
var field = this.fieldNames[r.selfIndex];
325+
genPipeCheck(r:ProtoRecord):string {
307326
var context = this.localNames[r.contextIndex];
308-
return structuralCheckTemplate(r.selfIndex - 1, field, context, this.genNotify(r));
327+
var pipe = this.pipeNames[r.selfIndex];
328+
var newValue = this.localNames[r.selfIndex];
329+
var oldValue = this.fieldNames[r.selfIndex];
330+
var change = this.changeNames[r.selfIndex];
331+
332+
var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
333+
var notify = this.genNotify(r);
334+
335+
return pipeCheckTemplate(context, pipe, newValue, change, addRecord, notify);
309336
}
310337

311338
genReferenceCheck(r:ProtoRecord):string {

modules/angular2/src/change_detection/change_detection_util.js

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
22
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
33
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
4-
import {ArrayChanges} from './array_changes';
5-
import {KeyValueChanges} from './keyvalue_changes';
64
import {ProtoRecord} from './proto_change_detector';
75
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
6+
import {NO_CHANGE} from './pipes/pipe';
87
import {ChangeRecord, ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
98

109
export var uninitialized = new Object();
@@ -85,10 +84,6 @@ function _changeRecord(bindingMemento, change) {
8584

8685
var _singleElementList = [null];
8786

88-
function _isBlank(val):boolean {
89-
return isBlank(val) || val === uninitialized;
90-
}
91-
9287
export class ChangeDetectionUtil {
9388
static unitialized() {
9489
return uninitialized;
@@ -149,32 +144,6 @@ export class ChangeDetectionUtil {
149144
return obj[args[0]];
150145
}
151146

152-
static structuralCheck(self, context) {
153-
if (_isBlank(self) && _isBlank(context)) {
154-
return null;
155-
} else if (_isBlank(context)) {
156-
return new SimpleChange(null, null);
157-
}
158-
159-
if (_isBlank(self)) {
160-
if (ArrayChanges.supports(context)) {
161-
self = new ArrayChanges();
162-
} else if (KeyValueChanges.supports(context)) {
163-
self = new KeyValueChanges();
164-
}
165-
}
166-
167-
if (isBlank(self) || !self.supportsObj(context)) {
168-
throw new BaseException(`Unsupported type (${context})`);
169-
}
170-
171-
if (self.check(context)) {
172-
return new SimpleChange(null, self); // TODO: don't wrap and return self instead
173-
} else {
174-
return null;
175-
}
176-
}
177-
178147
static findContext(name:string, c){
179148
while (c instanceof ContextWithVariableBindings) {
180149
if (c.hasBinding(name)) {
@@ -185,6 +154,10 @@ export class ChangeDetectionUtil {
185154
return c;
186155
}
187156

157+
static noChangeMarker(value):boolean {
158+
return value === NO_CHANGE;
159+
}
160+
188161
static throwOnChange(proto:ProtoRecord, change) {
189162
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
190163
}

modules/angular2/src/change_detection/dynamic_change_detector.js

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
33
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
44

55
import {AbstractChangeDetector} from './abstract_change_detector';
6+
import {PipeRegistry} from './pipes/pipe_registry';
67
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
78

89

@@ -26,23 +27,34 @@ import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './ex
2627
export class DynamicChangeDetector extends AbstractChangeDetector {
2728
dispatcher:any;
2829
formatters:Map;
30+
pipeRegistry;
31+
2932
values:List;
3033
changes:List;
34+
pipes:List;
35+
prevContexts:List;
36+
3137
protos:List<ProtoRecord>;
3238

33-
constructor(dispatcher:any, formatters:Map, protoRecords:List<ProtoRecord>) {
39+
constructor(dispatcher:any, formatters:Map, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
3440
super();
3541
this.dispatcher = dispatcher;
3642
this.formatters = formatters;
43+
this.pipeRegistry = pipeRegistry;
3744

3845
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
46+
this.pipes = ListWrapper.createFixedSize(protoRecords.length + 1);
47+
this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1);
3948
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
4049

4150
this.protos = protoRecords;
4251
}
4352

4453
setContext(context:any) {
4554
ListWrapper.fill(this.values, uninitialized);
55+
ListWrapper.fill(this.changes, false);
56+
ListWrapper.fill(this.pipes, null);
57+
ListWrapper.fill(this.prevContexts, uninitialized);
4658
this.values[0] = context;
4759
}
4860

@@ -71,7 +83,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
7183
_check(proto:ProtoRecord) {
7284
try {
7385
if (proto.mode == RECORD_TYPE_STRUCTURAL_CHECK) {
74-
return this._structuralCheck(proto);
86+
return this._pipeCheck(proto);
7587
} else {
7688
return this._referenceCheck(proto);
7789
}
@@ -147,15 +159,36 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
147159
}
148160
}
149161

150-
_structuralCheck(proto:ProtoRecord) {
151-
var self = this._readSelf(proto);
162+
_pipeCheck(proto:ProtoRecord) {
152163
var context = this._readContext(proto);
164+
var pipe = this._pipeFor(proto, context);
153165

154-
var change = ChangeDetectionUtil.structuralCheck(self, context);
155-
if (isPresent(change)) {
156-
this._writeSelf(proto, change.currentValue);
166+
var newValue = pipe.transform(context);
167+
if (! ChangeDetectionUtil.noChangeMarker(newValue)) {
168+
this._writeSelf(proto, newValue);
169+
this._setChanged(proto, true);
170+
171+
if (proto.lastInBinding) {
172+
var prevValue = this._readSelf(proto);
173+
return ChangeDetectionUtil.simpleChange(prevValue, newValue);
174+
} else {
175+
return null;
176+
}
177+
} else {
178+
this._setChanged(proto, false);
179+
return null;
180+
}
181+
}
182+
183+
_pipeFor(proto:ProtoRecord, context) {
184+
var storedPipe = this._readPipe(proto);
185+
if (isPresent(storedPipe) && storedPipe.supports(context)) {
186+
return storedPipe;
187+
} else {
188+
var pipe = this.pipeRegistry.get("[]", context);
189+
this._writePipe(proto, pipe);
190+
return pipe;
157191
}
158-
return change;
159192
}
160193

161194
_readContext(proto:ProtoRecord) {
@@ -170,6 +203,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
170203
this.values[proto.selfIndex] = value;
171204
}
172205

206+
_readPipe(proto:ProtoRecord) {
207+
return this.pipes[proto.selfIndex];
208+
}
209+
210+
_writePipe(proto:ProtoRecord, value) {
211+
this.pipes[proto.selfIndex] = value;
212+
}
213+
173214
_setChanged(proto:ProtoRecord, value:boolean) {
174215
this.changes[proto.selfIndex] = value;
175216
}

0 commit comments

Comments
 (0)