From 695b4ebbc74ac19a79ac7fcb41f63a57dec5a237 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Thu, 12 Feb 2015 14:56:41 -0800 Subject: [PATCH] feat(change_detection): add support for pipes --- modules/angular2/change_detection.js | 26 +- .../change_detection_jit_generator.es6 | 59 ++-- .../change_detection/change_detection_util.js | 37 +-- .../dynamic_change_detector.js | 57 +++- .../{ => pipes}/array_changes.js | 19 +- .../{ => pipes}/keyvalue_changes.js | 0 .../src/change_detection/pipes/null_pipe.js | 27 ++ .../src/change_detection/pipes/pipe.js | 6 + .../change_detection/pipes/pipe_registry.js | 27 ++ .../change_detection/proto_change_detector.js | 15 +- modules/angular2/src/directives/foreach.js | 23 +- .../change_detection/array_changes_spec.js | 10 +- .../change_detection/change_detection_spec.js | 253 +++++++++--------- .../change_detection/keyvalue_changes_spec.js | 2 +- .../transformer_registry_spec.js | 39 +++ .../pipeline/element_binder_builder_spec.js | 4 +- .../pipeline/shadow_dom_transformer_spec.js | 6 +- .../test/core/compiler/view_container_spec.js | 6 +- .../angular2/test/core/compiler/view_spec.js | 70 ++--- .../src/naive_infinite_scroll/index.js | 7 +- 20 files changed, 436 insertions(+), 257 deletions(-) rename modules/angular2/src/change_detection/{ => pipes}/array_changes.js (98%) rename modules/angular2/src/change_detection/{ => pipes}/keyvalue_changes.js (100%) create mode 100644 modules/angular2/src/change_detection/pipes/null_pipe.js create mode 100644 modules/angular2/src/change_detection/pipes/pipe.js create mode 100644 modules/angular2/src/change_detection/pipes/pipe_registry.js create mode 100644 modules/angular2/test/change_detection/transformer_registry_spec.js diff --git a/modules/angular2/change_detection.js b/modules/angular2/change_detection.js index 42b03d4ef7..aa5b8f5163 100644 --- a/modules/angular2/change_detection.js +++ b/modules/angular2/change_detection.js @@ -3,7 +3,6 @@ export {Lexer} from './src/change_detection/parser/lexer'; export {Parser} from './src/change_detection/parser/parser'; export {ContextWithVariableBindings} from './src/change_detection/parser/context_with_variable_bindings'; - export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './src/change_detection/exceptions'; export {ChangeRecord, ChangeDispatcher, ChangeDetector, @@ -12,9 +11,15 @@ export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector} from './src/change_detection/proto_change_detector'; export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector'; +export * from './src/change_detection/pipes/pipe_registry'; +export * from './src/change_detection/pipes/pipe'; + import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector} from './src/change_detection/proto_change_detector'; +import {PipeRegistry} from './src/change_detection/pipes/pipe_registry'; +import {ArrayChanges} from './src/change_detection/pipes/array_changes'; +import {NullPipe} from './src/change_detection/pipes/null_pipe'; export class ChangeDetection { createProtoChangeDetector(name:string):ProtoChangeDetector{ @@ -23,15 +28,30 @@ export class ChangeDetection { } } +export var defaultPipes = { + "[]" : [ + { + "supports" : ArrayChanges.supportsObj, + "pipe" : () => new ArrayChanges() + }, + { + "supports" : NullPipe.supportsObj, + "pipe" : () => new NullPipe() + } + ] +}; + +var _registry = new PipeRegistry(defaultPipes); + export class DynamicChangeDetection extends ChangeDetection { createProtoChangeDetector(name:string):ProtoChangeDetector{ - return new DynamicProtoChangeDetector(); + return new DynamicProtoChangeDetector(_registry); } } export class JitChangeDetection extends ChangeDetection { createProtoChangeDetector(name:string):ProtoChangeDetector{ - return new JitProtoChangeDetector(); + return new JitProtoChangeDetector(_registry); } } diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 b/modules/angular2/src/change_detection/change_detection_jit_generator.es6 index 1bee7bdd74..4cb9f13df6 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.es6 @@ -95,6 +95,7 @@ var PROTOS_ACCESSOR = "this.protos"; var CHANGE_LOCAL = "change"; var CHANGES_LOCAL = "changes"; var TEMP_LOCAL = "temp"; +var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry"; function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string { return ` @@ -102,18 +103,19 @@ ${cons} ${detectChanges} ${setContext}; -return function(dispatcher, formatters) { - return new ${type}(dispatcher, formatters, protos); +return function(dispatcher, formatters, pipeRegistry) { + return new ${type}(dispatcher, formatters, pipeRegistry, protos); } `; } function constructorTemplate(type:string, fieldsDefinitions:string):string { return ` -var ${type} = function ${type}(dispatcher, formatters, protos) { +var ${type} = function ${type}(dispatcher, formatters, pipeRegistry, protos) { ${ABSTRACT_CHANGE_DETECTOR}.call(this); ${DISPATCHER_ACCESSOR} = dispatcher; ${FORMATTERS_ACCESSOR} = formatters; +${PIPE_REGISTRY_ACCESSOR} = pipeRegistry; ${PROTOS_ACCESSOR} = protos; ${fieldsDefinitions} } @@ -162,14 +164,18 @@ if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) { `; } - -function structuralCheckTemplate(selfIndex:number, field:string, context:string, notify:string):string{ +function pipeCheckTemplate(context:string, pipe:string, + value:string, change:string, addRecord:string, notify:string):string{ return ` -${CHANGE_LOCAL} = ${UTIL}.structuralCheck(${field}, ${context}); -if (${CHANGE_LOCAL}) { - ${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL}, - ${UTIL}.changeRecord(${PROTOS_ACCESSOR}[${selfIndex}].bindingMemento, ${CHANGE_LOCAL})); - ${field} = ${CHANGE_LOCAL}.currentValue; +if (${pipe} === ${UTIL}.unitialized() || !${pipe}.supports(${context})) { + ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('[]', ${context}); +} + +${CHANGE_LOCAL} = ${pipe}.transform(${context}); +if (! ${UTIL}.noChangeMarker(${CHANGE_LOCAL})) { + ${value} = ${CHANGE_LOCAL}; + ${change} = true; + ${addRecord} } ${notify} `; @@ -235,6 +241,7 @@ export class ChangeDetectorJITGenerator { localNames:List; changeNames:List; fieldNames:List; + pipeNames:List; constructor(typeName:string, records:List) { this.typeName = typeName; @@ -243,6 +250,7 @@ export class ChangeDetectorJITGenerator { this.localNames = this.getLocalNames(records); this.changeNames = this.getChangeNames(this.localNames); this.fieldNames = this.getFieldNames(this.localNames); + this.pipeNames = this.getPipeNames(this.localNames); } getLocalNames(records:List):List { @@ -262,6 +270,9 @@ export class ChangeDetectorJITGenerator { return localNames.map((n) => `this.${n}`); } + getPipeNames(localNames:List):List { + return localNames.map((n) => `this.${n}_pipe`); + } generate():Function { var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genSetContext()); @@ -269,7 +280,16 @@ export class ChangeDetectorJITGenerator { } genConstructor():string { - return constructorTemplate(this.typeName, fieldDefinitionsTemplate(this.fieldNames)); + var fields = []; + fields = fields.concat(this.fieldNames); + + this.records.forEach((r) => { + if (r.mode === RECORD_TYPE_STRUCTURAL_CHECK) { + fields.push(this.pipeNames[r.selfIndex]); + } + }); + + return constructorTemplate(this.typeName, fieldDefinitionsTemplate(fields)); } genSetContext():string { @@ -295,17 +315,24 @@ export class ChangeDetectorJITGenerator { } genRecord(r:ProtoRecord):string { - if (r.mode == RECORD_TYPE_STRUCTURAL_CHECK) { - return this.getStructuralCheck(r); + if (r.mode === RECORD_TYPE_STRUCTURAL_CHECK) { + return this.genPipeCheck (r); } else { return this.genReferenceCheck(r); } } - getStructuralCheck(r:ProtoRecord):string { - var field = this.fieldNames[r.selfIndex]; + genPipeCheck(r:ProtoRecord):string { var context = this.localNames[r.contextIndex]; - return structuralCheckTemplate(r.selfIndex - 1, field, context, this.genNotify(r)); + var pipe = this.pipeNames[r.selfIndex]; + var newValue = this.localNames[r.selfIndex]; + var oldValue = this.fieldNames[r.selfIndex]; + var change = this.changeNames[r.selfIndex]; + + var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue); + var notify = this.genNotify(r); + + return pipeCheckTemplate(context, pipe, newValue, change, addRecord, notify); } genReferenceCheck(r:ProtoRecord):string { diff --git a/modules/angular2/src/change_detection/change_detection_util.js b/modules/angular2/src/change_detection/change_detection_util.js index bbe2edaacb..16e85217fc 100644 --- a/modules/angular2/src/change_detection/change_detection_util.js +++ b/modules/angular2/src/change_detection/change_detection_util.js @@ -1,10 +1,9 @@ import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang'; import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {ContextWithVariableBindings} from './parser/context_with_variable_bindings'; -import {ArrayChanges} from './array_changes'; -import {KeyValueChanges} from './keyvalue_changes'; import {ProtoRecord} from './proto_change_detector'; import {ExpressionChangedAfterItHasBeenChecked} from './exceptions'; +import {NO_CHANGE} from './pipes/pipe'; import {ChangeRecord, ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces'; export var uninitialized = new Object(); @@ -85,10 +84,6 @@ function _changeRecord(bindingMemento, change) { var _singleElementList = [null]; -function _isBlank(val):boolean { - return isBlank(val) || val === uninitialized; -} - export class ChangeDetectionUtil { static unitialized() { return uninitialized; @@ -149,32 +144,6 @@ export class ChangeDetectionUtil { return obj[args[0]]; } - static structuralCheck(self, context) { - if (_isBlank(self) && _isBlank(context)) { - return null; - } else if (_isBlank(context)) { - return new SimpleChange(null, null); - } - - if (_isBlank(self)) { - if (ArrayChanges.supports(context)) { - self = new ArrayChanges(); - } else if (KeyValueChanges.supports(context)) { - self = new KeyValueChanges(); - } - } - - if (isBlank(self) || !self.supportsObj(context)) { - throw new BaseException(`Unsupported type (${context})`); - } - - if (self.check(context)) { - return new SimpleChange(null, self); // TODO: don't wrap and return self instead - } else { - return null; - } - } - static findContext(name:string, c){ while (c instanceof ContextWithVariableBindings) { if (c.hasBinding(name)) { @@ -185,6 +154,10 @@ export class ChangeDetectionUtil { return c; } + static noChangeMarker(value):boolean { + return value === NO_CHANGE; + } + static throwOnChange(proto:ProtoRecord, change) { throw new ExpressionChangedAfterItHasBeenChecked(proto, change); } diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.js b/modules/angular2/src/change_detection/dynamic_change_detector.js index 8ea2a9aa09..1605f386ec 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.js +++ b/modules/angular2/src/change_detection/dynamic_change_detector.js @@ -3,6 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca import {ContextWithVariableBindings} from './parser/context_with_variable_bindings'; import {AbstractChangeDetector} from './abstract_change_detector'; +import {PipeRegistry} from './pipes/pipe_registry'; import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util'; @@ -26,16 +27,24 @@ import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './ex export class DynamicChangeDetector extends AbstractChangeDetector { dispatcher:any; formatters:Map; + pipeRegistry; + values:List; changes:List; + pipes:List; + prevContexts:List; + protos:List; - constructor(dispatcher:any, formatters:Map, protoRecords:List) { + constructor(dispatcher:any, formatters:Map, pipeRegistry:PipeRegistry, protoRecords:List) { super(); this.dispatcher = dispatcher; this.formatters = formatters; + this.pipeRegistry = pipeRegistry; this.values = ListWrapper.createFixedSize(protoRecords.length + 1); + this.pipes = ListWrapper.createFixedSize(protoRecords.length + 1); + this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1); this.changes = ListWrapper.createFixedSize(protoRecords.length + 1); this.protos = protoRecords; @@ -43,6 +52,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector { setContext(context:any) { ListWrapper.fill(this.values, uninitialized); + ListWrapper.fill(this.changes, false); + ListWrapper.fill(this.pipes, null); + ListWrapper.fill(this.prevContexts, uninitialized); this.values[0] = context; } @@ -71,7 +83,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { _check(proto:ProtoRecord) { try { if (proto.mode == RECORD_TYPE_STRUCTURAL_CHECK) { - return this._structuralCheck(proto); + return this._pipeCheck(proto); } else { return this._referenceCheck(proto); } @@ -147,15 +159,36 @@ export class DynamicChangeDetector extends AbstractChangeDetector { } } - _structuralCheck(proto:ProtoRecord) { - var self = this._readSelf(proto); + _pipeCheck(proto:ProtoRecord) { var context = this._readContext(proto); + var pipe = this._pipeFor(proto, context); - var change = ChangeDetectionUtil.structuralCheck(self, context); - if (isPresent(change)) { - this._writeSelf(proto, change.currentValue); + var newValue = pipe.transform(context); + if (! ChangeDetectionUtil.noChangeMarker(newValue)) { + this._writeSelf(proto, newValue); + this._setChanged(proto, true); + + if (proto.lastInBinding) { + var prevValue = this._readSelf(proto); + return ChangeDetectionUtil.simpleChange(prevValue, newValue); + } else { + return null; + } + } else { + this._setChanged(proto, false); + return null; + } + } + + _pipeFor(proto:ProtoRecord, context) { + var storedPipe = this._readPipe(proto); + if (isPresent(storedPipe) && storedPipe.supports(context)) { + return storedPipe; + } else { + var pipe = this.pipeRegistry.get("[]", context); + this._writePipe(proto, pipe); + return pipe; } - return change; } _readContext(proto:ProtoRecord) { @@ -170,6 +203,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector { this.values[proto.selfIndex] = value; } + _readPipe(proto:ProtoRecord) { + return this.pipes[proto.selfIndex]; + } + + _writePipe(proto:ProtoRecord, value) { + this.pipes[proto.selfIndex] = value; + } + _setChanged(proto:ProtoRecord, value:boolean) { this.changes[proto.selfIndex] = value; } diff --git a/modules/angular2/src/change_detection/array_changes.js b/modules/angular2/src/change_detection/pipes/array_changes.js similarity index 98% rename from modules/angular2/src/change_detection/array_changes.js rename to modules/angular2/src/change_detection/pipes/array_changes.js index a427366205..7437563ecf 100644 --- a/modules/angular2/src/change_detection/array_changes.js +++ b/modules/angular2/src/change_detection/pipes/array_changes.js @@ -14,7 +14,9 @@ import { looseIdentical, } from 'angular2/src/facade/lang'; -export class ArrayChanges { +import {NO_CHANGE, Pipe} from './pipe'; + +export class ArrayChanges extends Pipe { _collection; _length:int; _linkedRecords:_DuplicateMap; @@ -30,6 +32,7 @@ export class ArrayChanges { _removalsTail:CollectionChangeRecord; constructor() { + super(); this._collection = null; this._length = null; /// Keeps track of the used records at any point in time (during & across `_check()` calls) @@ -48,12 +51,12 @@ export class ArrayChanges { this._removalsTail = null; } - static supports(obj):boolean { + static supportsObj(obj):boolean { return isListLikeIterable(obj); } - supportsObj(obj):boolean { - return ArrayChanges.supports(obj); + supports(obj):boolean { + return ArrayChanges.supportsObj(obj); } get collection() { @@ -99,6 +102,14 @@ export class ArrayChanges { } } + transform(collection){ + if (this.check(collection)) { + return this; + } else { + return NO_CHANGE; + } + } + // todo(vicb): optim for UnmodifiableListView (frozen arrays) check(collection):boolean { this._reset(); diff --git a/modules/angular2/src/change_detection/keyvalue_changes.js b/modules/angular2/src/change_detection/pipes/keyvalue_changes.js similarity index 100% rename from modules/angular2/src/change_detection/keyvalue_changes.js rename to modules/angular2/src/change_detection/pipes/keyvalue_changes.js diff --git a/modules/angular2/src/change_detection/pipes/null_pipe.js b/modules/angular2/src/change_detection/pipes/null_pipe.js new file mode 100644 index 0000000000..3e0766c630 --- /dev/null +++ b/modules/angular2/src/change_detection/pipes/null_pipe.js @@ -0,0 +1,27 @@ +import {isBlank} from 'angular2/src/facade/lang'; +import {Pipe, NO_CHANGE} from './pipe'; + +export class NullPipe extends Pipe { + called:boolean; + constructor() { + super(); + this.called = false; + } + + static supportsObj(obj):boolean { + return isBlank(obj); + } + + supports(obj) { + return NullPipe.supportsObj(obj); + } + + transform(value) { + if (! this.called) { + this.called = true; + return null; + } else { + return NO_CHANGE; + } + } +} diff --git a/modules/angular2/src/change_detection/pipes/pipe.js b/modules/angular2/src/change_detection/pipes/pipe.js new file mode 100644 index 0000000000..002d1827f4 --- /dev/null +++ b/modules/angular2/src/change_detection/pipes/pipe.js @@ -0,0 +1,6 @@ +export var NO_CHANGE = new Object(); + +export class Pipe { + supports(obj):boolean {return false;} + transform(value:any):any {return null;} +} \ No newline at end of file diff --git a/modules/angular2/src/change_detection/pipes/pipe_registry.js b/modules/angular2/src/change_detection/pipes/pipe_registry.js new file mode 100644 index 0000000000..1a013bab20 --- /dev/null +++ b/modules/angular2/src/change_detection/pipes/pipe_registry.js @@ -0,0 +1,27 @@ +import {List, ListWrapper} from 'angular2/src/facade/collection'; +import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang'; +import {Pipe} from './pipe'; + +export class PipeRegistry { + config; + + constructor(config){ + this.config = config; + } + + get(type:string, obj):Pipe { + var listOfConfigs = this.config[type]; + if (isBlank(listOfConfigs)) { + throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`); + } + + var matchingConfig = ListWrapper.find(listOfConfigs, + (pipeConfig) => pipeConfig["supports"](obj)); + + if (isBlank(matchingConfig)) { + throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`); + } + + return matchingConfig["pipe"](); + } +} \ No newline at end of file diff --git a/modules/angular2/src/change_detection/proto_change_detector.js b/modules/angular2/src/change_detection/proto_change_detector.js index f5f1b3009e..67de6def9b 100644 --- a/modules/angular2/src/change_detection/proto_change_detector.js +++ b/modules/angular2/src/change_detection/proto_change_detector.js @@ -27,6 +27,7 @@ import {ChangeRecord, ChangeDispatcher, ChangeDetector} from './interfaces'; import {ChangeDetectionUtil} from './change_detection_util'; import {DynamicChangeDetector} from './dynamic_change_detector'; import {ChangeDetectorJITGenerator} from './change_detection_jit_generator'; +import {PipeRegistry} from './pipes/pipe_registry'; import {coalesce} from './coalesce'; @@ -89,6 +90,7 @@ export class ProtoRecord { } } + export class ProtoChangeDetector { addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, structural:boolean = false){} instantiate(dispatcher:any, formatters:Map):ChangeDetector{ @@ -99,9 +101,11 @@ export class ProtoChangeDetector { export class DynamicProtoChangeDetector extends ProtoChangeDetector { _records:List; _recordBuilder:ProtoRecordBuilder; + _pipeRegistry:PipeRegistry; - constructor() { + constructor(pipeRegistry:PipeRegistry) { super(); + this._pipeRegistry = pipeRegistry; this._records = null; this._recordBuilder = new ProtoRecordBuilder(); } @@ -112,7 +116,8 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector { instantiate(dispatcher:any, formatters:Map) { this._createRecordsIfNecessary(); - return new DynamicChangeDetector(dispatcher, formatters, this._records); + return new DynamicChangeDetector(dispatcher, formatters, + this._pipeRegistry, this._records); } _createRecordsIfNecessary() { @@ -127,9 +132,11 @@ var _jitProtoChangeDetectorClassCounter:number = 0; export class JitProtoChangeDetector extends ProtoChangeDetector { _factory:Function; _recordBuilder:ProtoRecordBuilder; + _pipeRegistry; - constructor() { + constructor(pipeRegistry) { super(); + this._pipeRegistry = pipeRegistry; this._factory = null; this._recordBuilder = new ProtoRecordBuilder(); } @@ -140,7 +147,7 @@ export class JitProtoChangeDetector extends ProtoChangeDetector { instantiate(dispatcher:any, formatters:Map) { this._createFactoryIfNecessary(); - return this._factory(dispatcher, formatters); + return this._factory(dispatcher, formatters, this._pipeRegistry); } _createFactoryIfNecessary() { diff --git a/modules/angular2/src/directives/foreach.js b/modules/angular2/src/directives/foreach.js index a276c61aba..858330b6f5 100644 --- a/modules/angular2/src/directives/foreach.js +++ b/modules/angular2/src/directives/foreach.js @@ -1,5 +1,4 @@ -import {Viewport, onChange} from 'angular2/src/core/annotations/annotations'; -import {OnChange} from 'angular2/src/core/compiler/interfaces'; +import {Viewport} from 'angular2/src/core/annotations/annotations'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; import {View} from 'angular2/src/core/compiler/view'; import {isPresent, isBlank} from 'angular2/src/facade/lang'; @@ -7,21 +6,19 @@ import {ListWrapper} from 'angular2/src/facade/collection'; @Viewport({ selector: '[foreach][in]', - lifecycle: [onChange], bind: { - 'in': 'iterable[]' + 'in': 'iterableChanges[]' } }) -export class Foreach extends OnChange { +export class Foreach { viewContainer: ViewContainer; - iterable; - constructor(viewContainer: ViewContainer) { + constructor(viewContainer:ViewContainer) { super(); this.viewContainer = viewContainer; } - onChange(changes) { - var iteratorChanges = changes['iterable']; - if (isBlank(iteratorChanges) || isBlank(iteratorChanges.currentValue)) { + + set iterableChanges(changes) { + if (isBlank(changes)) { this.viewContainer.clear(); return; } @@ -29,17 +26,17 @@ export class Foreach extends OnChange { // TODO(rado): check if change detection can produce a change record that is // easier to consume than current. var recordViewTuples = []; - iteratorChanges.currentValue.forEachRemovedItem( + changes.forEachRemovedItem( (removedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(removedRecord, null)) ); - iteratorChanges.currentValue.forEachMovedItem( + changes.forEachMovedItem( (movedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(movedRecord, null)) ); var insertTuples = Foreach.bulkRemove(recordViewTuples, this.viewContainer); - iteratorChanges.currentValue.forEachAddedItem( + changes.forEachAddedItem( (addedRecord) => ListWrapper.push(insertTuples, new RecordViewTuple(addedRecord, null)) ); diff --git a/modules/angular2/test/change_detection/array_changes_spec.js b/modules/angular2/test/change_detection/array_changes_spec.js index 2130e010ed..aeea7169a1 100644 --- a/modules/angular2/test/change_detection/array_changes_spec.js +++ b/modules/angular2/test/change_detection/array_changes_spec.js @@ -1,5 +1,5 @@ import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; -import {ArrayChanges} from 'angular2/src/change_detection/array_changes'; +import {ArrayChanges} from 'angular2/src/change_detection/pipes/array_changes'; import {NumberWrapper} from 'angular2/src/facade/lang'; import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; @@ -23,10 +23,10 @@ export function main() { }); it('should support list and iterables', () => { - expect(ArrayChanges.supports([])).toBeTruthy(); - expect(ArrayChanges.supports(new TestIterable())).toBeTruthy(); - expect(ArrayChanges.supports(MapWrapper.create())).toBeFalsy(); - expect(ArrayChanges.supports(null)).toBeFalsy(); + expect(ArrayChanges.supportsObj([])).toBeTruthy(); + expect(ArrayChanges.supportsObj(new TestIterable())).toBeTruthy(); + expect(ArrayChanges.supportsObj(MapWrapper.create())).toBeFalsy(); + expect(ArrayChanges.supportsObj(null)).toBeFalsy(); }); it('should support iterables', () => { diff --git a/modules/angular2/test/change_detection/change_detection_spec.js b/modules/angular2/test/change_detection/change_detection_spec.js index 01c7b29967..ce9c7cab6e 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.js +++ b/modules/angular2/test/change_detection/change_detection_spec.js @@ -8,6 +8,7 @@ import {Lexer} from 'angular2/src/change_detection/parser/lexer'; import {arrayChangesAsString, kvChangesAsString} from './util'; import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, ContextWithVariableBindings, + PipeRegistry, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from 'angular2/change_detection'; @@ -17,8 +18,8 @@ import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/c export function main() { describe("change detection", () => { StringMapWrapper.forEach( - { "dynamic": () => new DynamicProtoChangeDetector(), - "JIT": () => new JitProtoChangeDetector() + { "dynamic": (registry = null) => new DynamicProtoChangeDetector(registry), + "JIT": (registry = null) => new JitProtoChangeDetector(registry) }, (createProtoChangeDetector, name) => { if (name == "JIT" && IS_DARTIUM) return; @@ -29,8 +30,8 @@ export function main() { } function createChangeDetector(memo:string, exp:string, context = null, formatters = null, - structural = false) { - var pcd = createProtoChangeDetector(); + registry = null, structural = false) { + var pcd = createProtoChangeDetector(registry); pcd.addAst(ast(exp), memo, memo, structural); var dispatcher = new TestDispatcher(); @@ -41,8 +42,8 @@ export function main() { } function executeWatch(memo:string, exp:string, context = null, formatters = null, - content = false) { - var res = createChangeDetector(memo, exp, context, formatters, content); + registry = null, content = false) { + var res = createChangeDetector(memo, exp, context, formatters, registry, content); res["changeDetector"].detectChanges(); return res["dispatcher"].log; } @@ -281,124 +282,6 @@ export function main() { }); }); - describe("collections", () => { - it("should not register a change when going from null to null", () => { - var context = new TestData(null); - - var c = createChangeDetector('a', 'a', context, null, true); - var cd = c["changeDetector"]; - var dispatcher = c["dispatcher"]; - - cd.detectChanges(); - expect(dispatcher.log).toEqual([]); - }); - - it("should register changes when switching from null to collection and back", () => { - var context = new TestData(null); - - var c = createChangeDetector('a', 'a', context, null, true); - var cd = c["changeDetector"]; - var dispatcher = c["dispatcher"]; - - context.a = [0]; - cd.detectChanges(); - - expect(dispatcher.log).toEqual(["a=" + - arrayChangesAsString({ - collection: ['0[null->0]'], - additions: ['0[null->0]'] - }) - ]); - dispatcher.clear(); - - context.a = null; - cd.detectChanges(); - expect(dispatcher.log).toEqual(['a=null']); - }); - - describe("list", () => { - it("should support list changes", () => { - var context = new TestData([1, 2]); - - expect(executeWatch("a", "a", context, null, true)) - .toEqual(["a=" + - arrayChangesAsString({ - collection: ['1[null->0]', '2[null->1]'], - additions: ['1[null->0]', '2[null->1]'] - })]); - }); - - it("should handle reference changes", () => { - var context = new TestData([1, 2]); - var objs = createChangeDetector("a", "a", context, null, true); - var cd = objs["changeDetector"]; - var dispatcher = objs["dispatcher"]; - cd.detectChanges(); - dispatcher.clear(); - - context.a = [2, 1]; - cd.detectChanges(); - expect(dispatcher.log).toEqual(["a=" + - arrayChangesAsString({ - collection: ['2[1->0]', '1[0->1]'], - previous: ['1[0->1]', '2[1->0]'], - moves: ['2[1->0]', '1[0->1]'] - })]); - }); - }); - - describe("map", () => { - it("should support map changes", () => { - var map = MapWrapper.create(); - MapWrapper.set(map, "foo", "bar"); - var context = new TestData(map); - expect(executeWatch("a", "a", context, null, true)) - .toEqual(["a=" + - kvChangesAsString({ - map: ['foo[null->bar]'], - additions: ['foo[null->bar]'] - })]); - }); - - it("should handle reference changes", () => { - var map = MapWrapper.create(); - MapWrapper.set(map, "foo", "bar"); - var context = new TestData(map); - var objs = createChangeDetector("a", "a", context, null, true); - var cd = objs["changeDetector"]; - var dispatcher = objs["dispatcher"]; - cd.detectChanges(); - dispatcher.clear(); - - context.a = MapWrapper.create(); - MapWrapper.set(context.a, "bar", "foo"); - cd.detectChanges(); - expect(dispatcher.log).toEqual(["a=" + - kvChangesAsString({ - map: ['bar[null->foo]'], - previous: ['foo[bar->null]'], - additions: ['bar[null->foo]'], - removals: ['foo[bar->null]'] - })]); - }); - }); - - if (!IS_DARTIUM) { - describe("js objects", () => { - it("should support object changes", () => { - var map = {"foo": "bar"}; - var context = new TestData(map); - expect(executeWatch("a", "a", context, null, true)) - .toEqual(["a=" + - kvChangesAsString({ - map: ['foo[null->bar]'], - additions: ['foo[null->bar]'] - })]); - }); - }); - } - }); - describe("ContextWithVariableBindings", () => { it('should read a field from ContextWithVariableBindings', () => { var locals = new ContextWithVariableBindings(null, @@ -543,10 +426,132 @@ export function main() { expect(checkedChild.mode).toEqual(CHECK_ONCE); }); }); + + describe("pipes", () => { + it("should support pipes", () => { + var registry = new FakePipeRegistry(() => new CountingPipe()); + var ctx = new Person("Megatron"); + + var c = createChangeDetector("memo", "name", ctx, null, registry, true); + var cd = c["changeDetector"]; + var dispatcher = c["dispatcher"]; + + cd.detectChanges(); + + expect(dispatcher.log).toEqual(['memo=Megatron state:0']); + + dispatcher.clear(); + cd.detectChanges(); + + expect(dispatcher.log).toEqual(['memo=Megatron state:1']); + }); + + it("should lookup pipes in the registry when the context is not supported", () => { + var registry = new FakePipeRegistry(() => new OncePipe()); + var ctx = new Person("Megatron"); + + var c = createChangeDetector("memo", "name", ctx, null, registry, true); + var cd = c["changeDetector"]; + + cd.detectChanges(); + + expect(registry.numberOfLookups).toEqual(1); + + ctx.name = "Optimus Prime"; + cd.detectChanges(); + + expect(registry.numberOfLookups).toEqual(2); + }); + }); + + it("should do nothing when returns NO_CHANGE", () => { + var registry = new FakePipeRegistry(() => new IdentityPipe()) + var ctx = new Person("Megatron"); + + var c = createChangeDetector("memo", "name", ctx, null, registry, true); + var cd = c["changeDetector"]; + var dispatcher = c["dispatcher"]; + + cd.detectChanges(); + cd.detectChanges(); + + expect(dispatcher.log).toEqual(['memo=Megatron']); + + ctx.name = "Optimus Prime"; + dispatcher.clear(); + cd.detectChanges(); + + expect(dispatcher.log).toEqual(['memo=Optimus Prime']); + }); }); }); } +class CountingPipe { + state:number; + + constructor() { + this.state = 0; + } + + supports(newValue) { + return true; + } + + transform(value) { + return `${value} state:${this.state ++}`; + } +} + +class OncePipe { + called:boolean; + constructor() { + this.called = false;; + } + + supports(newValue) { + return !this.called; + } + + transform(value) { + this.called = true; + return value; + } +} + +class IdentityPipe { + state:any; + + supports(newValue) { + return true; + } + + transform(value) { + if (this.state === value) { + return NO_CHANGE; + } else { + this.state = value; + return value; + } + } +} + +class FakePipeRegistry extends PipeRegistry { + numberOfLookups:number; + factory:Function; + + constructor(factory) { + super({}); + this.factory = factory; + this.numberOfLookups = 0; + } + + get(type:string, obj) { + this.numberOfLookups ++; + return this.factory(); + } +} + class TestRecord { a; b; diff --git a/modules/angular2/test/change_detection/keyvalue_changes_spec.js b/modules/angular2/test/change_detection/keyvalue_changes_spec.js index 7ef078f4a1..8bf5810513 100644 --- a/modules/angular2/test/change_detection/keyvalue_changes_spec.js +++ b/modules/angular2/test/change_detection/keyvalue_changes_spec.js @@ -1,5 +1,5 @@ import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; -import {KeyValueChanges} from 'angular2/src/change_detection/keyvalue_changes'; +import {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes'; import {NumberWrapper, isJsObject} from 'angular2/src/facade/lang'; import {MapWrapper} from 'angular2/src/facade/collection'; import {kvChangesAsString} from './util'; diff --git a/modules/angular2/test/change_detection/transformer_registry_spec.js b/modules/angular2/test/change_detection/transformer_registry_spec.js new file mode 100644 index 0000000000..d62451ec7d --- /dev/null +++ b/modules/angular2/test/change_detection/transformer_registry_spec.js @@ -0,0 +1,39 @@ +import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib'; + +import {PipeRegistry} from 'angular2/src/change_detection/pipes/pipe_registry'; +import {Pipe} from 'angular2/src/change_detection/pipes/pipe'; + +export function main() { + describe("pipe registry", () => { + var firstPipe = new Pipe(); + var secondPipe = new Pipe(); + + it("should return the first pipe supporting the data type", () => { + var r = new PipeRegistry({ + "type": [ + {"supports": (obj) => false, "pipe": () => firstPipe}, + {"supports": (obj) => true, "pipe": () => secondPipe} + ] + }); + + expect(r.get("type", "some object")).toBe(secondPipe); + }); + + it("should throw when no matching type", () => { + var r = new PipeRegistry({}); + expect(() => r.get("unknown", "some object")).toThrowError( + `Cannot find a pipe for type 'unknown' object 'some object'` + ); + }); + + it("should throw when no matching pipe", () => { + var r = new PipeRegistry({ + "type" : [] + }); + + expect(() => r.get("type", "some object")).toThrowError( + `Cannot find a pipe for type 'type' object 'some object'` + ); + }); + }); +} diff --git a/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js b/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js index 18a724e687..443f202361 100644 --- a/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js +++ b/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js @@ -71,7 +71,7 @@ export function main() { if (isPresent(current.element.getAttribute('viewroot'))) { current.isViewRoot = true; current.inheritedProtoView = new ProtoView(current.element, - new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); } else if (isPresent(parent)) { current.inheritedProtoView = parent.inheritedProtoView; } @@ -379,7 +379,7 @@ export function main() { var results = pipeline.process(el('
')); var pv = results[0].inheritedProtoView; results[0].inheritedElementBinder.nestedProtoView = new ProtoView( - el('
'), new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + el('
'), new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); instantiateView(pv); evalContext.prop1 = 'a'; diff --git a/modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js b/modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js index ff9d1c2e91..892097793f 100644 --- a/modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js +++ b/modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js @@ -15,9 +15,9 @@ export function main() { function createPipeline(selector, strategy:ShadowDomStrategy, styleHost) { var component = new Component({selector: selector}); var meta = new DirectiveMetadata(null, component); - var transformer = new ShadowDomTransformer(meta, strategy, styleHost); - transformer.clearCache(); - return new CompilePipeline([transformer]); + var pipe = new ShadowDomTransformer(meta, strategy, styleHost); + pipe.clearCache(); + return new CompilePipeline([pipe]); } it('it should set ignoreBindings to true for style elements', () => { diff --git a/modules/angular2/test/core/compiler/view_container_spec.js b/modules/angular2/test/core/compiler/view_container_spec.js index 79cb6c18d3..7e0a4b0260 100644 --- a/modules/angular2/test/core/compiler/view_container_spec.js +++ b/modules/angular2/test/core/compiler/view_container_spec.js @@ -10,7 +10,7 @@ import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_str import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular2/change_detection'; function createView(nodes) { - var view = new View(null, nodes, new DynamicProtoChangeDetector(), MapWrapper.create()); + var view = new View(null, nodes, new DynamicProtoChangeDetector(null), MapWrapper.create()); view.init([], [], [], [], [], [], []); return view; } @@ -69,7 +69,7 @@ export function main() { dom = el(`
`); var insertionElement = dom.childNodes[1]; parentView = createView([dom.childNodes[0]]); - protoView = new ProtoView(el('
hi
'), new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + protoView = new ProtoView(el('
hi
'), new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); elementInjector = new ElementInjector(null, null, null, null); viewContainer = new ViewContainer(parentView, insertionElement, protoView, elementInjector, null); customViewWithOneNode = createView([el('
single
')]); @@ -213,7 +213,7 @@ export function main() { viewContainer.hydrate(new Injector([]), null); var pv = new ProtoView(el('
{{}}
'), - new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective])); pv.bindTextNode(0, parser.parseBinding('foo', null)); fancyView = pv.instantiate(null, null); diff --git a/modules/angular2/test/core/compiler/view_spec.js b/modules/angular2/test/core/compiler/view_spec.js index d7590553c7..af25e86740 100644 --- a/modules/angular2/test/core/compiler/view_spec.js +++ b/modules/angular2/test/core/compiler/view_spec.js @@ -59,7 +59,7 @@ export function main() { describe('instantiated from protoView', () => { var view; beforeEach(() => { - var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(), null); + var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(null), null); view = pv.instantiate(null, null); }); @@ -77,7 +77,7 @@ export function main() { }); it('should use the view pool to reuse views', () => { - var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(), null); + var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(null), null); var fakeView = new FakeView(); pv.returnToPool(fakeView); @@ -88,7 +88,7 @@ export function main() { describe('with locals', function() { var view; beforeEach(() => { - var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(), null); + var pv = new ProtoView(el('
'), new DynamicProtoChangeDetector(null), null); pv.bindVariable('context-foo', 'template-foo'); view = createView(pv); }); @@ -125,7 +125,7 @@ export function main() { it('should collect the root node in the ProtoView element', () => { var pv = new ProtoView(templateAwareCreateElement('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); var view = pv.instantiate(null, null); view.hydrate(null, null, null); expect(view.nodes.length).toBe(1); @@ -136,7 +136,7 @@ export function main() { it('should collect property bindings on the root element if it has the ng-binding class', () => { var pv = new ProtoView(templateAwareCreateElement('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(null); pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop')); @@ -148,7 +148,7 @@ export function main() { it('should collect property bindings on child elements with ng-binding class', () => { var pv = new ProtoView(templateAwareCreateElement('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(null); pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a')); @@ -164,7 +164,7 @@ export function main() { it('should collect text nodes under the root element', () => { var pv = new ProtoView(templateAwareCreateElement('
{{}}{{}}
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(null); pv.bindTextNode(0, parser.parseBinding('a', null)); pv.bindTextNode(2, parser.parseBinding('b', null)); @@ -178,7 +178,7 @@ export function main() { it('should collect text nodes with bindings on child elements with ng-binding class', () => { var pv = new ProtoView(templateAwareCreateElement('
{{}}
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(null); pv.bindTextNode(0, parser.parseBinding('b', null)); @@ -194,7 +194,7 @@ export function main() { describe('inplace instantiation', () => { it('should be supported.', () => { var template = el('
'); - var pv = new ProtoView(template, new DynamicProtoChangeDetector(), + var pv = new ProtoView(template, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); pv.instantiateInPlace = true; var view = pv.instantiate(null, null); @@ -204,7 +204,7 @@ export function main() { it('should be off by default.', () => { var template = el('
') - var view = new ProtoView(template, new DynamicProtoChangeDetector(), + var view = new ProtoView(template, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()) .instantiate(null, null); view.hydrate(null, null, null); @@ -223,7 +223,7 @@ export function main() { describe('create ElementInjectors', () => { it('should use the directives of the ProtoElementInjector', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective])); var view = pv.instantiate(null, null); @@ -234,7 +234,7 @@ export function main() { it('should use the correct parent', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); pv.bindElement(protoParent); pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective])); @@ -248,7 +248,7 @@ export function main() { it('should not pass the host injector when a parent injector exists', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); pv.bindElement(protoParent); var testProtoElementInjector = new TestProtoElementInjector(protoParent, 1, [AnotherDirective]); @@ -264,7 +264,7 @@ export function main() { it('should pass the host injector when there is no parent injector', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective])); var testProtoElementInjector = new TestProtoElementInjector(null, 1, [AnotherDirective]); pv.bindElement(testProtoElementInjector); @@ -281,7 +281,7 @@ export function main() { it('should collect a single root element injector', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]); pv.bindElement(protoParent); pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective])); @@ -294,7 +294,7 @@ export function main() { it('should collect multiple root element injectors', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective])); pv.bindElement(new ProtoElementInjector(null, 2, [AnotherDirective])); @@ -312,7 +312,7 @@ export function main() { function createComponentWithSubPV(subProtoView) { var pv = new ProtoView(el(''), - new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true)); binder.componentDirective = someComponentDirective; binder.nestedProtoView = subProtoView; @@ -327,7 +327,7 @@ export function main() { } it('should expose component services to the component', () => { - var subpv = new ProtoView(el(''), new DynamicProtoChangeDetector(), null); + var subpv = new ProtoView(el(''), new DynamicProtoChangeDetector(null), null); var pv = createComponentWithSubPV(subpv); var view = createNestedView(pv); @@ -340,7 +340,7 @@ export function main() { () => { var subpv = new ProtoView( el('
hello shadow dom
'), - new DynamicProtoChangeDetector(), + new DynamicProtoChangeDetector(null), null); subpv.bindElement( new ProtoElementInjector(null, 0, [ServiceDependentDecorator])); @@ -365,7 +365,7 @@ export function main() { it('dehydration should dehydrate child component views too', () => { var subpv = new ProtoView( el('
hello shadow dom
'), - new DynamicProtoChangeDetector(), + new DynamicProtoChangeDetector(null), null); subpv.bindElement( new ProtoElementInjector(null, 0, [ServiceDependentDecorator])); @@ -382,7 +382,7 @@ export function main() { it('should create shadow dom (Native Strategy)', () => { var subpv = new ProtoView(el('hello shadow dom'), - new DynamicProtoChangeDetector(), + new DynamicProtoChangeDetector(null), null); var pv = createComponentWithSubPV(subpv); @@ -393,10 +393,10 @@ export function main() { it('should emulate shadow dom (Emulated Strategy)', () => { var subpv = new ProtoView(el('hello shadow dom'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); var pv = new ProtoView(el(''), - new DynamicProtoChangeDetector(), new EmulatedShadowDomStrategy()); + new DynamicProtoChangeDetector(null), new EmulatedShadowDomStrategy()); var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true)); binder.componentDirective = new DirectiveMetadataReader().read(SomeComponent); binder.nestedProtoView = subpv; @@ -410,9 +410,9 @@ export function main() { describe('with template views', () => { function createViewWithViewport() { var templateProtoView = new ProtoView( - el('
'), new DynamicProtoChangeDetector(), null); + el('
'), new DynamicProtoChangeDetector(null), null); var pv = new ProtoView(el(''), - new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeViewport])); binder.viewportDirective = someViewportDirective; binder.nestedProtoView = templateProtoView; @@ -456,7 +456,7 @@ export function main() { function createProtoView() { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new TestProtoElementInjector(null, 0, [])); pv.bindEvent('click', parser.parseBinding('callMe($event)', null)); return pv; @@ -491,7 +491,7 @@ export function main() { it('should support custom event emitters', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new TestProtoElementInjector(null, 0, [EventEmitterDirective])); pv.bindEvent('click', parser.parseBinding('callMe($event)', null)); @@ -522,7 +522,7 @@ export function main() { it('should consume text node changes', () => { var pv = new ProtoView(el('
{{}}
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(null); pv.bindTextNode(0, parser.parseBinding('foo', null)); createViewAndChangeDetector(pv); @@ -534,7 +534,7 @@ export function main() { it('should consume element binding changes', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(null); pv.bindElementProperty(parser.parseBinding('foo', null), 'id', reflector.setter('id')); createViewAndChangeDetector(pv); @@ -546,7 +546,7 @@ export function main() { it('should consume directive watch expression change', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective])); pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop'), false); createViewAndChangeDetector(pv); @@ -558,7 +558,7 @@ export function main() { it('should notify a directive about changes after all its properties have been set', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new ProtoElementInjector(null, 0, [ DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]})) @@ -577,7 +577,7 @@ export function main() { it('should provide a map of updated properties', () => { var pv = new ProtoView(el('
'), - new DynamicProtoChangeDetector(), null); + new DynamicProtoChangeDetector(null), null); pv.bindElement(new ProtoElementInjector(null, 0, [ DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]})) @@ -604,13 +604,13 @@ export function main() { var element, pv; beforeEach(() => { element = DOM.createElement('div'); - pv = new ProtoView(el('
hi
'), new DynamicProtoChangeDetector(), + pv = new ProtoView(el('
hi
'), new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); }); it('should create the root component when instantiated', () => { var rootProtoView = ProtoView.createRootProtoView(pv, element, - someComponentDirective, new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + someComponentDirective, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); var view = rootProtoView.instantiate(null, null); view.hydrate(new Injector([]), null, null); expect(view.rootElementInjectors[0].get(SomeComponent)).not.toBe(null); @@ -618,7 +618,7 @@ export function main() { it('should inject the protoView into the shadowDom', () => { var rootProtoView = ProtoView.createRootProtoView(pv, element, - someComponentDirective, new DynamicProtoChangeDetector(), new NativeShadowDomStrategy()); + someComponentDirective, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy()); var view = rootProtoView.instantiate(null, null); view.hydrate(new Injector([]), null, null); expect(element.shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hi'); diff --git a/modules/benchmarks/src/naive_infinite_scroll/index.js b/modules/benchmarks/src/naive_infinite_scroll/index.js index a9ed7102df..7b08debf26 100644 --- a/modules/benchmarks/src/naive_infinite_scroll/index.js +++ b/modules/benchmarks/src/naive_infinite_scroll/index.js @@ -44,7 +44,7 @@ export function setupReflector() { reflector.registerGetters({ 'scrollAreas': (o) => o.scrollAreas, 'length': (o) => o.length, - 'iterable': (o) => o.iterable, + 'iterableChanges': (o) => o.iterableChanges, 'scrollArea': (o) => o.scrollArea, 'item': (o) => o.item, 'visibleItems': (o) => o.visibleItems, @@ -95,7 +95,7 @@ export function setupReflector() { 'scrollArea': (o, v) => o.scrollArea = v, 'item': (o, v) => o.item = v, 'visibleItems': (o, v) => o.visibleItems = v, - 'iterable': (o, v) => o.iterable = v, + 'iterableChanges': (o, v) => o.iterableChanges = v, 'width': (o, v) => o.width = v, 'value': (o, v) => o.value = v, 'company': (o, v) => o.company = v, @@ -167,9 +167,8 @@ export function setupReflectorForAngular() { 'parameters': [[ViewContainer]], 'annotations' : [new Viewport({ selector: '[foreach]', - lifecycle: [onChange], bind: { - 'in': 'iterable[]' + 'in': 'iterableChanges[]' } })] });