Last active
July 8, 2017 15:38
-
-
Save ghetolay/227da1ac0cdad015fc0523817734db7e to your computer and use it in GitHub Desktop.
Proposal for ViewSync
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class LoopContext<T> { | |
index: number; | |
count: number; | |
constructor() {} | |
get first(): boolean { return this.index === 0; } | |
get last(): boolean { return this.index === this.count - 1; } | |
get even(): boolean { return this.index % 2 === 0; } | |
get odd(): boolean { return !this.even; } | |
} | |
export const enum ItemAction { | |
ADDED, | |
MOVED, | |
REMOVED, | |
UPDATED | |
} | |
interface RecordViewTuple<T> { | |
item: T; | |
context: LoopContext<T>; | |
action: ItemAction; | |
} | |
export class ViewSyncer<T, R extends LoopContext<T>> { | |
private _differ: IterableDiffer<T>|null = null; | |
constructor( | |
private _viewContainer: ViewContainerRef, | |
private _template: TemplateRef<R>, | |
private _trackBy: TrackByFunction<T>, | |
private _differs: IterableDiffers, | |
private _updateContext: (context: LoopContext<T> | null, item: T, action: ItemAction) => void = | |
(context, item) => { context.$implicit = item }; | |
) { } | |
updateView(iterable: NgIterable<T> | null | undefined) { | |
if (this._differ === null) { | |
if (!iterable) return; | |
try { | |
this._differ = this._differs.find(iterable).create(this._trackBy); | |
} catch (e) { ... } | |
} | |
const changes = this._differ.diff(iterable); | |
if (changes) this._applyChanges(changes); | |
} | |
private _applyChanges(changes: IterableChanges<T>) { | |
const insertTuples: RecordViewTuple<T>[] = []; | |
changes.forEachOperation( | |
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => { | |
if (item.previousIndex == null) { | |
const view = this._viewContainer.createEmbeddedView( | |
this._template, new LoopContext<T>(), currentIndex); | |
insertTuples.push({ | |
item: item.item, | |
context: view.context, | |
action: ItemAction.ADDED | |
}); | |
} else if (currentIndex == null) { | |
this._viewContainer.remove(adjustedPreviousIndex); | |
this._updateContext(null, item.item, ItemAction.REMOVED); | |
} else { | |
const view = this._viewContainer.get(adjustedPreviousIndex) !; | |
this._viewContainer.move(view, currentIndex); | |
insertTuples.push({ | |
item: item.item, | |
context: (<EmbeddedViewRef<LoopContext<T>>>view).context, | |
action: ItemAction.MOVED | |
}); | |
} | |
}); | |
for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) { | |
const viewRef = <EmbeddedViewRef<LoopContext<T>>>this._viewContainer.get(i); | |
viewRef.context.index = i; | |
viewRef.context.count = ilen; | |
} | |
for (let tuple of insertTuples) { | |
this._updateContext(tuple.context, tuple.item, tuple.action); | |
} | |
changes.forEachIdentityChange((record: any) => { | |
const viewRef = | |
<EmbeddedViewRef<LoopContext<T>>>this._viewContainer.get(record.currentIndex); | |
this._updateContext(viewRef.context, record.item, ItemAction.UPDATED); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment