【问题标题】:Alternative to setTimeout for Angular lifecyclesAngular 生命周期的 setTimeout 的替代方案
【发布时间】:2021-12-27 16:11:58
【问题描述】:

我的问题来自一种情况(我已经解决了它,但我正在寻找好的做法)。我有一个自动从 API 获取数组的输入。然后使用此数组运行*ngFor 以显示选项并关注第一个选项,但这是我的问题:我必须使用 setTimeout 以便 angular 获取呈现的选项,否则我会得到未定义的选项。我一直在使用它,但我知道使用 setTimeout 不好,因为要重新观察所有状态,我的问题是最好的方法是什么?是用 rxjs 吗?

父组件

@Component({
  template:`
    <generic-child-component></child-component>
  `
})
export class ParentComponent implements OnInit{
  @ViewChild(ChildGenericComponent)child: ChildGenericComponent
  delaySubject: Subject<string> = new Subject();

  contructor (private apiService: ApiService){}

  ngOnInit(){
    this.delaySubject.pipe(
      debounceTime(2500),
    ).subscribe(query=>{
      this.apiService.get(query).subscribe(fetchedData=>{
        this.child.fetchedOptions = fetchedData
        //Here I want to put a focus in the first Object
        //but i need to make a setTimeout to get the rendered option in the child component
        setTimeout(() => {
          this.child.optionsInputs.first.nativeElement.focus() //If this its outside this setTimeout the child.optionsInputs = undefined
        })
      })
    })
  }

  ngAfterViewInit(): void {
    this.child.form.controls['filter'].subscribe(query=>this.delaySubject.next(query))
  }

}

子组件

import { Component, ElementRef, QueryList, ViewChildren } from "@angular/core";
import { FormBuilder, FormGroup} from "@angular/forms";

@Component({
  selector:'generic-child-component',
  template:`
    <form [formGroup]="form">
    <input [formControlName]="'filter'" type="text">
    </form>
    <div *ngFor="let option of fetchedOptions">
      <input #optionsInputs >
      {{option}}
    </div>
  `
})
export class GenericDelayComponent {

  @ViewChildren('optionsInputs')optionsInputs:QueryList<ElementRef>

  constructor(private fBuilder:FormBuilder){}

  fetchedOptions=[]

  form: FormGroup = this.fBuilder.group({
    filter:[null]
  })

}

【问题讨论】:

  • setTimeout 在这种情况下绝不是一个好主意 - 如果 API 需要更长的时间来处理请求/回答会发生什么?你在 component.ts 中使用subscribe() 吗?
  • 初始化数组为空数组。因此, *ngFor 不会抛出任何错误。而不是使用 setTimeout 使用 JeffryHouser 提到的选项 1。
  • @iLuvLogix 我在获取数据后使用 setTimeout。我会尝试提供一个示例代码。

标签: angular rxjs settimeout


【解决方案1】:

我的解决方案遵循@JeffryHouser 的回答: 父组件

@Component({
  template:`
    <generic-child-component></child-component>
  `
})
export class ParentComponent implements OnInit{
  @ViewChild(ChildGenericComponent)child: ChildGenericComponent
  delaySubject: Subject<string> = new Subject();

  contructor (private apiService: ApiService){}

  ngOnInit(){
    this.delaySubject.pipe(
      debounceTime(2500),
    ).subscribe(query=>{
      this.apiService.get(query).subscribe(fetchedData=>{
        this.child.setOptions(fetchedData)
        })
      })
    })
  }

  ngAfterViewInit(): void {
    this.child.form.controls['filter'].subscribe(query=>this.delaySubject.next(query))
  }

}

子组件

import { Component, ElementRef, QueryList, ViewChildren } from "@angular/core";
import { FormBuilder, FormGroup} from "@angular/forms";

@Component({
  selector:'generic-child-component',
  template:`
    <form [formGroup]="form">
    <input [formControlName]="'filter'" type="text">
    </form>
    <div *ngFor="let option of fetchedOptions">
      <input #optionsInputs >
      {{option}}
    </div>
  `
})
export class GenericDelayComponent {

  @ViewChildren('optionsInputs')optionsInputs:QueryList<ElementRef>

  constructor(private fBuilder:FormBuilder){}
  
  ngAfterViewInit(){
    if(this.fetchedOptions.length){
      this.child.optionsInputs.first.nativeElement.focus()
    }  
  }  

  fetchedOptions=[]

  setOptions(options:[]{
    this.fetchedOptions = options
  }

  form: FormGroup = this.fBuilder.group({
    filter:[null]
  })

}

【讨论】:

    【解决方案2】:

    我必须使用 setTimeout 以便 angular 获取渲染选项 否则我会得到它的未定义

    没有代码,很难回答这个问题,我不清楚如何使用 setTimeout() 来防止 Angular 呈现未定义的变量。但是,有一些想法:

    选项 1:默认结果。

    一个例子可能是这样的:

    export class AppComponent  implements OnInit {
      myResults = [];
    
      ngOnInit(): void {
         myService.loadData.subscribe((resultsFromServer) => {
              this.myResults = resultsFromServer;
         });
      }
    }
    

    这是我最常用的。

    选项 2:在组件初始化之前使用Resolvers 加载数据。解析器基本上是在路由加载之前和组件初始化之前运行的一些代码。文档中有示例,这里不再赘述。

    选项 3:如果您真的需要 setTimeout() 功能,您可以使用 rxjs delay 作为可观察链的一部分:

    一般来说,可能是这样的:

    of(([])).pipe(
     delay(6000)
    ).subscribe((results) => { 
    // do something
     });
    

    【讨论】:

    • 嗨,谢谢你的回答,我会提供代码,但我认为它不是那么重要,我的意思是 setTimeout 不是我需要计时器,它是制作异步的解决方案函数,所以它进入下一个循环,我可以访问孩子。我知道解析器,但我在同一个视图中呈现数据。谢谢,我会尝试以我的代码为例。
    • 我用一些代码更新了这个问题。
    • 我只看了一眼。看起来您正在尝试从父母那里更新孩子的孩子。该方法打破了几个层次的封装,因为父级需要知道子级的实现细节。理想情况下,父母应该在孩子身上调用一个方法;它应该处理设置焦点。然后你可以在方法中设置一个标志,并绑定到生命周期方法中以在视图刷新后设置焦点。
    • 是的,这就像一个魅力,谢谢,我很感激。我在此之后使用解决方案更新了问题。
    • @RigolMuhammadAli 我很高兴能提供帮助。我建议您发布自己的问题的答案,而不是将您的解决方案添加到原始问题中。它更适合 Stack Overflow 的结构方式。
    猜你喜欢
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 1970-01-01
    • 2017-11-22
    • 2020-06-20
    • 2018-03-23
    相关资源
    最近更新 更多