Angular Material 6 中用于自动完成的无限滚动

2023-11-30

我正在尝试在 Angular Material 6 中实现自动完成的无限滚动。我的场景很简单,我有一个启用了自动完成功能的输入字段。当用户键入时,我将使用输入字段中的文本进行 HTTP 调用,以将结果显示为建议。但我只想显示 25 条建议,如果当用户滚动到底部时结果计数超过 25 条,我想再添加 25 条。 喜欢this角度 2

我在网上找不到。

请给我建议或帮助我。先感谢您。

<mat-form-field>
  <input matInput placeholder="Experiment Name" formControlName="experimentName" [matAutocomplete]="expNamesAutocomplete">
</mat-form-field>
<mat-autocomplete #expNamesAutocomplete="matAutocomplete">
  <mat-option *ngFor="let option of suggestedExpNames" [value]="option">
              {{ option }}
  </mat-option>
</mat-autocomplete>

我知道这篇文章已经过时了,但我将解决方案留在这里,以防有人需要。

诀窍是获取对 mat-autocomplete 面板滚动条的引用。我已经使用自定义指令完成了此操作:

import { Directive, ElementRef, EventEmitter, Input, Output, Host, Self, Optional, AfterViewInit, OnDestroy } from '@angular/core';
import { MatAutocomplete } from '@angular/material';
import { Observable, fromEvent, of, Subject, merge, combineLatest } from 'rxjs';
import { map, startWith, switchMap, tap, debounceTime, filter, scan, withLatestFrom, mergeMap, takeUntil, takeWhile, distinctUntilChanged, skipUntil, exhaustMap, endWith } from 'rxjs/operators';
import { takeWhileInclusive } from 'rxjs-take-while-inclusive';

export interface IAutoCompleteScrollEvent {
  autoComplete: MatAutocomplete;
  scrollEvent: Event;
}

@Directive({
  selector: 'mat-autocomplete[optionsScroll]'
})
export class OptionsScrollDirective implements OnDestroy {

  @Input() thresholdPercent = .8;
  @Output('optionsScroll') scroll = new EventEmitter<IAutoCompleteScrollEvent>();
  _onDestroy = new Subject();

  constructor(public autoComplete: MatAutocomplete) {
    this.autoComplete.opened.pipe(
      tap(() => {
        // Note: When autocomplete raises opened, panel is not yet created (by Overlay)
        // Note: The panel will be available on next tick
        // Note: The panel wil NOT open if there are no options to display
        setTimeout(() => {
          // Note: remove listner just for safety, in case the close event is skipped.
          this.removeScrollEventListener();
          this.autoComplete.panel.nativeElement
            .addEventListener('scroll', this.onScroll.bind(this))
        });
      }),
      takeUntil(this._onDestroy)).subscribe();

    this.autoComplete.closed.pipe(
      tap(() => this.removeScrollEventListener()),
      takeUntil(this._onDestroy)).subscribe();
  }

  private removeScrollEventListener() {
    this.autoComplete.panel.nativeElement
      .removeEventListener('scroll', this.onScroll);
  }

  ngOnDestroy() {
    this._onDestroy.next();
    this._onDestroy.complete();

    this.removeScrollEventListener();
  }

  onScroll(event: Event) {

    if (this.thresholdPercent === undefined) {
      this.scroll.next({ autoComplete: this.autoComplete, scrollEvent: event });
    } else {
      const threshold = this.thresholdPercent * 100 * event.target.scrollHeight / 100;
      const current = event.target.scrollTop + event.target.clientHeight;

      //console.log(`scroll ${current}, threshold: ${threshold}`)
      if (current > threshold) {
        //console.log('load next page');
        this.scroll.next({ autoComplete: this.autoComplete, scrollEvent: event });
      }
    }
  }
}

之后剩下的就是当滚动条达到 80% 阈值时从服务器加载更多数据:

import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable, fromEvent, of, Subject, merge, combineLatest } from 'rxjs';
import { map, startWith, switchMap, tap, debounceTime, filter, scan, withLatestFrom, mergeMap, takeUntil, takeWhile, distinctUntilChanged, skipUntil, exhaustMap, endWith } from 'rxjs/operators';
import { MatAutocomplete } from '@angular/material/autocomplete';
import { takeWhileInclusive } from 'rxjs-take-while-inclusive';

export interface ILookup {
  id: number,
  name: string
}
@Component({
  selector: 'autocomplete-filter-example',
  templateUrl: 'autocomplete-filter-example.html',
  styleUrls: ['autocomplete-filter-example.scss'],
})
export class AutocompleteFilterExample implements OnInit {

  searchText = new FormControl({ id: 2, name: 'ana' });
  filteredLookups$: Observable<ILookup[]>;
  private lookups: ILookup[] = [];
  private nextPage$ = new Subject();
  private _onDestroy = new Subject();

  // Fake backend api
  private getProducts(startsWith: string, page: number): Observable<ILookup[]> {
    console.log(`api call filter: ${startsWith}`);

    const take = 10;
    const skip = page > 0 ? (page - 1) * take : 0;

    const filtered = this.lookups
      .filter(option => option.name.toLowerCase().startsWith(startsWith.toLowerCase()))

    console.log(`skip: ${skip}, take: ${take}`);

    return of(filtered.slice(skip, skip + take));
  }

  ngOnInit() {

    // Note: Generate some mock data
    this.lookups = [{ id: 1994, name: 'ana' }, { id: 1989, name: 'narcis' }]
    for (let i = 1; i < 100; i++) {
      this.lookups.push({ id: i, name: 'test' + i })
    }

    // Note: listen for search text changes
    const filter$ = this.searchText.valueChanges.pipe(
      startWith(''),
      debounceTime(200),
      // Note: If the option valye is bound to object, after selecting the option
      // Note: the value will change from string to {}. We want to perform search 
      // Note: only when the type is string (no match)
      filter(q => typeof q === "string"));

    // Note: There are 2 stream here: the search text changes stream and the nextPage$ (raised by directive at 80% scroll)
    // Note: On every search text change, we issue a backend request starting the first page
    // Note: While the backend is processing our request we ignore any other NextPage emitts (exhaustMap).
    // Note: If in this time the search text changes, we don't need those results anymore (switchMap)
    this.filteredLookups$ = filter$.pipe(
      switchMap(filter => {
        //Note: Reset the page with every new seach text
        let currentPage = 1;
        return this.nextPage$.pipe(
          startWith(currentPage),
          //Note: Until the backend responds, ignore NextPage requests.
          exhaustMap(_ => this.getProducts(filter, currentPage)),
          tap(() => currentPage++),
          //Note: This is a custom operator because we also need the last emitted value.
          //Note: Stop if there are no more pages, or no results at all for the current search text.
          takeWhileInclusive(p => p.length > 0),
          scan((allProducts, newProducts) => allProducts.concat(newProducts), []),
        );
      })); // Note: We let asyncPipe subscribe.

  }

  displayWith(lookup) {
    return lookup ? lookup.name : null;
  }

  onScroll() {
    //Note: This is called multiple times after the scroll has reached the 80% threshold position.
    this.nextPage$.next();
  }

  ngOnDestroy() {
    this._onDestroy.next();
    this._onDestroy.complete();
  }
}

注意:我正在使用自定义 rxjs 运算符 rxjs-take-while-inclusive。

您可以在这里看到它的实际效果:DEMO

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Angular Material 6 中用于自动完成的无限滚动 的相关文章

  • 为什么内部 DIV 会溢出到外部 DIV 之外?

    我已经远离 HTML 和 CSS 很久了 找不到这个简单问题的解决方案 我有一个 div 位于另一个 div 中 外层黑色 内层橙色 我的 HTML 和 CSS 是 outer position fixed width 30 height
  • 如何使用 Spring Boot 传输音频

    我想让用户能够播放声音 我的实现在 Firefox 上运行良好 在 Safari 上 不播放声音 我验证了音频控制可以在 Safari 中与其他网站一起使用 所以 我认为我必须更改控制器中的某些内容 控制器 RequestMapping v
  • 为某个时刻添加持续时间 (moment.js)

    时刻版本 2 0 0 阅读文档后 http momentjs com docs manipulating add 我认为这很简单 Chrome 控制台 var timestring1 2013 05 09T00 00 00Z var tim
  • 如何使用 HTML 和 CSS 在我的网站上显示社交图标?

    我拼命尝试向我的网站添加一些图标 但无法让它们正确显示 我想要的只是一个包含图像并在线显示它们的容器 然后我想在该容器和图像之间添加一些填充 仅此而已 看这里我的方法 如果有人可以帮助我并纠正我的代码 使其真正有效 我会非常高兴 在我的 H
  • NodeJS - 将相对路径转换为绝对路径

    In my 文件系统我的工作目录在这里 C temp a b c d 在 b bb 下有文件 tmp txt C temp a b bb tmp txt 如果我想从工作目录转到该文件 我将使用以下路径 bb tmp txt 如果该文件不存在
  • 等待异步 grunt 任务完成

    我收到了 grunt 设置 其中一个新任务应该执行 grunt task run 已经存在的任务 要执行的任务是异步的 新任务应该等待异步任务完成 执行此操作的首选方法是什么 grunt 已经涵盖了这一点 你应该将你的任务声明为异步任务 并
  • 如何跨多个 React Redux 组件使用 requestAnimationFrame 实现游戏循环?

    努力思考最好的解决办法 我可以使用递归调用requestAnimationFrame有一个游戏循环 export interface Props name string points number onIncrement gt void o
  • JS:修改 JS 对象中的值/对

    我正在尝试找出修改对象的最佳方法 而无需三次写出类似的对象 所以我有这三个对象 var object1 start start end end type 1 var object2 start start end end type 2 va
  • 当 Chrome 中嵌套滚动中的数据更改时防止页面滚动

    我在页面中有一个固定大小的元素 带有 溢出 滚动 其内容经常更改 我预计该元素内部发生的更改会影响该元素的滚动 但不会影响页面滚动 但是当这个元素位于页面顶部时 页面本身开始滚动 我怎样才能防止这种情况发生 要重现此行为 我在 chrome
  • CSS以两种颜色显示一个字符[重复]

    这个问题在这里已经有答案了 css中是否可以用两种颜色制作单个字符 我的意思是例如字符 B 上半部分为红色 下半部分为蓝色 h1 font size 72px background webkit linear gradient red 49
  • 即使我的情况按预期发生变化,Angular ngClass 也不会更新我的课程

    我正在创建的模板中有类似的内容 div class nng 3 div 价值app layout isNavbarFixed等用零或一初始化 并且页面第一次加载时 适当的类被插入到我的div 不过 此后通
  • 浮动CSS属性导致父div不继承高度?

    我在 div 中有一个元素设置为float right但是 它会导致最外面的 div 不环绕 这是jsfiddle http jsfiddle net W792X 5 for it 我试图让提交按钮在 div 内浮动 但设置该属性似乎会导致
  • chrome 选项卡/窗口中的 window.open 行为

    我有一小段 javascript 旨在打开两个或更多选项卡 这在 FF 和 IE 中工作正常 但 chrome 会在新窗口而不是选项卡中打开第二个窗口 它不依赖于 url 因为我已经尝试过使用两个相同的 url 第一个在选项卡中打开 第二个
  • WebpackError:ReferenceError:Gatsby 上未定义窗口

    我已经在互联网上进行了大量搜索 但无法解决这个问题 我正在使用 Gasby 开发静态页面 但遇到此错误 WebpackError ReferenceError window is not defined 我的线索是 这与我正在使用的引导 模
  • 如何在不使用额外组件的情况下在多个地方重用 HTML

    我目前正在尝试国际化菜单以显示要显示的列 我意识到我可以使用以下内容来自定义其文本
  • 如何计算一行中Flexbox项目的数量?

    网格是使用 CSS flexbox 实现的 Example http jsbin com jumosicasi edit html css js output 本示例中的行数为 4 因为我出于演示目的固定了容器宽度 但是 实际上 它可以根据
  • 有序 JSON 对象

    我有一个 servlet 它与数据库通信 然后返回有序 按时间排序 对象的列表 在servlet部分 我有 access DB returns a list of User objects ordered ArrayList users M
  • Radiobutton-带有纯 html/css 的按钮

    是否可以创建像这样的单选按钮JQuery http jqueryui com demos button radio那些 用纯html css thanks 试试这个小提琴 http jsfiddle net mcXm7 1 http jsf
  • YouTube 点击时禁用 HTML5

    有没有办法让我们通过javascript禁用HTML5视频的 播放 暂停 点击全屏 功能 然后在我们再次需要时将其放回去 我不知道你是否可以禁用它们 但你可以使用 css 删除它们 video webkit media controls f
  • 如何强制下载图片?

    我的页面上有一个动态生成的图像 如下所示 img src 我不想告诉我的用户右键单击图像并点击保存 而是想公开一个下载链接 单击该链接将提示下载图像 如何实现这一目标 最初我在 js 中尝试这样做 var path my image att

随机推荐

  • 为什么 Collections.Counter 没有对称差异?

    因此 对于集合 您可以执行对称差 这相当于并减交集 为什么 是 Counter 对象不受支持的操作数 而并集和交集仍然有效 扩展我的评论 结果发现它当时被讨论过 但被拒绝了 单击完整消息 及其主题 的链接 我将引用 Raymond Hett
  • 如何移动正交图中的点?

    我正在尝试在迈克 博斯托克创建的以下地图上的某些地理位置添加红点 https bl ocks org mbostock 3795040 我的点会显示 但不会随地图移动 如何编辑代码以使点随地图移动 谢谢 add circles to svg
  • 如何使用javascript从mysql数据库获取数据?

    如果可能的话 如何使用 javascript 从 mysql 数据库获取 并发布 数据 我知道我可以使用 php 和其他语言 但我只需要知道这是否可以用 javascript 实现 提前致谢 这对于 Javascript 来说是不可能的 我
  • 如何响应 UNUserNotification 上的点击?

    我正在使用新的UNUserNotificationiOS 10 中的框架 我可以看到如何添加操作按钮 但是当用户点击通知本身时我如何响应 就我而言 它将是带有一些文本的图像 默认行为是应用程序打开 我可以使用自定义代码来检测我的应用程序是否
  • 如何在pyodbc中使用executemany运行多个SELECT查询

    我使用 PYODBC 根据 pandas 数据帧列的值多次查询 SQL DB 如下所示为值列表 因为我使用 ToList 函数将该列转换为列表 the connection string cnxn pyodbc connect driver
  • 扫描随机数量的浮点数,直到 C 中出现新行

    我正在尝试从包含以下文本的文件中读取 502 601 596 465 464597 599 600 598602 591 596 601588 565 548 260 62 61 595583 595 61 558 561237 241 4
  • 分析 C# 中的方法以了解其运行时间

    我需要获取计时报告以了解在类中运行 C 方法需要多长时间 我考虑使用profiler要做到这一点 输入是类中方法的名称 输出是 什么方法 类调用这个方法 运行该方法的时间量 有哪些工具 商业产品可用于 Visual Studio 2010
  • 在 TypoScript 中获取 FlexForm 配置

    我需要从 pi flexform 获取 typescript 中的 page headerData 如何实现我的要求 page PAGE page headerData 10 TEXT 10 value 我不太确定你真正需要什么 我是gue
  • SlidingDrawer 动画速度

    我是 Android 编程和堆栈溢出的新手 我需要减慢应用程序中 SlidingDrawer 的动画速度 我已经像这样子类化了 SlidingDrawer import android content Context import andr
  • 最大化两个数组元素的乘积之和的算法

    竞赛中有一个问题需要计算仅包含数学和生物科目的班级的表现 所以 没有 数学学生 n 没有 的生物学生 每个学生都有一个单独的分数 数学学生和生物学生的分数分别存储在数组 mathScore 和 bioScore 中 全班成绩计算如下 mat
  • 从存储过程填充 DataGridView

    我使用 SQL Server 2008 创建了一个名为 MyStoreProc 的存储过程 它在管理工具中运行良好 在 VB Net 2008 中 我创建了一个新的数据集和一个新的 TableAdaptor 在此表适配器中 我创建了一个名为
  • 如何从树状数组创建 ul - li 菜单?

    我有一个数组title and children index title始终不为空 children是一个数组 空或非空 Any children have title and children等等 myArray 0 gt title g
  • JTable右键复制/粘贴菜单一键复制单元格数据

    我创建了我的JPopupMenu 它出现在我的JTable当我右键单击一个单元格时 但是 我无法复制单元格中的数据 除非我首先双击然后突出显示数据 然后右键单击当前单元格以外的任何位置以显示弹出菜单和复制选项 我想复制单元格中的数据 而不必
  • Perl - 子例程“Hash::Merge::merge”的深度递归

    下列的this问题 我在那里使用了答案 也发布在这里 现在我失败了 我知道失败可能来自于 return bless self gt merge left right class left 但我不明白可能是什么问题 My code usr b
  • 使用 Windows 服务和 SQL Server 在 OneWay WCF 消息中排队

    我需要为 WCF 服务请求实现一个排队机制 该服务将由客户端以单向方式调用 这些请求消息应存储在 SQL Server 数据库中 并且 Windows 服务对消息进行排队 处理请求的时间是可配置的 如果处理消息时发生错误 则需要重试最多10
  • MySQL 5.7 错误(1093:您无法在 FROM 子句中指定目标表 ___ 进行更新) - 通常的解决方案不起作用

    我有一个表 员工 我试图将一些属性 例如薪水 设置为与表中其他值相同的值 我对这个错误的理解是 可以通过以下解决方法来避免它 使用临时表 UPDATE employees SET salary SELECT salary FROM SELE
  • 当使用非虚拟析构函数“删除”基类时,Clang 和 GCC 会做什么?

    已经有一个问题询问 现实世界 的行为delete指向缺少虚拟析构函数的基类的指针 但问题仅限于非常有限的情况 派生类没有具有非平凡析构函数的成员 并且接受的答案只是说没有办法知道不检查每个编译器的行为 但这实际上并不是很有帮助 知道每个编译
  • authorize.net json返回额外字符

    我有这个代码 ch curl init curl setopt ch CURLOPT URL url curl setopt ch CURLOPT RETURNTRANSFER 1 curl setopt ch CURLOPT HTTPHE
  • Laravel 5 如何在保存时验证每个活动下的唯一客户名称

    我有三个模型 活动模型 客户模型和客户项目模型 如何在商店功能中进行验证检查 使每个活动中的客户名称应该是唯一的 以下是每个迁移文件 活动模型 public function up Schema create activities func
  • Angular Material 6 中用于自动完成的无限滚动

    我正在尝试在 Angular Material 6 中实现自动完成的无限滚动 我的场景很简单 我有一个启用了自动完成功能的输入字段 当用户键入时 我将使用输入字段中的文本进行 HTTP 调用 以将结果显示为建议 但我只想显示 25 条建议