feat(change_detection): add support for pipes
This commit is contained in:
@ -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<String>;
|
||||
changeNames:List<String>;
|
||||
fieldNames:List<String>;
|
||||
pipeNames:List<String>;
|
||||
|
||||
constructor(typeName:string, records:List<ProtoRecord>) {
|
||||
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<ProtoRecord>):List<String> {
|
||||
@ -262,6 +270,9 @@ export class ChangeDetectorJITGenerator {
|
||||
return localNames.map((n) => `this.${n}`);
|
||||
}
|
||||
|
||||
getPipeNames(localNames:List<String>):List<String> {
|
||||
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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<ProtoRecord>;
|
||||
|
||||
constructor(dispatcher:any, formatters:Map, protoRecords:List<ProtoRecord>) {
|
||||
constructor(dispatcher:any, formatters:Map, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
||||
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;
|
||||
}
|
||||
|
@ -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();
|
27
modules/angular2/src/change_detection/pipes/null_pipe.js
vendored
Normal file
27
modules/angular2/src/change_detection/pipes/null_pipe.js
vendored
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
6
modules/angular2/src/change_detection/pipes/pipe.js
vendored
Normal file
6
modules/angular2/src/change_detection/pipes/pipe.js
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export var NO_CHANGE = new Object();
|
||||
|
||||
export class Pipe {
|
||||
supports(obj):boolean {return false;}
|
||||
transform(value:any):any {return null;}
|
||||
}
|
27
modules/angular2/src/change_detection/pipes/pipe_registry.js
vendored
Normal file
27
modules/angular2/src/change_detection/pipes/pipe_registry.js
vendored
Normal file
@ -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"]();
|
||||
}
|
||||
}
|
@ -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<ProtoRecord>;
|
||||
_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() {
|
||||
|
23
modules/angular2/src/directives/foreach.js
vendored
23
modules/angular2/src/directives/foreach.js
vendored
@ -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))
|
||||
);
|
||||
|
||||
|
Reference in New Issue
Block a user