I am trying to use my service for getting values from database server to display onscreen. I do so with a resolver for the service as the database is a little slow sometimes.
But the data this.route.data.subscribe gives me is always undefined no matte what I tried. I checked if the service is getting a response from the server, and it does. Weird thing is that if I use the service directly everything works fine.
Component where the data is processed:
import { Component, OnInit, Input } from '#angular/core';
import { TempsService, Temps } from '../../temps.service';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-temps',
templateUrl: './temps.component.html',
styleUrls: ['./temps.component.scss']
})
export class TempsComponent implements OnInit {
#Input() solar: boolean;
solarURL: string = 'tempSolar';
waterURL: string = 'tempWater';
tempSolar: number;
tempWater: number;
timestamp: string;
temps: Temps;
constructor(private route: ActivatedRoute,
private tempService: TempsService) { }
showWaterTemp() {
this.tempService.getTemp(this.waterURL)
.subscribe(data => {
this.tempWater = data.rawValue;
this.timestamp = data.time;
});
}
showSolarTemp() {
this.route.data
.subscribe(data => {
this.tempSolar = data.rawValue;
});
}
ngOnInit() {
if (this.solar) {
this.showSolarTemp();
this.showWaterTemp();
}
}
}
This is he routing module (I am using the NowUI Angular theme by CreativeTim, so most things were done by them):
import { Routes } from '#angular/router';
import { DashboardComponent } from '../../dashboard/dashboard.component';
import { UserProfileComponent } from '../../user-profile/user-profile.component';
import { TableListComponent } from '../../table-list/table-list.component';
import { TypographyComponent } from '../../typography/typography.component';
import { IconsComponent } from '../../icons/icons.component';
import { MapsComponent } from '../../maps/maps.component';
import { NotificationsComponent } from '../../notifications/notifications.component';
import { TempsComponent } from '../../dashboard/temps/temps.component';
import { TempResolver } from '../../temp-resolver/temp-resolver.resolver';
export const AdminLayoutRoutes: Routes = [
{ path: 'dashboard', component: DashboardComponent, children: [
{ path: '', component: TempsComponent, resolve: { temps: TempResolver } }
] },
{ path: 'user-profile', component: UserProfileComponent },
{ path: 'table-list', component: TableListComponent },
{ path: 'typography', component: TypographyComponent },
{ path: 'icons', component: IconsComponent },
{ path: 'maps', component: MapsComponent },
{ path: 'notifications', component: NotificationsComponent }
];
And this is how the resolver looks like:
import { Injectable } from '#angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Temps, TempsService } from '../temps.service';
import { Observable } from 'rxjs/internal/Observable';
#Injectable()
export class TempResolver implements Resolve<Temps> {
test: number;
constructor(private tempService: TempsService) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Temps> {
this.tempService.getTemp('tempSolar').subscribe(data => {this.test = data.rawValue})
alert(this.test)
return this.tempService.getTemp('tempSolar');
}
}
In my opinion this is a really strange problem.
UPDATE:
This is the service for getting the data:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { TempsComponent } from './dashboard/temps/temps.component'
export interface Temps {
id: string;
time: string;
date: string;
name: string;
rawValue: number;
}
#Injectable()
export class TempsService {
constructor(private http: HttpClient) { }
url: string = window.location.hostname;
tempUrl = 'http://' + this.url + ':3000/latestTime/';
getTemp(temp: String) {
return this.http.get<Temps>(this.tempUrl + temp);
}
}
I just tried adding the resolve to the dashboard component in which the Temp component is used. And now it works like a charm.
It looks now like this:
{ path: 'dashboard', component: DashboardComponent, resolve: {temps: TempResolver} }
instead of this:
{ path: 'dashboard', component: DashboardComponent,
children: [{ path: '', component: TempsComponent, resolve: { temps: TempResolver } }]
},
Can you try this
this.route.data
.subscribe(({temps}) => {
this.tempSolar = temps;
});
No matter what, avoid subscribing to getTemps() in resolve(), simply return an Observable<whatever>. Keep in mind the asynchronous nature of getTemps(). alert(this.test) will almost always execute before getTemps() has been completed, basically guaranteeing it will be undefined at the time of alert.
Simply return getTemp() so that it returns an Observable<Temps>:
import { Injectable } from '#angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Temps, TempsService } from '../temps.service';
import { Observable } from 'rxjs';
#Injectable()
export class TempResolver implements Resolve<Temps> {
constructor(private tempService: TempsService) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Temps> {
return this.tempService.getTemp('tempSolar');
}
}
Then in the component extracting the rawValue property as needed:
showSolarTemp() {
this.route.data.subscribe((data: { temps: Temps }) => {
this.tempSolar = data.temps.rawValue;
});
}
Here is a StackBlitz showing the functionality in action.
Hopefully that helps!
Related
I am working on an e-commerce app who's front-end is made in Angular 13.
I use a service to handle the products coming from an API. I run into a problem while ring to display the product details.
See Stackblitz demo HERE.
In app\services\product.service.ts I have:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '#angular/common/http';
import { Product, ProductResponse } from '../models/product';
#Injectable({
providedIn: 'root'
})
export class ProductService {
products: Product[] = [];
apiURL: string = 'https://dummyjson.com';
constructor(private http: HttpClient) {}
// Product List
public getProducts(): Observable<ProductResponse>{
return this.http.get<ProductResponse>(`${this.apiURL}/products`);
}
// Product Details (single product)
public getProductDetails(id: Number): Observable<ProductResponse>{
return this.http.get<ProductResponse>(`${this.apiURL}/products/${id}`);
}
}
In app\app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { CommonModule } from '#angular/common';
import { HttpClientModule } from '#angular/common/http';
import { AppComponent } from './app.component';
import { Routes, RouterModule } from '#angular/router';
import { NavbarComponent } from './components/navbar/navbar.component';
import { FooterComponent } from './components/footer/footer.component';
import { SidebarComponent } from './components/sidebar/sidebar.component';
import { HomeComponent } from './components/home/home.component';
import { ProductItemComponent } from './components/product-item/product-item.component';
import { ProductsListComponent } from './components/products-list/products-list.component';
import { ProductsDetailsComponent } from './components/products-details/products-details.component';
const routes: Routes = [
{ path: '', component: HomeComponent},
{ path: 'products', component: ProductsListComponent},
{ path: 'products/show/:id', component: ProductsDetailsComponent},
];
#NgModule({
declarations: [
AppComponent,
NavbarComponent,
FooterComponent,
SidebarComponent,
ProductsListComponent,
ProductItemComponent ,
ProductsDetailsComponent
],
imports: [
CommonModule,
BrowserModule,
HttpClientModule,
RouterModule.forRoot(routes),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
In app\models\product.ts:
export class Product {
id?: number;
title?: string;
description?: string;
price?: number;
discountPercentage?: number;
rating?: number;
stock?: number;
brand?: string;
category?: string;
thumbnail?: string;
}
export interface ProductResponse {
products: Product[];
total: number;
skip: number;
limit: number;
}
In app\components\products-details\products-details.component.ts I have:
import { Component, OnInit, InputDecorator, Input } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { Product, ProductResponse } from '../../models/product';
import { ProductService } from '../../services/product.service';
#Component({
selector: 'app-products-details',
templateUrl: './products-details.component.html',
styleUrls: ['./products-details.component.css']
})
export class ProductsDetailsComponent implements OnInit {
#Input() product!: Product;
productResponse: any;
constructor(private ProductService: ProductService, private Router: Router, private ActivatedRoute:ActivatedRoute) { }
ngOnInit(): void {
const id = Number(this.ActivatedRoute.snapshot.paramMap.get('id'));
this.ProductService.getProductDetails(id).subscribe((response) => (this.productResponse = response));
}
}
In app\components\products-details\products-details.component.html I have:
<h1>{{ product.title }}</h1>
The problem
When I access a product details route (for instance, http://localhost:4200/products/show/1), the page displays an empty <h1> tag and the Chrome console shows Cannot read properties of undefined (reading 'title').
Where is my mistake?
Unlike the https://dummyjson.com/products endpoint, the https://dummyjson.com/products/{id} returns a plain Product object, so:
// product.service.ts
public getProductDetails(id: Number): Observable<Product>{
return this.http.get<Product>(`${this.apiURL}/products/${id}`);
}
// products-details.component.ts
// the #Input decorator is wrong — the data is not passed to the component from outside
// but instead fetched inside of the component
#Input() product!: Product;
// productResponse: any — this field is unused and should be removed
ngOnInit(): void {
...
this.ProductService.getProductDetails(id).subscribe((product) => (this.product = product));
}
The #Input() property only works for external assignment of values.
The following will probably do what you want:
product: Product | undefined;
this.ProductService.getProductDetails(id).subscribe((response) => (this.product = response));
<h1>{{ product?.title }}</h1>
Furthermore I believe handling raw responses should happen in the service instead of the component, but that may be a matter of personal preference.
I develop an application in angular, And I want testing my application with files spec.
But when I launch the file spec I have an error
"TypeError: settings is null in http://localhost:9876/karma_webpack/vendor.js (line 163664)"
I start with angular so I have any idea to correct my problem
modal-result-training.component.spec.ts
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { IAttempt } from '../../models/attempt.model';
import { ITraining } from '../../models/training.model';
import { ModalResultTrainingComponent } from './modal-result-training.component';
import { OpinionsService } from 'app/feature/elearning-training/services/opinions.service';
import { EntityService } from '#core/services/entity.service';
import { FireModule } from '../../../../#core/fire/fire/fire.module';
import { UserService } from '#core/services/user.service';
import { NbAclService } from '#nebular/security';
import { NbAuthModule } from '#nebular/auth';
import { NbDialogModule, NbThemeModule } from '#nebular/theme';
import { Router, RouterModule } from '#angular/router';
import { DialogService } from '#core/services/dialog.service';
const training = {
id: 'ZGtz6yrEemCNTo5KAytu',
refProject: 'JGvD1faO8L2vWb66BQ87',
publicationDate: new Date(),
version: 1,
name: 'My project',
groups: [],
category: '',
description: '',
minimalScore: 80,
previewPNG: '',
level: 5,
gain: 5,
fromDate: new Date(),
toDate: new Date(),
totalSlides: 20,
stars: 3,
averageStars: 4,
comments: 15,
groupsHistoric: [],
} as ITraining;
const trainingResults = {
id: 'ZDqzqg',
version: 1,
trainingID: 'xSOvDC6vpZTzVqXy5owQ',
userID: 'qdZDZDqg',
groupsIDs: [],
date: new Date(),
time: 10,
score: 10,
validated: true,
finished: true,
currentStep: 4,
} as IAttempt;
fdescribe('ModalResultTrainingComponent', () => {
let component: ModalResultTrainingComponent;
let fixture: ComponentFixture<ModalResultTrainingComponent>;
let opinionsService: OpinionsService;
let dialogService: DialogService;
let userService: UserService;
let router: Router;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ModalResultTrainingComponent],
providers: [EntityService, OpinionsService, UserService, NbAclService],
imports: [FireModule, RouterModule.forRoot([]), NbThemeModule.forRoot(), NbDialogModule.forRoot(), NbAuthModule.forRoot()],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ModalResultTrainingComponent);
component = fixture.componentInstance;
opinionsService = TestBed.inject(OpinionsService);
userService = TestBed.inject(UserService);
dialogService = TestBed.inject(DialogService);
router = TestBed.inject(Router);
component.training = training;
component.results = trainingResults;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
modal-result-training.component.ts
the begin of modal
import { Component, Input, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { IOpinion } from '#core/models/opinion.model';
import { IUser } from '#core/models/user.model';
import { DialogService } from '#core/services/dialog.service';
import { UserService } from '#core/services/user.service';
import { NbDialogRef, NbGlobalPhysicalPosition, NbToastrService } from '#nebular/theme';
import { TranslateService } from '#ngx-translate/core';
import { OpinionsService } from 'app/feature/elearning-training/services/opinions.service';
import { cleanObject } from 'app/shared/utils';
import { first, switchMap } from 'rxjs/operators';
import { IAttempt } from '../../models/attempt.model';
import { ITraining } from '../../models/training.model';
/**
* A modal to displays the training results when it's complete.
*
* #export
* #class ResultTrainingComponent
* #implements {OnInit}
*/
#Component({
selector: 'ngx-modal-result-training',
templateUrl: './modal-result-training.component.html',
styleUrls: ['./modal-result-training.component.scss'],
})
export class ModalResultTrainingComponent implements OnInit {
#Input() results: IAttempt;
#Input() training: ITraining;
public validated = false;
public opinionForm: FormGroup;
public selectedStars = 0;
public hasAlreadyComment = true;
public percentageScore: number;
constructor(
private opinionsService: OpinionsService,
private userService: UserService,
private ref: NbDialogRef<ModalResultTrainingComponent>,
private router: Router,
private toastrService: NbToastrService,
private translateService: TranslateService,
private dialogService: DialogService
) {}
Thanks for help
I'm trying to unit test a component that requires a Resolver, Router and ActivatedRoute as dependencies. I've tried to use the RouterTestingModule and mock my resolver to provide them in the testing module, but it seems to have some side effects on the creation of the component instance.
Here is the code of my component:
History.component.ts
import { Component, OnDestroy, OnInit } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { Subscription } from 'rxjs';
import { Transaction } from '../models/transaction.model';
#Component({
selector: 'app-history',
templateUrl: './history.component.html',
styleUrls: ['./history.component.scss']
})
export class HistoryComponent implements OnInit, OnDestroy {
history: Transaction[] = [];
selectedTransaction: Transaction | undefined;
subscription: Subscription = new Subscription();
constructor(
private route: ActivatedRoute,
private router: Router,
) { }
ngOnInit(): void {
this.history = this.route.snapshot.data.history;
const routeSubscription = this.route.params.subscribe((params) => {
if (params.id) {
this.setSelectedTransaction(+params.id);
}
});
this.subscription.add(routeSubscription);
}
setSelectedTransaction(transactionId: number): void {
const historyTransaction = this.history.find((transaction) => transaction.id === transactionId);
this.selectedTransaction = historyTransaction;
}
displayTransaction(transaction: Transaction): void {
this.router.navigate(['transactions', transaction.id]);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
And here is the current unit test:
History.spec.ts
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { ActivatedRoute, Resolve, Routes } from '#angular/router';
import { RouterTestingModule } from '#angular/router/testing';
import { Observable, of } from 'rxjs';
import { Transaction } from '../models/transaction.model';
import { HistoryComponent } from './history.component';
import { HistoryResolver } from './history.resolver';
const mockRawTransactions = [
{
"id":"1",
"created_at":"2016-01-01T08:30:39-0300",
"counterparty_name":"Uber",
"debit":"false",
"credit":"true",
"amount":"44.20",
"currency":"EUR",
"operation_type":"refund",
"attachements":[
{
"url":"https:\/\/fakeimg.pl\/350x200\/?text=Hello"
}
],
}
];
const mockTransactions = mockRawTransactions.map((transaction) => new Transaction(transaction));
class HistoryMockResolver implements Resolve<Transaction[]> {
resolve(): Observable<Transaction[]> {
return of(mockTransactions);
}
}
describe('HistoryComponent', () => {
const historyRoutes: Routes = [
{ path: 'transactions', component: HistoryComponent },
{ path: 'transactions/:id', component: HistoryComponent },
];
let component: HistoryComponent;
let fixture: ComponentFixture<HistoryComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HistoryComponent ],
imports: [
RouterTestingModule.withRoutes(historyRoutes),
],
providers: [
{
provide: ActivatedRoute,
useValue: {
snapshot: {
params: { id: 1 },
},
},
},
{ provide: HistoryResolver, useClass: HistoryMockResolver },
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HistoryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
console.log('Component', component);
expect(component).toBeTruthy();
});
it('should display transactions', () => {
component.history = mockTransactions;
expect(component.history).toBeDefined();
expect(component.history).toHaveSize(mockRawTransactions.length);
});
it('should display a single transaction', () => {
component.history = mockTransactions;
component.displayTransaction(component.history[0]);
expect(component.selectedTransaction).toBeDefined();
expect(component.selectedTransaction).toEqual(component.history[0]);
});
});
The component is defined and well displayed in the console.log while running the tests in Karma, but Karma raises errors for each test case and evaluates the component as undefined.
Here is the first error:
TypeError: Cannot read property 'history' of undefined
at HistoryComponent.ngOnInit (http://localhost:9876/_karma_webpack_/webpack:/src/app/history/history.component.ts:22:45)
at callHook (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:2486:1)
at callHooks (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:2457:1)
at executeInitAndCheckHooks (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:2408:1)
at refreshView (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:9207:1)
at renderComponentOrTemplate (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:9306:1)
at tickRootContext (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:10532:1)
at detectChangesInRootView (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:10557:1)
at RootViewRef.detectChanges (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/core.js:22569:1)
at ComponentFixture._tick (http://localhost:9876/_karma_webpack_/webpack:/node_modules/#angular/core/__ivy_ngcc__/fesm2015/testing.js:141:1)
What should I fix in my testing module ?
This is because of this.route.snapshot.data.history and data being undefined as your have not passed it in your mock activated snapshot.
You can update your provider for activated route snapshot in History.spec.ts
{
provide: ActivatedRoute,
useValue: {
snapshot: {
params: { id: 1 },
data: {history: 'something-history-obj'}
},
},
},
Or you can always use this.route.snapshot.data?.history within History.component.ts if it is indeed nullable
I want to show a snackbar when my login fails that says 'error connecting'. That much is simple. But then I would like it to try again either after 10 seconds when it is dismissed or after the action dismisses the snackbar. But my observable runs immediately and I am stuck in an infinite observable loop trying to login immediately after it has failed.
login.page.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { UserService, HelpersService, AuthService } from '../../services/';
#Component({
selector: 'login-page',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss']
})
export class LoginPage {
loginError: any;
constructor(
private router: Router,
private auth: AuthService,
public helpers: HelpersService,
) { }
login() {
this.auth.login().then((response) => {
this.router.navigate(['/']);
}).catch(error => {
this.loginError = this.helpers.notify('error connecting', 'try again', 10000);
this.helpers.notifyAction(this.loginError, this.login());
});
};
}
helpers.service.ts
import { Injectable } from '#angular/core';
import { MdSnackBar, MdSnackBarRef } from '#angular/material';
#Injectable()
export class HelpersService {
constructor(public snackBar: MdSnackBar) {}
notify(message: string, action: string, duration: number) {
return this.snackBar.open(message, action, {duration});
}
notifyAction(notification: MdSnackBarRef<any>, next) {
return notification.onAction().subscribe(() => next);
}
}
You were almost there. Please pay attention to my comments in your sources.
login.page.ts
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { UserService, HelpersService, AuthService } from '../../services/';
#Component({
selector: 'login-page',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss']
})
export class LoginPage {
loginError: any;
constructor(
private router: Router,
private auth: AuthService,
public helpers: HelpersService,
) { }
login() {
this.auth.login().then((response) => {
this.router.navigate(['/']);
}).catch(error => {
this.loginError = this.helpers.notify('error connecting', 'try again', 10000);
this.helpers.notifyAction(this.loginError, this.login); // no parenthesis here!
});
};
}
helpers.service.ts
import { Injectable } from '#angular/core';
import { MdSnackBar, MdSnackBarRef } from '#angular/material';
#Injectable()
export class HelpersService {
constructor(public snackBar: MdSnackBar) {}
notify(message: string, action: string, duration: number) {
return this.snackBar.open(message, action, {duration});
}
notifyAction(notification: MdSnackBarRef<any>, next) {
return notification.onAction().subscribe(() => next()); // they are here!
}
}
Live Example Infinity Login
You need to pass function instead of calling it. And also take care about context by using arrow function or bind.
login.page.ts
this.helpers.notifyAction(this.loginError, () => this.login());
helpers.service.ts
notifyAction(notification: MdSnackBarRef<any>, next) {
notification.afterDismissed().subscribe(next);
return notification.onAction().subscribe(notification.dismiss);
}
I am using Angular2 and Auth0 for authentication. I can see the token is being stored into local storage. I am writing a function in my auth.service.ts file to check for a valid token then calling that function on init in the component. I have tried many different variations of this function but cannot get the app to validate correctly.
After I login it forwards me back to the home page even when I do login and retrieve a valid token.
My goal is to not allow access to this page without a valid token. But when there is a valid token allow access to this page.
This is what I have tried currently,
auth.service.ts
import { Injectable } from '#angular/core';
import { Router, CanActivate } from '#angular/router';
import { tokenNotExpired, JwtHelper } from 'angular2-jwt';
import { myConfig } from './auth.config';
// Avoid name not found warnings
declare var Auth0Lock: any;
var options = {
theme: {
logo: '../assets/img/logo.png',
primaryColor: '#779476'
},
auth: {
responseType: 'token',
redirect: true,
redirectUrl: "http://localhost:3000/dashboard",
},
languageDictionary: {
emailInputPlaceholder: "email#example.com",
title: "Login or SignUp"
},
};
#Injectable()
export class Auth {
// Configure Auth0
lock = new Auth0Lock(myConfig.clientID, myConfig.domain, options,{});
constructor(private router: Router ) {
// Add callback for lock `authenticated` event
this.lock.on('authenticated', (authResult) => {
localStorage.setItem('jwtToken', authResult.idToken);
});
}
public login() {
this.lock.show();
};
public authenticated() {
return tokenNotExpired();
};
public isAuthenticated(): boolean {
try {
var jwtHelper: JwtHelper = new JwtHelper();
var token = this.accessToken;
if (jwtHelper.isTokenExpired(token))
return false;
return true;
}
catch (err) {
return false;
}
}
private get accessToken(): string {
return localStorage.getItem('jwtToken');
}
public logout() {
localStorage.removeItem('jwtToken');
};
}
guard.service.ts
import { Injectable } from '#angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Auth } from './auth.service';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class Guard implements CanActivate {
constructor(protected router: Router, protected auth: Auth ) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
if (state.url !== '/pages/home' && !this.auth.isAuthenticated()) {
this.auth.logout();
this.router.navigate(['/pages/home']);
return false;
}
return true;
}
}
app.routing.ts
import {Guard} from "./services/guard.service";
const appRoutes: Routes = [
{
path: '',
redirectTo: 'pages/home',
pathMatch: 'full',
},
{
path: '',
component: FullLayoutComponent,
canActivate: [Guard],
data: {
title: 'Home'
},
children: [
{
path: 'dashboard',
component: DashboardComponent,
data: {
title: 'Dashboard'
}
},
app.module.ts
import { Guard } from "./services/guard.service";
import { Auth } from "./services/auth.service";
providers: [
Guard,
Auth
],
right way to achieve this to use Guards
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { AuthService } from './authService';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(protected router: Router, protected authService: AuthService) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
if (state.url !== '/login' && !this.authService.isAuthenticated()) {
this.authService.logOut();
this.router.navigate(['/login']);
return false;
}
return true;
}
}
And in you rotes set
path: 'admin',
component: AdminPages.AdminPagesComponent,
canActivate: [AuthGuard],
children: [
{
path: 'dashboard',
component: Dashboard,
data: {
menu: {
title: 'Dashboard',
icon: 'ion-android-home',
selected: false,
expanded: false,
order: 0
}
}
},
authservice
public isAuthenticated(): boolean {
try {
var jwtHelper: JwtHelper = new JwtHelper();
var token = this.accessToken;
if (jwtHelper.isTokenExpired(token))
return false;
return true;
}
catch (err) {
return false;
}
}
public logOut(): void {
localStorage.removeItem("access_token");
}
private get accessToken(): string {
return localStorage.getItem('access_token');
}
private saveJwt(jwt): void {
if (jwt) {
localStorage.setItem('access_token', jwt)
}
}
public login(loginModel: LoginModel): Promise<void> {
return new Promise<void>((resolve, reject) => {
var username = loginModel.email;
var password = loginModel.password;
var creds =
"username=" + username + "&password=" + password + "&client_id=" + this.clientId + "&grant_type=" + this.grant_type;
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
this.httpClient.post(this.identityServerUrl + "/connect/token", creds, { headers })
.toPromise()
.then(response => {
this.saveJwt(response.json().access_token);
resolve();
})
.catch(err => {
reject(err.json().error_description)
});
});
}