RXJS Остановить распространение наблюдаемой цепи, если выполнено определенное условие
Вступление
Я пытаюсь создать маршрутную охрану в Angular2+
используя Observable из общего сервиса, который содержит строковое значение текущей роли пользователя.
Проблема, очевидно, заключается в том, чтобы переключить мой разум с Обещаний на Наблюдаемые.
То, что я сделал до сих пор, основано на эвристике и методе проб и ошибок, но я упал в стену, убив браузер. Решено благодаря danday74
,
Попытка (улучшена благодаря @danday74)
С помощью последовательности RxJS, эквивалентной обещанию.then()? я перевел то, что я хочу сделать в эту цепочку:
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | boolean {
return this.auth.isRoleAuthenticated(route.data.roles)
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.of(true) : this.auth.isRole(Roles.DEFAULT_USER);
})
.do((isDefaultUser: boolean) => {
const redirectUrl: string = isDefaultUser ? 'SOMEWHERE' : 'SOMEWHERE_ELSE';
this.router.navigate([redirectUrl]);
})
.map((isDefaultUser: boolean) => {
return false;
});
}
Вопрос
Как остановить дальнейшее распространение наблюдаемой цепи, если isRoleAuthenticated = true
? Мне нужно вернуть это логическое значение, если такое условие выполнено, и убедиться, что .do
блок оператора не вызывается после.
Ограничением является то, что логическое значение должно быть возвращено из canActivate
охранник.
3 ответа
Все, что вы возвращаете из первого mergeMap, передается во второй mergeMap, поэтому дальнейшее распространение не будет остановлено. Если вы хотите остановить распространение, используйте фильтр (хотя это может привести к зависанию в этом сценарии).
Вы используете mergeMap только тогда, когда возвращаете Observable, но нет необходимости возвращать Observable из 2-го mergeMap. Просто делать:
.mergeMap((isRoleAuthenticated: boolean) => {
if (isRoleAuthenticated) {
return Observable.of(true)
}
return this.auth.isRole(Roles.DEFAULT_USER)
})
.tap((isDefaultUser: boolean) => {
if (isDefaultUser) {
this.router.navigate(['SOMEWHERE'])
} else {
this.router.navigate(['SOMEWHERE_ELSE'])
}
})
.map((isDefaultUser: boolean) => {
return false
})
Кроме того, вы используете синтаксис RxJs v5, но должны использовать v6. В v6 операторы - mergeMap, tap, map - разделяются запятой в трубе.
Возможно, навигация маршрутизатора препятствует окончательному возврату и вызывает зависание? Прокомментируйте это и посмотрите, остановит ли это зависание.
Не уверен, что это полностью исправит вашу проблему, но, надеюсь, некоторые полезные идеи для 1 утра
Я предполагаю, что эти возвращаемые Observables:
- this.auth.isRoleAuthenticated
- this.auth.isRole (Roles.DEFAULT_USER)
Если этого не произойдет, у вас возникнут проблемы.
Решение
Вместо того, чтобы сосредоточиться на остановке цепочки, вы можете создать объект, состоящий из результатов собранных Observables, и распространять его дальше, что решает проблему:
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> | boolean {
return this.auth.getRole()
.mergeMap((role: string) => {
return this.auth.isRoleAuthorized(route.data.roles)
.map((authorized: boolean) => ({
role: role,
authorized: authorized
}));
})
.do((markers: { role: string, authorized: boolean }) => {
const redirectUrl: string = markers.role === Roles.DEFAULT_USER ? 'SOMEWHERE' : 'SOMEWHERE_ELSE';
this.router.navigate([redirectUrl]);
})
.map((markers: { role: string, authorized: boolean }) => {
return markers.authorized;
});
}
do()
оператор работает только с next
уведомления, так что если вы не хотите обрабатывать то, что приходит после .mergeMap
Вы можете отфильтровать это с filter()
:
return this.auth.isRoleAuthenticated(route.data.roles)
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.of(true) : this.auth.isRole(Roles.DEFAULT_USER);
})
.filter(authenticated => authenticated !== true)
Тем не менее, похоже, что вы могли бы просто вернуться Observable.empty()
вместо Observable.of(true)
потому что это будет только излучать complete
уведомление и нет next
предметы, так что нечего будет передавать do()
:
.mergeMap((isRoleAuthenticated: boolean) => {
return isRoleAuthenticated ? Observable.empty() : this.auth.isRole(Roles.DEFAULT_USER);
})
Вместо возвращения Observable.of(true)
, вы можете вернуться Observable.empty()
, который будет просто завершен, не испуская никаких значений. Таким образом, следующие цепочки не будут выполнены.