Skip to content

Commit

Permalink
feat(stream): implement ViewPortRenderStrategy to only run change det…
Browse files Browse the repository at this point in the history
…ection when element is visible in the viewport
  • Loading branch information
michaelbe812 committed Apr 11, 2023
1 parent 2cb1719 commit 95cec91
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 18 deletions.
79 changes: 61 additions & 18 deletions libs/stream/src/lib/stream.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,30 @@ import {
} from '@angular/core';
import {
BehaviorSubject,
combineLatestWith,
distinctUntilChanged,
filter,
map,
mergeAll,
Observable,
of,
ReplaySubject,
skip,
startWith,
Subject,
Subscription,
switchMap,
takeUntil,
Unsubscribable,
withLatestFrom,
} from 'rxjs';
import {STREAM_DIR_CONFIG, STREAM_DIR_CONTEXT, StreamDirectiveConfig} from './stream-directive-config';
import {RenderStrategies} from './types/render-strategies';
import {isViewportRenderStrategy, RenderStrategies} from './types/render-strategies';
import {coerceObservable} from './util/coerce-observable';
import {RenderContext} from './types/render-context';
import {StreamDirectiveContext} from './types/stream-directive-context';
import {setupOperator$} from './util/setup-operator';
import {createIntersectionObserver} from "@angular-kit/rx/platform";

@Directive({
selector: '[stream]',
Expand Down Expand Up @@ -90,9 +95,38 @@ export class StreamDirective<T> implements OnInit, OnDestroy {
renderCount: 0,
};

readonly isViewPortStrategy$ = this.renderStrategy$$.pipe(
mergeAll(),
map((strategy) => strategy.type === 'viewport'),
);
readonly renderStrategyOperator$ = setupOperator$(this.renderStrategy$$);
readonly source$ = this.source$$.pipe(distinctUntilChanged());

/**
* Again problem that e.g above observable chain is not finished when switching strategies
* at runtime.
* also this observable is not unusbscirbed.
*/
viewPortObserver$: Observable<IntersectionObserverEntry[] | null> = this.renderStrategy$$.pipe(
mergeAll(),
switchMap((strategy) => {
if (isViewportRenderStrategy(strategy)){
return createIntersectionObserver(this.viewContainerRef.element.nativeElement.parentElement, {
threshold: strategy.threshold,
rootMargin: strategy.rootMargin,
root: strategy.root,
})
.pipe(
takeUntil(this.renderStrategy$$.pipe(skip(1)))
)
}

return of(null);
})
);
visible$ = this.viewPortObserver$.pipe(
map((entries) => entries?.some((entry) => entry.isIntersecting)))

readonly sourceWithOperator$ = this.renderStrategyOperator$.pipe(
withLatestFrom(this.source$),
/**
Expand All @@ -105,8 +139,8 @@ export class StreamDirective<T> implements OnInit, OnDestroy {
*/
switchMap(([o, source$]) => {
return source$.pipe(
o
//takeUntil(this.renderStrategyOperator$.pipe(skip(1)))
o,
//takeUntil(this.renderStrategy$$.pipe(skip(1)))
);
})
);
Expand All @@ -122,12 +156,15 @@ export class StreamDirective<T> implements OnInit, OnDestroy {
private readonly templateRef: TemplateRef<StreamDirectiveContext<T>>,
private readonly viewContainerRef: ViewContainerRef,
@Optional() @Inject(STREAM_DIR_CONFIG) private readonly config: StreamDirectiveConfig
) {}
) {

}

ngOnInit(): void {
if (!this.embeddedView) {
this.createEmbeddedView();
}

// todo refactor into smaller chunks
this.refreshEffect$$
.pipe(distinctUntilChanged(), mergeAll(), withLatestFrom(this.loadingTemplate$$.pipe(startWith(null))))
Expand Down Expand Up @@ -160,24 +197,30 @@ export class StreamDirective<T> implements OnInit, OnDestroy {
this.subscription = this.sourceWithOperator$
.pipe(
distinctUntilChanged(),
filter((v) => v !== undefined)
filter((v) => v !== undefined),
combineLatestWith(this.visible$)
)
.subscribe({
next: (v: any) => {
this.context.$implicit = v;
this.context.stream = v;
this.context.loading = false;
this.context.renderCount++;
next: (val) => {
console.log('next', val)
const v = val[0] as any;
const visible = val[1] ?? true;
if (visible) {
this.context.$implicit = v;
this.context.stream = v;
this.context.loading = false;
this.context.renderCount++;

this.viewContainerRef.clear();
this.embeddedView = this.viewContainerRef.createEmbeddedView(this.templateRef, this.context);
this.viewContainerRef.clear();
this.embeddedView = this.viewContainerRef.createEmbeddedView(this.templateRef, this.context);

this.embeddedView.detectChanges();
this.renderCallback$$?.next({
renderCycle: 'next',
value: this.context.$implicit,
error: this.context.error,
});
this.embeddedView.detectChanges();
this.renderCallback$$?.next({
renderCycle: 'next',
value: this.context.$implicit,
error: this.context.error,
});
}
},
error: (err) => {
this.context.error = err;
Expand Down
1 change: 1 addition & 0 deletions libs/stream/src/lib/types/render-strategies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface DefaultRenderStategy extends RenderStrategy {

export interface ViewportRenderStrategy extends RenderStrategy {
type: 'viewport';
root?: HTMLElement
rootMargin?: string;
threshold?: number | number[];
}
Expand Down

0 comments on commit 95cec91

Please sign in to comment.