Как получить значение из Observable для использования для вызова API службы

У меня есть приложение Angular, использующее ngrx-store. У меня есть следующие файлы для моего компонента функции

<componentname>.actions.ts
<componentname>.effects.ts
<componentname>.model.ts
<componentname>.module.ts
<componentname>.reducer.ts
<componentname>.state.ts
<componentname>.selectors.ts
<componentname>-routing.module.ts

Я новичок в хранилище Observables и NGRX, и мне нужна помощь в получении значения (emailAddress) из хранилища для последующего использования в вызове API службы. В методе обслуживания я могу подписаться и записать значение в консоль, однако, когда выполняется вызов службы, значение остается пустым, поэтому я не получаю данные обратно.

Как я могу подписаться на селектор emailAddress и одновременно вызвать сервисный API, чтобы убедиться в наличии значения. Адрес электронной почты в магазине сохраняется только один раз, когда пользователь входит в систему. Значение никогда не меняется.

Мой компонент

import { selectStrava } from "@app/strava/strava.selector";
import { selectEmailAddress } from "@app/core/auth/auth.selectors";

@Component({
    selector: "srm-strava",
    templateUrl: "./strava.component.html",
    styleUrls: ["./strava.component.scss"],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class StravaComponent implements OnInit {
    @Input()
    strava$: Observable<Strava>;


    constructor(private stravaStore: Store<IStravaState>) {
        this.strava$ = this.stravaStore.pipe(select(selectStrava));
        }

    ngOnInit() {
        this.stravaStore.dispatch(new GetStravaAuthorization());
    }
}

Мои селекторы компонентов

import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromAppStore from "@app/core/auth/auth.reducer";
import { IStravaState } from './strava.state';

export const selectStravaState = createFeatureSelector<IStravaState>('strava');
export const state = createSelector(selectStravaState, (stravaState: IStravaState) => stravaState);
export const selectStrava = createSelector(state, (stravaState: IStravaState) => stravaState.strava);

Мой метод в моей службе API

constructor(http: HttpClient, notificationService: NotificationService, appState: Store<AppState>) {
        this.http = http;
        this.notificationService = notificationService;
        this.appState = appState;               
    }

    public getStravaAuthorization(): Observable<Strava> {    
        this.emailAddress$ = this.appState.pipe(select(selectEmailAddress));
        //the following outputs to the console OK
        this.emailAddress$.subscribe(res => { console.log(res) });            
        //the email address is blank on the next call
        let getStravaApi = `${AppSettings.CONTACTS_API_HOST}employee/strava?emailaddress=${this.emailAddress$}`;
        return this.http.get<Strava>(getStravaApi).pipe(
            tap(result => console.log('getStravaAccess: executed with email ')),
            map(result => result));            

    };

Мой эффект следующий

@Effect()
    getStravaAuthorization$ = this.actions$.pipe(
        ofType<GetStravaAuthorization>(StravaActionTypes.GetStravaAuthorization), mergeMap(() => this.stravaService.getStravaAuthorization()
            .pipe(map((strava: Strava) => new GetStravaAuthorizationSuccess(strava))))
    );

Селектор адреса электронной почты для получения значения из магазина:

export const selectEmailAddress = createSelector(
    selectAuth, (state: AuthState) => {
        if ((state.userDetails === null || state.userDetails === undefined))
            return "";
        else
            return state.userDetails.email
                ;
    }
);

Мой журнал консоли выглядит следующим образом

вывод console.log

С кодом, перемещенным из службы в компонент, как рекомендовано, теперь я получаю сообщение об ошибке this.emailAddress $ с указанием «не удалось выбрать перегрузку для« нового »несоответствия типа выражения. Параметр emailAddress должен иметь тип, присваиваемый строке, но он имеет тип Observable

Обновленный код компонента

import { Component, ChangeDetectionStrategy, OnInit, Input } from "@angular/core";
import { Observable } from "rxjs";
import { take } from "rxjs/operators";
import { Store, select } from "@ngrx/store";
import { GetStravaAuthorization } from "@app/strava/strava.actions";
import { Strava } from "@app/strava/strava.model";
import { IStravaState } from "@app/strava/strava.state"
import { AuthState } from "@app/core/auth/auth.model.ts";
import { AppState } from "@app/core/core.state.ts"
import { selectStrava } from "@app/strava/strava.selector";
import { selectEmailAddress } from "@app/core/auth/auth.selectors";

@Component({
    selector: "srm-strava",
    templateUrl: "./strava.component.html",
    styleUrls: ["./strava.component.scss"],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class StravaComponent implements OnInit {
    @Input()
    strava$: Observable<Strava>;
    @Input()
    emailAddress$: Observable<string>;

    constructor(private stravaStore: Store<IStravaState>, private appState: Store<AppState>) {
        this.strava$ = this.stravaStore.pipe(select(selectStrava));
    }

    ngOnInit() {
        this.emailAddress$ = this.appState.pipe(select(selectEmailAddress));
        this.stravaStore.dispatch(new GetStravaAuthorization(this.emailAddress$));
    }
}

Обновить код

Мой компонент

ngOnInit() {
        this.appState
            .pipe(
                select(selectEmailAddress),
                first()
            )
            .subscribe((emailAddress) => {
                this.stravaStore.dispatch(new GetStravaAuthorization(emailAddress)); //dispatch action with the payload containing email address
            });
    }

Мой эффект

@Effect()

    getStravaAuthorization$ = this.actions$
        .pipe(
            ofType<GetStravaAuthorization>(StravaActionTypes.GetStravaAuthorization),
            mergeMap((action) => {
                // passing the action's payload (email address) below to service

             return this.stravaService.getStravaAuthorization(action.payload);
            },
                map((strava: Strava) => new GetStravaAuthorizationSuccess(strava)))
        );

Моя служба

 public getStravaAuthorization(emailAddress): Observable<Strava> {
            let getStravaApi = `${AppSettings.CONTACTS_API_HOST}employee/strava?emailaddress=${emailAddress}`;
            return this.http.get<Strava>(getStravaApi).pipe(
                tap(result => console.log('getStravaAccess: executed with emaiL address ')),
                map(result => result));
        }

Действия

export class GetStravaAuthorization implements Action {
    readonly type = StravaActionTypes.GetStravaAuthorization;
    constructor(public payload: string) { }
}

export class GetStravaAuthorizationSuccess implements Action {
    readonly type = StravaActionTypes.GetStravaAuthorizationSuccess;
    constructor(public payload: Strava) { }
}

Еще кое-что, чтобы указать, что EmailAddress не является частью IStraviaState

import { Strava } from "@app/strava/strava.model";

export interface IStravaState {
    strava: Strava;
}

export const initialStravaState: IStravaState = {
    strava: null
};
export class Strava {
    stravaAuthorization: StravaAuthorization
}

export class StravaAuthorization {
    entityId: string;
    accessToken: string;
    refreshToken: string;
    isAuthorized: boolean;
}

Ошибки, которые я теперь вижу с обновленным кодом

Ошибка компонента

ошибка эффекта


person Jason Sargood    schedule 27.03.2019    source источник


Ответы (4)


Похоже, вы пытаетесь использовать Observable как строковое значение при создании запроса.

let getStravaApi = `${AppSettings.CONTACTS_API_HOST}employee/strava?emailaddress=${this.emailAddress$}`;`

Есть несколько способов добиться этого, я поделюсь маршрутом async / await.

Вы можете дождаться результата наблюдаемого, преобразовав его в обещание с помощью метода .toPromise().

public async getStravaAuthorization(): Observable<Strava> {
  ...
  const emailAddress = await this.emailAddress$.toPromise();
  ...
}
person Community    schedule 27.03.2019

Вы должны выбрать адрес электронной почты из эффекта getStravaAuthorization. Таким образом, вы отправляете: new GetStravaAuthorization () из вашего компонента без электронной почты, но эффект получает электронное письмо от вашего селектора, если оно не равно null или undefined, и передаёт его в нашу службу.

       @Effect()
        getStravaAuthorization$ = this.actions$.pipe(
            ofType<GetStravaAuthorization>(StravaActionTypes.GetStravaAuthorization),
                 switchMap(() => this.store.pipe(select(selectEmailAddress))), 
                 filter(Boolean), 
                 mergeMap((email: string) => this.stravaService.getStravaAuthorization(email)
                .pipe(map((strava: Strava) => new GetStravaAuthorizationSuccess(strava))))
    );
person ukn    schedule 27.03.2019

В строке ниже вы передаете this.emailAddress$, предполагая, что это ЗНАЧЕНИЕ адреса электронной почты, в то время как это Observable. Вот почему это не сработает.

let getStravaApi = `${AppSettings.CONTACTS_API_HOST}employee/strava?emailaddress=${this.emailAddress$}`;

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

public getStravaAuthorization(): Observable<Strava> {    
        return this.appState.pipe(
          select(selectEmailAddress),
          first(), // the subscription is immediately ended after retrieval
          mergeMap((emailAddress) => { // getting email address and chaining observables
            let getStravaApi = `${AppSettings.CONTACTS_API_HOST}employee/strava?emailaddress=${emailAddress}`;
            return this.http.get<Strava>(getStravaApi);

          }),
          tap(result => console.log('getStravaAccess: executed with email ')),
          map(result => result)); // you actually don't need this if you're not modifying result
        );
    };

ОТВЕТИТЕ НА ОБНОВЛЕННЫЙ КОМПОНЕНТ.

Убедитесь, что ваше действие поддерживает параметр с именем payload. Т.е. что-то вроде:

export class MyAction {
  readonly type = MY_TYPE;
  constructor(public payload: string) {}
}

СОСТАВНАЯ ЧАСТЬ:

export class StravaComponent implements OnInit {
    @Input()
    strava$: Observable<Strava>;
    @Input()
    emailAddress$: Observable<string>;

    constructor(private stravaStore: Store<IStravaState>, private appState: Store<AppState>) {
        this.strava$ = this.stravaStore.pipe(select(selectStrava));
    }

    ngOnInit() {
      this.appState
        .pipe(
           select(selectEmailAddress),
           first()
        )
        .subscribe((emailAddress) => {
          this.stravaStore.dispatch(new GetStravaAuthorization(emailAddress)); //dispatch action with the payload containing email address
        });
    }
}

ЭФФЕКТ:

  @Effect()
    getStravaAuthorization$ = this.actions$
    .pipe(
        ofType<GetStravaAuthorization>(StravaActionTypes.GetStravaAuthorization),
        mergeMap((action) => {
          // passing the action's payload (email address) below to service
          return this.stravaService.getStravaAuthorization(action.payload);
        },
        map((strava: Strava) => new GetStravaAuthorizationSuccess(strava)))
    );

УСЛУГА:

public getStravaAuthorization(emailAddress): Observable<Strava> {  
        // assigning the emailAdress to the url's param  
        let getStravaApi = `${AppSettings.CONTACTS_API_HOST}employee/strava?emailaddress=${emailAddress}`;
        return this.http.get<Strava>(getStravaApi).pipe(
            tap(result => console.log('getStravaAccess: executed with email ')),
            map(result => result));            

    };
person Muhammad Ahsan Ayaz    schedule 27.03.2019
comment
Спасибо. Я попробовал обновить, но все равно получил пустой адрес электронной почты. Я знаю, что тот же селектор адреса электронной почты работает в другом месте приложения, поскольку я показываю адрес электронной почты в меню навигации. Я обновил свой вопрос, чтобы включить обновленный код компонента, чтобы включить вашу рекомендацию, но теперь я вижу ошибку несоответствия типа? Если бы вы могли посоветовать, было бы здорово. - person Jason Sargood; 27.03.2019
comment
@JasonSargood Я добавил свой ответ в соответствии с вашим обновленным компонентом. - person Muhammad Ahsan Ayaz; 27.03.2019
comment
Большое спасибо. Обновил код, как было предложено, но все еще вижу некоторые ошибки. Обновили, а также добавили дополнительную информацию, надеясь, что это поможет - person Jason Sargood; 27.03.2019

Я смог решить, обновив компонент следующим образом

 ngOnInit() {
        this.emailAddress$ = this.authStore.pipe(select(selectEmailAddress));
        this.refreshToken$ = this.stravaStore.pipe(select(selectRefreshToken));        
        this.emailAddress$.pipe(
            skipUntil(this.emailAddress$)).subscribe(res => {
            if (res) {
                this.stravaStore.dispatch(new GetStravaAuthorization(res));
            }
        });               
    }
person Jason Sargood    schedule 27.03.2019