How to approach multiple async requests from back-end in Angular 6? - javascript

I am new to Angular and having hard time grasping how to deal with async requests.
I have 3 components:
parent - AppComponent
children - LoginComponent,NavbarComponent,DashboardComponent,MainComponent,SidebarComponent
and one AuthService
On pressing "Login" button at the logging component I need to transfer boolean "true" value to all components.
On pressing "Logout" button at the navbar component I need to transfer boolean "false" value to all components and set user=null
if true ->
set token in localStorage with user ID
preform http.get("http://localhost:3000/user/"+id) request to retrieve full user info and inject user info to Dashboard,Main,Sidebar,App and Navbar components.
The problem is that whenever I logout the false/true value updates on all components immediately but the user info does not turn into null unless I refresh the page or send it with router to another component and then return to MainComponent, same thing with new login.
How do I update both user info and status in all components immediately without refreshing the page?
authService:
import { Injectable } from "#angular/core";
import { HttpClient } from "#angular/common/http";
import { Router } from "#angular/router";
import { User } from "../models/User";
#Injectable({ providedIn: "root" })
export class AuthService {
user: User;
private _url = "http://localhost:5000/user/";
constructor(private http: HttpClient, private _router: Router) {}
registerUser(user) {
return this.http.post<any>(this._url + "register", user);
}
loginUser(user) {
return this.http.post<any>(this._url + "login", user);
}
logoutUser() {
localStorage.clear();
this._router.navigate(["/"]);
}
loggedIn() {
return !!localStorage.getItem("token");
}
getToken() {
return localStorage.getItem("token");
}
getCurrentUser() {
return this.http.get<any>("http://localhost:5000/shop/current");
}
}
Main/Sidebar component:
import { Component, OnInit, DoCheck } from "#angular/core";
import { AuthService } from "src/app/services/auth.service";
import { User } from "src/app/models/User";
#Component({
selector: "app-sidebar",
templateUrl: "./sidebar.component.html",
styleUrls: ["./sidebar.component.css"]
})
export class SidebarComponent implements OnInit, DoCheck {
isSidenavOpen: boolean = true;
user: User;
constructor(private _authService: AuthService) {}
ngOnInit() {
if (this._authService.loggedIn()) this._authService.getCurrentUser().subscribe(res => (this.user = res.user));
else this.user = null;
}
ngDoCheck() {
if (!this._authService.loggedIn()) this.user = null;
}
}
login:
constructor(private _authService: AuthService, private _router: Router) {}
// onLoginUser() {
// this._authService.loginUser(this.loginUserData).subscribe(
// res => {
// localStorage.setItem("token", res.token);
// localStorage.setItem("user", res.user._id);
// this._router.navigate(["/"]);
// },
// err => console.log(err)
// );
// }
}

Use event EventEmitter to emit event on login, logout etc and listen in each component, that depends on user data.
In service, where you call logoutUser, loginUser methods of AuthService:
// LoginService
userLoggin: EventEmitter<User> = new EventEmitter();
userLoggout: EventEmitter<any> = new EventEmitter();
constructor(private _authService: AuthService, private _router:
Router) {}
loginUser() {
this._authService.loginUser(this.loginUserData).subscribe(
res => {
localStorage.setItem("token", res.token);
localStorage.setItem("user", res.user._id);
this.userLoggin.emit(res.user);
this._router.navigate(["/"]);
},
err => console.log(err)
);
}
logoutUser() {
this._authService.logoutUser().subscribe(
res => {
this.userLoggout.emit();
},
err => console.log(err)
);
}
}
Then in component:
import { Component, OnInit, DoCheck } from "#angular/core";
import { AuthService } from "src/app/services/auth.service";
import { User } from "src/app/models/User";
import { LoginService } from "src/app/services/login.service";
#Component({
selector: "app-sidebar",
templateUrl: "./sidebar.component.html",
styleUrls: ["./sidebar.component.css"]
})
export class SidebarComponent implements OnInit, DoCheck {
isSidenavOpen: boolean = true;
user: User;
loggin$: Subscription;
logout$: Subscription;
constructor(private _authService: AuthService, private _loginService: LoginService) {
this.loggin$ = this._loginService.userLoggin.subscribe(user => {
this.user = user;
});
this.logout$ = this._loginService.userLoggout.subscribe(user => {
this.user = null;
});
}
ngOnInit() {
}
ngOnDestroy() {
this.loggin$.unsubscribe();
this.logout$.unsubscribe();
}
}

Related

AngularFire doesn't trigger change detection

I'm using AngularFire and Angular 8 to build an app but I have a silly problem (I believe it is silly actually).
I built a simple service to wrap AngularFireAuth:
import { Injectable } from '#angular/core';
import { AngularFireAuth } from '#angular/fire/auth';
import { User } from 'firebase';
import { Subject } from 'rxjs';
import { MessageService } from 'primeng/api';
#Injectable({
providedIn: 'root'
})
export class AuthService {
private user: Subject<User> = new Subject();
private isLoggedIn: Subject<boolean> = new Subject();
constructor(private afAuth: AngularFireAuth, private messageService: MessageService) {
this.afAuth.auth.onAuthStateChanged(user => {
this.user.next(user);
this.isLoggedIn.next(user !== null);
});
}
isAuthenticated() {
return this.isLoggedIn.asObservable();
}
}
Then, I injected it in my HomeComponent and subscribed to the Observable returned by the isAuthenticated method:
import { Component, OnInit } from "#angular/core"
import { AuthService } from '../auth/auth.service';
#Component({
selector: 'app-homepage',
styleUrls: ['./homepage.component.scss'],
templateUrl: './homepage.component.html'
})
export class HomepageComponent implements OnInit {
isAuthenticated: boolean = false;
constructor(private authService: AuthService) { }
ngOnInit() {
this.authService.isAuthenticated().subscribe((isAuth) => {
this.isAuthenticated = isAuth;
console.log(`User is authenticated? ${this.isAuthenticated}`);
});
}
}
However, when the arrow function passed to the subscribe method is invoked, no re-rendering is executed. But, the console.log call does show "User is authenticated? true" on DevTools.
Some other tests that I've done: if I call setTimeout from within the arrow function passed to subscribe, the result is the same. No re-render and the message on DevTools says "User is authenticated? true".
But, if I invoke setTimeout (in this test with a 10 secs delay) outside subscribe, the component is re-rendered after these 10 seconds:
import { Component, OnInit } from "#angular/core"
import { AuthService } from '../auth/auth.service';
#Component({
selector: 'app-homepage',
styleUrls: ['./homepage.component.scss'],
templateUrl: './homepage.component.html'
})
export class HomepageComponent implements OnInit {
isAuthenticated: boolean = false;
constructor(private authService: AuthService) { }
ngOnInit() {
this.authService.isAuthenticated().subscribe((isAuth) => {
this.isAuthenticated = isAuth;
console.log(`User is authenticated? ${this.isAuthenticated}`);
});
setTimeout(() => {
this.isAuthenticated = true;
console.log(`User is authenticated? ${this.isAuthenticated}`);
}, 10000)
}
}
What am I missing here? What have I misunderstood?
it's because after component init you're calling your authentication
call it in constructor it works
import { Component, OnInit } from "#angular/core"
import { AuthService } from '../auth/auth.service';
#Component({
selector: 'app-homepage',
styleUrls: ['./homepage.component.scss'],
templateUrl: './homepage.component.html'
})
export class HomepageComponent implements OnInit {
isAuthenticated: boolean = false;
constructor(private authService: AuthService) {
this.authService.isAuthenticated().subscribe((isAuth) => {
this.isAuthenticated = isAuth;
console.log(`User is authenticated? ${this.isAuthenticated}`);
});
}
ngOnInit(){}
}

Angular 6 : Issue of component data binding

I call service which make http call, I assign response to component variable now when I try access that component variable to view it display blank.
Means component variable assign in subscribe successfully but cant acceess in html view.
I think view is loaded before values assign to component data.
component
import {Component, OnInit, ChangeDetectionStrategy} from '#angular/core';
import { UserService } from '../../../../../core/services/users/user.service';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'm-user-list',
templateUrl: './user-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListComponent implements OnInit {
list;
roles = {};
current_page: any
totalRecords: any
public showContent: boolean = false;
constructor(private userService: UserService, private http: HttpClient) {
}
ngOnInit() {
this.getRecords();
}
getRecords(){
this.getResultedPage(1);
}
getResultedPage(page){
return this.userService.getrecords()
.subscribe(response => {
this.list = response.data;
});
}
}
Service
import { Injectable } from '#angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpParams , HttpErrorResponse, HttpHeaders } from '#angular/common/http';
import { map, catchError, tap, switchMap } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
import { UtilsService } from '../../services/utils.service';
import { AppConfig } from '../../../config/app'
#Injectable({
providedIn: 'root'
})
export class UserService{
public appConfig: AppConfig;
public API_URL;
constructor(private http: HttpClient, private util: UtilsService) {
this.appConfig = new AppConfig();
this.API_URL = this.appConfig.config.api_url;
}
private extractData(res: Response) {
let body = res;
return body || { };
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError('Something bad happened; please try again later.');
};
getrecords(): Observable<any> {
return this.http.get('/api/users', httpOptions).pipe(
map(this.extractData),
catchError(this.handleError));
}
}

Storing and accessing a global flag/variable in each component, The value keeps changing

I am trying to make a global service class which will store few variables which will influence behaviour on HTML components base on flags.
My current only flag is a BehaviourSubject which navbar component subscribes to update a navbar with different buttons. The issue is when I refresh the page in a browser the flag reverse to the original value and forgets what has set before. The current scenario is when user log in the flag is being set to true and should stay true until a user logs out. It may not be a right way to do it so if there is a better way of approaching it; then I am happy to implement it.
Data sharing class:
import {
Injectable
} from '#angular/core';
import {
BehaviorSubject
} from 'rxjs';
#Injectable()
export class ServiceClassDatasharing {
public isUserLoggedIn: BehaviorSubject < boolean > = new BehaviorSubject < boolean > (false);
public setUserLoggedInStatus(status) {
this.isUserLoggedIn.next(status);
}
}
Nav Component:
import {
Component,
OnInit
} from '#angular/core';
import {
MatDialog,
MatDialogRef,
MAT_DIALOG_DATA
} from '#angular/material';
import {
Inject
} from '#angular/core';
import {
ServiceClassDatasharing
} from '../service/service-class-datasharing';
import {
ServiceClassAuth
} from '../service/service-class-auth';
import {
SigninComponent
} from './../signin/signin.component';
import {
Router
} from '#angular/router';
#Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css']
})
export class NavComponent implements OnInit {
id_token: Boolean;
username: String;
constructor(public dialog: MatDialog, private dataSharingService: ServiceClassDatasharing,
private authService: ServiceClassAuth, private router: Router) {
this.dataSharingService.isUserLoggedIn.subscribe(res => {
this.id_token = res;
if (this.id_token) {
const user = JSON.parse(localStorage.getItem('user'));
this.username = user['user'].user_username;
}
});
if (!this.id_token) {
router.navigate(['index']);
}
}
ngOnInit() {}
openDialog(): void {
let dialogRef = this.dialog.open(SigninComponent, {
width: '450px',
data: {}
});
}
public logout() {
this.authService.logout().subscribe(res => {
if (res['success']) {
localStorage.clear();
this.dataSharingService.setUserLoggedInStatus(false);
}
});
this.router.navigate(['index']);
}
}
Index Component as an example it should redirect a user to dashboard if the global flag is set to true.
import {
Component,
OnInit
} from '#angular/core';
import {
ServiceClassDatasharing
} from '../service/service-class-datasharing';
import {
Router
} from '#angular/router';
#Component({
selector: 'app-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.css']
})
export class IndexComponent implements OnInit {
constructor(private dataSharingService: ServiceClassDatasharing, private router: Router) {
if (this.dataSharingService.isUserLoggedIn.value) {
this.router.navigate(['dashboard']);
}
}
ngOnInit() {}
}
Try using localStorage variable to achieve the same .
Create a function in service class which will set the variable with token if user logs in and function to get the same token.
const key = 'abcde'
setUseLoggedIn(token){
localStorage.setItem(this.key,token);
}
getUserLoggedIn(){
return localStorage.getItem(this.key);
}
set token as null if user is not logged in and check for the same when retrieving the token.

Angular 5 component expecting an argument

Im trying a simple profile app, and all the sudden Im getting error TS2554
ERROR in /app/components/profile/profile.component.ts(25,3): error TS2554: Expected 1 arguments, but got 0.
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { FlashMessagesService } from 'angular2-flash-messages';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: Object;
constructor(
private auth: AuthService,
private flashMsg: FlashMessagesService,
private router: Router
) {
}
ngOnInit() {
this.auth.getProfile().subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
}
}
auth.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import { tokenNotExpired } from 'angular2-jwt';
#Injectable()
export class AuthService {
authToken: any;
user: any;
constructor(
private http: Http
) {
}
getProfile(user) {
let headers = new Headers();
this.loadToken();
headers.append('Authorization', this.authToken);
headers.append('Content-Type','application/json');
return this.http.get('http://localhost:3000/users/profile', {headers:headers})
.map(res => res.json());
}
loadToken() {
const token = localStorage.getItem('id_token');
this.authToken = token;
}
}
Your getProfile is expecting an argument named user but you are not passing it from the component
You need to pass an argument as follows,
this.auth.getProfile(user).subscribe( profile => {
this.user = profile.user;
},
err => {
return false;
});
or if you don't need an argument , remove it from your service method.

Sharing an object from cone component to another Angular 2

I want to share an object from my first component to the second. I am able to log the 'strVal'(string) defined in the Login Component, in my Home Component but I am unable to log the value of 'abc'(Object) from the Login Component, in the HomeComponent. I am confused why one value from Login Component gets available to Home Component and other does not! The code for Login Component in below
Login.Component.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { AuthenticationService } from '../_services/index';
import { User } from '../contract';
#Component({
moduleId: module.id,
templateUrl: 'login.component.html'
})
export class LoginComponent implements OnInit {
model: any = {};
loading = false;
error = '';
us: User[];
abc: any[];
strVal: string = "Rehan";
current: any;
constructor(
private router: Router,
private authenticationService: AuthenticationService) { }
ngOnInit() {
// reset login status
this.authenticationService.logout();
this.getUs();
}
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(result => {
if (result) {
this.router.navigate(['/']);
}
else {
alert('Username and Password Incorrect');
this.loading = false;
this.model = [];
this.router.navigate(['/login']);
}
});
}
getUs() {
this.authenticationService.getUsers().subscribe(
res => this.us = res
);
}
chek() {
this.abc = this.us.filter(a => a.Login === this.model.username);
console.log(this.abc);
}
}
Home.Component.ts
import { Component, OnInit, Input } from '#angular/core';
import { AuthenticationService } from '../_services/index';
import { LoginComponent } from '../login/index';
import { User } from '../contract';
#Component({
moduleId: module.id,
templateUrl: 'home.component.html',
providers: [LoginComponent]})
export class HomeComponent implements OnInit {
users: User[];
constructor(private userService: AuthenticationService, private Log: LoginComponent) { }
ngOnInit() {
this.userService.getUsers().subscribe(
res => this.users = res
);
console.log(this.Log.strVal);
console.log(this.Log.abc);
}
}
Any hint or help will be appreciated. Thanks!

Categories

Resources