Skip to content

Instantly share code, notes, and snippets.

@ghetolay
Last active July 8, 2017 15:38
Show Gist options
  • Save ghetolay/227da1ac0cdad015fc0523817734db7e to your computer and use it in GitHub Desktop.
Save ghetolay/227da1ac0cdad015fc0523817734db7e to your computer and use it in GitHub Desktop.
Proposal for ViewSync
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