最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Intersection observer in Angular - Stack Overflow

programmeradmin1浏览0评论

I am trying to set up IntersectionObserver in Angular but I can't get it to work. I want to load data when I scroll down using HttpClient from the server. I'm doing something like this: In HTML on the bottom of the page I have this:

<div #ob></div>

In the .ts file I am using ViewChild to grab the element and pass it to the IntersectionObserverService:

@ViewChild('ob') ob: ElementRef;

constructor(
    private inter: IntersectionObserverService) { }

 ngAfterViewInit(): void {
    this.inter.createAndObserve(this.ob);
  }

Then in the service I observe the passed in element and try to console.log the entries but nothing is happening.

@Injectable({
    providedIn: 'root'
})
export class IntersectionObserverService {
    _observer: IntersectionObserver | undefined;

    constructor() { }

    createAndObserve(element: ElementRef) {
        const options = {
            root: null,
            threshold: 0.1
        };

        this._observer = new IntersectionObserver((entries, observer) => {
            console.log('on')
            entries.forEach((entry: IntersectionObserverEntry) => {
                console.log(entry);
                console.log('okk');
                // observer.unobserve(entry.target);
            });
        }, options);
        this._observer.observe(element.nativeElement);
    }

}

I would want here to pass a callback to get my data from the backend when I intersect <div #ob></div>. Then pass it forward. How can I make the IntersectionObserver to observe it and display entries?

I am trying to set up IntersectionObserver in Angular but I can't get it to work. I want to load data when I scroll down using HttpClient from the server. I'm doing something like this: In HTML on the bottom of the page I have this:

<div #ob></div>

In the .ts file I am using ViewChild to grab the element and pass it to the IntersectionObserverService:

@ViewChild('ob') ob: ElementRef;

constructor(
    private inter: IntersectionObserverService) { }

 ngAfterViewInit(): void {
    this.inter.createAndObserve(this.ob);
  }

Then in the service I observe the passed in element and try to console.log the entries but nothing is happening.

@Injectable({
    providedIn: 'root'
})
export class IntersectionObserverService {
    _observer: IntersectionObserver | undefined;

    constructor() { }

    createAndObserve(element: ElementRef) {
        const options = {
            root: null,
            threshold: 0.1
        };

        this._observer = new IntersectionObserver((entries, observer) => {
            console.log('on')
            entries.forEach((entry: IntersectionObserverEntry) => {
                console.log(entry);
                console.log('okk');
                // observer.unobserve(entry.target);
            });
        }, options);
        this._observer.observe(element.nativeElement);
    }

}

I would want here to pass a callback to get my data from the backend when I intersect <div #ob></div>. Then pass it forward. How can I make the IntersectionObserver to observe it and display entries?

Share Improve this question edited Oct 30, 2021 at 18:03 Roy 8,0694 gold badges27 silver badges48 bronze badges asked Apr 26, 2021 at 19:14 Luker asdLuker asd 871 gold badge1 silver badge10 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 17

In your component do:

@ViewChild('ob', { read: ElementRef })
ob: ElementRef;

ngAfterViewInit(): void {
  this.inter.createAndObserve(this.ob).pipe(
    filter((isVisible: boolean) => isVisible),
    switchMap(() => this.yourService.loadData())
  ).subscribe(data => { ... });
}

Then go to your service and update your method, so now it will return an observable that will emit a boolean event everytime you intersect your host element:

createAndObserve(element: ElementRef): Observable<boolean> {
  return new Observable(observer => {
    const intersectionObserver = new IntersectionObserver(entries => {
      observer.next(entries);
    });

    intersectionObserver.observe(element.nativeElement);

    return () => { intersectionObserver.disconnect(); };
  }).pipe(
    mergeMap((entries: IntersectionObserverEntry[]) => entries),
    map(entry => entry.isIntersecting),
    distinctUntilChanged()
  );
}

I highly suggest you create a directive to do this.

Single IntersectionObserver approach

Parent Component

@Component({
  selector: 'app-log-view',
  template: `
<ul>
    <li *ngFor="let log of this.logs$ | async" appObserverChild [observer]="this.observer">
      <p>{{ log.message }}</p>
      <small>{{ log.createdAt | date : 'short' }}</small>
    </li>
  </ul>
`,
  styleUrls: ['./log-view.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LogViewComponent {
  readonly observer = new IntersectionObserver((entries) => console.log(entries));

  constructor(private logService: LogService, private route: ActivatedRoute) {}
}

appObserverChild Directive

import { AfterViewInit, Directive, ElementRef, Input } from '@angular/core';

@Directive({
  selector: '[appObserverChild]',
})
export class ObserverChildDirective implements AfterViewInit {
  @Input() observer!: IntersectionObserver;

  constructor(private el: ElementRef) {}

  ngAfterViewInit(): void {
    this.observer.observe(this.el.nativeElement)
  }

  static ngTemplateContextGuard(directive: ObserverChildDirective, context: unknown): context is ObserverChildContext {
    return true;
  }
}

interface ObserverChildContext {
  observer: IntersectionObserver;
}

Intersection Observer for single element

I would not suggest using this approach if you want to observer multiple elements. Performance will get really choppy.

@Directive({
  selector: '[appObservable]',
})
export class ObservableDirective {
  private observer: IntersectionObserver;
  @Output() intersection = new EventEmitter<void>();

  constructor(public el: ElementRef) {
    this.observer = new IntersectionObserver(this.callback, { rootMargin: '100px', threshold: 0.5, root: null });
    this.observer.observe(this.el.nativeElement);
  }

  private callback: ConstructorParameters<typeof IntersectionObserver>[0] = (entries) =>
    entries
      .filter((entry) => entry.isIntersecting)
      .forEach((_entry) => {
        this.intersection.emit();
      });
}

Using a directive like this will make your life easier.

发布评论

评论列表(0)

  1. 暂无评论