Как связать эти методы?

#angular #rxjs

#angular #rxjs

Вопрос:

Я создал метод обслуживания, в котором я могу проверить, существует ли данный идентификатор компании уже в моей базе данных … этот метод прост, возвращая true или false:

     public checkIfOndernemingsnummerAlreadyExists(ondernemingsnummer: string): Observable<boolean> {
        const url = `${environment.apiUrl}/ondernemingen/exists/${ondernemingsnummer}`;
        return this.http.get<boolean>(url);
    }
  

Теперь я создал своего рода мастер, в котором пользователь может создать новую компанию за определенные шаги.

На первом этапе пользователь должен ввести идентификационный номер компании. Переходя к следующему шагу, я хотел бы проверить, существует ли этот заданный идентификационный номер уже в моей базе данных.

Если он не существует, пользователь может перейти к следующему шагу внутри мастера. Если он существует, должно появиться предупреждающее сообщение, и пользователь должен просто остаться на текущем шаге мастера.

Итак, я добавил следующий код при отправке идентификатора компании на первом шаге:

 public submitOnderneming(): void {
    this.loading = false;
    this.isDuplicate = false;

    of(true).pipe(
      tap(() => this.loading = true),
      switchMap(() => this.kandidatuurService.checkIfOndernemingsnummerAlreadyExists(this.onderneming.ondernemingsnummer)),
      tap((res) => { this.isDuplicate = res; }),
      switchMap(() => { if (this.isDuplicate !== true) { return this.requestService.selectOnderneming(this.onderneming); }})
    ).subscribe(v => { this.loading = false; this.ref.detectChanges(); });
  }
  

Но это дает мне следующую ошибку:

Вы указали ‘undefined’ там, где ожидался поток. Вы можете предоставить Observable, Promise, Array или Iterable .

Теперь я предполагаю, что это потому, что внутри 2-го switchMap я возвращаю значение только тогда, когда isDuplicate неверно. В другом случае я ничего не возвращаю.

Мой requestService.selectOnderneming выглядит так:

     public selectOnderneming(onderneming: OndernemingDetail): Observable<RequestSteps> {
        this.store.dispatch(new SetOndernemingOfRequest(onderneming));
        return this.gotoNextStep();
    }
  

Итак, что должно быть записано внутри switchMap, когда isDuplicate имеет значение true?
Или я должен полностью изменить свой код?

Чего я хочу добиться, так это:

  1. установите для свойства loading значение true
  2. вызовите kandidatuurService.checkIfOndernemingsnummerAlreadyExists
  3. на основе этого результата установите свойство isDuplicate
  4. когда это не повторяющийся вызов requestService.selectOnderneming
  5. когда все закончится, установите для свойства loading значение false

Ответ №1:

Может быть, есть лучшее решение, но, надеюсь, для рабочей версии:

 this.loading = true;
this.kandidatuurService.checkIfOndernemingsnummerAlreadyExists(this.onderneming.ondernemingsnummer)
 .pipe(tap((isDuplicate) => this.isDuplicate = isDuplicate),
       switchMap((isDuplicate) => (isDuplicate ? of(true) : this.requestService.selectOnderneming(this.onderneming))))
 .subscribe(v => { this.loading = false; this.ref.detectChanges(); });
  

Комментарии:

1. Это выдает мне ошибку: InvalidPipeArgument: ‘true’ для канала ‘AsyncPipe’

2. Вы правы, ваш код работает правильно. Ошибка на моей стороне генерируется строкой this.ref.DetectChanges();

3. Внутри моего пользовательского интерфейса у меня есть этот div: <div class=»spinner» *ngIf=»loading»> Но мой пользовательский интерфейс не обновляется, поэтому я использовал метод DetectChanges(). Но теперь это DetectChanges вызывает проблемы, и пользовательский интерфейс по-прежнему не обновляется

4. Хорошо, я понял. Просто нужно было изменить (дублировать? из(true) : this.requestService.selectOnderneming(this.onderneming) в (дублируется? of(RequestSteps) : this.requestService.selectOnderneming(this.onderneming) теперь все работает нормально. Спасибо за помощь!

5. Попробуйте изменить свою версию следующим образом stackblitz.com/edit/angular-pipe-and-ui

Ответ №2:

Нет необходимости в двух картах переключения, реактивная история начинается с первого запроса, вы можете сделать это началом вашей цепочки. Отфильтруйте ответы, которые являются дубликатами, затем переключите карту, и это должно быть так.

Редактировать: как написано ниже, filter следует позаботиться о loading флаге при возврате false, потому что больше ни у кого не будет такой возможности.

 this.loading = true;

this.kandidatuurService.checkIfOndernemingsnummerAlreadyExists(this.onderneming.ondernemingsnummer)
    .pipe(
        filter(
            //... logic checking for duplicates here
        ),
        switchMap(() => this.requestService.selectOnderneming(this.onderneming))
    )
    .subscribe( // subscribers here
  

Комментарии:

1. Я бы предположил, что он должен был выполнить часть подписки также в случае дублирования, подразумевается checkIfOndernemingsnummerAlreadyExists , что всегда будет выдаваться только одно дублирующее значение или без дублирования. @tompynation: каким образом он ведет себя?

2. После фильтрации он будет уменьшен до нуля или единицы.

3. Да, так что в нулевом случае подписка не будет выполняться при наложении загрузки или что-либо еще не будет отключено, или я ошибаюсь? oO

4. @J. Knabenschuh вы правы… окончательная подписка всегда должна выполняться, поэтому свойство загрузки получает значение false.

Ответ №3:

Хорошо, я реализовал предложение следующим образом:

     this.loading = true;

    this.kandidatuurService.checkIfOndernemingsnummerAlreadyExists(this.onderneming.ondernemingsnummer)
      .pipe(
        tap((isDuplicate) => this.isDuplicate = isDuplicate),
        filter(x => x !== true),
        switchMap(() => this.requestService.selectOnderneming(this.onderneming))
      ).subscribe(v => { this.loading = false; this.ref.detectChanges(); });
  

но теперь окончательная подписка не выполняется, когда она является дубликатом… Это не совсем то, чего я хотел…

Комментарии:

1. Хорошо, каково ожидаемое поведение, когда это дубликат?

2. почему вы сохраняете isDuplicate это?

3. Вы можете отменить loading флаг здесь: tap((isDuplicate) => { this.isDuplicate = isDuplicate); this.loading = this.loading amp;amp; !isDuplicate; }

4. Я сохраняю дубликат isDuplicate, потому что он мне нужен впоследствии внутри пользовательского интерфейса