How can I make a ngrx effects that gets the current state of email and send that to a AuthApiService if it succedes?

huangapple go评论65阅读模式
英文:

How can I make a ngrx effects that gets the current state of email and send that to a AuthApiService if it succedes?

问题

我已经为您翻译好了代码部分:

@Injectable()
export class AuthEffects {
  constructor(
    private readonly actions$: Actions,
    private readonly store: Store,
    private readonly authApiService: AuthApiService,
    private router: Router,
  ) {}

  revokeTokenAtLogout$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(AuthActions.logoutInit),
      concatLatestFrom(() => this.store.select(selectEmail)),
      switchMap(([, email]) =>
        this.authApiService.revokeToken(email).pipe(
          map(() => AuthActions.logoutSuccess()),
          catchError(error => of(AuthActions.logoutFailed({ error })))
        ),
      ),
    );
  });

  logoutNavigate$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(AuthActions.logoutSuccess),
      tap(() => {
        this.router.navigate(['login']).then(() => window.location.reload());
      })
    );
  }, { dispatch: false })
}

希望这对您有所帮助。如果您有任何其他问题,请随时提问。

英文:

I am using Angular 16 with Google Identity and google one tap, to make users being able to login:<br>
https://developers.google.com/identity/gsi/web/guides/overview

Currently I am working on the logout feature of the application, where I want to revoke the token in my .net backend. <br>
To do that I want to dispatch an action when the user does that.<br>

This is what I have tried:

This is revokeToken() from AuthApiService, where I send the information to the backend, which use the email (a string) to logout the user. It takes in a string email, which it should get when I dispatch the action.

revokeToken(email: string) {
    const header = new HttpHeaders().set(&#39;Content-type&#39;, &#39;application/json&#39;);
    return this.httpClient.delete(`api/v1/Auth/RevokeToken/` + email, { headers: header, withCredentials: true });
}

Therefore, I have created actions:

import { createActionGroup, emptyProps, props } from &#39;@ngrx/store&#39;;
import { HttpErrorResponse } from &#39;@angular/common/http&#39;;

export const AuthActions = createActionGroup({
  source: &#39;Auth&#39;,
  events: {
    &#39;Logout Init&#39;: emptyProps(),
    &#39;Logout Success&#39;: emptyProps(),
    &#39;Logout Failed&#39;: props&lt;{ error: HttpErrorResponse }&gt;(),
  },
});

and reducers to implement this:

import { createFeature, createReducer, on } from &#39;@ngrx/store&#39;;
import { AuthActions } from &#39;./auth.actions&#39;;
import { LoadingState } from &#39;../../shared/models/loading-state.model&#39;;

export interface AuthState {
  token: string;
  email: string;
  fullName: string;
}

export const initialState: AuthState = {
  token: &#39;&#39;,
  email: &#39;&#39;,
  fullName: &#39;&#39;,
};

export const authFeature = createFeature({
  name: &#39;auth&#39;,
  reducer: createReducer(
    initialState,

    on(AuthActions.logoutInit, (state) =&gt; ({
      ...state,
      ...LoadingState.loading(),
    })),
    on(AuthActions.logoutSuccess, (state) =&gt; ({
      ...state,
      token: &#39;&#39;,
      email: &#39;&#39;,
      fullName: &#39;&#39;,
      ...LoadingState.loaded(),
    })),
    on(AuthActions.logoutFailed, (state, { error }) =&gt; ({
      ...state,
      ...LoadingState.failed(error),
    })),
  )
})

and I now need the effect. This is what I have tried:

revokeTokenAtLogout$ = createEffect(() =&gt; {
  return this.actions$.pipe(
    ofType(AuthActions.logoutInit),
    concatLatestFrom(() =&gt; this.store.select(selectEmail)),
    map(([action, email]) =&gt; {
      this.authApiService.revokeToken(email).pipe(
        AuthActions.logoutSuccess(),
        catchError(error =&gt; of(AuthActions.logoutFailed({error})))
      )
    })
   )
})

Which I know does not work.

I know what it should do though.

  1. It must listen on logoutInit,
  2. then get the current state of email from the store,
  3. then give that to the revokeToken as we call it,
  4. if it succeedes, logout and navigate back to the login screen.
  5. if it fails, return the logoutFailed action.

I am hopelessy stuck with it and been looking at it for days..<br>

What I need help with is how to implement the effect correctly, so that it works as intended.
Can anybody help me with this?<br>

EDIT:<br>
The solution for this problem is:<br>

@Injectable()
export class AuthEffects {
  constructor(
    private readonly actions$: Actions,
    private readonly store: Store,
    private readonly authApiService: AuthApiService,
    private router: Router,
  ) {}

  revokeTokenAtLogout$ = createEffect(() =&gt; {
    return this.actions$.pipe(
      ofType(AuthActions.logoutInit),
      concatLatestFrom(() =&gt; this.store.select(selectEmail)),
      switchMap(([, email]) =&gt;
        this.authApiService.revokeToken(email).pipe(
          map(() =&gt; AuthActions.logoutSuccess()),
          catchError(error =&gt; of(AuthActions.logoutFailed({ error })))
        ),
      ),
    );
  });

  logoutNavigate$ = createEffect(() =&gt; {
    return this.actions$.pipe(
      ofType(AuthActions.logoutSuccess),
      tap(() =&gt; {
        this.router.navigate([&#39;login&#39;]).then(() =&gt; window.location.reload());
      })
    );
  }, { dispatch: false })
}

答案1

得分: 2

当提出问题时,请定义“不起作用”是什么意思。
因为对我来说,代码与描述相符:

revokeTokenAtLogout$ = createEffect(() => {
  return this.actions$.pipe(
    ofType(AuthActions.logoutInit),
    concatLatestFrom(() => this.store.select(selectEmail)),
    map(([action, email]) => {
      this.authApiService.revokeToken(email).pipe(
        map(() => AuthActions.logoutSuccess()),
        catchError(error => of(AuthActions.logoutFailed({error})))
      )
    })
  )
})

我们看不到但可能影响结果的因素有:

  • email 属性未设置为一个值
  • 在 reducer 中的 LoadingState.abc() 方法背后的逻辑
  • HTTP 响应
  • 存储如何注册
英文:

When asking questions please define "does not work".
Because, to me, the code matches the description:

revokeTokenAtLogout$ = createEffect(() =&gt; {
  return this.actions$.pipe(
// ✅ It must listen on logoutInit,
    ofType(AuthActions.logoutInit),
// ✅ then get the current state of email from the store,    concatLatestFrom(() =&gt; this.store.select(selectEmail)),
    map(([action, email]) =&gt; {
// ✅ then give that to the revokeToken as we call it,
      this.authApiService.revokeToken(email).pipe(
// ✅ if it succeedes, logout and navigate back to the login screen., 
// ⚠️ but wrap it in a `map`
        map(() =&gt; AuthActions.logoutSuccess()),
// ✅ if it fails, return the logoutFailed action.
        catchError(error =&gt; of(AuthActions.logoutFailed({error})))
      )
    })
   )
})

What we don't see but can affect the outcome:

  • the email property is not set to a value
  • the logic behind the LoadingState.abc() methods in the reducer
  • the http response
  • how the store is registered

huangapple
  • 本文由 发表于 2023年7月10日 20:04:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76653553.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定