2016-10-04 13 views
13

Próbujesz obsłużyć scenariusz logowania OAuth, w przypadku gdy użytkownik znajdzie się na stronie z authorization_code w ciągu zapytania, przetwarzamy token i kontynuujemy lub, jeśli znajdą się na stronie bez tego, my sprawdź pamięć lokalną pod kątem istniejącego tokena, upewnij się, że jest nadal poprawny i przekieruj do logowania lub kontynuuj, na podstawie jego ważności.Subskrypcje subskrypcji Angular2 Query Dwa razy

Problem polega na tym, że gdy sprawdzamy istnienie parametru ciągu zapytań o wartości authorization_code, subskrypcja jest uruchamiana dwa razy. Za pierwszym razem, gdy jest pusty, po raz drugi ma poprawną wartość w słowniku.

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); 
     }); 
    } 
} 

ten kod wyjścia: output

Plunker (trzeba ją wysunąć do nowego okna i dodać ciąg kwerendy ?test=test).

Pytania

  1. Czy coś robię źle, aby zwolnić dwa razy?
  2. Nie mogę po prostu zignorować pustego obiektu warunkowo, ponieważ jest to scenariusz, w którym musimy zweryfikować istniejący token autoryzacji - czy jest inny sposób podejścia, który nie jest kompletnym hackerem?
+0

Ciekawym skręcają ... kiedy jest dostarczany bez znaków zapytania, subskrypcja odpala tylko raz. – lukiffer

+1

Przetestowałem to jako [https://embed.plnkr.co/XgCauzZdTSqqokCdFWCi/?test=test](https://embed.plnkr.co/XgCauzZdTSqqokCdFWCi/?test=test) i nie wydarzyło się nic nieoczekiwanego! –

+0

@A_Singh testujesz niewłaściwy adres URL, aby uzyskać tylko pierwszą pustą wartość – yurzui

Odpowiedz

12

Router obserwable (jak inna odpowiedź wskazuje) are BehaviorSubject subjects różnią się one od zwykłych RxJS Subject lub kątowa 2 EventEmitter się tym, że mają wartość początkowa popychany do sekwencji (pusty obiektu w przypadku queryParams).

Pożądana jest ogólnie możliwość subskrybowania za pomocą logiki inicjalizacyjnej.

Wartość początkową można pominąć za pomocą operatora skip.

this._route.queryParams 
.skip(1) 
.subscribe(params => ...); 

Ale bardziej naturalny sposób obsłużyć to jest odfiltrować wszystkie istotne params (początkowa params należy do tej kategorii). Duplikaty wartości authorization_code można także filtrować za pomocą operatora distinctUntilChanged, aby uniknąć niepotrzebnych wywołań do zaplecza.

this._route.queryParams 
.filter(params => 'authorization_code' in params) 
.map(params => params.authorization_code) 
.distinctUntilChanged() 
.subscribe(authCode => ...); 

Zauważ, że kątowa 2 importuje ograniczoną ilość RxJS operatorów (przynajmniej map w przypadku @angular/router). Jeśli nie zostanie użyty pełny pakiet rxjs/Rx, może być konieczne zaimportowanie dodatkowych operatorów (filter, distinctUntilChanged), które są używane z import 'rxjs/add/operator/<operator_name>'.

+0

+1 za dotarcie do źródła i wyjaśnienie problemu. Ale odpowiedź nie rozwiązuje problemu, ponieważ brak kodu authorization_code musi wysłać go na inną ścieżkę kodu. – lukiffer

+0

Pytanie nie jest wystarczająco jasne, w jaki sposób można oczekiwać obsługi przekierowań. W każdym razie sądzę, że pierwszy fragment kodu obejmuje to, co jest najbardziej idiotyczne dla RxJS. BehaviorTheubject powoduje dodatkową wartość, a skip (1) ją eliminuje. – estus

+1

W pierwszym przykładzie, jeśli nie ma zapytańParams pierwszy "pusty" obiekt zostanie pominięty i blok subskrypcji nigdy nie zostanie wywołany. – Failpunk

2

Sądzę, że jest to zgodne z projektem.

queryParams jest BehaviorSubject

Jak widać w docs

Jednym z wariantów jest BehaviorSubject Przedmioty, które ma pojęcie „wartości bieżącej”. Przechowuje on najnowszą wartość emitowaną do klientów o wartości , a za każdym razem, gdy nowy obserwator subskrybuje, natychmiast otrzyma "aktualną wartość" z BehaviorSubject.

Jako obejście można użyć debounceTime operatora następująco:

import 'rxjs/add/operator/debounceTime'; 

this._route.queryParams 
    .debounceTime(200) 
    .subscribe(params => { 
    console.log(params); 
    }); 
+0

Działa to przez większość czasu, ale nie podoba mi się możliwość przekroczenia limitu czasu debounceTime. Trzeba poradzić sobie z tym parametrem zapytania niezależnie od strony, na której się znajdują, a przy różnych czasach ładowania to nie działa. – lukiffer

+0

To nie jest dobre rozwiązanie, ponieważ opiera się na arbitralnych wartościach czasowych, które spowalniają aplikację, lub całkowicie ją zepsuć, jeśli czas nie zostanie osiągnięty. – Doughy

-1

Wystarczy użyć Location klasę uzyskać wstępną URL UrlSerializer klasy do analizowania URL UrlTree uzyskać params zapytania.

+0

Chociaż to nie rozwiązuje problemu technicznego, który miałem z 'Observable ' to rozwiązuje dla naszego konkretnego przypadku użycia. – lukiffer

+1

Czy możesz pokazać przykład, proszę – TeodorKolev

+2

Jak to jest zaakceptowana odpowiedź? – Riscie

0

jeśli pójdziesz do tego linka https://dev-hubs.github.io/ReactiveXHub/#/operators/conditional/skipUntil

1) Kopiuj Wklej ten kod w edytorze kodu.

/* 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) Hit Run Przycisk

Zobaczysz, że będzie dwukrotnie ostrzegany, jeden bezpośrednio na abonament i drugim BCz ostatniej linii.

Obecny wynik nie jest źle, to jest to filozofię RX „operatorów zdziała” można odnośnika to drzewo decyzyjne, aby zobaczyć operatorowi szukasz http://reactivex.io/documentation/operators.html#tree zwykle wykorzystać pominąć (1)

4

najlepszym sposobem, aby przezwyciężyć ten był subskrypcji zdarzeń routera i przetwarzanie zapytań params tylko po trasie jest zaznaczone na navigated stanie:

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; 
    } 
Powiązane problemy