在 Angular Signals 中,您可以使用 viewChild() +effect() 来处理子元素的更改吗?

问题描述 投票:0回答:1

假设我想知道组件中的特定子元素何时滚动。假设我声明:

  contentPanel = viewChild.required<ElementRef>('contentPanel');
  contentScrollTop = computed(
    () => this.contentPanel().nativeElement.scrollTop,
  );

然后在构造函数中出现如下效果:

effect(() => {
  console.log('scrolltop: ', this.contentScrollTop());
});

所以这实际上不起作用,但是......这种方法有办法起作用吗?或者 nativeElement 的属性是否超出了 Signals 可以检测到的变化?我可以使用事件侦听器轻松地做到这一点,只是好奇是否有更多信号方式来做到这一点。

angular signals effects viewchild
1个回答
0
投票

截至 2024 年 12 月 14 日,HTML 元素中没有

HostListener
信号或
listening
事件信号。

在那之前我们必须想办法来实现这一点,我可以想到两种方法。

使用

fromEvent
scroll
监听更改,我们必须确保在
ngAfterViewInit
上完成此操作,因为
viewChild
信号将在此处具有值。

我正在使用

takeUntilDestroyed
destroyRef
在组件销毁时自动销毁侦听器。

然后我们使用

tap
将事件值设置为信号
contentScrollTop
,您可以使用它来实现信号的反应性。

destroyRef = inject(DestroyRef);
contentPanel = viewChild.required<ElementRef>('contentPanel');
contentScrollTop = signal(0);

ngAfterViewInit() {
  console.log(this.contentPanel());
  fromEvent(this.contentPanel().nativeElement, 'scroll')
    .pipe(
      startWith(this.contentPanel().nativeElement?.scrollTop),
      takeUntilDestroyed(this.destroyRef),
      tap((response: any) => {
        this.contentScrollTop.set(response.target?.scrollTop);
      })
    )
    .subscribe();
}

完整代码:

import {
  Component,
  DestroyRef,
  ElementRef,
  inject,
  signal,
  viewChild,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { fromEvent, tap, startWith } from 'rxjs';

@Component({
  selector: 'app-root',
  template: `
    <div style="height:300px;overflow:auto;border:1px solid black"  #contentPanel >
      <div style="height:1000px">
        Scrollable content!
      </div>
    </div>

    {{contentScrollTop()}}
  `,
})
export class App {
  destroyRef = inject(DestroyRef);
  contentPanel = viewChild.required<ElementRef>('contentPanel');
  contentScrollTop = signal(0);

  ngAfterViewInit() {
    console.log(this.contentPanel());
    fromEvent(this.contentPanel().nativeElement, 'scroll')
      .pipe(
        startWith(this.contentPanel().nativeElement?.scrollTop),
        takeUntilDestroyed(this.destroyRef),
        tap((response: any) => {
          this.contentScrollTop.set(response.target?.scrollTop);
        })
      )
      .subscribe();
  }
}

bootstrapApplication(App);

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.