【问题标题】:How to implement a debounce time in keyup event in Angular 6如何在 Angular 6 的 keyup 事件中实现去抖动时间
【发布时间】:2018-05-10 15:02:14
【问题描述】:

我创建了一个从 API 搜索学生的 Angular 应用。它工作正常,但每次更改输入值时都会调用 API。我做了一项研究,我需要一种叫做 debounce 的东西,但我不知道如何在我的应用程序中实现它。

App.component.html

    <div class="container">
  <h1 class="mt-5 mb-5 text-center">Student</h1>
<div class="form-group">
  <input  class="form-control form-control-lg" type="text" [(ngModel)]=q (keyup)=search() placeholder="Search student by id or firstname or lastname">
</div>
 <hr>
 <table class="table table-striped mt-5">
    <thead class="thead-dark">
      <tr>
        <th scope="col" class="text-center" style="width: 10%;">ID</th>
        <th scope="col" class="text-center" style="width: 30%;">Name</th>
       <th scope="col" style="width: 30%;">E-mail</th>
        <th scope="col" style="width: 30%;">Phone</th> 
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let result of results">
        <th scope="row">{{result.stu_id}}</th>
        <td>{{result.stu_fname}} {{result.stu_lname}}</td>
         <td>{{result.stu_email}}</td>
        <td>{{result.stu_phonenumber}}</td> 
      </tr>
    </tbody>
  </table>
</div>

App.component.ts

import { Component} from '@angular/core';
import { Http,Response } from '@angular/http';
import { Subject, Observable } from 'rxjs';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {


  results;
  q = '';

  constructor(private http:Http) {


  }


  search() {
    this.http.get("https://www.example.com/search/?q="+this.q)
    .subscribe(
      (res:Response) => {
          const studentResult = res.json();
          console.log(studentResult);
          if(studentResult.success) {
            this.results = studentResult.data;
          } else {
            this.results = [];
          }
      }
    )
  }
}

截图

我尝试过类似的方法,但它是错误的 主题类型上不存在属性 debounceTime

  mySubject = new Subject();
  constructor(private http:Http)  {
    this.mySubject
    .debounceTime(5000)
    .subscribe(val => {
      //do what you want
    });
  }

这也行不通。 “typeof Observable”类型上不存在属性“fromEvent”

    Observable.fromEvent<KeyboardEvent>(this.getNativeElement(this.term), 'keyup')

那么,实现这个的正确方法是什么?

谢谢。

【问题讨论】:

    标签: angular typescript rxjs


    【解决方案1】:

    在组件中你可以做这样的事情。创建 RxJS Subject,在 search 事件上调用的 search 方法中,对您创建的 Subject 执行 .next()。然后subscribe 中的ngOnInit()debounce 持续1 秒,如下面的代码。

    searchTextChanged = new Subject<string>();
    constructor(private http:Http) {
    
    }
    
    
    ngOnInit(): void {
        this.subscription = this.searchTextChanged
            .debounceTime(1000)
            .distinctUntilChanged()
            .mergeMap(search => this.getValues())
            .subscribe(() => { });
    }
    
    getValues() {
        return this.http.get("https://www.example.com/search/?q="+this.q)
        .map(
          (res:Response) => {
              const studentResult = res.json();
              console.log(studentResult);
              if(studentResult.success) {
                this.results = studentResult.data;
              } else {
                this.results = [];
              }
          }
        )
    }
    
    search($event) {
        this.searchTextChanged.next($event.target.value);
    }
    

    rxjs v6 有几个重大变化,包括简化运算符的导入点。尝试安装rxjs-compat,它会重新添加这些导入路径,直到代码被迁移。

    RxJS 导入必要的运算符。以下是针对 RxJS 5.x 的

    import { Subject } from "rxjs/Subject";
    import "rxjs/add/operator/debounceTime";
    import "rxjs/add/operator/distinctUntilChanged";
    import { Observable } from "rxjs/Observable";
    import "rxjs/add/operator/mergeMap";
    

    【讨论】:

    • 它仍然得到一个错误 Property 'debounceTime' does not exist on type 'Subject' at .debounceTime(1000) 这是我的新代码。 link
    • @mongkonsrisin ,rxjs v6 有几个重大变化,包括简化运算符的导入点。尝试安装rxjs-compat,它会重新添加这些导入路径,直到代码被迁移。
    • 安装 rxjs-compat 后,该错误已消失,但 this.getValues(search) 处还有另一个错误 预期为 0 个参数,但得到了 1 个
    • @mongkonsrisin ,对此感到抱歉,ngOnInit this.getValues(search) 应该是 this.getValues(),因为它不期待任何参数。我已经更新了答案
    • 仍然是错误 '(search: string) => void' 类型的参数不能分配给'(value: string, index: number) => ObservableInput'。
    【解决方案2】:

    对于任何在较新版本的 angular(和 rxjs)中遇到此问题的人。

    新的 Rxjs 具有可管道化的运算符,它们可以像这样使用(来自接受的答案代码)

    ngOnInit() {
     this.subscription = this.searchTextChanged.pipe(
       debounceTime(1000),
       distinctUntilChanged(),
       mergeMap(search => this.getValues())
      ).subscribe((res) => {
        console.log(res);
      });
    

    【讨论】:

      【解决方案3】:

      另外,您可以使用 angular formControls 来绑定输入搜索字段

      <input  class="form-control form-control-lg" 
      type="text" [formControl]="searchField"
      placeholder="Search student by id or firstname or lastname">
      

      并使用我们的 searchField 上可观察的 valueChanges 来对 App.component.ts 文件中搜索字段的更改做出反应。

      searchField: FormControl; 
      
      ngOnInit() {
          this.searchField.valueChanges
            .debounceTime(5000) 
            .subscribe(term => {
          // call your service endpoint.
            });
      }
      

      您可以选择使用 distinctUntilChanged(仅当发布的值与前一个不同时才会发布到其输出流)

      searchField: FormControl; 
      
      ngOnInit() {
          this.searchField.valueChanges
            .debounceTime(5000) 
           .distinctUntilChanged()
           .subscribe(term => {
                  // call your service endpoint.
            });
      }
      

      【讨论】:

        【解决方案4】:

        如果你使用 angular 6 和 rxjs 6,试试这个:
        注意.pipe(debounceTime(1000)) 之前的subscribe

        import { debounceTime } from 'rxjs/operators';
        
        
        search() {
            this.http.get("https://www.example.com/search/?q="+this.q)
            .pipe(debounceTime(1000))
            .subscribe(
              (res:Response) => {
                  const studentResult = res.json();
                  console.log(studentResult);
                  if(studentResult.success) {
                    this.results = studentResult.data;
                  } else {
                    this.results = [];
                  }
              }
            )
          }
        

        【讨论】:

        • 这不会以任何方式解决 keyup 事件
        • @Logus 在问题的描述中,这家伙问他需要实现一个叫做 debounceTime 的东西,但他不知道如何实现。
        • @NadhirFalta,标题实际上提到他想在keyup事件中实现去抖时间。
        • @Logus 这就是它在问题描述中所说的:“我做了一项研究,我需要一种叫做 debounce 的东西,但我不知道如何在我的应用程序中实现它。”跨度>
        • 这个实现不完整,但是这个答案.pipe(debounceTime(1000))的关键部分结合接受的答案解决了我的问题
        【解决方案5】:

        就这样使用,不用RXJS。

        它可能会在每次按键时调用'search()'函数本身,但它不会每次都调用函数内部的内容(例如http连接)。非常简单的解决方案。

        export class MyComponent implements OnInit {
        
          debounce:any;
        
          constructor(){}
          
          search(){
            clearTimeout(this.debounce);
            this.debounce = setTimeout(function(){
              // Your Http Function..
            },500); // Debounce time is set to 0.5s
          }
        }
        

        【讨论】:

          【解决方案6】:

          user.component.html

             <input type="text" #userNameRef class="form-control"  name="userName" >  <!-- template-driven -->
           <form [formGroup]="regiForm"> 
                email: <input formControlName="emailId"> <!-- formControl -->
              </form>
          

          user.component.ts

                 import { fromEvent } from 'rxjs';
                 import { switchMap,debounceTime, map } from 'rxjs/operators';
          
          
                  @Component({
                    selector: 'app-user',
                    templateUrl: './user.component.html',
                    styleUrls: ['./user.component.css']
                  })
                  export class UserComponent implements OnInit {
          
                    constructor(private userService : UserService) { }
          
          
                       @ViewChild('userNameRef') userNameRef : ElementRef;
          
                       emailId = new FormControl(); 
             regiForm: FormGroup = this.formBuilder.group({
                emailId: this.bookId
             });   
          
                       ngOnInit() {
          
                              fromEvent(this.userNameRef.nativeElement,"keyup").pipe(
                              debounceTime(3000),
                              map((userName : any) =>userName.target.value )
                            ).subscribe(res =>{
                              console.log("User Name is :"+ res);
          
                            } );
              //--------------For HTTP Call------------------
          
                            fromEvent(this.userNameRef.nativeElement,"keyup").pipe(
                              debounceTime(3000),
                              switchMap((userName : any) =>{
                             return this.userService.search(userName.target.value)
                           })
                            ).subscribe(res =>{
                              console.log("User Name is :"+ res);
          
                            } );
          
          
          
          ----------
                          // For formControl 
          
                            this.emailId.valueChanges.pipe(
                            debounceTime(3000),
                            switchMap(emailid => {
                                   console.log(emailid);
                                  return this.userService.search(emailid);
                                  })
                                   ).subscribe(res => {
                                         console.log(res);
                                              });
          
          
                      }
          

          *注意:确保您的输入元素不存在于 ngIf 块中。

          【讨论】:

            【解决方案7】:

            演示Link

            教程来源Link

            使用模板变量

            <input type="text" #movieSearchInput class="form-control" placeholder="Type any movie name" />
            
                ...
                ...    
                    fromEvent(this.movieSearchInput.nativeElement, 'keyup').pipe(
                    // get value
                    map((event: any) => {
                        return event.target.value;
                    })
                    // if character length greater then 2
                    ,filter(res => res.length > 2)
                    // Time in milliseconds between key events
                    ,debounceTime(1000)        
                    // If previous query is diffent from current   
                    ,distinctUntilChanged()
                    // subscription for response
                    ).subscribe((text: string) => {
                        this.isSearching = true;
                        this.searchGetCall(text).subscribe((res)=>{
                        console.log('res',res);
                        this.isSearching = false;
                        this.apiResponse = res;
                        },(err)=>{
                        this.isSearching = false;
                        console.log('error',err);
                        });
                    });
                ...
                ...
            

            【讨论】:

            • 首先,@ViewChild 在我的 ngOnInit() 方法中未定义。所以我在我的 ngAfterViewInit() 上添加了整个 fromEvent。但是,当我输入我的输入时,在第一个 keyup 中,我的 subscribe 事件仅在第一种类型中被调用,并且在我再次输入时不会被调用。
            猜你喜欢
            • 2017-06-15
            • 2021-11-21
            • 2021-03-07
            • 2013-11-17
            • 2017-07-01
            • 1970-01-01
            • 2022-06-16
            • 1970-01-01
            相关资源
            最近更新 更多