Get user data in ngOnInit

I have this code (cleaned) in my app.component.ts

import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Platform, ToastController, NavController, MenuController, IonRouterOutlet } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import {
    Plugins,
    LocalNotificationScheduleResult } from '@capacitor/core';
import { AuthService } from './Services/auth.service';
import { Router } from '@angular/router';
import { User } from './Services/user.service';
const { Network, Toast, PushNotifications, LocalNotifications } = Plugins;
import { Socket } from 'ngx-socket-io';

@Component({
    selector: 'app-root',
    templateUrl: 'app.component.html',
    styleUrls: ['app.component.scss']
})

export class AppComponent implements OnInit, OnDestroy {

    @ViewChild(IonRouterOutlet, {static: false}) routerOutlet: IonRouterOutlet;

    public user: User;

    constructor(
        private platform: Platform,
        private splashScreen: SplashScreen,
        private statusBar: StatusBar,
        private authService: AuthService,
        private socket: Socket,
    ) {
        this.initializeApp();
        this.hardwareBackButton();
    }

    async ngOnInit() {
        this.socket.connect();

        // local notifications
        await LocalNotifications.requestPermissions();
        try {
        LocalNotifications.addListener('localNotificationReceived', (notification) => {
            console.log('Notification: ', notification);
        });
        LocalNotifications.addListener('localNotificationActionPerformed', (notification) => {
            console.log('Notification action performed', notification);
        });
        } catch (e) {
        console.log('Notification errors: ', e);
        }

        // chat notif
        this.socket.fromEvent('oneononemessage').subscribe(async (message: any) => {
        console.log('chat message: ', message);
        // if (message.msg.message.user.username !== this.user.username) { // if message receiver with auth user is same, show notif
            const notifs = LocalNotifications.schedule({
            notifications: [
                {
                title: message.msg.message.user.username,
                body: message.msg.message.note,
                id: Math.floor(Math.random() * 10),
                schedule: { at: new Date(Date.now() + 1000 * 2) },
                sound: 'beep.wav',
                attachments: null,
                actionTypeId: 'OPEN_CHAT',
                extra: null
                }
            ]
            });
            console.log('scheduled notifications', notifs);
        // }
        });
        // chat notif
    }

  // ionViewWillLeave() {
  //   this.socket.disconnect();
  // }

    initializeApp() {
        this.platform.ready().then(() => {
            this.statusBar.styleDefault();
            this.splashScreen.hide();
            this.authService.getToken();
        });
    }

    async ionViewDidEnter() {
        (await this.authService.user()).subscribe(user => {
        this.user = user;
        });
    }
}

As you see in my code in ngOnInit I have this line for notifications

if (message.msg.message.user.username !== this.user.username) {

Which currently I commented it as it was returning error.

Issue

As of Ionic life cycle ionViewDidEnter comes after ngOnInit so I cannot have access to this.user data.

Question

How can I have access to this.user data in ngOnInit?

1 Like

console log the object and share the screenshot of what you get?

Which object? this.user.username?

just this.user or the object you are trying to access but facing error

02

@Hammad6264 any idea?

The way you have written things, what you describe is categorically impossible, because there is no conceivable way to know outside of the nameless subscription created in ionViewDidEnter when user is valid.

The first thing I would recommend is refactoring everything to eliminate all use of async/await, because it obscures code flow.

Secondly, I would initialize user to a sane dummy value ({} at a bare minimum) at the point of its declaration.

Then, you can either wait to register your socket listeners until user has received a usable (non-dummy) User or put guard code in them that (for example) nerfs them completely unless user is in a desired state.

1 Like

Hi thanks for response,

I’ve tried to use your logic still couldn’t get my user.

Changes I've made in auth service file

user() {
    if (this.token === undefined) {
      this.storageIonic.getItem('token').then((token) => {
        this.token = token.access_token;
      }).catch(error => console.error(error));
    }

    const headers = new HttpHeaders({
      Accept: 'application/json',
      'Authorization': this.token.access_token
    });
    
    return this.http.get<User>(this.env.AUTH_URL + '/user', { headers })
      .pipe(
        tap(user => {
          return user;
        })
      )
  }

  getToken() {
    return this.storageIonic.getItem('token').then(
      data => {
        this.token = data;
        if (this.token != null) {
          this.isLoggedIn = true;
        } else {
          this.isLoggedIn = false;
        }
      },
      error => {
        this.token = null;
        this.isLoggedIn = false;
      }
    );
  }

I removed all async’s from service file and tried to access user in app.component.ts like this

async ngOnInit() {
  this.authService.user().subscribe(
      user => {
        this.user = user;
        console.log('this.user 1: ', user);
        console.log('this.user 2: ', this.user);
      }
  );
}

Still nothing prints in console.

Then I thought it might be because of async before ngOnInit so I moved my code to constructor and result was no different couldn’t get user data

I’m not 100% sure but I don’t really think it has anything to do with async/await as you suggested.

Outside of that then block, you have no idea when token is valid.

The reason I want async/await to go away is that they obscure the flow of code, which is the fundamental problem here. You are writing in an imperative style: do this, do that, then do this. async and await exist in order to make things look imperative, which IMHO has the bad side-effect of convincing people that they can actually make things imperative. They can’t.

You are saying “do I have a token? if not, get a token. now go fire off an http request”. Web apps don’t work like that: they are reactive by necessity, because you the programmer does not control the flow of execution: it’s dependent on external factors like networks and storage latency.

You might want to look at the latter half of this thread, which covers very similar territory to your use case: getting a token from storage and using it in HTTP requests. One strategy I often use is to break each step up into a separate function, giving each of them explicit parameter and return types. This allows the compiler to tell me when I’m structuring things in an impossible way. Never use external state (like this.user, this.token, &c) amongst asynchronous code: it can be very difficult for either humans or static code analysis tools to spot situations where you introduce race conditions like the one you have here with the token.

The more strictly I follow the rules of functional programming when doing asynchronous JavaScript, the happier I find myself.

I’ve tried this

Service

user() {
    const headers = new HttpHeaders({
      Accept: 'application/json',
      'Content-Type': 'application/json',
      Authorization: this.token['access_token']
    });

    return this.http.get<User>(this.env.AUTH_URL + '/user', { headers })
      .pipe(
        tap(user => {
          return user;
        })
      );
  }

app.component.ts

async ngOnInit() {
    this.socket.fromEvent('message').subscribe(async (message: any) => {
        console.log('user in socket: ', this.user); // undefined
        //....
    }
}

ionViewDidEnter() {
    this.authService.user().subscribe(
      user => {
        this.user = user;
        console.log('current user 1: ', user);  // nothing prints
        console.log('current user 2: ', this.user);  // nothing prints
      }
}

Result

user in socket:  undefined

I am really confused, can’t think of anything else. Would you please show some sample code?

OK after a day of struggling it fixed finally.

What I did was to remove ionViewDidEnter and add new function I named it getUser then called this function in my ngOnInit and it returned my current user.

Code

getUser() {
    this.authService.user().subscribe(
      user => {
        this.user = user;
        console.log('current user 1: ', user); // get returned user data
        console.log('current user 2: ', this.user); // just to make sure user value is set :)
      }
    );
  }

Then in ngOnInit

async ngOnInit() {
    this.socket.fromEvent('message').subscribe(async (message: any) => {
        if(this.user === undefined) {
          this.getUser();
          // placing my notification codes
        } else {
          console.log('user in socket: ', this.user);
         // also here placing my notification codes just to make sure it fires in any condition
       }
    }

I’m not sure if this is the best way possible, but it works for me and I hope it can cure next person in needs headaches :smiley: