feat(pipes): replaces iterable and key value diffing pipes with services
BREAKING CHANGE: Directives that previously injected Pipes to get iterableDiff or keyvalueDiff, now should inject IterableDiffers and KeyValueDiffers.
This commit is contained in:
parent
c20a5d65d8
commit
392de4af67
|
@ -23,6 +23,12 @@ export {
|
||||||
defaultPipes,
|
defaultPipes,
|
||||||
Pipe,
|
Pipe,
|
||||||
Pipes,
|
Pipes,
|
||||||
|
IterableDiffers,
|
||||||
|
IterableDiffer,
|
||||||
|
IterableDifferFactory,
|
||||||
|
KeyValueDiffers,
|
||||||
|
KeyValueDiffer,
|
||||||
|
KeyValueDifferFactory,
|
||||||
PipeFactory,
|
PipeFactory,
|
||||||
BasePipe,
|
BasePipe,
|
||||||
NullPipe,
|
NullPipe,
|
||||||
|
|
|
@ -9,8 +9,6 @@ export {UpperCasePipe} from './src/change_detection/pipes/uppercase_pipe';
|
||||||
export {LowerCasePipe} from './src/change_detection/pipes/lowercase_pipe';
|
export {LowerCasePipe} from './src/change_detection/pipes/lowercase_pipe';
|
||||||
export {ObservablePipe} from './src/change_detection/pipes/observable_pipe';
|
export {ObservablePipe} from './src/change_detection/pipes/observable_pipe';
|
||||||
export {JsonPipe} from './src/change_detection/pipes/json_pipe';
|
export {JsonPipe} from './src/change_detection/pipes/json_pipe';
|
||||||
export {IterableChanges} from './src/change_detection/pipes/iterable_changes';
|
|
||||||
export {KeyValueChanges} from './src/change_detection/pipes/keyvalue_changes';
|
|
||||||
export {DatePipe} from './src/change_detection/pipes/date_pipe';
|
export {DatePipe} from './src/change_detection/pipes/date_pipe';
|
||||||
export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/change_detection/pipes/number_pipe';
|
export {DecimalPipe, PercentPipe, CurrencyPipe} from './src/change_detection/pipes/number_pipe';
|
||||||
export {LimitToPipe} from './src/change_detection/pipes/limit_to_pipe';
|
export {LimitToPipe} from './src/change_detection/pipes/limit_to_pipe';
|
||||||
|
|
|
@ -3,8 +3,10 @@ import {PregenProtoChangeDetector} from './pregen_proto_change_detector';
|
||||||
import {DynamicProtoChangeDetector} from './proto_change_detector';
|
import {DynamicProtoChangeDetector} from './proto_change_detector';
|
||||||
import {PipeFactory, Pipe} from './pipes/pipe';
|
import {PipeFactory, Pipe} from './pipes/pipe';
|
||||||
import {Pipes} from './pipes/pipes';
|
import {Pipes} from './pipes/pipes';
|
||||||
import {IterableChangesFactory} from './pipes/iterable_changes';
|
import {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs';
|
||||||
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
|
import {DefaultIterableDifferFactory} from './differs/default_iterable_differ';
|
||||||
|
import {KeyValueDiffers, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
||||||
|
import {DefaultKeyValueDifferFactory} from './differs/default_keyvalue_differ';
|
||||||
import {ObservablePipeFactory} from './pipes/observable_pipe';
|
import {ObservablePipeFactory} from './pipes/observable_pipe';
|
||||||
import {PromisePipeFactory} from './pipes/promise_pipe';
|
import {PromisePipeFactory} from './pipes/promise_pipe';
|
||||||
import {UpperCasePipe} from './pipes/uppercase_pipe';
|
import {UpperCasePipe} from './pipes/uppercase_pipe';
|
||||||
|
@ -52,6 +54,8 @@ export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||||
export {DynamicChangeDetector} from './dynamic_change_detector';
|
export {DynamicChangeDetector} from './dynamic_change_detector';
|
||||||
export {ChangeDetectorRef} from './change_detector_ref';
|
export {ChangeDetectorRef} from './change_detector_ref';
|
||||||
export {Pipes} from './pipes/pipes';
|
export {Pipes} from './pipes/pipes';
|
||||||
|
export {IterableDiffers, IterableDiffer, IterableDifferFactory} from './differs/iterable_differs';
|
||||||
|
export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
||||||
export {WrappedValue, Pipe, PipeFactory, BasePipe} from './pipes/pipe';
|
export {WrappedValue, Pipe, PipeFactory, BasePipe} from './pipes/pipe';
|
||||||
export {NullPipe, NullPipeFactory} from './pipes/null_pipe';
|
export {NullPipe, NullPipeFactory} from './pipes/null_pipe';
|
||||||
|
|
||||||
|
@ -59,14 +63,14 @@ export {NullPipe, NullPipeFactory} from './pipes/null_pipe';
|
||||||
/**
|
/**
|
||||||
* Structural diffing for `Object`s and `Map`s.
|
* Structural diffing for `Object`s and `Map`s.
|
||||||
*/
|
*/
|
||||||
export const keyValDiff: List<PipeFactory> =
|
export const keyValDiff: KeyValueDifferFactory[] =
|
||||||
CONST_EXPR([CONST_EXPR(new KeyValueChangesFactory()), CONST_EXPR(new NullPipeFactory())]);
|
CONST_EXPR([CONST_EXPR(new DefaultKeyValueDifferFactory())]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structural diffing for `Iterable` types such as `Array`s.
|
* Structural diffing for `Iterable` types such as `Array`s.
|
||||||
*/
|
*/
|
||||||
export const iterableDiff: List<PipeFactory> =
|
export const iterableDiff: IterableDifferFactory[] =
|
||||||
CONST_EXPR([CONST_EXPR(new IterableChangesFactory()), CONST_EXPR(new NullPipeFactory())]);
|
CONST_EXPR([CONST_EXPR(new DefaultIterableDifferFactory())]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async binding to such types as Observable.
|
* Async binding to such types as Observable.
|
||||||
|
@ -127,8 +131,6 @@ export const date: List<PipeFactory> =
|
||||||
|
|
||||||
|
|
||||||
export const defaultPipes: Pipes = CONST_EXPR(new Pipes({
|
export const defaultPipes: Pipes = CONST_EXPR(new Pipes({
|
||||||
"iterableDiff": iterableDiff,
|
|
||||||
"keyValDiff": keyValDiff,
|
|
||||||
"async": async,
|
"async": async,
|
||||||
"uppercase": uppercase,
|
"uppercase": uppercase,
|
||||||
"lowercase": lowercase,
|
"lowercase": lowercase,
|
||||||
|
@ -140,6 +142,10 @@ export const defaultPipes: Pipes = CONST_EXPR(new Pipes({
|
||||||
"date": date
|
"date": date
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const defaultIterableDiffers = CONST_EXPR(new IterableDiffers(iterableDiff));
|
||||||
|
|
||||||
|
export const defaultKeyValueDiffers = CONST_EXPR(new KeyValueDiffers(keyValDiff));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map from {@link ChangeDetectorDefinition#id} to a factory method which takes a
|
* Map from {@link ChangeDetectorDefinition#id} to a factory method which takes a
|
||||||
* {@link Pipes} and a {@link ChangeDetectorDefinition} and generates a
|
* {@link Pipes} and a {@link ChangeDetectorDefinition} and generates a
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {CONST} from 'angular2/src/facade/lang';
|
import {CONST, BaseException} from 'angular2/src/facade/lang';
|
||||||
import {
|
import {
|
||||||
isListLikeIterable,
|
isListLikeIterable,
|
||||||
iterateListLike,
|
iterateListLike,
|
||||||
|
@ -15,17 +15,16 @@ import {
|
||||||
isArray
|
isArray
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {WrappedValue, Pipe, BasePipe, PipeFactory} from './pipe';
|
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
import {IterableDiffer, IterableDifferFactory} from '../differs/iterable_differs';
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
export class IterableChangesFactory implements PipeFactory {
|
export class DefaultIterableDifferFactory implements IterableDifferFactory {
|
||||||
supports(obj: any): boolean { return IterableChanges.supportsObj(obj); }
|
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
||||||
|
create(cdRef: ChangeDetectorRef): any { return new DefaultIterableDiffer(); }
|
||||||
create(cdRef: ChangeDetectorRef): Pipe { return new IterableChanges(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IterableChanges extends BasePipe {
|
export class DefaultIterableDiffer implements IterableDiffer {
|
||||||
private _collection = null;
|
private _collection = null;
|
||||||
private _length: int = null;
|
private _length: int = null;
|
||||||
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
||||||
|
@ -42,12 +41,6 @@ export class IterableChanges extends BasePipe {
|
||||||
private _removalsHead: CollectionChangeRecord = null;
|
private _removalsHead: CollectionChangeRecord = null;
|
||||||
private _removalsTail: CollectionChangeRecord = null;
|
private _removalsTail: CollectionChangeRecord = null;
|
||||||
|
|
||||||
constructor() { super(); }
|
|
||||||
|
|
||||||
static supportsObj(obj: Object): boolean { return isListLikeIterable(obj); }
|
|
||||||
|
|
||||||
supports(obj: Object): boolean { return IterableChanges.supportsObj(obj); }
|
|
||||||
|
|
||||||
get collection() { return this._collection; }
|
get collection() { return this._collection; }
|
||||||
|
|
||||||
get length(): int { return this._length; }
|
get length(): int { return this._length; }
|
||||||
|
@ -87,14 +80,21 @@ export class IterableChanges extends BasePipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(collection: any, args: List<any> = null): any {
|
diff(collection: any): DefaultIterableDiffer {
|
||||||
|
if (isBlank(collection)) collection = [];
|
||||||
|
if (!isListLikeIterable(collection)) {
|
||||||
|
throw new BaseException(`Error trying to diff '${collection}'`);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.check(collection)) {
|
if (this.check(collection)) {
|
||||||
return WrappedValue.wrap(this);
|
return this;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDestroy() {}
|
||||||
|
|
||||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||||
check(collection: any): boolean {
|
check(collection: any): boolean {
|
||||||
this._reset();
|
this._reset();
|
|
@ -1,16 +1,23 @@
|
||||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {stringify, looseIdentical, isJsObject, CONST} from 'angular2/src/facade/lang';
|
import {
|
||||||
|
stringify,
|
||||||
|
looseIdentical,
|
||||||
|
isJsObject,
|
||||||
|
CONST,
|
||||||
|
isBlank,
|
||||||
|
BaseException
|
||||||
|
} from 'angular2/src/facade/lang';
|
||||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
import {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe';
|
import {KeyValueDiffer, KeyValueDifferFactory} from '../differs/keyvalue_differs';
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
export class KeyValueChangesFactory implements PipeFactory {
|
export class DefaultKeyValueDifferFactory implements KeyValueDifferFactory {
|
||||||
supports(obj: any): boolean { return KeyValueChanges.supportsObj(obj); }
|
supports(obj: any): boolean { return obj instanceof Map || isJsObject(obj); }
|
||||||
|
|
||||||
create(cdRef: ChangeDetectorRef): Pipe { return new KeyValueChanges(); }
|
create(cdRef: ChangeDetectorRef): KeyValueDiffer { return new DefaultKeyValueDiffer(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyValueChanges extends BasePipe {
|
export class DefaultKeyValueDiffer implements KeyValueDiffer {
|
||||||
private _records: Map<any, any> = new Map();
|
private _records: Map<any, any> = new Map();
|
||||||
private _mapHead: KVChangeRecord = null;
|
private _mapHead: KVChangeRecord = null;
|
||||||
private _previousMapHead: KVChangeRecord = null;
|
private _previousMapHead: KVChangeRecord = null;
|
||||||
|
@ -21,18 +28,6 @@ export class KeyValueChanges extends BasePipe {
|
||||||
private _removalsHead: KVChangeRecord = null;
|
private _removalsHead: KVChangeRecord = null;
|
||||||
private _removalsTail: KVChangeRecord = null;
|
private _removalsTail: KVChangeRecord = null;
|
||||||
|
|
||||||
static supportsObj(obj: any): boolean { return obj instanceof Map || isJsObject(obj); }
|
|
||||||
|
|
||||||
supports(obj: any): boolean { return KeyValueChanges.supportsObj(obj); }
|
|
||||||
|
|
||||||
transform(map: Map<any, any>, args: List<any> = null): any {
|
|
||||||
if (this.check(map)) {
|
|
||||||
return WrappedValue.wrap(this);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDirty(): boolean {
|
get isDirty(): boolean {
|
||||||
return this._additionsHead !== null || this._changesHead !== null ||
|
return this._additionsHead !== null || this._changesHead !== null ||
|
||||||
this._removalsHead !== null;
|
this._removalsHead !== null;
|
||||||
|
@ -73,6 +68,21 @@ export class KeyValueChanges extends BasePipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diff(map: Map<any, any>): any {
|
||||||
|
if (isBlank(map)) map = MapWrapper.createFromPairs([]);
|
||||||
|
if (!(map instanceof Map || isJsObject(map))) {
|
||||||
|
throw new BaseException(`Error trying to diff '${map}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.check(map)) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy() {}
|
||||||
|
|
||||||
check(map: Map<any, any>): boolean {
|
check(map: Map<any, any>): boolean {
|
||||||
this._reset();
|
this._reset();
|
||||||
var records = this._records;
|
var records = this._records;
|
|
@ -0,0 +1,80 @@
|
||||||
|
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
import {Binding, SkipSelfMetadata, OptionalMetadata, Injectable} from 'angular2/di';
|
||||||
|
|
||||||
|
export interface IterableDiffer {
|
||||||
|
diff(object: Object): any;
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a factory for {@link IterableDiffer}.
|
||||||
|
*/
|
||||||
|
export interface IterableDifferFactory {
|
||||||
|
supports(objects: Object): boolean;
|
||||||
|
create(cdRef: ChangeDetectorRef): IterableDiffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@CONST()
|
||||||
|
export class IterableDiffers {
|
||||||
|
constructor(public factories: IterableDifferFactory[]) {}
|
||||||
|
|
||||||
|
static create(factories: IterableDifferFactory[], parent?: IterableDiffers): IterableDiffers {
|
||||||
|
if (isPresent(parent)) {
|
||||||
|
var copied = ListWrapper.clone(parent.factories);
|
||||||
|
factories = factories.concat(copied);
|
||||||
|
return new IterableDiffers(factories);
|
||||||
|
} else {
|
||||||
|
return new IterableDiffers(factories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of {@link IterableDifferFactory} and returns a binding used to extend the
|
||||||
|
* inherited {@link IterableDiffers} instance with the provided factories and return a new
|
||||||
|
* {@link IterableDiffers} instance.
|
||||||
|
*
|
||||||
|
* The following example shows how to extend an existing list of factories,
|
||||||
|
* which will only be applied to the injector for this component and its children.
|
||||||
|
* This step is all that's required to make a new {@link IterableDiffer} available.
|
||||||
|
*
|
||||||
|
* # Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Component({
|
||||||
|
* viewBindings: [
|
||||||
|
* IterableDiffers.extend([new ImmutableListDiffer()])
|
||||||
|
* ]
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
static extend(factories: IterableDifferFactory[]): Binding {
|
||||||
|
return new Binding(IterableDiffers, {
|
||||||
|
toFactory: (parent: IterableDiffers) => {
|
||||||
|
if (isBlank(parent)) {
|
||||||
|
// Typically would occur when calling IterableDiffers.extend inside of dependencies passed
|
||||||
|
// to
|
||||||
|
// bootstrap(), which would override default pipes instead of extending them.
|
||||||
|
throw new BaseException('Cannot extend IterableDiffers without a parent injector');
|
||||||
|
}
|
||||||
|
return IterableDiffers.create(factories, parent);
|
||||||
|
},
|
||||||
|
// Dependency technically isn't optional, but we can provide a better error message this way.
|
||||||
|
deps: [[IterableDiffers, new SkipSelfMetadata(), new OptionalMetadata()]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
find(iterable: Object): IterableDifferFactory {
|
||||||
|
var factory = ListWrapper.find(this.factories, f => f.supports(iterable));
|
||||||
|
if (isPresent(factory)) {
|
||||||
|
return factory;
|
||||||
|
} else {
|
||||||
|
throw new BaseException(`Cannot find a differ supporting object '${iterable}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||||
|
import {Binding, SkipSelfMetadata, OptionalMetadata, Injectable} from 'angular2/di';
|
||||||
|
|
||||||
|
export interface KeyValueDiffer {
|
||||||
|
diff(object: Object);
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a factory for {@link KeyValueDiffer}.
|
||||||
|
*/
|
||||||
|
export interface KeyValueDifferFactory {
|
||||||
|
supports(objects: Object): boolean;
|
||||||
|
create(cdRef: ChangeDetectorRef): KeyValueDiffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
@CONST()
|
||||||
|
export class KeyValueDiffers {
|
||||||
|
constructor(public factories: KeyValueDifferFactory[]) {}
|
||||||
|
|
||||||
|
static create(factories: KeyValueDifferFactory[], parent?: KeyValueDiffers): KeyValueDiffers {
|
||||||
|
if (isPresent(parent)) {
|
||||||
|
var copied = ListWrapper.clone(parent.factories);
|
||||||
|
factories = factories.concat(copied);
|
||||||
|
return new KeyValueDiffers(factories);
|
||||||
|
} else {
|
||||||
|
return new KeyValueDiffers(factories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of {@link KeyValueDifferFactory} and returns a binding used to extend the
|
||||||
|
* inherited {@link KeyValueDiffers} instance with the provided factories and return a new
|
||||||
|
* {@link KeyValueDiffers} instance.
|
||||||
|
*
|
||||||
|
* The following example shows how to extend an existing list of factories,
|
||||||
|
* which will only be applied to the injector for this component and its children.
|
||||||
|
* This step is all that's required to make a new {@link KeyValueDiffer} available.
|
||||||
|
*
|
||||||
|
* # Example
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Component({
|
||||||
|
* viewBindings: [
|
||||||
|
* KeyValueDiffers.extend([new ImmutableMapDiffer()])
|
||||||
|
* ]
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
static extend(factories: KeyValueDifferFactory[]): Binding {
|
||||||
|
return new Binding(KeyValueDiffers, {
|
||||||
|
toFactory: (parent: KeyValueDiffers) => {
|
||||||
|
if (isBlank(parent)) {
|
||||||
|
// Typically would occur when calling KeyValueDiffers.extend inside of dependencies passed
|
||||||
|
// to
|
||||||
|
// bootstrap(), which would override default pipes instead of extending them.
|
||||||
|
throw new BaseException('Cannot extend KeyValueDiffers without a parent injector');
|
||||||
|
}
|
||||||
|
return KeyValueDiffers.create(factories, parent);
|
||||||
|
},
|
||||||
|
// Dependency technically isn't optional, but we can provide a better error message this way.
|
||||||
|
deps: [[KeyValueDiffers, new SkipSelfMetadata(), new OptionalMetadata()]]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
find(kv: Object): KeyValueDifferFactory {
|
||||||
|
var factory = ListWrapper.find(this.factories, f => f.supports(kv));
|
||||||
|
if (isPresent(factory)) {
|
||||||
|
return factory;
|
||||||
|
} else {
|
||||||
|
throw new BaseException(`Cannot find a differ supporting object '${kv}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,11 @@ import {
|
||||||
JitChangeDetection,
|
JitChangeDetection,
|
||||||
PreGeneratedChangeDetection,
|
PreGeneratedChangeDetection,
|
||||||
Pipes,
|
Pipes,
|
||||||
defaultPipes
|
defaultPipes,
|
||||||
|
IterableDiffers,
|
||||||
|
defaultIterableDiffers,
|
||||||
|
KeyValueDiffers,
|
||||||
|
defaultKeyValueDiffers
|
||||||
} from 'angular2/src/change_detection/change_detection';
|
} from 'angular2/src/change_detection/change_detection';
|
||||||
import {ExceptionHandler} from './exception_handler';
|
import {ExceptionHandler} from './exception_handler';
|
||||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||||
|
@ -134,6 +138,8 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
||||||
CompilerCache,
|
CompilerCache,
|
||||||
ViewResolver,
|
ViewResolver,
|
||||||
bind(Pipes).toValue(defaultPipes),
|
bind(Pipes).toValue(defaultPipes),
|
||||||
|
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||||
|
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||||
bind(ChangeDetection).toClass(bestChangeDetection),
|
bind(ChangeDetection).toClass(bestChangeDetection),
|
||||||
ViewLoader,
|
ViewLoader,
|
||||||
DirectiveResolver,
|
DirectiveResolver,
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
import {isPresent, isString, StringWrapper, isBlank} from 'angular2/src/facade/lang';
|
||||||
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
||||||
import {ElementRef} from 'angular2/core';
|
import {ElementRef} from 'angular2/core';
|
||||||
import {Pipes} from 'angular2/src/change_detection/pipes/pipes';
|
|
||||||
import {Pipe} from 'angular2/src/change_detection/pipes/pipe';
|
|
||||||
import {Renderer} from 'angular2/src/render/api';
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
import {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes';
|
import {
|
||||||
import {IterableChanges} from 'angular2/src/change_detection/pipes/iterable_changes';
|
KeyValueDiffer,
|
||||||
import {isPresent, isString, StringWrapper} from 'angular2/src/facade/lang';
|
IterableDiffer,
|
||||||
|
IterableDiffers,
|
||||||
|
KeyValueDiffers
|
||||||
|
} from 'angular2/change_detection';
|
||||||
import {ListWrapper, StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
import {ListWrapper, StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,10 +36,12 @@ import {ListWrapper, StringMapWrapper, isListLikeIterable} from 'angular2/src/fa
|
||||||
properties: ['rawClass: class']
|
properties: ['rawClass: class']
|
||||||
})
|
})
|
||||||
export class CSSClass {
|
export class CSSClass {
|
||||||
_pipe: Pipe;
|
private _differ: any;
|
||||||
|
private _mode: string;
|
||||||
_rawClass;
|
_rawClass;
|
||||||
|
|
||||||
constructor(private _pipes: Pipes, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
constructor(private _iterableDiffers: IterableDiffers, private _keyValueDiffers: KeyValueDiffers,
|
||||||
|
private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||||
|
|
||||||
set rawClass(v) {
|
set rawClass(v) {
|
||||||
this._cleanupClasses(this._rawClass);
|
this._cleanupClasses(this._rawClass);
|
||||||
|
@ -47,16 +51,26 @@ export class CSSClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._rawClass = v;
|
this._rawClass = v;
|
||||||
this._pipe = this._pipes.get(isListLikeIterable(v) ? 'iterableDiff' : 'keyValDiff', v);
|
if (isPresent(v)) {
|
||||||
|
if (isListLikeIterable(v)) {
|
||||||
|
this._differ = this._iterableDiffers.find(v).create(null);
|
||||||
|
this._mode = 'iterable';
|
||||||
|
} else {
|
||||||
|
this._differ = this._keyValueDiffers.find(v).create(null);
|
||||||
|
this._mode = 'keyValue';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCheck(): void {
|
onCheck(): void {
|
||||||
var diff = this._pipe.transform(this._rawClass, null);
|
if (isPresent(this._differ)) {
|
||||||
if (isPresent(diff) && isPresent(diff.wrapped)) {
|
var changes = this._differ.diff(this._rawClass);
|
||||||
if (diff.wrapped instanceof IterableChanges) {
|
if (isPresent(changes)) {
|
||||||
this._applyArrayChanges(diff.wrapped);
|
if (this._mode == 'iterable') {
|
||||||
} else {
|
this._applyIterableChanges(changes);
|
||||||
this._applyObjectChanges(diff.wrapped);
|
} else {
|
||||||
|
this._applyKeyValueChanges(changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,19 +89,19 @@ export class CSSClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyObjectChanges(diff: KeyValueChanges): void {
|
private _applyKeyValueChanges(changes: any): void {
|
||||||
diff.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
changes.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||||
diff.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
changes.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||||
diff.forEachRemovedItem((record) => {
|
changes.forEachRemovedItem((record) => {
|
||||||
if (record.previousValue) {
|
if (record.previousValue) {
|
||||||
this._toggleClass(record.key, false);
|
this._toggleClass(record.key, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyArrayChanges(diff: IterableChanges): void {
|
private _applyIterableChanges(changes: any): void {
|
||||||
diff.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
|
changes.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
|
||||||
diff.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
|
changes.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleClass(className: string, enabled): void {
|
private _toggleClass(className: string, enabled): void {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
||||||
import {ViewContainerRef, ViewRef, TemplateRef} from 'angular2/core';
|
import {ViewContainerRef, ViewRef, TemplateRef} from 'angular2/core';
|
||||||
import {ChangeDetectorRef, Pipe, Pipes} from 'angular2/change_detection';
|
import {ChangeDetectorRef, IterableDiffer, IterableDiffers} from 'angular2/change_detection';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,27 +37,26 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
{selector: '[ng-for][ng-for-of]', properties: ['ngForOf'], lifecycle: [LifecycleEvent.onCheck]})
|
{selector: '[ng-for][ng-for-of]', properties: ['ngForOf'], lifecycle: [LifecycleEvent.onCheck]})
|
||||||
export class NgFor {
|
export class NgFor {
|
||||||
_ngForOf: any;
|
_ngForOf: any;
|
||||||
_pipe: Pipe;
|
private _differ: IterableDiffer;
|
||||||
|
|
||||||
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef,
|
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef,
|
||||||
private pipes: Pipes, private cdr: ChangeDetectorRef) {}
|
private iterableDiffers: IterableDiffers, private cdr: ChangeDetectorRef) {}
|
||||||
|
|
||||||
set ngForOf(value: any) {
|
set ngForOf(value: any) {
|
||||||
this._ngForOf = value;
|
this._ngForOf = value;
|
||||||
this._pipe = this.pipes.get("iterableDiff", value, this.cdr, this._pipe);
|
if (isBlank(this._differ) && isPresent(value)) {
|
||||||
|
this._differ = this.iterableDiffers.find(value).create(this.cdr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCheck() {
|
onCheck() {
|
||||||
var diff = this._pipe.transform(this._ngForOf, null);
|
if (isPresent(this._differ)) {
|
||||||
if (isPresent(diff)) this._applyChanges(diff.wrapped);
|
var changes = this._differ.diff(this._ngForOf);
|
||||||
|
if (isPresent(changes)) this._applyChanges(changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyChanges(changes) {
|
private _applyChanges(changes) {
|
||||||
if (isBlank(changes)) {
|
|
||||||
this.viewContainer.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(rado): check if change detection can produce a change record that is
|
// TODO(rado): check if change detection can produce a change record that is
|
||||||
// easier to consume than current.
|
// easier to consume than current.
|
||||||
var recordViewTuples = [];
|
var recordViewTuples = [];
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
||||||
import {ElementRef} from 'angular2/core';
|
import {ElementRef} from 'angular2/core';
|
||||||
import {Pipe} from 'angular2/src/change_detection/pipes/pipe';
|
import {KeyValueDiffer, KeyValueDiffers} from 'angular2/change_detection';
|
||||||
import {Pipes} from 'angular2/src/change_detection/pipes/pipes';
|
import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
|
||||||
import {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes';
|
|
||||||
import {isPresent, print} from 'angular2/src/facade/lang';
|
|
||||||
import {Renderer} from 'angular2/src/render/api';
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,27 +31,32 @@ import {Renderer} from 'angular2/src/render/api';
|
||||||
properties: ['rawStyle: ng-style']
|
properties: ['rawStyle: ng-style']
|
||||||
})
|
})
|
||||||
export class NgStyle {
|
export class NgStyle {
|
||||||
_pipe: Pipe;
|
|
||||||
_rawStyle;
|
_rawStyle;
|
||||||
|
_differ: KeyValueDiffer;
|
||||||
|
|
||||||
constructor(private _pipes: Pipes, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
constructor(private _differs: KeyValueDiffers, private _ngEl: ElementRef,
|
||||||
|
private _renderer: Renderer) {}
|
||||||
|
|
||||||
set rawStyle(v) {
|
set rawStyle(v) {
|
||||||
this._rawStyle = v;
|
this._rawStyle = v;
|
||||||
this._pipe = this._pipes.get('keyValDiff', this._rawStyle);
|
if (isBlank(this._differ) && isPresent(v)) {
|
||||||
}
|
this._differ = this._differs.find(this._rawStyle).create(null);
|
||||||
|
|
||||||
onCheck() {
|
|
||||||
var diff = this._pipe.transform(this._rawStyle, null);
|
|
||||||
if (isPresent(diff) && isPresent(diff.wrapped)) {
|
|
||||||
this._applyChanges(diff.wrapped);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyChanges(diff: KeyValueChanges): void {
|
onCheck() {
|
||||||
diff.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
if (isPresent(this._differ)) {
|
||||||
diff.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
var changes = this._differ.diff(this._rawStyle);
|
||||||
diff.forEachRemovedItem((record) => { this._setStyle(record.key, null); });
|
if (isPresent(changes)) {
|
||||||
|
this._applyChanges(changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _applyChanges(changes: any): void {
|
||||||
|
changes.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||||
|
changes.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||||
|
changes.forEachRemovedItem((record) => { this._setStyle(record.key, null); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setStyle(name: string, val: string): void {
|
private _setStyle(name: string, val: string): void {
|
||||||
|
|
|
@ -2,10 +2,10 @@ library angular2.directives.observable_list_iterable_diff;
|
||||||
|
|
||||||
import 'package:observe/observe.dart' show ObservableList;
|
import 'package:observe/observe.dart' show ObservableList;
|
||||||
import 'package:angular2/change_detection.dart';
|
import 'package:angular2/change_detection.dart';
|
||||||
import 'package:angular2/src/change_detection/pipes/iterable_changes.dart';
|
import 'package:angular2/src/change_detection/differs/default_iterable_differ.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
class ObservableListDiff extends IterableChanges {
|
class ObservableListDiff extends DefaultIterableDiffer {
|
||||||
ChangeDetectorRef _ref;
|
ChangeDetectorRef _ref;
|
||||||
ObservableListDiff(this._ref);
|
ObservableListDiff(this._ref);
|
||||||
|
|
||||||
|
@ -13,11 +13,6 @@ class ObservableListDiff extends IterableChanges {
|
||||||
ObservableList _collection;
|
ObservableList _collection;
|
||||||
StreamSubscription _subscription;
|
StreamSubscription _subscription;
|
||||||
|
|
||||||
bool supports(obj) {
|
|
||||||
if (obj is ObservableList) return true;
|
|
||||||
throw "Cannot change the type of a collection";
|
|
||||||
}
|
|
||||||
|
|
||||||
onDestroy() {
|
onDestroy() {
|
||||||
if (this._subscription != null) {
|
if (this._subscription != null) {
|
||||||
this._subscription.cancel();
|
this._subscription.cancel();
|
||||||
|
@ -26,10 +21,14 @@ class ObservableListDiff extends IterableChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic transform(ObservableList collection, [List args]) {
|
dynamic diff(ObservableList collection) {
|
||||||
|
if (collection is! ObservableList) {
|
||||||
|
throw "Cannot change the type of a collection";
|
||||||
|
}
|
||||||
|
|
||||||
// A new collection instance is passed in.
|
// A new collection instance is passed in.
|
||||||
// - We need to set up a listener.
|
// - We need to set up a listener.
|
||||||
// - We need to transform collection.
|
// - We need to diff collection.
|
||||||
if (!identical(_collection, collection)) {
|
if (!identical(_collection, collection)) {
|
||||||
_collection = collection;
|
_collection = collection;
|
||||||
|
|
||||||
|
@ -39,14 +38,14 @@ class ObservableListDiff extends IterableChanges {
|
||||||
_ref.requestCheck();
|
_ref.requestCheck();
|
||||||
});
|
});
|
||||||
_updated = false;
|
_updated = false;
|
||||||
return super.transform(collection, args);
|
return super.diff(collection);
|
||||||
|
|
||||||
// An update has been registered since the last change detection check.
|
// An update has been registered since the last change detection check.
|
||||||
// - We reset the flag.
|
// - We reset the flag.
|
||||||
// - We diff the collection.
|
// - We diff the collection.
|
||||||
} else if (_updated) {
|
} else if (_updated) {
|
||||||
_updated = false;
|
_updated = false;
|
||||||
return super.transform(collection, args);
|
return super.diff(collection);
|
||||||
|
|
||||||
// No updates has been registered.
|
// No updates has been registered.
|
||||||
// Returning this tells change detection that object has not change,
|
// Returning this tells change detection that object has not change,
|
||||||
|
@ -57,10 +56,10 @@ class ObservableListDiff extends IterableChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObservableListDiffFactory implements PipeFactory {
|
class ObservableListDiffFactory implements IterableDifferFactory {
|
||||||
const ObservableListDiffFactory();
|
const ObservableListDiffFactory();
|
||||||
bool supports(obj) => obj is ObservableList;
|
bool supports(obj) => obj is ObservableList;
|
||||||
Pipe create(ChangeDetectorRef cdRef) {
|
IterableDiffer create(ChangeDetectorRef cdRef) {
|
||||||
return new ObservableListDiff(cdRef);
|
return new ObservableListDiff(cdRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,3 +33,8 @@ class SpyDependencyProvider extends SpyObject implements DependencyProvider {
|
||||||
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {
|
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
class SpyIterableDifferFactory extends SpyObject implements IterableDifferFactory {
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
||||||
|
|
|
@ -24,3 +24,5 @@ export class SpyPipe extends SpyObject {
|
||||||
export class SpyPipeFactory extends SpyObject {}
|
export class SpyPipeFactory extends SpyObject {}
|
||||||
|
|
||||||
export class SpyDependencyProvider extends SpyObject {}
|
export class SpyDependencyProvider extends SpyObject {}
|
||||||
|
|
||||||
|
export class SpyIterableDifferFactory extends SpyObject {}
|
||||||
|
|
|
@ -8,7 +8,11 @@ import {
|
||||||
ChangeDetection,
|
ChangeDetection,
|
||||||
DynamicChangeDetection,
|
DynamicChangeDetection,
|
||||||
Pipes,
|
Pipes,
|
||||||
defaultPipes
|
defaultPipes,
|
||||||
|
IterableDiffers,
|
||||||
|
defaultIterableDiffers,
|
||||||
|
KeyValueDiffers,
|
||||||
|
defaultKeyValueDiffers
|
||||||
} from 'angular2/src/change_detection/change_detection';
|
} from 'angular2/src/change_detection/change_detection';
|
||||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||||
|
@ -119,8 +123,10 @@ function _getAppBindings() {
|
||||||
CompilerCache,
|
CompilerCache,
|
||||||
bind(ViewResolver).toClass(MockViewResolver),
|
bind(ViewResolver).toClass(MockViewResolver),
|
||||||
bind(Pipes).toValue(defaultPipes),
|
bind(Pipes).toValue(defaultPipes),
|
||||||
Log,
|
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||||
|
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||||
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
||||||
|
Log,
|
||||||
ViewLoader,
|
ViewLoader,
|
||||||
DynamicComponentLoader,
|
DynamicComponentLoader,
|
||||||
DirectiveResolver,
|
DirectiveResolver,
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||||
import {IterableChanges} from 'angular2/src/change_detection/pipes/iterable_changes';
|
import {
|
||||||
|
DefaultIterableDiffer,
|
||||||
|
DefaultIterableDifferFactory
|
||||||
|
} from 'angular2/src/change_detection/differs/default_iterable_differ';
|
||||||
|
|
||||||
import {NumberWrapper} from 'angular2/src/facade/lang';
|
import {NumberWrapper} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
@ -9,36 +12,35 @@ import {iterableChangesAsString} from '../util';
|
||||||
|
|
||||||
// todo(vicb): UnmodifiableListView / frozen object when implemented
|
// todo(vicb): UnmodifiableListView / frozen object when implemented
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('collection_changes', function() {
|
describe('iterable differ', function() {
|
||||||
describe('CollectionChanges', function() {
|
describe('DefaultIterableDiffer', function() {
|
||||||
var changes;
|
var differ;
|
||||||
|
|
||||||
beforeEach(() => { changes = new IterableChanges(); });
|
beforeEach(() => { differ = new DefaultIterableDiffer(); });
|
||||||
|
|
||||||
afterEach(() => { changes = null; });
|
|
||||||
|
|
||||||
it('should support list and iterables', () => {
|
it('should support list and iterables', () => {
|
||||||
expect(IterableChanges.supportsObj([])).toBeTruthy();
|
var f = new DefaultIterableDifferFactory();
|
||||||
expect(IterableChanges.supportsObj(new TestIterable())).toBeTruthy();
|
expect(f.supports([])).toBeTruthy();
|
||||||
expect(IterableChanges.supportsObj(new Map())).toBeFalsy();
|
expect(f.supports(new TestIterable())).toBeTruthy();
|
||||||
expect(IterableChanges.supportsObj(null)).toBeFalsy();
|
expect(f.supports(new Map())).toBeFalsy();
|
||||||
|
expect(f.supports(null)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support iterables', () => {
|
it('should support iterables', () => {
|
||||||
let l = new TestIterable();
|
let l = new TestIterable();
|
||||||
|
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString()).toEqual(iterableChangesAsString({collection: []}));
|
expect(differ.toString()).toEqual(iterableChangesAsString({collection: []}));
|
||||||
|
|
||||||
l.list = [1];
|
l.list = [1];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(
|
.toEqual(
|
||||||
iterableChangesAsString({collection: ['1[null->0]'], additions: ['1[null->0]']}));
|
iterableChangesAsString({collection: ['1[null->0]'], additions: ['1[null->0]']}));
|
||||||
|
|
||||||
l.list = [2, 1];
|
l.list = [2, 1];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['2[null->0]', '1[0->1]'],
|
collection: ['2[null->0]', '1[0->1]'],
|
||||||
previous: ['1[0->1]'],
|
previous: ['1[0->1]'],
|
||||||
|
@ -49,29 +51,29 @@ export function main() {
|
||||||
|
|
||||||
it('should detect additions', () => {
|
it('should detect additions', () => {
|
||||||
let l = [];
|
let l = [];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString()).toEqual(iterableChangesAsString({collection: []}));
|
expect(differ.toString()).toEqual(iterableChangesAsString({collection: []}));
|
||||||
|
|
||||||
l.push('a');
|
l.push('a');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(
|
.toEqual(
|
||||||
iterableChangesAsString({collection: ['a[null->0]'], additions: ['a[null->0]']}));
|
iterableChangesAsString({collection: ['a[null->0]'], additions: ['a[null->0]']}));
|
||||||
|
|
||||||
l.push('b');
|
l.push('b');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString(
|
.toEqual(iterableChangesAsString(
|
||||||
{collection: ['a', 'b[null->1]'], previous: ['a'], additions: ['b[null->1]']}));
|
{collection: ['a', 'b[null->1]'], previous: ['a'], additions: ['b[null->1]']}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support changing the reference', () => {
|
it('should support changing the reference', () => {
|
||||||
let l = [0];
|
let l = [0];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
l = [1, 0];
|
l = [1, 0];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['1[null->0]', '0[0->1]'],
|
collection: ['1[null->0]', '0[0->1]'],
|
||||||
previous: ['0[0->1]'],
|
previous: ['0[0->1]'],
|
||||||
|
@ -80,8 +82,8 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
l = [2, 1, 0];
|
l = [2, 1, 0];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['2[null->0]', '1[0->1]', '0[1->2]'],
|
collection: ['2[null->0]', '1[0->1]', '0[1->2]'],
|
||||||
previous: ['1[0->1]', '0[1->2]'],
|
previous: ['1[0->1]', '0[1->2]'],
|
||||||
|
@ -92,13 +94,13 @@ export function main() {
|
||||||
|
|
||||||
it('should handle swapping element', () => {
|
it('should handle swapping element', () => {
|
||||||
let l = [1, 2];
|
let l = [1, 2];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
ListWrapper.clear(l);
|
ListWrapper.clear(l);
|
||||||
l.push(2);
|
l.push(2);
|
||||||
l.push(1);
|
l.push(1);
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['2[1->0]', '1[0->1]'],
|
collection: ['2[1->0]', '1[0->1]'],
|
||||||
previous: ['1[0->1]', '2[1->0]'],
|
previous: ['1[0->1]', '2[1->0]'],
|
||||||
|
@ -108,12 +110,12 @@ export function main() {
|
||||||
|
|
||||||
it('should handle swapping element', () => {
|
it('should handle swapping element', () => {
|
||||||
let l = ['a', 'b', 'c'];
|
let l = ['a', 'b', 'c'];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
ListWrapper.removeAt(l, 1);
|
ListWrapper.removeAt(l, 1);
|
||||||
ListWrapper.insert(l, 0, 'b');
|
ListWrapper.insert(l, 0, 'b');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['b[1->0]', 'a[0->1]', 'c'],
|
collection: ['b[1->0]', 'a[0->1]', 'c'],
|
||||||
previous: ['a[0->1]', 'b[1->0]', 'c'],
|
previous: ['a[0->1]', 'b[1->0]', 'c'],
|
||||||
|
@ -122,8 +124,8 @@ export function main() {
|
||||||
|
|
||||||
ListWrapper.removeAt(l, 1);
|
ListWrapper.removeAt(l, 1);
|
||||||
l.push('a');
|
l.push('a');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['b', 'c[2->1]', 'a[1->2]'],
|
collection: ['b', 'c[2->1]', 'a[1->2]'],
|
||||||
previous: ['b', 'a[1->2]', 'c[2->1]'],
|
previous: ['b', 'a[1->2]', 'c[2->1]'],
|
||||||
|
@ -133,24 +135,24 @@ export function main() {
|
||||||
|
|
||||||
it('should detect changes in list', () => {
|
it('should detect changes in list', () => {
|
||||||
let l = [];
|
let l = [];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
l.push('a');
|
l.push('a');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(
|
.toEqual(
|
||||||
iterableChangesAsString({collection: ['a[null->0]'], additions: ['a[null->0]']}));
|
iterableChangesAsString({collection: ['a[null->0]'], additions: ['a[null->0]']}));
|
||||||
|
|
||||||
l.push('b');
|
l.push('b');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString(
|
.toEqual(iterableChangesAsString(
|
||||||
{collection: ['a', 'b[null->1]'], previous: ['a'], additions: ['b[null->1]']}));
|
{collection: ['a', 'b[null->1]'], previous: ['a'], additions: ['b[null->1]']}));
|
||||||
|
|
||||||
l.push('c');
|
l.push('c');
|
||||||
l.push('d');
|
l.push('d');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['a', 'b', 'c[null->2]', 'd[null->3]'],
|
collection: ['a', 'b', 'c[null->2]', 'd[null->3]'],
|
||||||
previous: ['a', 'b'],
|
previous: ['a', 'b'],
|
||||||
|
@ -158,8 +160,8 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
ListWrapper.removeAt(l, 2);
|
ListWrapper.removeAt(l, 2);
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['a', 'b', 'd[3->2]'],
|
collection: ['a', 'b', 'd[3->2]'],
|
||||||
previous: ['a', 'b', 'c[2->null]', 'd[3->2]'],
|
previous: ['a', 'b', 'c[2->null]', 'd[3->2]'],
|
||||||
|
@ -172,8 +174,8 @@ export function main() {
|
||||||
l.push('c');
|
l.push('c');
|
||||||
l.push('b');
|
l.push('b');
|
||||||
l.push('a');
|
l.push('a');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['d[2->0]', 'c[null->1]', 'b[1->2]', 'a[0->3]'],
|
collection: ['d[2->0]', 'c[null->1]', 'b[1->2]', 'a[0->3]'],
|
||||||
previous: ['a[0->3]', 'b[1->2]', 'd[2->0]'],
|
previous: ['a[0->3]', 'b[1->2]', 'd[2->0]'],
|
||||||
|
@ -184,32 +186,32 @@ export function main() {
|
||||||
|
|
||||||
it('should test string by value rather than by reference (Dart)', () => {
|
it('should test string by value rather than by reference (Dart)', () => {
|
||||||
let l = ['a', 'boo'];
|
let l = ['a', 'boo'];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
var b = 'b';
|
var b = 'b';
|
||||||
var oo = 'oo';
|
var oo = 'oo';
|
||||||
ListWrapper.set(l, 1, b + oo);
|
ListWrapper.set(l, 1, b + oo);
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({collection: ['a', 'boo'], previous: ['a', 'boo']}));
|
.toEqual(iterableChangesAsString({collection: ['a', 'boo'], previous: ['a', 'boo']}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore [NaN] != [NaN] (JS)', () => {
|
it('should ignore [NaN] != [NaN] (JS)', () => {
|
||||||
let l = [NumberWrapper.NaN];
|
let l = [NumberWrapper.NaN];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString(
|
.toEqual(iterableChangesAsString(
|
||||||
{collection: [NumberWrapper.NaN], previous: [NumberWrapper.NaN]}));
|
{collection: [NumberWrapper.NaN], previous: [NumberWrapper.NaN]}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect [NaN] moves', () => {
|
it('should detect [NaN] moves', () => {
|
||||||
let l = [NumberWrapper.NaN, NumberWrapper.NaN];
|
let l = [NumberWrapper.NaN, NumberWrapper.NaN];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
ListWrapper.insert<any>(l, 0, 'foo');
|
ListWrapper.insert<any>(l, 0, 'foo');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['foo[null->0]', 'NaN[0->1]', 'NaN[1->2]'],
|
collection: ['foo[null->0]', 'NaN[0->1]', 'NaN[1->2]'],
|
||||||
previous: ['NaN[0->1]', 'NaN[1->2]'],
|
previous: ['NaN[0->1]', 'NaN[1->2]'],
|
||||||
|
@ -220,11 +222,11 @@ export function main() {
|
||||||
|
|
||||||
it('should remove and add same item', () => {
|
it('should remove and add same item', () => {
|
||||||
let l = ['a', 'b', 'c'];
|
let l = ['a', 'b', 'c'];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
ListWrapper.removeAt(l, 1);
|
ListWrapper.removeAt(l, 1);
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['a', 'c[2->1]'],
|
collection: ['a', 'c[2->1]'],
|
||||||
previous: ['a', 'b[1->null]', 'c[2->1]'],
|
previous: ['a', 'b[1->null]', 'c[2->1]'],
|
||||||
|
@ -233,8 +235,8 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
ListWrapper.insert(l, 1, 'b');
|
ListWrapper.insert(l, 1, 'b');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['a', 'b[null->1]', 'c[1->2]'],
|
collection: ['a', 'b[null->1]', 'c[1->2]'],
|
||||||
previous: ['a', 'c[1->2]'],
|
previous: ['a', 'c[1->2]'],
|
||||||
|
@ -245,11 +247,11 @@ export function main() {
|
||||||
|
|
||||||
it('should support duplicates', () => {
|
it('should support duplicates', () => {
|
||||||
let l = ['a', 'a', 'a', 'b', 'b'];
|
let l = ['a', 'a', 'a', 'b', 'b'];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
ListWrapper.removeAt(l, 0);
|
ListWrapper.removeAt(l, 0);
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['a', 'a', 'b[3->2]', 'b[4->3]'],
|
collection: ['a', 'a', 'b[3->2]', 'b[4->3]'],
|
||||||
previous: ['a', 'a', 'a[2->null]', 'b[3->2]', 'b[4->3]'],
|
previous: ['a', 'a', 'a[2->null]', 'b[3->2]', 'b[4->3]'],
|
||||||
|
@ -260,11 +262,11 @@ export function main() {
|
||||||
|
|
||||||
it('should support insertions/moves', () => {
|
it('should support insertions/moves', () => {
|
||||||
let l = ['a', 'a', 'b', 'b'];
|
let l = ['a', 'a', 'b', 'b'];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
ListWrapper.insert(l, 0, 'b');
|
ListWrapper.insert(l, 0, 'b');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['b[2->0]', 'a[0->1]', 'a[1->2]', 'b', 'b[null->4]'],
|
collection: ['b[2->0]', 'a[0->1]', 'a[1->2]', 'b', 'b[null->4]'],
|
||||||
previous: ['a[0->1]', 'a[1->2]', 'b[2->0]', 'b'],
|
previous: ['a[0->1]', 'a[1->2]', 'b[2->0]', 'b'],
|
||||||
|
@ -275,20 +277,43 @@ export function main() {
|
||||||
|
|
||||||
it('should not report unnecessary moves', () => {
|
it('should not report unnecessary moves', () => {
|
||||||
let l = ['a', 'b', 'c'];
|
let l = ['a', 'b', 'c'];
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
|
|
||||||
ListWrapper.clear(l);
|
ListWrapper.clear(l);
|
||||||
l.push('b');
|
l.push('b');
|
||||||
l.push('a');
|
l.push('a');
|
||||||
l.push('c');
|
l.push('c');
|
||||||
changes.check(l);
|
differ.check(l);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(iterableChangesAsString({
|
.toEqual(iterableChangesAsString({
|
||||||
collection: ['b[1->0]', 'a[0->1]', 'c'],
|
collection: ['b[1->0]', 'a[0->1]', 'c'],
|
||||||
previous: ['a[0->1]', 'b[1->0]', 'c'],
|
previous: ['a[0->1]', 'b[1->0]', 'c'],
|
||||||
moves: ['b[1->0]', 'a[0->1]']
|
moves: ['b[1->0]', 'a[0->1]']
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('diff', () => {
|
||||||
|
it('should return self when there is a change',
|
||||||
|
() => { expect(differ.diff(['a', 'b'])).toBe(differ); });
|
||||||
|
|
||||||
|
it('should return null when there is no change', () => {
|
||||||
|
differ.diff(['a', 'b']);
|
||||||
|
expect(differ.diff(['a', 'b'])).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should treat null as an empty list', () => {
|
||||||
|
differ.diff(['a', 'b']);
|
||||||
|
expect(differ.diff(null).toString())
|
||||||
|
.toEqual(iterableChangesAsString({
|
||||||
|
previous: ['a[0->null]', 'b[1->null]'],
|
||||||
|
removals: ['a[0->null]', 'b[1->null]']
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when given an invalid collection', () => {
|
||||||
|
expect(() => differ.diff("invalid")).toThrowErrorWith("Error trying to diff 'invalid'");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -1,34 +1,37 @@
|
||||||
import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||||
import {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes';
|
import {
|
||||||
|
DefaultKeyValueDiffer,
|
||||||
|
DefaultKeyValueDifferFactory
|
||||||
|
} from 'angular2/src/change_detection/differs/default_keyvalue_differ';
|
||||||
import {NumberWrapper, isJsObject} from 'angular2/src/facade/lang';
|
import {NumberWrapper, isJsObject} from 'angular2/src/facade/lang';
|
||||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {kvChangesAsString} from '../util';
|
import {kvChangesAsString} from '../util';
|
||||||
|
|
||||||
// todo(vicb): Update the code & tests for object equality
|
// todo(vicb): Update the code & tests for object equality
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('keyvalue_changes', function() {
|
describe('keyvalue differ', function() {
|
||||||
describe('KeyValueChanges', function() {
|
describe('DefaultKeyValueDiffer', function() {
|
||||||
var changes;
|
var differ;
|
||||||
var m: Map<any, any>;
|
var m: Map<any, any>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
changes = new KeyValueChanges();
|
differ = new DefaultKeyValueDiffer();
|
||||||
m = new Map();
|
m = new Map();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => { changes = null; });
|
afterEach(() => { differ = null; });
|
||||||
|
|
||||||
it('should detect additions', () => {
|
it('should detect additions', () => {
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
m.set('a', 1);
|
m.set('a', 1);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({map: ['a[null->1]'], additions: ['a[null->1]']}));
|
.toEqual(kvChangesAsString({map: ['a[null->1]'], additions: ['a[null->1]']}));
|
||||||
|
|
||||||
m.set('b', 2);
|
m.set('b', 2);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString(
|
.toEqual(kvChangesAsString(
|
||||||
{map: ['a', 'b[null->2]'], previous: ['a'], additions: ['b[null->2]']}));
|
{map: ['a', 'b[null->2]'], previous: ['a'], additions: ['b[null->2]']}));
|
||||||
});
|
});
|
||||||
|
@ -36,12 +39,12 @@ export function main() {
|
||||||
it('should handle changing key/values correctly', () => {
|
it('should handle changing key/values correctly', () => {
|
||||||
m.set(1, 10);
|
m.set(1, 10);
|
||||||
m.set(2, 20);
|
m.set(2, 20);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
m.set(2, 10);
|
m.set(2, 10);
|
||||||
m.set(1, 20);
|
m.set(1, 20);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({
|
.toEqual(kvChangesAsString({
|
||||||
map: ['1[10->20]', '2[20->10]'],
|
map: ['1[10->20]', '2[20->10]'],
|
||||||
previous: ['1[10->20]', '2[20->10]'],
|
previous: ['1[10->20]', '2[20->10]'],
|
||||||
|
@ -53,12 +56,12 @@ export function main() {
|
||||||
var previous, current;
|
var previous, current;
|
||||||
|
|
||||||
m.set(1, 10);
|
m.set(1, 10);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
m.set(1, 20);
|
m.set(1, 20);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
changes.forEachChangedItem((record) => {
|
differ.forEachChangedItem((record) => {
|
||||||
previous = record.previousValue;
|
previous = record.previousValue;
|
||||||
current = record.currentValue;
|
current = record.currentValue;
|
||||||
});
|
});
|
||||||
|
@ -68,23 +71,23 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do basic map watching', () => {
|
it('should do basic map watching', () => {
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
m.set('a', 'A');
|
m.set('a', 'A');
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({map: ['a[null->A]'], additions: ['a[null->A]']}));
|
.toEqual(kvChangesAsString({map: ['a[null->A]'], additions: ['a[null->A]']}));
|
||||||
|
|
||||||
m.set('b', 'B');
|
m.set('b', 'B');
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString(
|
.toEqual(kvChangesAsString(
|
||||||
{map: ['a', 'b[null->B]'], previous: ['a'], additions: ['b[null->B]']}));
|
{map: ['a', 'b[null->B]'], previous: ['a'], additions: ['b[null->B]']}));
|
||||||
|
|
||||||
m.set('b', 'BB');
|
m.set('b', 'BB');
|
||||||
m.set('d', 'D');
|
m.set('d', 'D');
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({
|
.toEqual(kvChangesAsString({
|
||||||
map: ['a', 'b[B->BB]', 'd[null->D]'],
|
map: ['a', 'b[B->BB]', 'd[null->D]'],
|
||||||
previous: ['a', 'b[B->BB]'],
|
previous: ['a', 'b[B->BB]'],
|
||||||
|
@ -93,21 +96,21 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
MapWrapper.delete(m, 'b');
|
MapWrapper.delete(m, 'b');
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString(
|
.toEqual(kvChangesAsString(
|
||||||
{map: ['a', 'd'], previous: ['a', 'b[BB->null]', 'd'], removals: ['b[BB->null]']}));
|
{map: ['a', 'd'], previous: ['a', 'b[BB->null]', 'd'], removals: ['b[BB->null]']}));
|
||||||
|
|
||||||
m.clear();
|
m.clear();
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString(
|
.toEqual(kvChangesAsString(
|
||||||
{previous: ['a[A->null]', 'd[D->null]'], removals: ['a[A->null]', 'd[D->null]']}));
|
{previous: ['a[A->null]', 'd[D->null]'], removals: ['a[A->null]', 'd[D->null]']}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should test string by value rather than by reference (DART)', () => {
|
it('should test string by value rather than by reference (DART)', () => {
|
||||||
m.set('foo', 'bar');
|
m.set('foo', 'bar');
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
var f = 'f';
|
var f = 'f';
|
||||||
var oo = 'oo';
|
var oo = 'oo';
|
||||||
|
@ -115,48 +118,49 @@ export function main() {
|
||||||
var ar = 'ar';
|
var ar = 'ar';
|
||||||
|
|
||||||
m.set(f + oo, b + ar);
|
m.set(f + oo, b + ar);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
expect(changes.toString()).toEqual(kvChangesAsString({map: ['foo'], previous: ['foo']}));
|
expect(differ.toString()).toEqual(kvChangesAsString({map: ['foo'], previous: ['foo']}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not see a NaN value as a change (JS)', () => {
|
it('should not see a NaN value as a change (JS)', () => {
|
||||||
m.set('foo', NumberWrapper.NaN);
|
m.set('foo', NumberWrapper.NaN);
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString()).toEqual(kvChangesAsString({map: ['foo'], previous: ['foo']}));
|
expect(differ.toString()).toEqual(kvChangesAsString({map: ['foo'], previous: ['foo']}));
|
||||||
});
|
});
|
||||||
|
|
||||||
// JS specific tests (JS Objects)
|
// JS specific tests (JS Objects)
|
||||||
if (isJsObject({})) {
|
if (isJsObject({})) {
|
||||||
describe('JsObject changes', () => {
|
describe('JsObject changes', () => {
|
||||||
it('should support JS Object', () => {
|
it('should support JS Object', () => {
|
||||||
expect(KeyValueChanges.supportsObj({})).toBeTruthy();
|
var f = new DefaultKeyValueDifferFactory();
|
||||||
expect(KeyValueChanges.supportsObj("not supported")).toBeFalsy();
|
expect(f.supports({})).toBeTruthy();
|
||||||
expect(KeyValueChanges.supportsObj(0)).toBeFalsy();
|
expect(f.supports("not supported")).toBeFalsy();
|
||||||
expect(KeyValueChanges.supportsObj(null)).toBeFalsy();
|
expect(f.supports(0)).toBeFalsy();
|
||||||
|
expect(f.supports(null)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do basic object watching', () => {
|
it('should do basic object watching', () => {
|
||||||
let m = {};
|
let m = {};
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
|
|
||||||
m['a'] = 'A';
|
m['a'] = 'A';
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({map: ['a[null->A]'], additions: ['a[null->A]']}));
|
.toEqual(kvChangesAsString({map: ['a[null->A]'], additions: ['a[null->A]']}));
|
||||||
|
|
||||||
m['b'] = 'B';
|
m['b'] = 'B';
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString(
|
.toEqual(kvChangesAsString(
|
||||||
{map: ['a', 'b[null->B]'], previous: ['a'], additions: ['b[null->B]']}));
|
{map: ['a', 'b[null->B]'], previous: ['a'], additions: ['b[null->B]']}));
|
||||||
|
|
||||||
m['b'] = 'BB';
|
m['b'] = 'BB';
|
||||||
m['d'] = 'D';
|
m['d'] = 'D';
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({
|
.toEqual(kvChangesAsString({
|
||||||
map: ['a', 'b[B->BB]', 'd[null->D]'],
|
map: ['a', 'b[B->BB]', 'd[null->D]'],
|
||||||
previous: ['a', 'b[B->BB]'],
|
previous: ['a', 'b[B->BB]'],
|
||||||
|
@ -167,8 +171,8 @@ export function main() {
|
||||||
m = {};
|
m = {};
|
||||||
m['a'] = 'A';
|
m['a'] = 'A';
|
||||||
m['d'] = 'D';
|
m['d'] = 'D';
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({
|
.toEqual(kvChangesAsString({
|
||||||
map: ['a', 'd'],
|
map: ['a', 'd'],
|
||||||
previous: ['a', 'b[BB->null]', 'd'],
|
previous: ['a', 'b[BB->null]', 'd'],
|
||||||
|
@ -176,14 +180,38 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
m = {};
|
m = {};
|
||||||
changes.check(m);
|
differ.check(m);
|
||||||
expect(changes.toString())
|
expect(differ.toString())
|
||||||
.toEqual(kvChangesAsString({
|
.toEqual(kvChangesAsString({
|
||||||
previous: ['a[A->null]', 'd[D->null]'],
|
previous: ['a[A->null]', 'd[D->null]'],
|
||||||
removals: ['a[A->null]', 'd[D->null]']
|
removals: ['a[A->null]', 'd[D->null]']
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('diff', () => {
|
||||||
|
it('should return self when there is a change', () => {
|
||||||
|
m.set('a', 'A');
|
||||||
|
expect(differ.diff(m)).toBe(differ);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null when there is no change', () => {
|
||||||
|
m.set('a', 'A');
|
||||||
|
differ.diff(m);
|
||||||
|
expect(differ.diff(m)).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should treat null as an empty list', () => {
|
||||||
|
m.set('a', 'A');
|
||||||
|
differ.diff(m);
|
||||||
|
expect(differ.diff(null).toString())
|
||||||
|
.toEqual(kvChangesAsString({previous: ['a[A->null]'], removals: ['a[A->null]']}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when given an invalid collection', () => {
|
||||||
|
expect(() => differ.diff("invalid")).toThrowErrorWith("Error trying to diff 'invalid'");
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,70 @@
|
||||||
|
import {
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
xit,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
SpyIterableDifferFactory
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
import {IterableDiffers} from 'angular2/src/change_detection/differs/iterable_differs';
|
||||||
|
import {Injector, bind} from 'angular2/di';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('IterableDiffers', function() {
|
||||||
|
var factory1;
|
||||||
|
var factory2;
|
||||||
|
var factory3;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
factory1 = new SpyIterableDifferFactory();
|
||||||
|
factory2 = new SpyIterableDifferFactory();
|
||||||
|
factory3 = new SpyIterableDifferFactory();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when no suitable implementation found', () => {
|
||||||
|
var differs = new IterableDiffers([]);
|
||||||
|
expect(() => differs.find("some object"))
|
||||||
|
.toThrowErrorWith("Cannot find a differ supporting object 'some object'")
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the first suitable implementation', () => {
|
||||||
|
factory1.spy("supports").andReturn(false);
|
||||||
|
factory2.spy("supports").andReturn(true);
|
||||||
|
factory3.spy("supports").andReturn(true);
|
||||||
|
|
||||||
|
var differs = IterableDiffers.create(<any>[factory1, factory2, factory3]);
|
||||||
|
expect(differs.find("some object")).toBe(factory2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should copy over differs from the parent repo', () => {
|
||||||
|
factory1.spy("supports").andReturn(true);
|
||||||
|
factory2.spy("supports").andReturn(false);
|
||||||
|
|
||||||
|
var parent = IterableDiffers.create(<any>[factory1]);
|
||||||
|
var child = IterableDiffers.create(<any>[factory2], parent);
|
||||||
|
|
||||||
|
expect(child.factories).toEqual([factory2, factory1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(".extend()", () => {
|
||||||
|
it('should throw if calling extend when creating root injector', () => {
|
||||||
|
var injector = Injector.resolveAndCreate([IterableDiffers.extend([])]);
|
||||||
|
|
||||||
|
expect(() => injector.get(IterableDiffers))
|
||||||
|
.toThrowErrorWith("Cannot extend IterableDiffers without a parent injector");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extend di-inherited diffesr', () => {
|
||||||
|
var parent = new IterableDiffers([factory1]);
|
||||||
|
var injector = Injector.resolveAndCreate([bind(IterableDiffers).toValue(parent)]);
|
||||||
|
var childInjector = injector.resolveAndCreateChild([IterableDiffers.extend([factory2])]);
|
||||||
|
|
||||||
|
expect(injector.get(IterableDiffers).factories).toEqual([factory1]);
|
||||||
|
expect(childInjector.get(IterableDiffers).factories).toEqual([factory2, factory1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import 'package:angular2/di.dart';
|
||||||
import 'package:angular2/test_lib.dart';
|
import 'package:angular2/test_lib.dart';
|
||||||
import 'package:observe/observe.dart';
|
import 'package:observe/observe.dart';
|
||||||
import 'package:angular2/src/directives/observable_list_diff.dart';
|
import 'package:angular2/src/directives/observable_list_diff.dart';
|
||||||
import 'package:angular2/src/change_detection/pipes/iterable_changes.dart';
|
import 'package:angular2/src/change_detection/differs/default_iterable_differ.dart';
|
||||||
|
|
||||||
class MockException implements Error {
|
class MockException implements Error {
|
||||||
var message;
|
var message;
|
||||||
|
@ -269,14 +269,12 @@ class OnChangeComponent implements OnChange {
|
||||||
changeDetection: ON_PUSH,
|
changeDetection: ON_PUSH,
|
||||||
properties: const ['list'],
|
properties: const ['list'],
|
||||||
bindings: const [
|
bindings: const [
|
||||||
const Binding(Pipes,
|
const Binding(IterableDiffers,
|
||||||
toValue: const Pipes(const {
|
toValue: const IterableDiffers(const [
|
||||||
"iterableDiff": const [
|
|
||||||
const ObservableListDiffFactory(),
|
const ObservableListDiffFactory(),
|
||||||
const IterableChangesFactory(),
|
const DefaultIterableDifferFactory()
|
||||||
const NullPipeFactory()
|
|
||||||
]
|
]
|
||||||
}))
|
))
|
||||||
])
|
])
|
||||||
@View(
|
@View(
|
||||||
template: '<span *ng-for="#item of list">{{item}}</span><directive-logging-checks></directive-logging-checks>',
|
template: '<span *ng-for="#item of list">{{item}}</span><directive-logging-checks></directive-logging-checks>',
|
||||||
|
|
|
@ -6,64 +6,64 @@ import 'package:angular2/src/directives/observable_list_diff.dart';
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
describe('ObservableListDiff', () {
|
describe('ObservableListDiff', () {
|
||||||
var pipeFactory, changeDetectorRef;
|
var factory, changeDetectorRef;
|
||||||
|
|
||||||
beforeEach(() {
|
beforeEach(() {
|
||||||
pipeFactory = const ObservableListDiffFactory();
|
factory = const ObservableListDiffFactory();
|
||||||
changeDetectorRef = new SpyChangeDetectorRef();
|
changeDetectorRef = new SpyChangeDetectorRef();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("supports", () {
|
describe("supports", () {
|
||||||
it("should be true for ObservableList", () {
|
it("should be true for ObservableList", () {
|
||||||
expect(pipeFactory.supports(new ObservableList())).toBe(true);
|
expect(factory.supports(new ObservableList())).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false otherwise", () {
|
it("should be false otherwise", () {
|
||||||
expect(pipeFactory.supports([1, 2, 3])).toBe(false);
|
expect(factory.supports([1, 2, 3])).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the wrapped value to trigger change detection on first invocation of transform",
|
it("should return itself when called the first time",
|
||||||
() {
|
() {
|
||||||
final pipe = pipeFactory.create(changeDetectorRef);
|
final d = factory.create(changeDetectorRef);
|
||||||
final c = new ObservableList.from([1, 2]);
|
final c = new ObservableList.from([1, 2]);
|
||||||
expect(pipe.transform(c, []).wrapped).toBe(pipe);
|
expect(d.diff(c)).toBe(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return itself when no changes between the calls", () {
|
it("should return itself when no changes between the calls", () {
|
||||||
final pipe = pipeFactory.create(changeDetectorRef);
|
final d = factory.create(changeDetectorRef);
|
||||||
|
|
||||||
final c = new ObservableList.from([1, 2]);
|
final c = new ObservableList.from([1, 2]);
|
||||||
|
|
||||||
pipe.transform(c, []);
|
d.diff(c);
|
||||||
|
|
||||||
expect(pipe.transform(c, [])).toBe(pipe);
|
expect(d.diff(c)).toBe(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the wrapped value once a change has been trigger",
|
it("should return the wrapped value once a change has been triggered",
|
||||||
fakeAsync(() {
|
fakeAsync(() {
|
||||||
final pipe = pipeFactory.create(changeDetectorRef);
|
final d = factory.create(changeDetectorRef);
|
||||||
|
|
||||||
final c = new ObservableList.from([1, 2]);
|
final c = new ObservableList.from([1, 2]);
|
||||||
|
|
||||||
pipe.transform(c, []);
|
d.diff(c);
|
||||||
|
|
||||||
c.add(3);
|
c.add(3);
|
||||||
|
|
||||||
// same value, because we have not detected the change yet
|
// same value, because we have not detected the change yet
|
||||||
expect(pipe.transform(c, [])).toBe(pipe);
|
expect(d.diff(c)).toBe(d);
|
||||||
|
|
||||||
// now we detect the change
|
// now we detect the change
|
||||||
flushMicrotasks();
|
flushMicrotasks();
|
||||||
expect(pipe.transform(c, []).wrapped).toBe(pipe);
|
expect(d.diff(c)).toBe(d);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("should request a change detection check upon receiving a change",
|
it("should request a change detection check upon receiving a change",
|
||||||
fakeAsync(() {
|
fakeAsync(() {
|
||||||
final pipe = pipeFactory.create(changeDetectorRef);
|
final d = factory.create(changeDetectorRef);
|
||||||
|
|
||||||
final c = new ObservableList.from([1, 2]);
|
final c = new ObservableList.from([1, 2]);
|
||||||
pipe.transform(c, []);
|
d.diff(c);
|
||||||
|
|
||||||
c.add(3);
|
c.add(3);
|
||||||
flushMicrotasks();
|
flushMicrotasks();
|
||||||
|
@ -72,28 +72,28 @@ main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("should return the wrapped value after changing a collection", () {
|
it("should return the wrapped value after changing a collection", () {
|
||||||
final pipe = pipeFactory.create(changeDetectorRef);
|
final d = factory.create(changeDetectorRef);
|
||||||
|
|
||||||
final c1 = new ObservableList.from([1, 2]);
|
final c1 = new ObservableList.from([1, 2]);
|
||||||
final c2 = new ObservableList.from([3, 4]);
|
final c2 = new ObservableList.from([3, 4]);
|
||||||
|
|
||||||
expect(pipe.transform(c1, []).wrapped).toBe(pipe);
|
expect(d.diff(c1)).toBe(d);
|
||||||
expect(pipe.transform(c2, []).wrapped).toBe(pipe);
|
expect(d.diff(c2)).toBe(d);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not unbsubscribe from the stream of chagnes after changing a collection",
|
it("should not unbsubscribe from the stream of chagnes after changing a collection",
|
||||||
() {
|
() {
|
||||||
final pipe = pipeFactory.create(changeDetectorRef);
|
final d = factory.create(changeDetectorRef);
|
||||||
|
|
||||||
final c1 = new ObservableList.from([1, 2]);
|
final c1 = new ObservableList.from([1, 2]);
|
||||||
expect(pipe.transform(c1, []).wrapped).toBe(pipe);
|
expect(d.diff(c1)).toBe(d);
|
||||||
|
|
||||||
final c2 = new ObservableList.from([3, 4]);
|
final c2 = new ObservableList.from([3, 4]);
|
||||||
expect(pipe.transform(c2, []).wrapped).toBe(pipe);
|
expect(d.diff(c2)).toBe(d);
|
||||||
|
|
||||||
// pushing into the first collection has no effect, and we do not see the change
|
// pushing into the first collection has no effect, and we do not see the change
|
||||||
c1.add(3);
|
c1.add(3);
|
||||||
expect(pipe.transform(c2, [])).toBe(pipe);
|
expect(d.diff(c2)).toBe(d);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue