【问题标题】:Check if object in array of objects exists against user input in RxJS HTTP Request检查对象数组中的对象是否存在于 RxJS HTTP 请求中的用户输入
【发布时间】:2021-05-30 14:13:24
【问题描述】:

通过 RxJS 使用数据/动作流,如果用户输入了无效的托架编号,我想返回/传递一个错误。如果它与用户输入的数字匹配,我的代码目前将返回一个托架对象,但是如果用户输入我的列表中不存在的无效托架编号,我无法弄清楚如何引发错误海湾

  1. 为了在多个组件/页面之间共享数据,我在 BayService 类中完成了大部分工作:
  private baysUrl = 'api/bays';

  bays$ = this.http.get<Bay[]>(this.baysUrl)
    .pipe(
      tap(data => console.log('Bays: ', JSON.stringify(data))),
      catchError(this.handleError)
    );

  /*--------------------------------------------------------------*/
  // Grab A Single Bay
  private baySelectedSubject = new BehaviorSubject<number>(0);
  baySelectedAction$ = this.baySelectedSubject.asObservable();

  selectedBay$ = combineLatest([
    this.bays$,
    this.baySelectedAction$
  ])
    .pipe(
      map(([bays, selectedBayNumber]) =>
        bays.find(bay => bay.bayCode === selectedBayNumber)
      ),
    );

  selectedBayChanged(selectedBayNumber: number): void {
    this.baySelectedSubject.next(selectedBayNumber);
  }
  1. 我通过创建一个 BehaviorSubject 创建了一个动作流。然后我创建了一个方法来向我的 Action 流发出一个值。然后我在 bay.page.ts 中调用此方法,并在其中传递输入参数,然后将其发送到我的数据流。
  2. 然后我将我的数据流和我的动作流组合起来,然后返回一个与我的动作流中的值相匹配的bay object。
  3. ISSUE:所以,我已经可以将用户输入的值发送到我的 observable 中,并根据数字返回 bay 对象,如下所示:
onSubmit() {
     this.bayDoesNotExistError = false;
      this.bayService.selectedBayChanged(this.bayForm.get('bayStart').value);
      this.navCtrl.navigateForward([`/results/`]);
      this.bayForm.reset();
    }

,但是如何处理无效号码?例如,当用户在输入数字后按下提交按钮时,如何检查它是否对我的 observable 无效,然后将某些内容返回给我的组件以显示在 UI 上?目前,我在客户端检查用户输入的内容然后显示错误,但我需要实际检查输入值是否确实存在于我的 Observable 对象中,如果不存在,则返回错误或其他内容返回到我的 bay.page.ts 文件以显示在我的 HTML 中。

对不起,如果我没有很好地解释这一点,因为我正在努力弄清楚如何措辞。

这是我的 BayService: BayService.ts

这是我的 Bay.page.ts:Bay.page.ts

【问题讨论】:

  • 嗨,Donny,您可以编辑帖子以在页面中包含代码吗? (而不是图片)
  • @bsheps 好的,我添加了我的代码!如您所见,它将处理成功的用户输入 (1-3) 并将显示它,因为我只创建了 3 个 Bay Objects 并将向前导航。但我想首先检查该号码是否是有效的托架号码,如果不是,以某种方式将错误传递回我的 bay.page.ts 文件并将其显示在我的 gui 上?希望这会有所帮助

标签: angular rxjs rxjs6 rxjs-observables rxjs-pipeable-operators


【解决方案1】:

我认为最简单的解决方案是为您的响应创建一个包装器。

export interface BaySelectionResponse {
 type: BayResponseType;
 message: string;
 bay: Bay;
}

export enum BayResponseType {
 ERR = "Bay Error",
 OK = "Success",
 NOT_IN_LIST = "BAY NOT FOUND IN LIST"
}

通过这种方式,您可以适当地分离职责。

您的服务将根据响应分配状态和消息,您的页面可以决定如何实现显示结果。

使用这种方法,您甚至可以根据您的服务返回的任何状态,使用适当的模板将结果显示分成自己的组件。

示例

服务

首先,让我们创建一个服务,它的作用是检查我们的远程是否有可用数据:

@Injectable({providedIn: 'root'})
export class ApiLookupService {
}

首先,我们需要一个方法来简单地查询我们的遥控器以获取所有可能的值。我已经使用 rxjs of 函数 see here 模拟了这一点

  mockNumbersEndpoint() {
    return of([1, 3, 7, 9, 13])
  }

其次,我们需要一个方法,该方法将接受来自消费者(即我们的路由组件,它将利用我们的服务)的输入,该方法可以检查针对给定输入值的响应。

  checkValidity(input: number): Observable<ValidityResponseModel> {
    // pipe the response from the api, and switch it into a ValidityResponseModel
    return this.mockNumbersEndpoint().pipe(
      switchMap(apiResponse => {
        const validity = {} as ValidityResponseModel;
        // check if the user's input is valid: Exists in response from server
        if (apiResponse.some(number => input == number)) {
          return of(this.createValidityResponse(
            `Your chosen value ${input} is available`,
            ValidityResponseTypes.SUCCESS,
            input
          ));
        }
        // if our code reaches this point, it means we did not find the users input
        return of(this.createValidityResponse(
          `Your chosen value ${input} is NOT available`,
          ValidityResponseTypes.INVALID,
          input
        ));
      }),
      // we have now left the level of the 'switch' and are back in the pipe.
      // We include catchError to handle if any error is thrown, such as network issues.
      catchError(error => {
        return of(this.createValidityResponse(
          "Something went wrong with your request",
          ValidityResponseTypes.ERR,
          input
        ));
      })
    )
  }

最后,我添加了一个实用函数,它将响应重组为ValidityResponseType

  createValidityResponse(message: string, responseType: ValidityResponseTypes, response: number): ValidityResponseModel {
    return {
      message,
      responseType,
      response
    } as ValidityResponseModel
  }

完整的服务列表

@Injectable({providedIn: 'root'})
export class ApiLookupService {

  mockNumbersEndpoint() {
    return of([1, 3, 7, 9, 13])
  }

  checkValidity(input: number): Observable<ValidityResponseModel> {
    return this.mockNumbersEndpoint().pipe(
      switchMap(apiResponse => {
        const validity = {} as ValidityResponseModel;
        if (apiResponse.some(number => input == number)) {
          return of(this.createValidityResponse(
            `Your chosen value ${input} is available`,
            ValidityResponseTypes.SUCCESS,
            input
          ));

        }
        return of(this.createValidityResponse(
          `Your chosen value ${input} is NOT available`,
          ValidityResponseTypes.INVALID,
          input
        ));
      }),
      catchError(error => {
        return of(this.createValidityResponse(
          "Something went wrong with your request",
          ValidityResponseTypes.ERR,
          input
        ));
      })
    )
  }

  createValidityResponse(message: string, responseType: ValidityResponseTypes, response: number): ValidityResponseModel {
    return {
      message,
      responseType,
      response
    } as ValidityResponseModel
  }
  
}

使用服务的组件

由于在最初的问题中,您在用户按 Enter 时执行检查,因此我选择实现一个表单。

组件

@Component({
  templateUrl: './demo.component.html',
  styleUrls: ['./demo.component.scss']
})

export class DemoComponent implements OnInit {
  group: FormGroup;
  responseType = ValidityResponseTypes;
  validityResponse!: ValidityResponseModel;

  constructor(private fb: FormBuilder, private service: ApiLookupService) {
    this.group = fb.group({
      input: fb.control(0, [Validators.minLength(1)])
    })
  }

  ngOnInit(): void {
  }

  // ngSubmit.
  formSubmittal() {
    let v = this.group.get('input')?.value;
    if (this.group.dirty && this.group.valid) {
      this.group.reset();
      this.checkValidity(v);
    }
  }
  // pipe and take(1) to avoid having to manually unsubscribe.
  checkValidity(num: number) {
    this.service.checkValidity(num).pipe(take(1)).subscribe(responseFromApi => {
      // logic based on response here
      this.validityResponse = responseFromApi;
    })
  }
}

模板

<h2>demonstration</h2>

<div class="method">
  <!-- ngSubmit will capture the Enter key event. -->
  <form [formGroup]="group" (ngSubmit)="formSubmittal()">
    <input type="number" formControlName="input">
  </form>
</div>

<div class="result" [ngClass]="{'success': validityResponse?.responseType == responseType.SUCCESS, 'invalid': validityResponse?.responseType == responseType.INVALID}">
  {{validityResponse?.message}}
</div>

一旦我们从服务中获得响应,我们就会根据响应类型设置样式,并在服务中显示为我们准备的消息。

【讨论】:

  • 我想我对如何将它实现到我的 observable 中感到困惑。这就是我正在经历的斗争。我有一个客户端输入,我在其中抓取一个动作流(BehaviorSubject),以便将其发送到我的 Observable。如果他们输入正确的数字,它会完美运行。但是如果他们对我的可观察对象输入了一个无效的数字,我需要返回一些东西说,bay.page.ts 文件,以便我可以处理它并显示在我的 GUI 上?我不知道如何处理,或者我只是对如何处理它感到困惑。我添加了查看和图片的代码
  • 您需要订阅使用它的组件内部的baySelectedSubject。
  • 好的,但是在我的 BayService 类中,如果它无法根据用户输入找到指定的托架,我是否需要有一个:CatchError?因为我希望代码在找不到该托架编号时抛出错误,然后在我可以使用它的 bay.page.ts 文件中处理该错误。
  • @Donnygroezinger 我已经更新了我的答案以反映一个完整的例子
猜你喜欢
  • 2020-11-30
  • 1970-01-01
  • 1970-01-01
  • 2017-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多