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.

Retrieve user details from service in Angular

+3
−0

Angular 12; .NET Core 3.1

Following a code-maze tutorial, I have a working authentication (registration/login) service using .NET Core Identity. After login, I would like to add 'username' data to my app's header component and a 'Welcome username' message in the home component.

I used ng g s shared/user to create a user service. The service's getCurrentUser() code:

public currentUser?: any;
...
getCurrentUser(): Observable<any> {
  if (this._authService.isAuthenticated) {
    this._repoService.getData('api/accounts/user')
        .subscribe(res => {
          this.currentUser = res;
          console.log('UserService: currentUser = ' + this.currentUser?.email);
    });
  }
  return this.currentUser;
}

My header component and home component attempt to display the user's name (email) with the following:

getUsername(): string {
  this._userService.getCurrentUser().subscribe(
      res => { 
        this.userName = (res as ApplicationUserDTO).email;
      }
  );
  return this.userName;
}

This does not result in the name being displayed. The browser console shows:
ERROR TypeError: Cannot read properties of undefined (reading 'subscribe').

Navigating to another page and back to the home page results in a slightly different console error: ERROR TypeError: this._userService.getCurrentUser(...).subscribe is not a function

However the console.log call in the user.service.ts correctly shows my logged in user's email.

The service originally tried returning an ApplicationUserDTO which was resulting in only the ...subscribe is not a function error. I checked this on SO and found Angular subscribe to a service: subscribe is not a function and adjusted my service code to return Observable<any>. Making this change, added the ... cannot read properties of undefined (reading 'subscribe') error.

Where have I gone wrong?

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

1 answer

+3
−0

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 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.

History
Why does this post require moderator attention?
You might want to add some details to your flag.

1 comment thread

Many thanks @meriton lol, ok so the code I had sort of took care of bugs #2 and #3 ie I had, a) wra... (3 comments)

Sign up to answer this question »