import { Input, OnChanges, SimpleChanges, OnDestroy, QueryList, Directive, ContentChildren, AfterContentInit } from '@angular/core'; import { BehaviorSubject, combineLatest, Subscription } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { ScrollSyncFactory } from './scroll-sync.factory'; import { ScrollSet } from './ScrollSet'; import { HoverSyncFactory } from './hover-sync.factory'; import { HoverSet } from './HoverSet'; @Directive({ selector: 'app-grid-sync', providers: [ScrollSyncFactory, HoverSyncFactory] }) export class GridSyncDirective implements OnDestroy, OnChanges, AfterContentInit { @Input() gridDataChanged: any; private dataChange$ = new BehaviorSubject(true); private elementSubs: ScrollSet; private hoverSubs: HoverSet; private subs: Subscription[] = []; @ContentChildren(YourGridComponent) gridChildren: QueryList; constructor( private _scrollSyncFactory: ScrollSyncFactory, private _hoverSyncFactory: HoverSyncFactory) { } ngAfterContentInit(): void { const gridChanges = this.gridChildren.changes.pipe(startWith(this.gridChildren.toArray())) const sub1 = gridChanges .subscribe(grids => this.setScrollSync(grids)); const sub2 = combineLatest([ gridChanges, this.dataChange$ ]) .pipe(map(([grids]) => grids)) .subscribe(grids => this.setHoverSync(grids)); this.subs.push(sub1, sub2); } ngOnChanges(changes: SimpleChanges): void { if (changes["gridDataChanged"]) { //if the grid contents are changed, we need to reattach listeners //Might be better to use a MutationObserver, which would allow this directive to be used without any hints about the grids' state this.dataChange$.next(true); } } ngOnDestroy() { this.clearScrollSync(); this.clearHoverSync(); this.subs.forEach(sub => sub.unsubscribe()); } private clearScrollSync() { this.elementSubs?.subs.forEach(sub => sub.unsubscribe()); } private clearHoverSync() { this.hoverSubs?.subs.forEach(sub => sub.unsubscribe()); } private setScrollSync(grids) { this.clearScrollSync(); if (!grids?.length) { return; } this.elementSubs = this._scrollSyncFactory.syncVerticalScroll(grids, "contentScroll", "k-grid-content"); } private setHoverSync(grids) { this.clearHoverSync(); if (!grids?.length) { return; } setTimeout(() => { //If I remember correctly, this allows the tr elements time to be created. Might be better to use a MutationObserver this.hoverSubs = this._hoverSyncFactory.syncHover(grids, ".k-grid-content tr", ["k-state-hover", "hover"]); }, 0); } }