【问题标题】:Implementing an asynchronous sorting pipe in Angular 2在 Angular 2 中实现异步排序管道
【发布时间】:2016-03-10 22:15:43
【问题描述】:

我正在尝试在 Angular 2 中创建一个自定义管道,它将对一组对象进行排序。我从this post 获得了一些帮助。但是,我似乎无法正常工作。

我的管道是这样的:

@Pipe({
  name: "orderByAsync",
  pure: false
})
export class AsyncArrayOrderByPipe  {
  private _promise : Promise<Array<Object>>;
  private _output: Array<Object>;
 
  transform(promise: Promise<Array<Object>>, args: any): Array<Object>{
    var _property : string = "";
    var _descending : boolean = false;

    this._property = args[0]["property"] || "";
    this._descending = args[0]["descending"] || false;

    if(!this._promise) {
      this._promise = promise.then((result) => {
        result.sort((a: any, b: any) => {
          if (a[this._property] < b[this._property])  return (this._descending ? 1: -1);
          else if (a[this._property] > b[this._property]) return (this._descending ? -1: 1);
          else return 0;
        });
    
        this._output = result;
      });
    }

    return this._output;
  }
}

管道的使用如下所示:

<div *ngFor="#c of countries | orderByAsync">{{c.name}}</div>

就像从未通知视图承诺已解决并且数据已返回。

我错过了什么?

【问题讨论】:

  • 能否请您创建一个快速 bin 以便可以播放 sn-p。

标签: angular


【解决方案1】:

内置的async 管道注入ChangeDetectorRef 并在promise 解决时调用markForCheck()。要在一个管道中完成所有操作,您应该遵循该示例。您可以查看该 here 的 Typescript 源代码。

但是,我建议忘记自己处理异步,而是编写一个纯无状态排序管道并将其与内置的 async 管道链接。为此,您将编写管道来处理裸露的Array,而不是承诺,并像这样使用它:

<div *ngFor="#c of countries | async | orderBy">{{c.name}}</div>

【讨论】:

  • 我最初尝试过这个;但是,我遇到了问题——我认为是因为在承诺解决之前调用了我的管道,所以我的 array.sort 抛出了一个错误。也许我只需要处理一个空数组就可以解决已解析数组的延迟问题。我会试一试。
  • @RHarris async 管道在 Promise 解决之前返回 null,因此您的管道需要处理 null 而不会出现错误链接才能工作。
  • @Douglas 感谢nullhint.. 我注意到了这种行为,但我认为我犯了一个错误,因此得到了null.. 但当然这是不希望的行为尚未解决的承诺;)
  • @MarcBorni 在 github 上快速搜索找到它。链接已更新。
  • @MarcBorni 对于这种设计,您需要编写 loadAsyncData 管道以始终返回 Promise。如果不需要加载额外的数据,您可以返回一个已经完成的 Promise,但它必须始终是一个 Promise,因为这是 async 管道处理的。进行该更改后,您的语法将是正确的。
【解决方案2】:

只需从管道中返回一个 BehaviorSubject,然后它就可以与角度异步管道绑定。

小例子(把它放在你管道的转换方法中)应该在 3 秒后给你“价值”:

const sub = new BehaviorSubject(null);
setTimeout(() => { sub.next('value'); }, 3000);
return sub;

完整示例:

import { IOption } from 'somewhere';
import { FormsReflector } from './../forms.reflector';
import { BehaviorSubject } from 'rxjs';
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'getOptions' })
export class GetOptionsPipe implements PipeTransform  {

  public transform(value, ...args: any[]) {
    const _subject = new BehaviorSubject('-');
    if (args.length !== 2) {
      throw `getOptions pipe needs 2 arguments, use it like this: {{ 2 | getOptions:contract:'contractType' | async }}`;
    }
    const model = args[0];
    if (typeof model !== 'object') {
      throw `First argument on getOptions pipe needs to be the model, use it like this: {{ 2 | getOptions:contract:'contractType' | async }}`;
    }
    const propertyName = args[1];
    if (typeof propertyName !== 'string') {
      throw `Second argument on getOptions pipe needs to be the property to look for, ` +
        `use it like this: {{ 2 | getOptions:contract:'contractType' | async }}`;
    }
    const reflector = new FormsReflector(model);
    reflector.resolveOption(propertyName, value)
    .then((options: IOption) => {
      _subject.next(options.label);
    })
    .catch((err) => {
      throw 'getOptions pipe fail: ' + err;
    });
    return _subject;
  }
}

【讨论】:

  • 嘿,伙计,你不能把一些代码放在上下文之外。没有关于FormsReflector 的信息,尽管我可能猜到它的功能。为了您的灵感,+1。
  • 是的,所以重要的是您应该从管道中返回一个 BehaviorSubject。因此,您可以随时填充该值,它会得到反映: const sub = new BehaviorSubject(null); setTimeout(() => { sub.next('value'); }, 3000);返回子;这是您需要的简短版本。管道将它们的值从一个推到另一个。然后可以将 BehaviorSubject 传送到 angular 的异步管道。
  • 这个答案非常酷,但我想大多数人不会明白它有多酷!我在这里使用了这种方法 - stackoverflow.com/a/67732691/1205871 - 这可能有助于人们了解这有多酷!
猜你喜欢
  • 2017-05-05
  • 2017-12-11
  • 2017-07-28
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
  • 2017-10-11
  • 2017-03-27
  • 2019-03-07
相关资源
最近更新 更多