Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

71%
+3 −0
Q&A Retrieve user details from service in Angular

There are quite a few bugs there :-) Let's start with with the big one: In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does ...

posted 2y ago by meriton‭  ·  edited 2y ago by meriton‭

Answer
#6: Post edited by user avatar meriton‭ · 2021-10-07T02:41:57Z (over 2 years ago)
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, I'd therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • constructor(repoService: RepoService) {
  • repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • }
  • This presumes that your App is reinitialized after login, which happens naturally with many login methods. If NET Core Identity is different you could move the request from the constructor to a method that you manually invoke after login.
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • A remaining detail: What should happen when the call to get the user details fails. Should the call be retried? If so, how often? I'm leaving that to you.
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, I'd therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • constructor(repoService: RepoService) {
  • repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • }
  • This presumes that your App is reinitialized after login, which happens naturally with many login methods. If NET Core Identity is different you could move the request from the constructor to a method that you manually invoke after login.
  • Having done that, everyone can access the current user through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • A remaining detail: What should happen when the call to get the user details fails. Should the call be retried? If so, how often? I'm leaving that to you.
#5: Post edited by user avatar meriton‭ · 2021-10-06T15:18:35Z (over 2 years ago)
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, I'd therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • constructor(repoService: RepoService) {
  • repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • }
  • This presumes that your App is reinitialized after login, which happens naturally with many login methods. If NET Core Identity is different you could move the request from the constructor to a method that you manually invoke after login.
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, I'd therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • constructor(repoService: RepoService) {
  • repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • }
  • This presumes that your App is reinitialized after login, which happens naturally with many login methods. If NET Core Identity is different you could move the request from the constructor to a method that you manually invoke after login.
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • A remaining detail: What should happen when the call to get the user details fails. Should the call be retried? If so, how often? I'm leaving that to you.
#4: Post edited by user avatar meriton‭ · 2021-10-06T15:04:49Z (over 2 years ago)
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, it therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • constructor(repoService: RepoService) {
  • repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • }
  • This presumes that your App is reinitialized after login, which happens naturally with many login methods. If NET Core Identity is different you could move the request from the constructor to a method that you manually invoke after login.
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, I'd therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • constructor(repoService: RepoService) {
  • repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • }
  • This presumes that your App is reinitialized after login, which happens naturally with many login methods. If NET Core Identity is different you could move the request from the constructor to a method that you manually invoke after login.
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?
#3: Post edited by user avatar meriton‭ · 2021-10-06T15:02:15Z (over 2 years ago)
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, it therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • afterLogin() {
  • this._repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • This presumes that you are notified when the login occurs. I am not familiar with .NET Core Identity, but if login happens with OpenID Connect Implicit Flow, your AppComponent would be recreated after login, so you could do:
  • export class AppComponent {
  • constructor(userService: UserService) {
  • userService.afterLogin();
  • }
  • }
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, it therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • constructor(repoService: RepoService) {
  • repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • }
  • This presumes that your App is reinitialized after login, which happens naturally with many login methods. If NET Core Identity is different you could move the request from the constructor to a method that you manually invoke after login.
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?
#2: Post edited by user avatar meriton‭ · 2021-10-06T14:55:31Z (over 2 years ago)
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the browser happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, it therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • afterLogin() {
  • this._repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • This presumes that you are notified when the login occurs. I am not familiar with .NET Core Identity, but if login happens with OpenID Connect Implicit Flow, your AppComponent would be recreated after login, so you could do:
  • export class AppComponent {
  • constructor(userService: UserService) {
  • userService.afterLogin();
  • }
  • }
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?
  • There are quite a few bugs there :-)
  • Let's start with with the big one:
  • In Angular (and most client side web frameworks) requests to the server happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.
  • So when you do:
  • this._repoService.getData('api/accounts/user')
  • .subscribe(res => {
  • this.currentUser = res;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • return this.currentUser;
  • What happens is this:
  • send http request to api/accounts/user
  • return this.currentUser
  • --- angular updates the UI and waits for clicks or other events
  • --- once the response arrives
  • this.currentUser = res;
  • console.log(...)
  • That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.
  • The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.
  • Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.
  • So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.
  • Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.
  • Overall, it therefore start with something like this:
  • export class UserService {
  • currentUser?: ApplicationUserDTO;
  • afterLogin() {
  • this._repoService.getData('api/accounts/user').subscribe(res => {
  • this.currentUser = res as ApplicationUserDTO;
  • console.log('UserService: currentUser = ' + this.currentUser?.email);
  • });
  • }
  • This presumes that you are notified when the login occurs. I am not familiar with .NET Core Identity, but if login happens with OpenID Connect Implicit Flow, your AppComponent would be recreated after login, so you could do:
  • export class AppComponent {
  • constructor(userService: UserService) {
  • userService.afterLogin();
  • }
  • }
  • Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.
  • What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?
#1: Initial revision by user avatar meriton‭ · 2021-10-06T14:53:07Z (over 2 years ago)
There are quite a few bugs there :-)

Let's start with with the big one:

In Angular (and most client side web frameworks) requests to the browser happen asynchronously. That is, when a method does an http call, the method doesn't wait for the response, but continues immediately.

So when you do:

    this._repoService.getData('api/accounts/user')
        .subscribe(res => {
          this.currentUser = res;
          console.log('UserService: currentUser = ' + this.currentUser?.email);
    });
    return this.currentUser;

What happens is this:

    send http request to api/accounts/user
    return this.currentUser
    --- angular updates the UI and waits for clicks or other events
    --- once the response arrives
    this.currentUser = res;
    console.log(...)

That is, you're returning `this.currentUser` before it is assigned, which means that it is undefined.

The whole point of Observables is dealing with values that may not be here yet. You should read up about Observables and functional reactive programming.

Second bug: You're making an API call every time angular invokes `getCurrentUser()`. If you're doing this in a data binding expression, that will be after every change detection. Change detection happens every time angular updates the UI in response to a user interaction or other event. Every keypress, every mouse click, every animation step. Your poor server will be swamped with requests. And that's totally pointless: A user is very unlikely to change their name every millisecond.

So you should be making this request once after login, or maybe in regular intervals if you expect relevant changes to user data.

Third bug: You've typed currentUser as `any`. That's why TypeScript doesn't realize that returning currentUser is not compatible with the Observable<any> the getCurrentUser() is declared to return. That is, if you had specified a more expressive type, TypeScript would have told you that returning currentUser doesn't make sense here. Help TypeScript help you be telling it about the types you expect, and avoid `any` wherever possible.

Overall, it therefore start with something like this:

    export class UserService {

        currentUser?: ApplicationUserDTO;

        afterLogin() {
            this._repoService.getData('api/accounts/user').subscribe(res => {
                this.currentUser = res as ApplicationUserDTO;
                console.log('UserService: currentUser = ' + this.currentUser?.email);
        });
    }

This presumes that you are notified when the login occurs. I am not familiar with .NET Core Identity, but if login happens with OpenID Connect Implicit Flow, your AppComponent would be recreated after login, so you could do:

    export class AppComponent {
        constructor(userService: UserService) {
            userService.afterLogin();
        }
    }

Having done that, everyone can access the current user by injecting through userService.getCurrentUser, bearing in mind that this is undefined while the user is not logged in, and while the user details are in the process of being fetched from the server. That's probably easier than dealing with Observables all over the place.

What you should do is think about what should happen when the call to get the user details fails. Should the call be retried? If so, how often?