英文:
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('Content-type', 'application/json');
return this.httpClient.delete(`api/v1/Auth/RevokeToken/` + email, { headers: header, withCredentials: true });
}
Therefore, I have created actions:
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { HttpErrorResponse } from '@angular/common/http';
export const AuthActions = createActionGroup({
source: 'Auth',
events: {
'Logout Init': emptyProps(),
'Logout Success': emptyProps(),
'Logout Failed': props<{ error: HttpErrorResponse }>(),
},
});
and reducers to implement this:
import { createFeature, createReducer, on } from '@ngrx/store';
import { AuthActions } from './auth.actions';
import { LoadingState } from '../../shared/models/loading-state.model';
export interface AuthState {
token: string;
email: string;
fullName: string;
}
export const initialState: AuthState = {
token: '',
email: '',
fullName: '',
};
export const authFeature = createFeature({
name: 'auth',
reducer: createReducer(
initialState,
on(AuthActions.logoutInit, (state) => ({
...state,
...LoadingState.loading(),
})),
on(AuthActions.logoutSuccess, (state) => ({
...state,
token: '',
email: '',
fullName: '',
...LoadingState.loaded(),
})),
on(AuthActions.logoutFailed, (state, { error }) => ({
...state,
...LoadingState.failed(error),
})),
)
})
and I now need the effect. This is what I have tried:
revokeTokenAtLogout$ = createEffect(() => {
return this.actions$.pipe(
ofType(AuthActions.logoutInit),
concatLatestFrom(() => this.store.select(selectEmail)),
map(([action, email]) => {
this.authApiService.revokeToken(email).pipe(
AuthActions.logoutSuccess(),
catchError(error => of(AuthActions.logoutFailed({error})))
)
})
)
})
Which I know does not work.
I know what it should do though.
- It must listen on logoutInit,
- then get the current state of email from the store,
- then give that to the revokeToken as we call it,
- if it succeedes, logout and navigate back to the login screen.
- 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(() => {
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 })
}
答案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(() => {
return this.actions$.pipe(
// ✅ It must listen on logoutInit,
ofType(AuthActions.logoutInit),
// ✅ then get the current state of email from the store, concatLatestFrom(() => this.store.select(selectEmail)),
map(([action, email]) => {
// ✅ 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(() => AuthActions.logoutSuccess()),
// ✅ if it fails, return the logoutFailed action.
catchError(error => 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论