【问题标题】:How to instantiate an Observable of an array of objects before valuechanges?如何在值更改之前实例化对象数组的 Observable?
【发布时间】:2019-05-09 01:05:51
【问题描述】:

我正在关注Autocomplete Angular Material documentation(v5 设置单独的控制和显示值),但在加载数据时遇到了一些问题。

我遇到的问题是,在用户开始输入之前,我的代码中的过滤选项不会被填充。我需要在用户开始输入之前显示列表。我相信这是因为他们正在使用 valueChanges,但我不太确定如何让这段代码适合我的情况。我需要这个管道和地图功能,但我需要在用户更改 Mat Autocomplete 的值之前加载列表,而不是之后。

取自文档链接的代码或多或少反映在我自己的代码中:

<form class="example-form">
  <mat-form-field class="example-full-width">
    <input type="text" placeholder="Assignee" aria-label="Assignee" matInput [formControl]="myControl" [matAutocomplete]="auto">
    <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
      <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
        {{ option.name }}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>
filteredOptions: Observable<User[]>;

  ngOnInit() {
    this.filteredOptions = this.myControl.valueChanges
      .pipe(
        startWith<string | User>(''),
        map(value => typeof value === 'string' ? value : value.name),
        map(name => name ? this.filter(name) : this.categoryList.slice())
      );
  }

  filter(name: string): User[] {
    return this.categoryList.filter(option =>
      option.name.toLowerCase().indexOf(name.toLowerCase()) === 0);
  }

  displayFn(user?: User): string | undefined {
    return user ? user.name : undefined;
  }
this.categoryList = Object.entries(data.category).map(([key, value]) => ({ key, value }));

我在想我可能应该使用某种订阅,但我无法弄清楚将它们与管道和地图一起使用的语法。

【问题讨论】:

  • 你在哪里订阅可观察的?
  • 您不需要订阅。我查看了您发送的链接,这里有一个完整的堆栈闪电战示例stackblitz.com/angular/… 收到焦点后,它会显示列表。根据您向我们展示的内容,我看不出有任何明显的错误。但也许你在 html 方面遗漏了一些东西。如果您发布整个示例,我们可以更好地了解可能导致它的原因。
  • @EduardoVargas 我相信它是通过异步管道完成的。我还将包含该示例的 html 代码。
  • @ccamac 我更新了代码。示例和我的代码之间的唯一区别是它们实例化“选项”数组的方式。我的选项数组是 ngOnInit 期间 API 调用后填充的“categoryList”数组。
  • @masu9 你说得对,订阅是通过异步管道完成的。至于您的 categoryList 数组,在我看来这可能是问题所在。我无法通过您此时发布的内容来判断,但该数组应该是一个空数组(即 [])以启动,然后在 api 调用中 subscribe 将其值重新分配给返回的内容。如果你这样做,它应该与示例完全相同。

标签: angular rxjs angular-material observable


【解决方案1】:

我相信你需要做的就是startWith表单的值:

  ngOnInit() {
    this.filteredOptions = this.myControl.valueChanges
      .pipe(
        startWith(this.myControl.value as string), // typing the control's value
        map(name => name ? this.filter(name) : this.categoryList.slice())
      );
  }

请注意,在运算符内部使用类方法有点反模式。在使用 Observable 模式时,保持事物纯净通常很重要——尤其是对于更复杂的逻辑。

要解决这个问题,您可能希望将您的 categoryList 设为 Observable,并将您的 filter 设为纯函数(将其置于组件之外,并为其提供另一个参数,以便您可以传入 @987654325 @)。

function filter(name: string, categoryList: User[]): User[] {
  categoryList.filter(option =>
      option.name.toLowerCase().indexOf(name.toLowerCase()) === 0);
}

那么你可以这样做:

  ngOnInit() {
    this.filteredOptions = combineLatest(
      this.myControl.valueChanges.pipe(startWith(this.myControl.value as string)),
      this.categoryList
    ).pipe(
      map(([searchTerm, categoryList]) =>
        searchTerm ? filter(searchTerm, categoryList) : [ ...categoryList ])
    );
  }

【讨论】:

  • 我也在尝试使用 startWith 值,但我也无法让它工作。无论我在这里放什么,它都不会改变任何东西,我认为是因为在用户开始输入之前它不会被触发。 (值变化)。这让我回到了最初的问题,我应该改用订阅吗?
  • 您的其他建议涉及将我的 categoryList 更改为 Observable。如何将我从 API 获取的数据分配给这个 Observable?我假设您的意思与filteredOptions 相同。可观察的
  • 很难说不知道发生了什么。如果您使用的是 httpClient,您可以将其通过管道传输到您的 observable 中:categoryList$ = this.http.get&lt;User[]&gt;('endpoint')。否则,如果值是静态的,则可以使用 of;如果您希望值发生变化,则可以使用 Subject/BehaviorSubject
  • 如上所述,异步管道将为您订阅该 observable。我对 CDK 自动完成不是很熟悉,所以不确定 API 中是否有某些东西会停止在空输入上显示选项......?如果您开始输入然后删除所有文本,是否会显示选项?
  • 如果我开始输入,然后删除所有文本,那么选项仍然会显示。嗯,是的,我猜异步管道会处理可观察的......我真的只是简单地订阅我的 API 调用,然后将数据分配给我的 categoryList,如我上面的代码所示。不知道是什么导致数据在没有用户输入的情况下不显示:(
【解决方案2】:

不敢相信这么简单,但这个答案帮助解决了所有问题。我所要做的就是将我的代码移到 ngOnInit 之外。 how to show autocomplete options on focus

我的过滤选项没有等待 API 数据通过,因为我已将其放入 ngOnInit。一旦我将它移到我的 api 调用的订阅中,一切都很顺利。

【讨论】:

    猜你喜欢
    • 2021-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-06
    • 2014-10-31
    • 1970-01-01
    • 1970-01-01
    • 2019-11-18
    相关资源
    最近更新 更多