英文:
TypeError: Cannot read property 'pipe' of undefined Angular 14 with NGRX
问题
I receive the above error in my Effect, and I cant see why.
Here is my effect:
login$ = createEffect(
() =>
this.actions$.pipe(
ofType(fromAuthActions.login),
switchMap((auth) => {
return this.authService.doLogin(auth.email, auth.password)
.pipe(map((data) => fromAuthActions.loginComplete({ profile:
})
),
{ dispatch: false }
);
here are my two actions:
export const login = createAction('[Auth] login', props<{ email: string, password: string }>());
export const loginComplete = createAction(
'[Auth] loginComplete',
props<{ profile: LoggedInUser; isLoggedIn: boolean }>()
);
My reducer, where profile is declared LoggedInUser | null
on(authActions.loginComplete, (state, { profile, isLoggedIn }) => {
return {
...state,
profile,
isLoggedIn,
};
}),
and my Service that makes the call to the server (I get to the server and I can see via the Network tab in the browser, that the server is returning the correct data).
doLogin(email: string, password: string): any {
let user: LoginUser = {
email: email,
password: password,
};
var returnval = this.crud.post
returnval.subscribe((e: LoggedInUser) => { return e; });
}
I think my issue is that on my doLogin method my return type is any
and so in my effect data
passed back is of type any
but I'm strongly typing data to LoggedInUser
as that is what I really want. Also, I'm not sure about the Subscribe in my doLogin method, but if I don't have it there, then the server does not get called.
I am struggling to get this right. Any help greatly appreciated.
英文:
I receive the above error in my Effect, and I cant see why.
Here is my effect:
login$ = createEffect(
() =>
this.actions$.pipe(
ofType(fromAuthActions.login),
switchMap((auth) => {
return this.authService.doLogin(auth.email, auth.password)
.pipe(map((data) => fromAuthActions.loginComplete({ profile: <LoggedInUser>data, isLoggedIn: true })))
})
),
{ dispatch: false }
);
here are my two actions:
export const login = createAction('[Auth] login', props<{ email: string, password: string }>());
export const loginComplete = createAction(
'[Auth] loginComplete',
props<{ profile: LoggedInUser; isLoggedIn: boolean }>()
);
My reducer, where profile is declared LoggedInUser | null
on(authActions.loginComplete, (state, { profile, isLoggedIn }) => {
return {
...state,
profile,
isLoggedIn,
};
}),
and my Service that makes the call to the server (I get to the server and I can see via the Network tab in the browser, that the server is returning the correct data).
doLogin(email: string, password: string): any {
let user: LoginUser = {
email: email,
password: password,
};
var returnval = this.crud.post<LoggedInUser>(user, 'Auth/login');
returnval.subscribe((e: LoggedInUser) => { return e; });
}
I think my issue is that on my doLogin method my return type is any
and so in my effect data
passed back is of type any
but I'm strongly typing data to LoggedInUser
as that is what I really want. Also, i'm not sure about the Subscribe in my doLogin method, but if I dont have it there, then the server does not get called.
I am struggling to get this right. any help Greatly appreciated.
答案1
得分: 2
doLogin
方法存在问题:
doLogin(email: string, password: string) {
let user: LoginUser = {
email: email,
password: password,
};
return this.crud.post<LoggedInUser>(user, 'Auth/login');
}
修复后,doLogin
方法将返回实际的 Observable
,可以在你的效果中使用 pipe
。
额外信息:
.subscribe
立即启动 Observable,如果您不订阅它,服务器将不会被调用。对于 ngrx effects,您不需要立即订阅任何内容。
login$
Observable 实际上是一个组合的 Observable,它监听所有操作,但只在发出“login”操作时发出内容。这是通过 ofType(fromAuthActions.login),
完成的。
当您发出类型为 fromAuthActions.login
的操作(通过在某处调用 store.dispatch(login(...))
)时,login$
Observable 将切换到另一个 Observable,即this.authService.doLogin
返回的 Observable。这就是 switchMap
操作符的作用,这也是为什么需要从 doLogin
返回一个 Observable:您希望 login$
切换到新的 Observable 以启动 POST 请求。
doLogin
返回的 Observable 将再次使用 pipe
,这次返回另一个操作 - 这是 ngrx effects 预期的内容。NGRX 将知道如何在另一个 effect 中解释新的操作("login complete")。
英文:
The problem is with your doLogin
implementation:
doLogin(email: string, password: string): any {
let user: LoginUser = {
email: email,
password: password,
};
var returnval = this.crud.post<LoggedInUser>(user, 'Auth/login');
returnval.subscribe((e: LoggedInUser) => { return e; });
}
Here, returnval
does nothing, and doLogin
does not return anything - hence your error message: a function that returns nothing will implicitly return undefined
.
I believe you intended to actually return the observable that crud.post
creates.
doLogin(email: string, password: string) {
let user: LoginUser = {
email: email,
password: password,
};
return this.crud.post<LoggedInUser>(user, 'Auth/login');
}
Like this, doLogin
will return the actual Observable
, which in turn can be pipe
d within your effect.
Tip: never type your functions to return any
. This will allow typescript to detect when you made such a mistake early on.
Extra info
> Also, i'm not sure about the Subscribe in my doLogin method, but if I dont have it there, then the server does not get called.
.subscribe
starts the observable immediately, that's why your server isn't called if you never subscribe to it. Observables do not execute unless something subscribes to them; however for ngrx effects you don't want to subscribe to anything immediately.
Your login$
observable is actually a composed observable that listens to all actions, but it filters out to only emit something if the login
action was emitted. This is done by the line ofType(fromAuthActions.login),
.
When you emit an action of type fromAuthActions.login
(by calling store.dispatch(login(...))
somewhere), the login$
observable will then switch to another observable - the one returned by this.authService.doLogin
. This is what the switchMap
operator is for, and this is why you need to return an observable from doLogin
: you want to have login$
switching into the new observable in order to kick off your POST request.
The Observable returned by doLogin
will then be pipe
d again, this time returning another action - which is what ngrx effects expect. NGRX will know to interpret the new action("login complete") in another effect.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论