【问题标题】:How to wait for data from api to be loaded and then perform another action in the UI?如何等待来自 api 的数据被加载,然后在 UI 中执行另一个操作?
【发布时间】:2020-01-27 12:04:03
【问题描述】:

我有一个从 api 获取用户列表并填充列表组件的方法。现在我想做什么 从 api 加载列表后,突出显示列表的第一个元素。 我已经尝试了以下方法,它与 setTimeout 一起工作正常,但是如果没有 setTimeout 函数我怎么能做到这一点

这是我尝试过的代码,

public getUserList(){ 
    return this.http.get('https://jsonplaceholder.typicode.com/users').pipe(tap(x => {
      this.lists = x;
    }))
    .subscribe((data) => {
      setTimeout(() => { 
        this.clickFirstList();
      }, 1000);
    });
  }

clickFirstList() {
    Array.from(this.ulElement.nativeElement.children).forEach((element: HTMLElement, index: number) => {
      if (index === 0) {
        console.log(element);
        element.click();
      }
    });
}

这是 stackblitz 上的工作代码。 https://stackblitz.com/edit/angular-brpz2j?file=src/app/search-list.component.ts

【问题讨论】:

  • 不用setTimeout,所有在subscribe下的,只有在调用完成后才会执行。注意:您不使用代码中的数据,仅存储在“tap”中(您想在订阅中执行它?注意2:记住“Angular 样式规则”:创建服务以返回 observables -http.get 和其他 - 和订阅组件
  • 它在订阅中不起作用。请在 stackblitz 上编辑代码并将其写为答案。
  • 好吧,我看到了你的堆栈闪电战。您需要使用 setTimeout - 您可以使用 0 作为计时器 - 给 Angular 时间重新绘制列表,而不是等待调用完成。使用setTimeout(() => { this.clickFirstList(); });
  • 您可以使用解析器:这不会显示所有用户,直到您从 API 获得结果,然后您可以在订阅中做您想做的事情
  • 不要像在 Angular 中那样操作 dom。没有理由这样做。拥抱角度和角度绑定。

标签: javascript angular typescript rxjs rxjs6


【解决方案1】:

Tap 还接受一个对象映射来记录下一个、错误和完成

  public getUserList(){ 
    return this.http.get('https://jsonplaceholder.typicode.com/users')
    .pipe(
       tap( {
         next: x => {
           console.log('on next', x);
         },
         error: error => {
           console.log('on error', error.message);
         },
         complete: () => {
           console.log('on complete')
           this.clickFirstList()
         }
       }))
       .subscribe((x) => {
        console.log(x)
       });
  }

【讨论】:

    【解决方案2】:

    你不应该那样操作 DOM,没有必要。我们使用角度绑定,通过使用它们,我们还可以大大减少您当前拥有的代码。所以删除所有这些 DOM 操作。

    首先,我们可以忽略来自父母或孩子的lists 调用。现在您正在执行两次 get 请求,这实际上没有任何意义。我将获取请求保留在孩子身上。我们也可以在 OnInit 中进行请求,为什么要在AfterViewInit 中进行呢?

    ngOnInit() {
      this.getUserList();
    }
    
    public getUserList() {
      return this.http
         // DONT USE 'ANY', type your data using interface or class
        .get<any[]>("https://jsonplaceholder.typicode.com/users")
        .subscribe(data => {
          this.lists = data;
          // you want to emit initially the first item to parent via output
          // do it here.
          this.onClickList(0, this.lists[0] || {})
        });
     }
    

    我们还引入了一个新变量selectedIndex,我们将根据点击的项目进行更改。就像 Jens 在他的回答中所做的那样。您希望第一个项目最初处于活动状态,我们将0 作为索引传递给onClick 函数:

    public onClickList(i: number, item: any) {
      this.selectedIndex = i
      this.selectedList.emit(Object.assign({}, item));
    }
    

    我们像这样将它添加到模板中:

    <ng-container *ngFor="let item of lists | filter: searchText; index as i">
      <li [class.active]="i === selectedIndex"
        (click)="onClickList(i, list)">
        {{ list.name }}
      </li>
    </ng-container>
    

    Your forked STACKBLITZ

    正如代码中的注释所述。不要使用any,它完全违背了TypeScript 的目的,因此请使用接口或类来输入您的数据。我更喜欢接口。

    【讨论】:

    • 感谢您的回答。我们什么时候应该使用类而不是接口?
    • 我会在需要特定类的方法来执行某些业务逻辑时使用它们(无论如何我都不会这样做)。这是一个非常自以为是的问题,你会从不同的人那里得到不同的答案。因此我说我更喜欢接口,其他人可能会说不同:)
    • 为什么不在回调中直接设置this.selectedIndex = 0,跳过调用onclick方法
    • @JensAlenius,当然我们可以将this.selectedIndex = 0this.selectedList.emit(Object.assign({}, this.lists[0])); 添加到回调中,但是当我们有onClickList 函数时,为什么要添加“额外的行” ?
    • 我不明白为什么你需要在示例中发射。它是某个地方的@Output 吗?我在下面的回答应该足以默认第一个元素
    【解决方案3】:

    您可以使用以下代码

    public getUserList(){ 
        return this.http.get('https://jsonplaceholder.typicode.com/users').pipe(tap(x => {
          this.lists = x;
        }))
        .subscribe((data) => {
        },
        (err) => {
            // code to execute if request fails
        },
        () => {
        this.clickFirstList();
        }
      });
    }
    

    【讨论】:

      【解决方案4】:

      我会删除 clickFirstList。并使用 css 和 angular。

      我现在不知道你在哪个 html 元素上循环,但可以说它是一个列表 例如:html

      <ul>
        <li *ngFor="let user of list:let index = index" [class.selected]="index===selectedIndex" (click)="onClick(index)> 
          {{user.Name}}
        </li>
      </ul>
      

      和css

      .selected{
         background-color: yellow;
      }
      

      在打字稿组件中:

      selectedIndex = 0; //Default value. 
      onClick(index:number){
         this.selectedIndex=index;
      }  
      

      【讨论】:

      • 是的。我们可以这样做,但我想以编程方式做一些模板无法实现的条件。我想知道在加载 api 时如何调用 click first 元素。这不是我要寻找的答案。
      • 好的,然后专门写你的问题。所以你不会得到更多像我这样的答案。
      • 已经在问题中添加了。这些行,如何等待来自 api 的数据被加载,然后在 UI 中执行另一个操作?
      猜你喜欢
      • 2012-01-26
      • 1970-01-01
      • 2018-02-07
      • 2021-12-16
      • 2012-02-25
      • 1970-01-01
      • 2020-11-21
      • 1970-01-01
      • 2019-08-31
      相关资源
      最近更新 更多