#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?
Или я должен полностью изменить свой код?
Чего я хочу добиться, так это:
- установите для свойства loading значение true
- вызовите kandidatuurService.checkIfOndernemingsnummerAlreadyExists
- на основе этого результата установите свойство isDuplicate
- когда это не повторяющийся вызов requestService.selectOnderneming
- когда все закончится, установите для свойства 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, потому что он мне нужен впоследствии внутри пользовательского интерфейса