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:
@ -3,8 +3,10 @@ import {PregenProtoChangeDetector} from './pregen_proto_change_detector';
|
||||
import {DynamicProtoChangeDetector} from './proto_change_detector';
|
||||
import {PipeFactory, Pipe} from './pipes/pipe';
|
||||
import {Pipes} from './pipes/pipes';
|
||||
import {IterableChangesFactory} from './pipes/iterable_changes';
|
||||
import {KeyValueChangesFactory} from './pipes/keyvalue_changes';
|
||||
import {IterableDiffers, IterableDifferFactory} from './differs/iterable_differs';
|
||||
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 {PromisePipeFactory} from './pipes/promise_pipe';
|
||||
import {UpperCasePipe} from './pipes/uppercase_pipe';
|
||||
@ -52,6 +54,8 @@ export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
export {DynamicChangeDetector} from './dynamic_change_detector';
|
||||
export {ChangeDetectorRef} from './change_detector_ref';
|
||||
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 {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.
|
||||
*/
|
||||
export const keyValDiff: List<PipeFactory> =
|
||||
CONST_EXPR([CONST_EXPR(new KeyValueChangesFactory()), CONST_EXPR(new NullPipeFactory())]);
|
||||
export const keyValDiff: KeyValueDifferFactory[] =
|
||||
CONST_EXPR([CONST_EXPR(new DefaultKeyValueDifferFactory())]);
|
||||
|
||||
/**
|
||||
* Structural diffing for `Iterable` types such as `Array`s.
|
||||
*/
|
||||
export const iterableDiff: List<PipeFactory> =
|
||||
CONST_EXPR([CONST_EXPR(new IterableChangesFactory()), CONST_EXPR(new NullPipeFactory())]);
|
||||
export const iterableDiff: IterableDifferFactory[] =
|
||||
CONST_EXPR([CONST_EXPR(new DefaultIterableDifferFactory())]);
|
||||
|
||||
/**
|
||||
* Async binding to such types as Observable.
|
||||
@ -127,8 +131,6 @@ export const date: List<PipeFactory> =
|
||||
|
||||
|
||||
export const defaultPipes: Pipes = CONST_EXPR(new Pipes({
|
||||
"iterableDiff": iterableDiff,
|
||||
"keyValDiff": keyValDiff,
|
||||
"async": async,
|
||||
"uppercase": uppercase,
|
||||
"lowercase": lowercase,
|
||||
@ -140,6 +142,10 @@ export const defaultPipes: Pipes = CONST_EXPR(new Pipes({
|
||||
"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
|
||||
* {@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 {
|
||||
isListLikeIterable,
|
||||
iterateListLike,
|
||||
@ -15,17 +15,16 @@ import {
|
||||
isArray
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import {WrappedValue, Pipe, BasePipe, PipeFactory} from './pipe';
|
||||
import {ChangeDetectorRef} from '../change_detector_ref';
|
||||
import {IterableDiffer, IterableDifferFactory} from '../differs/iterable_differs';
|
||||
|
||||
@CONST()
|
||||
export class IterableChangesFactory implements PipeFactory {
|
||||
supports(obj: any): boolean { return IterableChanges.supportsObj(obj); }
|
||||
|
||||
create(cdRef: ChangeDetectorRef): Pipe { return new IterableChanges(); }
|
||||
export class DefaultIterableDifferFactory implements IterableDifferFactory {
|
||||
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
||||
create(cdRef: ChangeDetectorRef): any { return new DefaultIterableDiffer(); }
|
||||
}
|
||||
|
||||
export class IterableChanges extends BasePipe {
|
||||
export class DefaultIterableDiffer implements IterableDiffer {
|
||||
private _collection = null;
|
||||
private _length: int = null;
|
||||
// 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 _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 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)) {
|
||||
return WrappedValue.wrap(this);
|
||||
return this;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy() {}
|
||||
|
||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||
check(collection: any): boolean {
|
||||
this._reset();
|
@ -1,16 +1,23 @@
|
||||
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 {WrappedValue, BasePipe, Pipe, PipeFactory} from './pipe';
|
||||
import {KeyValueDiffer, KeyValueDifferFactory} from '../differs/keyvalue_differs';
|
||||
|
||||
@CONST()
|
||||
export class KeyValueChangesFactory implements PipeFactory {
|
||||
supports(obj: any): boolean { return KeyValueChanges.supportsObj(obj); }
|
||||
export class DefaultKeyValueDifferFactory implements KeyValueDifferFactory {
|
||||
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 _mapHead: KVChangeRecord = null;
|
||||
private _previousMapHead: KVChangeRecord = null;
|
||||
@ -21,18 +28,6 @@ export class KeyValueChanges extends BasePipe {
|
||||
private _removalsHead: 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 {
|
||||
return this._additionsHead !== null || this._changesHead !== 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 {
|
||||
this._reset();
|
||||
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,
|
||||
PreGeneratedChangeDetection,
|
||||
Pipes,
|
||||
defaultPipes
|
||||
defaultPipes,
|
||||
IterableDiffers,
|
||||
defaultIterableDiffers,
|
||||
KeyValueDiffers,
|
||||
defaultKeyValueDiffers
|
||||
} from 'angular2/src/change_detection/change_detection';
|
||||
import {ExceptionHandler} from './exception_handler';
|
||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
@ -134,6 +138,8 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
|
||||
CompilerCache,
|
||||
ViewResolver,
|
||||
bind(Pipes).toValue(defaultPipes),
|
||||
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||
bind(ChangeDetection).toClass(bestChangeDetection),
|
||||
ViewLoader,
|
||||
DirectiveResolver,
|
||||
|
@ -1,11 +1,13 @@
|
||||
import {isPresent, isString, StringWrapper, isBlank} from 'angular2/src/facade/lang';
|
||||
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
||||
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 {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes';
|
||||
import {IterableChanges} from 'angular2/src/change_detection/pipes/iterable_changes';
|
||||
import {isPresent, isString, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
KeyValueDiffer,
|
||||
IterableDiffer,
|
||||
IterableDiffers,
|
||||
KeyValueDiffers
|
||||
} from 'angular2/change_detection';
|
||||
import {ListWrapper, StringMapWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
@ -34,10 +36,12 @@ import {ListWrapper, StringMapWrapper, isListLikeIterable} from 'angular2/src/fa
|
||||
properties: ['rawClass: class']
|
||||
})
|
||||
export class CSSClass {
|
||||
_pipe: Pipe;
|
||||
private _differ: any;
|
||||
private _mode: string;
|
||||
_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) {
|
||||
this._cleanupClasses(this._rawClass);
|
||||
@ -47,16 +51,26 @@ export class CSSClass {
|
||||
}
|
||||
|
||||
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 {
|
||||
var diff = this._pipe.transform(this._rawClass, null);
|
||||
if (isPresent(diff) && isPresent(diff.wrapped)) {
|
||||
if (diff.wrapped instanceof IterableChanges) {
|
||||
this._applyArrayChanges(diff.wrapped);
|
||||
} else {
|
||||
this._applyObjectChanges(diff.wrapped);
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._rawClass);
|
||||
if (isPresent(changes)) {
|
||||
if (this._mode == 'iterable') {
|
||||
this._applyIterableChanges(changes);
|
||||
} else {
|
||||
this._applyKeyValueChanges(changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,19 +89,19 @@ export class CSSClass {
|
||||
}
|
||||
}
|
||||
|
||||
private _applyObjectChanges(diff: KeyValueChanges): void {
|
||||
diff.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
diff.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
diff.forEachRemovedItem((record) => {
|
||||
private _applyKeyValueChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachChangedItem((record) => { this._toggleClass(record.key, record.currentValue); });
|
||||
changes.forEachRemovedItem((record) => {
|
||||
if (record.previousValue) {
|
||||
this._toggleClass(record.key, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _applyArrayChanges(diff: IterableChanges): void {
|
||||
diff.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
|
||||
diff.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
|
||||
private _applyIterableChanges(changes: any): void {
|
||||
changes.forEachAddedItem((record) => { this._toggleClass(record.item, true); });
|
||||
changes.forEachRemovedItem((record) => { this._toggleClass(record.item, false); });
|
||||
}
|
||||
|
||||
private _toggleClass(className: string, enabled): void {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
||||
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';
|
||||
|
||||
/**
|
||||
@ -37,27 +37,26 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
{selector: '[ng-for][ng-for-of]', properties: ['ngForOf'], lifecycle: [LifecycleEvent.onCheck]})
|
||||
export class NgFor {
|
||||
_ngForOf: any;
|
||||
_pipe: Pipe;
|
||||
private _differ: IterableDiffer;
|
||||
|
||||
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef,
|
||||
private pipes: Pipes, private cdr: ChangeDetectorRef) {}
|
||||
private iterableDiffers: IterableDiffers, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
set ngForOf(value: any) {
|
||||
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() {
|
||||
var diff = this._pipe.transform(this._ngForOf, null);
|
||||
if (isPresent(diff)) this._applyChanges(diff.wrapped);
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._ngForOf);
|
||||
if (isPresent(changes)) this._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
|
||||
// easier to consume than current.
|
||||
var recordViewTuples = [];
|
||||
|
@ -1,9 +1,7 @@
|
||||
import {Directive, LifecycleEvent} from 'angular2/annotations';
|
||||
import {ElementRef} from 'angular2/core';
|
||||
import {Pipe} from 'angular2/src/change_detection/pipes/pipe';
|
||||
import {Pipes} from 'angular2/src/change_detection/pipes/pipes';
|
||||
import {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes';
|
||||
import {isPresent, print} from 'angular2/src/facade/lang';
|
||||
import {KeyValueDiffer, KeyValueDiffers} from 'angular2/change_detection';
|
||||
import {isPresent, isBlank, print} from 'angular2/src/facade/lang';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
|
||||
/**
|
||||
@ -33,27 +31,32 @@ import {Renderer} from 'angular2/src/render/api';
|
||||
properties: ['rawStyle: ng-style']
|
||||
})
|
||||
export class NgStyle {
|
||||
_pipe: Pipe;
|
||||
_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) {
|
||||
this._rawStyle = v;
|
||||
this._pipe = this._pipes.get('keyValDiff', this._rawStyle);
|
||||
}
|
||||
|
||||
onCheck() {
|
||||
var diff = this._pipe.transform(this._rawStyle, null);
|
||||
if (isPresent(diff) && isPresent(diff.wrapped)) {
|
||||
this._applyChanges(diff.wrapped);
|
||||
if (isBlank(this._differ) && isPresent(v)) {
|
||||
this._differ = this._differs.find(this._rawStyle).create(null);
|
||||
}
|
||||
}
|
||||
|
||||
private _applyChanges(diff: KeyValueChanges): void {
|
||||
diff.forEachAddedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||
diff.forEachChangedItem((record) => { this._setStyle(record.key, record.currentValue); });
|
||||
diff.forEachRemovedItem((record) => { this._setStyle(record.key, null); });
|
||||
onCheck() {
|
||||
if (isPresent(this._differ)) {
|
||||
var changes = this._differ.diff(this._rawStyle);
|
||||
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 {
|
||||
|
@ -2,10 +2,10 @@ library angular2.directives.observable_list_iterable_diff;
|
||||
|
||||
import 'package:observe/observe.dart' show ObservableList;
|
||||
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';
|
||||
|
||||
class ObservableListDiff extends IterableChanges {
|
||||
class ObservableListDiff extends DefaultIterableDiffer {
|
||||
ChangeDetectorRef _ref;
|
||||
ObservableListDiff(this._ref);
|
||||
|
||||
@ -13,11 +13,6 @@ class ObservableListDiff extends IterableChanges {
|
||||
ObservableList _collection;
|
||||
StreamSubscription _subscription;
|
||||
|
||||
bool supports(obj) {
|
||||
if (obj is ObservableList) return true;
|
||||
throw "Cannot change the type of a collection";
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
if (this._subscription != null) {
|
||||
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.
|
||||
// - We need to set up a listener.
|
||||
// - We need to transform collection.
|
||||
// - We need to diff collection.
|
||||
if (!identical(_collection, collection)) {
|
||||
_collection = collection;
|
||||
|
||||
@ -39,14 +38,14 @@ class ObservableListDiff extends IterableChanges {
|
||||
_ref.requestCheck();
|
||||
});
|
||||
_updated = false;
|
||||
return super.transform(collection, args);
|
||||
return super.diff(collection);
|
||||
|
||||
// An update has been registered since the last change detection check.
|
||||
// - We reset the flag.
|
||||
// - We diff the collection.
|
||||
} else if (_updated) {
|
||||
_updated = false;
|
||||
return super.transform(collection, args);
|
||||
return super.diff(collection);
|
||||
|
||||
// No updates has been registered.
|
||||
// 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();
|
||||
bool supports(obj) => obj is ObservableList;
|
||||
Pipe create(ChangeDetectorRef cdRef) {
|
||||
IterableDiffer create(ChangeDetectorRef cdRef) {
|
||||
return new ObservableListDiff(cdRef);
|
||||
}
|
||||
}
|
||||
|
@ -33,3 +33,8 @@ class SpyDependencyProvider extends SpyObject implements DependencyProvider {
|
||||
class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {
|
||||
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 SpyDependencyProvider extends SpyObject {}
|
||||
|
||||
export class SpyIterableDifferFactory extends SpyObject {}
|
||||
|
@ -8,7 +8,11 @@ import {
|
||||
ChangeDetection,
|
||||
DynamicChangeDetection,
|
||||
Pipes,
|
||||
defaultPipes
|
||||
defaultPipes,
|
||||
IterableDiffers,
|
||||
defaultIterableDiffers,
|
||||
KeyValueDiffers,
|
||||
defaultKeyValueDiffers
|
||||
} from 'angular2/src/change_detection/change_detection';
|
||||
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
|
||||
import {ViewLoader} from 'angular2/src/render/dom/compiler/view_loader';
|
||||
@ -119,8 +123,10 @@ function _getAppBindings() {
|
||||
CompilerCache,
|
||||
bind(ViewResolver).toClass(MockViewResolver),
|
||||
bind(Pipes).toValue(defaultPipes),
|
||||
Log,
|
||||
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
|
||||
bind(ChangeDetection).toClass(DynamicChangeDetection),
|
||||
Log,
|
||||
ViewLoader,
|
||||
DynamicComponentLoader,
|
||||
DirectiveResolver,
|
||||
|
Reference in New Issue
Block a user