【问题标题】:Subscribe to observable, async map result with input from dialog, use result from map to route使用对话框输入订阅可观察的异步映射结果,使用映射结果路由
【发布时间】:2021-12-21 08:29:18
【问题描述】:

我正在调用一个 API 服务,它返回一个 Observable - 包含一个元素数组。

apiMethod(input: Input): Observable<ResultElement[]>

从这里我一直在选择数组的第一个元素,订阅它。然后使用该元素路由到另一个页面,如下所示:

this.apiService
  .apiMethod(input)
  .pipe(map((results) => results[0])
  .subscribe(
    (result) => {
      return this.router.navigate('elements/', result.id)
  }
)

这很好用。

问题是,我不想只使用第一个元素,我想要一个 MatDialog,或者其他类似的弹出,并给用户选择哪个元素的选项,然后路由到正确的元素.

如果列表只包含一个元素,则不应显示对话框,应立即路由用户。

我试图在.pipe(map()) 函数中打开一个对话框,但是subscribe() 在我得到用户的回答之前发生,导致它失败。而且我不确定这是否是正确的方法。你们中的任何人将如何解决这个问题?

编辑 最终完成了@BizzyBob 建议的部分工作:

在 API 调用中将 map 更改为 switchmap,如下所示:

this.apiService
  .apiMethod(input)
  .pipe(switchMap((results) => this.mapToSingle(results)
  .subscribe(
    (result) => {
      return this.router.navigate('elements/', result.id)
  }
)

mapToSingle(ResultElement[]) 是这样的:

private mapToSingle(results: ResultElement[]): Observable<ResultElement> {
  if (result.length === 1){
    return of(results[0]);
  }
    const dialogConfig = new MatDialogConfig<ResultElement[]>();
    dialogConfig.data = results;

    const dialogRef = this.dialog.open(ResultDialogComponent, dialogConfig);

    return dialogRef.afterClosed();
}

【问题讨论】:

    标签: javascript angular typescript rxjs angular-material


    【解决方案1】:

    我会创建一个DialogComponent,它将选项列表作为输入,并在关闭时发出所选项目。

    然后,创建一个辅助方法(可以称之为promptUser),它只返回一个发出所选值的可观察对象:

    this.apiService.apiMethod(input)
        .pipe(
            switchMap(results => results.length > 1
                ? this.promptUser(results)
                : of(results[0])
            )
        )
        .subscribe(
            result => this.router.navigate('elements/', result.id)
        );
    

    这里我们简单地使用switchMap 来返回一个发出正确项目的可观察对象。如果长度大于 1,我们返回显示对话框并发出所选项目的辅助方法,否则只发出第一个(only)项目。请注意,我们使用 of 包装了普通值,因为在 switchMap 中,我们需要返回 observable。

    在任何一种情况下,您的订阅回调都会发出和接收所需的项目。

    【讨论】:

    • 谢谢,我或多或少做过类似的事情,而且效果很好。
    【解决方案2】:

    两种可能的选择:

    1. 通过用户输入或使用一个元素获取 api 结果的副作用,为所选结果提供“下一个”主题。
    2. 跟踪组件的整体状态并在状态中设置 selectedResult 时做出适当的响应。

    下面的示例是使用 Observable 来跟踪组件状态的草图。

    • 有两个输入流进入状态,来自 api 的结果和所选结果的用户输入。
    • 每个流都被转换为一个 reducer 函数,该函数将修改整体状态。
    • UI 应通过异步管道订阅此状态,在适当时显示模式,并通过 Subjects 从事件更新更新状态。
    • 当 selectedResult 有值时,重定向应该会影响状态的变化。
    readonly getResultsSubject = new Subject<MyInput>();
    readonly resultSelectedSubject = new Subject<ResultType>();
    
    private readonly apiResults$ = this.getResultsSubjects.pipe(
      switchMap((input) => this.apiMethod(input))
    );
    
    readonly state = combineLatest([
      this.apiResults$.pipe(map(results => (s) => results.length === 1 
        ? { ...s, results, selectedResult: x[0], showModal: false }
        : { ...s, results, showModal: results.length > 1 })),
      this.resultSelectedSubject.pipe(map(selectedResult => (s) => ({ ...s, selectedResult })))
    ]).pipe(
      scan((s, reducer) => reducer(s), { }),
      shareReplay(1)
    );
    
    ngOnInit() {
      this.state.pipe(
        filter(x => !!x.selectedResult)
      ).subscribe(x => this.router.navigate('elements/', x.selectedResult.id));
    }
    

    我最近经常使用这种模式。它可以很容易地增加状态的动作和属性的数量。

    【讨论】:

    • 感谢您的帮助,这个解决方案可能更好地扩展,但我选择了另一种方法(参见编辑),这使得它更容易基于当前代码。
    【解决方案3】:

    我会使用以下方法解决:

    1. 通过订阅获取数据(不使用管道)。并将这些数据保存在组件变量中
    options: any;
    
    this.apiService
      .apiMethod(input)
      .subscribe(
        (result) => {
           if (result.length === 1) {
             this.router.navigate([result[0]]);
             return;
           }
           options = result;
      }
    )
    
    1. 在模式上带有 ngIf(以选项数组的长度为条件 > 0 在接收数据时显示具有不同选项的组件
    <modal-component *ngIf="options.length > 0"></modal-component>
    
    1. 当用户(点击)您的模态框内的一个选项时,使用路由器进行重定向。
    html
    <div (click)="redirect(value)">option 1</div>
    
    ts
    redirect(value) {
        this.router.navigate([value]);
    }
    

    这将是最直接的

    【讨论】:

    • 感谢您的回答,如果结果数组的长度等于 1,您可能错过了我不想要这些选项。但我想我可以在 subscribe() 块中处理这种情况的路由,即使它可能有点奇怪。
    • 在订阅中处理路由很好。检查代码,如果选项长度为 1,我将返回 return。这意味着您不会看到模态框。
    猜你喜欢
    • 2015-12-13
    • 2020-06-30
    • 1970-01-01
    • 2021-10-07
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    • 1970-01-01
    • 2018-12-21
    相关资源
    最近更新 更多