【问题标题】:How to make ScrollIntoView work without having to use setTimeout如何使 ScrollIntoView 工作而不必使用 setTimeout
【发布时间】:2020-04-03 21:02:57
【问题描述】:

我的应用程序有两个父组件 A 和 B,每个组件都在自己的模块中(分别是模块 A 和 B)。组件 A 包括来自其自身模块(模块 C)的子组件 C。组件 B 包括子组件 C(来自模块 C)和来自模块 B 的子组件 B1。各种父/子组件之间的所有交互和行为都运行良好。我遇到的问题是试图让组件 (B1) 将元素 (elemOfInterest) 滚动到视口顶部,以响应组件 C 通过数据共享服务发出的标志 (flagA)。

当我从组件 A 的模板单击子组件 C 的表单提交按钮时,我被路由到组件 B。但是子组件 B1 中的 scrollToView 在没有 setTimeout 的情况下不起作用。一旦组件 B1 的 ngOnInit 生命周期被触发,单击组件 C 的表单提交按钮(这次来自组件 B 的模板)会导致 scrollToView 方法在没有 setTimeout 的情况下工作得非常好。我无法弄清楚为什么 ngOnInit 会干扰组件 B1 中的 scrollToView。这是我的代码:

ComponentC.component.ts:

export class ComponentC implements OnInit {
  constructor(private dataSharing: DataSharingService) {}
  ngOnInit() {
    doSomething(){
      this.dataSharing.transferData({'data':{'flagA':true}});
    }
  }
}

ComponentC.component.html:

<form class="mx-auto" [formGroup]="searchForm" (ngSubmit)="doSomething()">
  <div> … </div>
  <div> <button type="submit">Submit</button> </div>
</form>

ComponentB1.component.ts

export class ComponentB1 implements OnDestroy, OnInit {
  subscriptions: Subscription = new Subscription();
  constructor(private dataSharing: DataSharingService) {}
  ngOnInit() {
    this.subscriptions.add(
      this.dataSharing.outgoingData$.subscribe(data => {
        this.onSharedData(data);
      })
    )
  }

  onSharedData(data) {
    if (Object.keys(data).length && Object.keys(data['data']).length) {
      switch (true) {
        case data['data']['flagA']:
          this.func1();
          break;
        case data['data']['flagB']:
          this.func2();
          break;
      }
    }
  }

  ngOnDestroy() {this.subscriptions.unsubscribe()}

  func1(){
    //do something simple, like set html element values, etc.
    setTimeout(()=>document.getElementById('elemOfInterest').scrollIntoView({behavior: 'smooth', block: 'start', inline: 'nearest'}));
  }
}

数据共享服务代码:

import { Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class DataSharingService {
  outgoingData$ = new ReplaySubject(1);
  constructor() {}
  transferData(incomingData: object){
    this.outgoingData$.next(incomingData);
  }
}

为了它的价值,我也尝试过使用 jquery

$('html, body').animate({ scrollTop: $('#elemOfInterest).offset().top }, 'slow')

而不是 scrollToView 但在组件 B1 首次启动时仍需要 setTimeout 才能使滚动正常工作。知道如何在不使用 setTimeout 的情况下完成滚动行为吗?

【问题讨论】:

    标签: javascript scroll angular7 settimeout


    【解决方案1】:

    outgoingData$ 是一个 ReplaySubject,这意味着它会立即向每个新订阅者发出最后一个值。因此,如果应用程序早些时候调用了transferData,则此重播主题已经具有一些价值(请注意,DataSharingService 是您的代码 sn-p 中的应用程序范围的单例,它不会在导航时重新创建)。

    现在 ComponentB1 在 ngOnInit 中订阅它。重播主题在订阅后立即发出 - 就在此 ngOnInit 执行期间。此时 Angular 尚未渲染 ComponentB1 - 所以elemOfInterest 可能还不存在。

    【讨论】:

    • 实际上,我在可观察订阅行之前添加了 let elem=document.getElementById('elemOfInterest') in ngOnInit,并通过在订阅行设置断点以调试模式运行应用程序。当应用程序暂停时,elem 有一个有效值。这不是表明 elemOfInterest 已经呈现了吗?无论如何,除了使用 setTimeout 之外,您还打算如何解决这个问题?使用回调?还有什么?只是试图找到 setTimeout 的替代方案。(已被告知应尽可能避免使用 setTimeout,但我不确定我理解为什么)。
    • 好吧,我只是假设 elemOfInterest 是 ComponentB 内部的东西。在这种情况下,这个元素在 ngOnInit 期间不会存在于 DOM 中。但是,如果它在 ComponentB 外部,则不必如此。如果是这样,您可能想要调查该 elemOfInterest 的生命周期 - 它何时被创建,何时被销毁。如果它属于 ComponentB(根据我对我的假设的最初假设),那么您可能希望订阅 ngAfterViewInit() 挂钩而不是 ngOnInit()
    • elemOfInterest 在组件 B1(它是组件 B 的子组件)中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-08
    • 2019-07-19
    • 2018-06-14
    • 1970-01-01
    • 2020-01-09
    • 1970-01-01
    相关资源
    最近更新 更多