Angular2 Query Params подписывает огонь дважды
Попытка обработать сценарий входа в OAuth, где, если пользователь приземляется на странице с authorization_code
в строке запроса, мы обрабатываем токен и продолжаем или, если они приземляются на странице без этого, мы проверяем локальное хранилище на наличие существующего токена, убедитесь, что он по-прежнему действителен и либо перенаправлен на логин, либо продолжен, исходя из его действительности.
Проблема заключается в том, что если мы проверяем существование параметра строки запроса authorization_code
, подписка срабатывает дважды. В первый раз он пуст, второй раз он имеет правильное значение в словаре.
app.component.ts
export class App implements OnInit {
constructor(private _router: ActivatedRoute) {
}
public ngOnInit(): void {
console.log('INIT');
this._route.queryParams.subscribe(params => {
console.log(params);
});
}
}
Этот код выводит:
![output]()
Plunker (вам нужно вывести его в новое окно и добавить строку запроса ?test=test
).
Вопросы
- Есть ли что-то, что я делаю неправильно, чтобы сделать его дважды?
- Я не могу просто игнорировать пустой объект с условным, потому что сценарий, в котором нам нужно проверить существующий токен аутентификации - есть ли другой способ приблизиться к этому, что не является полным взломом?
Ответы
Ответ 1
Наблюдаемые на маршрутизаторе (как упоминается еще один ответ) являются BehaviorSubject
субъектами, они отличаются от обычного RxJS Subject
или Angular 2 EventEmitter
тем, что они имеют начальное значение, перенесенное в последовательность (пустой объект в случае queryParams
).
Как правило, желательна возможность подписки на логику инициализации.
Начальное значение можно пропустить с помощью оператора skip
.
this._route.queryParams
.skip(1)
.subscribe(params => ...);
Но более естественным способом справиться с этим является отфильтровать все нерелевантные параметры (начальная params
попадает в эту категорию). Дублирующие значения authorization_code
также могут быть отфильтрованы с помощью оператора distinctUntilChanged
, чтобы избежать ненужных вызовов на сервер.
this._route.queryParams
.filter(params => 'authorization_code' in params)
.map(params => params.authorization_code)
.distinctUntilChanged()
.subscribe(authCode => ...);
Обратите внимание, что Angular 2 импортирует ограниченное количество операторов RxJS (не менее map
в случае @angular/router
). Если полный пакет rxjs/Rx
не используется, может потребоваться импортировать дополнительные операторы (filter
, distinctUntilChanged
), которые используются с import 'rxjs/add/operator/<operator_name>'
.
Ответ 2
Лучший способ преодолеть это - подписывать события маршрутизатора и обрабатывать параметры запроса только после того, как маршрут отмечен как navigated
:
public doSomethingWithQueryParams(): Observable<any> {
let observer: Observer<any>;
const observable = new Observable(obs => observer = obs);
this.router.events.subscribe(evt => {
// this is an injected Router instance
if (this.router.navigated) {
Observable.from(this.activatedRoute.queryParams)
// some more processing here
.subscribe(json => {
observer.next(json);
observer.complete();
});
}
});
return observable;
}
Ответ 3
Я думаю, что по дизайну.
queryParams
- BehaviorSubject
Как вы можете видеть в docs
Одним из вариантов объектов является объект BehaviorSubject, который имеет понятие "текущее значение". В нем хранится последнее значение, испускаемое его потребителей, и всякий раз, когда подписывается новый Наблюдатель, он будет немедленно получите "текущее значение" из объекта BehaviorSubject.
В качестве обходного пути вы можете использовать оператор debounceTime
следующим образом:
import 'rxjs/add/operator/debounceTime';
this._route.queryParams
.debounceTime(200)
.subscribe(params => {
console.log(params);
});
Ответ 4
если вы перейдете по этой ссылке https://dev-hubs.github.io/ReactiveXHub/#/operators/conditional/skipUntil
1) Копировать Вставьте этот код в редактор кода.
/* since queryParams is a BehaviorSubject */
var queryParams = new Rx.BehaviorSubject();//this will AUTOMATICALLY alert 'undefined'
var subscription = queryParams.subscribe(
function (x) {
alert(x);
},
function (err) {
alert(err);
},
function () {
alert('Completed');
});
queryParams.onNext('yay');//this will cause to alert 'yay'
2) Нажмите кнопку Выполнить
Вы увидите, что вы будете предупреждать дважды, один непосредственно по подписке и второй bcz последней строки.
Текущий результат не является неправильным, то есть философия, лежащая в основе операторов Rx, приводит к тому, что все происходит ", вы можете найти это дерево решений, чтобы увидеть оператора, которого вы ищете http://reactivex.io/documentation/operators.html#tree
Обычно я использую skip (1)
Ответ 5
Просто используйте класс Location
, чтобы получить начальный url, UrlSerializer
класс для разбора url, UrlTree
, чтобы получить параметры запроса.