简体   繁体   English

使用来自对话框的输入订阅可观察的异步地图结果,使用地图的结果到路由

[英]Subscribe to observable, async map result with input from dialog, use result from map to route

I am calling an API-service which returns an Observable - containing an array of elements.我正在调用一个 API 服务,它返回一个 Observable - 包含一个元素数组。

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

From this I have been choosing the first element of the array, subscribing to that.从这里我一直在选择数组的第一个元素,订阅那个。 Then used that element to route to another page like this:然后使用该元素路由到另一个页面,如下所示:

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

This works just fine.这工作得很好。

Problem is, I do not want to just use the first element, I want a MatDialog, or other similar to pop up, and give the user option of which element to choose, and THEN route to the correct one.问题是,我不想只使用第一个元素,我想要一个 MatDialog 或其他类似的元素弹出,并为用户提供选择哪个元素的选项,然后路由到正确的元素。

If the list only contain one element though, the dialog should not show, and the user should be routed immediately.如果列表只包含一个元素,则不应显示对话框,并且应立即路由用户。

I have tried to open a dialog in the .pipe(map()) function, but the subscribe() happens before I get answer from the user, causing it to fail.我试图在.pipe(map())函数中打开一个对话框,但是subscribe()发生在我从用户那里得到答案之前,导致它失败。 And I am not sure if that even is the correct approach.我不确定这是否是正确的方法。 How would any of you solve this problem?你们中的任何人将如何解决这个问题?

I would solve it using the following method:我将使用以下方法解决它:

  1. Get the data with your subscribe (without the pipe).通过订阅获取数据(不使用管道)。 And save this data in the component variable并将这些数据保存在组件变量中
options: any;

this.apiService
  .apiMethod(input)
  .subscribe(
    (result) => {
       if (result.length === 1) {
         this.router.navigate([result[0]]);
         return;
       }
       options = result;
  }
)
  1. with an ngIf on the modal (conditional of the length of the array of options > 0 display the component with the different choices when the data is received在模态上使用 ngIf(以选项数组的长度为条件 > 0,在接收到数据时显示具有不同选择的组件
<modal-component *ngIf="options.length > 0"></modal-component>
  1. when the user (click) on an option inside your modal, use the router to redirect.当用户(单击)模式中的选项时,使用路由器进行重定向。
html
<div (click)="redirect(value)">option 1</div>

ts
redirect(value) {
    this.router.navigate([value]);
}

That would be the most straight forward那将是最直接的

Two possible options:两种可能的选择:

  1. Having a subject for the selected result that is "nexted" either by user input or a side effect of getting an api result with one element.通过用户输入或通过一个元素获取 api 结果的副作用,为所选结果设置一个主题。
  2. Keeping track of an overall state of the component and responding appropriately whenever a selectedResult is set in the state.跟踪组件的整体状态并在状态中设置 selectedResult 时做出适当的响应。

The example below is a sketch of using an Observable to keep track of the component's state.下面的示例是使用 Observable 跟踪组件状态的草图。

  • There are two input streams into the state, the results from the api and the user input for the selected result.状态中有两个输入流,来自 api 的结果和所选结果的用户输入。
  • Each stream is converted into a reducer function that will modify the overall state.每个流都被转换为一个 reducer 函数,该函数将修改整体状态。
  • The UI should subscribe to this state via an async pipe, showing the modal when appropriate, and updating updating state from events via the Subjects. UI 应该通过异步管道订阅这个状态,在适当的时候显示模态,并通过主题从事件更新更新状态。
  • The redirection should come as an effect to the change of the state when selectedResult has a value.当 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));
}

I've been using this pattern a lot lately.我最近一直在使用这种模式。 It makes it pretty easy the number of actions and properties of the state grow.它使得状态的动作和属性的数量增长变得非常容易。

I would create a DialogComponent that takes in the list of choices as an input, and emits the chosen item when it's closed.我将创建一个DialogComponent ,它将选择列表作为输入,并在关闭时发出所选项目。

Then, create a helper method ( maybe call it promptUser ) that simply returns an observable that emits the selected value:然后,创建一个辅助方法(可能称为promptUser ), 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)
    );

Here we simply use switchMap to return an observable that emits the proper item.这里我们简单地使用switchMap来返回一个发出正确项目的 observable。 If the length is greater than 1, we return the helper method that displays the dialog and emits the chosen item, else just emit the first ( only ) item.如果长度大于 1,我们返回显示对话框并发出所选项目的辅助方法,否则只发出第一个(唯一)项目。 Notice that we wrapped plain value with of since within switchMap, we need to return observable.请注意,我们在 switchMap 中使用of包装了普通值,我们需要返回 observable。

In either case, the desired item is emitted and received by your subscribe callback.在任何一种情况下,您的订阅回调都会发出和接收所需的项目。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM