Refresh a particular component in Angular - javascript

New to angular, so might sound a trivial question, however all solution provided on SO have failed to work so far.
I have a simple login component wherein on submit, I redirect the user to profile page. I am able to send the user to the said component but the nav bar on top does not auto refresh, i.e. I have kept a session check, so when user logs in, the nav bar should automatically show the Logout button instead of Login/Register button. My code files are something like this:
login-page.component.html
<form #loginForm="ngForm" (ngSubmit)="loginUser(loginForm)" id="loginForm" class="loginbackground">
<input ngModel #emailAddress="ngModel" type="text" autocomplete="off" placeholder="Email" id="emailAddress" name="emailAddress" />
<button type="submit" id="submit">LOGIN</button>
login-page.component.ts
#Output() refreshEvent = new EventEmitter<any>();
loginUser(event) {
// Validations. If successful, proceed
const formData = event.value;
this.auth.loginUser(formData);
.subscribe(data => {
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
this.errorPopup = true;
this.errorText = 'Email Address and Password do not match';
} else {
this.refreshEvent.emit();
this.emailAvailable = true;
this.showLogin = false;
this.showRegister = false;
this.router.navigateByUrl('/404', { skipLocationChange: true }).then(() =>
this.router.navigate(['user-profile']));
}
});
});
}
Problem
When I manually refresh the page, the nav bar reflects the changes as per written logic which works fine. However, I want this to happen when the user actually logs in and there shouldn't be a need to manually refresh the page.
What I've tried
I have tried using the answer here but it does not work.
As shown above, I have tried an event emitter but I am not able to get it to work.
I tried refreshing the entire page using ngOnInit() to reload the
nac bar component but it goes into and infinite loop (which is
obviously a hack; but why not)
Is there a way that this can be achieved cleanly?

This is how I solved it:
nav.component.html
...
<li *ngIf="!auth.isLoggedIn()">
...
</li>
<li *ngIf="auth.isLoggedIn()">
...
</li>
...
nav.component.ts
export class NavComponent implements OnInit {
constructor(public auth: AuthService) {
}
...
auth.service.ts
export class AuthService {
...
public isLoggedIn() {
return this.getId() !== null;
}
...
In this last method, 'this.getId()' could be to get the token from localStorage.

The solution for this is basic, you should use the most common features of Angular. I will get you through the thought process and then show you some sample code.
Thought process:
Problem: We need to know whether the user is logged in or not at all times.
Solution: We will have a service that tells us whether the user is logged in or not
Problem: The navigation bar should rely on the user's authentication status
Solution: We will use the status returned by the authentication service, to conditionally show one set of items or another set of items based on the user's authentication status
Code level issues:
You have certain issues within your code that will make your life difficult to further develop other features that rely on authentication status.
I've wrote two steps in how to improve your code, this first one is just improving the flow of your data and the code quality. The second step is completing the corrected code with a more dynamic data flow.
Step 1
Service
We will have a variable within the Authentication Service that tells us whether the user is already logged in or not:
private isUserLoggedIn: boolean = false;
We need to move all the authentication logic into the Authentication Service. Since I don't have the code for this.auth.loginUser(formData), I will call it myself from the new Authentication Service, but note, that the code from that function, should be in our new login function.
Also, there is no need to keep the HTTP call for login as an observable, as you only get one answer, so we can convert it to a promise with .toPromise().
The login function that calls the API will look like this:
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
We will also want to check if the user is logged by checking the localStorage (in case we want the user to not have to log in after each refresh):
The double negation !! tells us whether a value is truthy or falsy, so if we have something on the loggedUser key in the localStorage, we will take it as the user is logged in
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
We will also need the login function that we invoke when we press the login button (we invoke it from the service, through the component):
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
And to make it complete, we will first check if the user is logged in (we do this by invoking isAlreadyLoggedIn() in the constructor of the service. Also, we will have a public function through which we can check if the user is already logged in:
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
The complete service looks like this:
#Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
Login Component:
This will check if the user is already logged in on initialization, if it is, then we redirect the user to the profile page, otherwise we show the login form. (The HTML remains the same as you have it, I would also add an error into a span tag). Please note that there are missing properties that you have in your login.ts, I just did the authentication part, add the form related variables to make the component complete and functional.
#Component({
selector: 'app-login'
})
export class LoginComponent implements OnInit {
public isLoggedIn: boolean = false;
public error: string = '';
constructor(authService: AuthService, router: Router) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
if (this.isLoggedIn) {
this.router.navigate(['user-profile']);
}
}
loginUser(event) {
const formData = event.value;
this.authService.login(formData)
.then(res => this.router.navigate(['user-profile']))
.catch(error => this.error = error);
}
}
Navigation Component:
The component gets the login status of the user, and renders its items accordingly:
#Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
}
}
Nav Template:
ng-template is a container that we are going to show, in case the user is not logged in.
<ul *ngIf="isLoggedIn; else notLoggedIn">
<li>Home</li>
<li>Profile</li>
<li>Log Out</li>
</ul>
<ng-template #notLoggedIn>
<ul>
<li>Home</li>
<li>Log In</li>
</ul>
</ng-template>
This approach would be the basic, using redirects.
Step 2
We can complete this now with a more dynamic way (although I would personally stick to redirects):
We will add the following variables to our service:
private subject = new Subject();
private observable = this.subject.asObservable();
What this does, is that we can subscribe to observable from any component, and from the service, we can pass data live through the subject to the observable's subscribers. More about these, here
Now, whenever we update the login status, we invoke the following:
this.subject.next(this.isUserLoggedIn);
And this way, all the subscribers will be notified of this change.
We need a function that returns the observable to which the components can subscribe:
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
And all that's left, is to subscribe to this observable from the components that need live updates regarding the authentication status, in our case, the nav component (within ngOnInit):
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
This is how the final service looks like:
#Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
private subject = new Subject();
private observable = this.subject.asObservable();
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
this.subject.next(this.isUserLoggedIn);
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
this.subject.next(this.isUserLoggedIn);
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
And this is how the final Nav Component looks like:
#Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
}
}
I hope this clarifies how the code should look like. As a recap, you should handle all your login within the service, and expose a boolean that you can get from any component, so that you know the status of the authentication and act based upon it, and with the observables, you will get the latest status at all times.

Related

Angular 10 - html template not showing correct info depending on variables

I am practically new to Angular (Angular 10 used) and maybe it's a pretty easy thing but I can't figure it out. In the info component I am getting the current user details and the current book details from the tables on the server and depending on 3 variables I want to switch the buttons in the html template.
If the user is publisher (publisherMenu) -> show the publisher menu, if not (!publisherMenu) -> show regular user menu. This one is working. The publisherMenu variable is true or false in the response and the template loads the correct pair of buttons streight away.
But the problem is with the Buy Now and Add to Wishlist buttons. When you click one of them they change to You own it and In your Wishlist. That's ok but if I revisit the same book after that, the buttons are back to Buy Now and Add to Wishlist. If I refresh the page they get fixed, but only when I refresh it. If I navigate to another page and get back to this page, again, the buttons are not ok.
These two buttons depend on bookBought and inWishlist variables. For both of them I get the info in a string, split it to get an array and search for the user Id, and put true/false respectively.
If I debug in the browser everything works ok and the buttons show the correct texts... Now the function is in the constructor, I tried putting it in ngOnInit() but no difference.
Any help will be highly appreciated!
Thank you!
info.component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import Backendless from 'backendless';
import { BookService } from '../book.service';
import { UserService } from 'src/app/user/user.service';
#Component({
selector: 'app-info',
templateUrl: './info.component.html',
styleUrls: ['./info.component.css']
})
export class DetailComponent implements OnInit {
currentBookData;
currentUserData;
publisherMenu: Boolean = false;
bookBought: Boolean = false;
inWishlist: Boolean = false;
constructor(private router: Router, private bookService: BookService, private userService: UserService) {
//get book data
let getBookData = Backendless.Data.of('books').findById({objectId})
.then(currentBook => {
//console.log('Current Data: ' + JSON.stringify(currentBook));
return currentBook;
})
.catch(error => {
console.log(error);
});
getBookData.then(result => {
console.log(JSON.stringify(current book));
this.currentBookData = result;
})
// get user data
let getUserData = Backendless.UserService.getCurrentUser()
.then(function(currentUser) {
return currentUser;
})
.then(result => {
this.currentUserData = result;
console.log('currentUser :' + JSON.stringify(result));
if (this.currentUserData.publisher) {
this.publisherMenu = true;
} else {
this.publisherMenu = false;
}
if(!this.currentUserData.wishlist) {
this.inWishlist = false;
} else {
let currentWishlist = this.currentUserData.wishlist.split(',');
if(currentWishlist.indexOf(this.currentGameData.objectId) !== -1) {
this.inWishlist = true;
} else {
this.inWishlist = false;
}
}
if(!this.currentUserData.orders) {
this.bookBought = false;
} else {
let currentOrders = this.currentUserData.orders.split(',');
if(currentOrders.indexOf(this.currentBookData.objectId) !== -1) {
this.bookBought= true;
} else {
this.bookBought= false;
}
}
})
.catch(function (error) {
console.error(error)
})
//current book id: 88443C15-9CEB-4FF2-B174-D88D7F3324D3
console.logs
current book :{"title":"Demo Book","ownerId":"7B8EF09F-9DF3-4CED-A6B7-
39C3CD90089D","sales":1,"price":"31.99","___class":"books","orders":"B63744A3-
C3C3-4FC5-8CF4-3FCD131A0929","updated":1607770188000,"objectId":"88443C15-
9CEB-4FF2-B174-D88D7F3324D3"}
currentUser :{"___class":"Users","wishlist":"D8005359-AFD5-487F-B130-
3012AF3A7F1E,63DD9C7D-36D5-4CCA-96A3-81BFCBCE92CD,88443C15-9CEB-4FF2-B174-
D88D7F3324D3,100D4187-5B37-4FD4-ADA3-77AE1392F905,96BD531E-B717-4AEB-8DA5-
51805143EC07","ownerId":"B63744A3-C3C3-4FC5-8CF4-3FCD131A0929","name":"Demo
User","publisher":false,"orders":"0BF4737D-F1C4-49C0-AD7A-
40279CF0E3CD,D8005359-AFD5-487F-B130-3012AF3A7F1E,88443C15-9CEB-4FF2-B174-
D88D7F3324D3,100D4187-5B37-4FD4-ADA3-77AE1392F905,96BD531E-B717-4AEB-8DA5-
51805143EC07","email":"demo#demo.demo","objectId":"B63744A3-C3C3-4FC5-8CF4-
3FCD131A0929"}
Results from the IFs on first page load or after navigating through the pages and return back:
publisherMenu :false
bookBought :false
inWishlist :false
Results from the IFs if I refresh the page:
publisherMenu: false
bookBought: true
inWishlist: true
info.component.html
<div *ngIf="!publisherMenu" class="book-buttons">
<a *ngIf="!bookBought" (click)="buyBook(currentBookData.objectId)">Buy Now</a>
<a *ngIf="bookBought">You own it</a>
<br><br>
<a *ngIf="!inWishlist" (click)="addBookToWishlist(currentBookData.objectId)">Add To Wishlist</a>
<a *ngIf="inWishlist">In your Wishlist</a>
</div>
<div *ngIf="publisherMenu" class="book-buttons">
<a [routerLink]="['../../add']">Add Book</a>
<br><br>
<a [routerLink]="['../../my-books']">My Books</a>
</div>
EDIT: I see now that if I don't manually refresh the page I only get the current book details and no current user details, I guess this is the reason, but why the second functions does not work like the first one, as they are practicaly the same..!?

Javascript / Angular - html displays before rendering code

I have a function to get rates from products, so lets say I have one product with two rates. So my product has two rates. Then, when I get those rates I must get the prices attached to my product. So for each rate I have to look for its prices.
The next code below explains this:
this.loadProductInfo = true; // bool to load data in my form
// First of all, I get rates from API
// const rates = this._http....
// Now, for each rate I must search If my product/products have a price:
this.rates.forEach((rate, index, arr) => {
this._glbGetPricesForProduct.getPrice(params).subscribe(response => {
if (!arr[index + 1]) {
this.initForm();
this.loadProductInfo = false;
}
})
});
The variable loadProductInfo it loads content in my form, so in my html I have:
<form *ngIf="!loadProductInfo"></form>
But form it still give me error: could not find control name.
But if I do this instead, it works correctlly:
setTimeout(() => {
this.initForm();
this.loadProductInfo = false;
}, 2000);
So what I want its to say my form to wait until I have all code loaded and then after it, load its contents. But instead it cant find the control because it loads before code. Any help I really appreciate it.
The main mistake I see there is that you are looping over async data which may not be there when your code execute the for each loop (your rates).
I would build an observable with your rates as a source:
...
$rates: Observable<any> = this._http.get(...);
rates.pipe(
mergeMap((rates) => {
const priceByRates: Observable<any>[] = rates.map((rate, index, arr) => this._glbGetPricesForProduct.getPrice(params));
return combineLatest(pricesByRates); // if getPrice complete right away, use forkJoin() instead
})
).subscribe(res => {
// No need to check for the last item, all rates have been checked for possible price
this.initForm();
this.loadProductInfo = false;
});
...
This implementation should wait for your api calls to resolve before printing your form.
Since you are hiding the entire form, it may be better to just move the API call into a resolver so that the page does not render until the data is ready.
Here is a minimal StackBlitz showcasing this behavior: https://stackblitz.com/edit/angular-ivy-4beuww
Component
In your component, include an ActivatedRoute parameter via DI.
#Component(/*omitted for brevity*/)
export class MyComponent {
constructor(private route: ActivatedRoute) {
// note: 'data' is whatever you label your resolver prop in your routing setup
route.data.subscribe(resolved => {
if ("data" in resolved) this.resolveData = resolved["data"];
});
}
}
Route Setup
And in your router setup you would have the following:
const routes: Routes = [
{
path: 'my-route-path',
component: MyComponent,
resolve: {
data: MyResolver
}
}
];
Resolver
Finally, your resolver would make your API call utilizing your service:
#Injectable({providedIn: 'root'})
export class MyResolver() implements Resolve<T> {
constructor(private service: MyService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | any {
return this.service.myRequest();
}
}
The final result will be that your view will not be rendered until your data is ready.

Vue & Vuex not retrieving proper value in html

I have a navbar where I only show certain menu items based off the user's role.
Here is the HTML (I removed all the menu items except one to simplify):
<v-speed-dial class="speed-dial-container contextual-text-menu" v-if="user && user.emailVerified" fixed top right
direction="bottom" transition="slide-y-transition">
<v-icon v-if="checkAuthorization(['superAdmin', 'admin', 'pastor', 'member']) === true" class="mt-2"
#click="sendComponent({ component: 'Dashboard' })">$admin</v-icon>
</v-speed-dial>
My method:
async checkAuthorization(permissions) {
if (permissions.length > 0) {
const temp = await this.$store.dispatch('UserData/isAuthorized', permissions)
return temp
}
},
Vuex store:
isAuthorized({
state
}, permissions) {
const userRoles = state.roles
if (permissions && userRoles) {
const found = userRoles.some(r => permissions.includes(r))
return found
}
},
All of my console logs show the correct values but the HTML is not responding accordingly.
Example: in this line of code checkAuthorization(['superAdmin', 'admin', 'pastor', 'member']) === true I added 'member' and I am logged in as a user that ONLY has the 'member' role. When looking through the console logs everything returns true so I should see this menu item but it does not show.
As someone pointed out in the comments, checkAuthorization is an async function and will return a Promise, so you cannot check for promise === true.
That aside, I would change isAuthorized to be a vuex getter and not an action, e.g.
getters: {
// ...
isAuthorized: (state) => (permissions) => {
if (permissions && state.roles) {
return state.roles.some(r => permissions.includes(r))
}
return false;
}
}
And update checkAuthorization to not return a promise e.g.
function checkAuthorization(permissions) {
if (permissions.length > 0) {
return this.$store.getters.isAuthorized(permissions);
}
return false;
}
What I usually do :
I add another user state as Unknown and make it the default state.
In main main.js (or main.ts) I call state.initialize(), which determines user's state.
And, the key thing is to use navigation guards. Instead of checking routing guards on router-link (or url or anywhere in this step), you should define it in the router. There is a router.beforeEach function you can use, so that you can check if user is authorized to use that route, and redirect the user to 401 page if the user don't have permission.
https://router.vuejs.org/guide/advanced/navigation-guards.html

Getting Data Before Rendering Component/HTML File in Angular

Please, I am having issues working with some async data on angular which comes from my API. I’ve spent some time trying to figure out how to scale through, but I still get stuck.
Scenario
When on edit mode of a patient form, I need to call my centre service to get all available centres from db. When the data is returned, I need to process the data to check which centres a patient belong to, then use this on the html. But I see that the component renders before data is received. This is because, when I click save button to check the data, I see the data there. But in the method where I need to write some logic, when I try to inspect the data returned from the API, it remains undefined.
NB: I can’t use a resolver in this case because, I’m not using a router link to navigate to the page.
I’ve tried to use an async pipe to conditionally check and render the html only if I receive the data which was one solution that worked for someone else. But this seem not to work in my case as i still get undefined on the variable which is inside a method, and where I need to process the data returned before showing my component/html.
Goal
The goal is to first get all centres first before initializing the reactive form, so that i can handle the data on the getPatientCentres() method. I intend to use the data gotten from the API to pre-populate an array when creating the form.
Done other steps and research but the solution doesn’t seem to solve my case.
Any help or logic on how to proceed would be highly appreciated.
Here is my TS code
export class Patient2Component implements OnInit {
formTitle: string;
patientForm: FormGroup;
centreList: ICentre[] = [];
loadedData: boolean = false;
patient: IPatient;
constructor(
private activatedRoute: ActivatedRoute,
private router: Router,
private fb: FormBuilder,
private centreService: CentreService,
) { }
ngOnInit() {
this.getCentres();
this.initCentreForm();
this.checkParamsForEditAction();
}
initCentreForm() {
this.patientForm = this.fb.group({
id: [null],
firstName: ['', Validators.required],
lastName: ['', Validators.required],
centres: [this.centreList]
});
}
getCentres() {
this.centreService.getCentres().subscribe(res => {
this.centreList = res;
// this.loadedData = true;
});
}
checkParamsForEditAction() {
this.activatedRoute.data.subscribe(data => {
this.patient = data['patient'];
if (this.patient) {
this.formTitle = 'Edit Patient';
this.getPatientCentres(this.patient);
this.assignValuesToControl(this.patient);
}
});
}
assignValuesToControl(patient: IPatient) {
this.patientForm.patchValue({
id: patient.id,
firstName: patient.firstName || '',
lastName: patient.lastName || '',
});
}
getPatientCentres(patient: IPatient) {
const patientCentres = patient.patientCentres;
/**Here, the centreList is undefined since data has not returned yet
* And i need this data for processing.
*/
console.log(this.centreList);
}
save() {
/**Here, i can see the data */
console.log(this.centreList);
}
Try this
in ngOnInit
ngOnInit() {
this.initCentreForm();
this.getCentres(this.checkParamsForEditAction);
}
getCenters Method
getCentres(callBack?) {
this.centreService.getCentres().subscribe(res => {
this.centreList = res;
// this.loadedData = true;
if(callBack) {
callBack();
}
});
}
you can also use forkJoin, or async await
getCentres(callBack?) {
this.centreService.getCentres().subscribe(res => {
this.centreList = res;
// this.loadedData = true;
//Now call your function directly
this.checkParamsForEditAction();
});
}
Call your function after the get centers is loaded
Order of calling
this.initCentreForm();
this.getCentres();

Firebase signin successful but unable to retrieve user info

I am trying to retrieve the authenticated user info after successful firebase signin but I seem to be missing something. Here's a step by step summary of my ionic app logic
User clicks a button that calls the authStatus()method to check if the user is signed in or not in schedules.page.ts. If the user is not signed in, a modal is presented like this
authStatus() {
// if user is not signed in, present register modal
if (this.authService.user == null) {
this.presentModal();
}
// if user is not null, do something
}
// present & dismiss modal
async presentModal() {
const modal = await this.modalCtrl.create({
component: RegisterModalPage
});
return await modal.present();
}
The modal displays buttons that should clicked based on a user's prefered method of signing in (gmail or facebook)
User clicks on button to signin with gmail account which calls the gmailReg() method of the register-modal.page.ts.
gmailReg() {
this.authService.signInWithGmail();
}
signInWithGmail() method is from the authentication.service.ts
export class AuthenticateService {
public user = null;
constructor(public afAuth: AngularFireAuth) {}
signInWithGmail() {
this.afAuth.auth.signInWithRedirect(new auth.GoogleAuthProvider());
this.afAuth.auth.getRedirectResult().then(result => {
this.user = result.user;
});
}
}
In my app.component.html file, I want to display the displayName of the authenticated user like this
<ion-toolbar>
<ion-title *ngIf="authService.user == null">Menu</ion-title>
<ion-title *ngIf="authService.user != null">{{
authService.user.displayName
}}</ion-title>
</ion-toolbar>
and the AuthenticateService is imported correctly in app.module.ts like this
import { AuthenticateService } from "./auth/authenticate.service";
constructor(
public authService: AuthenticateService,
...
)
But the user seems to remain null and not set as I intend for it to when the signInWithGmail() completes. What am I missing?
From firebase google sign-in doc getRedirectResult should be called when the page loads. So I added a method to get the current user in AuthenticateService
currentUser() {
this.afAuth.auth.getRedirectResult().then(result => {
if (result.user) {
this.user = result.user;
// const token = result.credential.toJSON();
console.log(this.user);
}
});
}
and then edited the signInWithGmail() method like this
signInWithGmail() {
this.afAuth.auth.signInWithRedirect(new auth.GoogleAuthProvider());
}
Now I can call the currentUser() method in any part of the app. One problem though is when I call the method in any page, the user object is not returned immediately and takes sometime to later. That still needs to be fixed.

Categories