Access activated route data from some other component - javascript

We have component ( ka-cockpit-panel) which is not mapped to any route and inserted manually in the some other component as shown below :
..
...
<section class="ka-cockpit-panel cockpit-1 pull-left">
<ka-cockpit-panel></ka-cockpit-panel>
</section>
...
..
In this component i want to access the current active route data.
For eg : if we have some other component ( say ka-integration-component ) and it has some route data associated with it ( as shown below ), whenever we navigate to this component ( via url or by clicking some routerlink ) , we want to access integration component route data in our ka-cockpit-component.
..
...
{
path: "",
component: IntegrationComponent,
data : {
cockpit1 : false,
cockpit2 : true,
kpi : true
},
}
..
...
Basically, we want to configure our ka-cockpit-component for certain components in our app which is mapped to some route so that we can hide / show or change its appearance.
Cockpit component code :
import { Component, OnInit } from '#angular/core';
import { Router,Event,NavigationEnd,ActivatedRoute } from '#angular/router';
#Component({
selector: 'ka-cockpit-panel',
templateUrl: './cockpit-panel.component.html',
styleUrls : ['./cockpit-panel.component.scss']
})
export class CockpitPanelComponent implements OnInit {
constructor(private router:Router,private activatedRoute : ActivatedRoute) {
this.router.events.subscribe( (event:Event) => {
if(event instanceof NavigationEnd) {
console.log("Cockpit Panel Component : Route successfully changed - ",event,this.router,this.activatedRoute);
// THIS IS WHAT WE WANT - get Integration component route data here whenever i navigate to integration component!!!
}
});
}
ngOnInit() { }
}

You have to use Resolve Guard for the thing you want to implement.
// MyDataResolver Service
import { Injectable } from '#angular/core';
import { Router, Resolve, RouterStateSnapshot,
ActivatedRouteSnapshot } from '#angular/router';
#Injectable()
export class MyDataResolver implements Resolve<any> {
constructor(private cs: CrisisService, private router: Router) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<any> {
let pathFromRoot = route.pathFromRoot;
// you can compare pathFromRoot with your route to return different data
return Promise.resolve({
cockpit1 : false,
cockpit2 : true,
kpi : true
});
}
}
// Routing configuration
.
.
{
path: "",
component: IntegrationComponent,
resolve : {
data: MyDataResolver
},
}
.
.
// Component
export class CockpitPanelComponent implements OnInit {
someBinding : string = "testing Value";
constructor(private router:Router,private activatedRoute : ActivatedRoute) {
this.activatedRoute.data.subscribe( (res) => {
// here you will get your data from resolve guard.
console.log(res);
});
}
ngOnInit() { }
}

Related

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.

How can I hide my Navigation Bar if the component is called on all my routes (Angular4)

I know variations of this question have been asked millions of times before but I just cant seem to solve my problem.
So i'm making an accounting site and my problem is that I cant seem to be able to hide the top navigation bar from the login page and still keep it on all my other pages/routes:
I call the Navigation Bar component in app.component.html so it shows on all my pages:
(app.component.html)
<app-navbar>
<router-outlet>
The login page has simple authentication as i'm still making the template, eventually, the username and password will come from a back-end database.
The login page ts file looks like this:
export class LoginComponent implements OnInit {
emailFormControl = new FormControl('', [Validators.required,
Validators.pattern(EMAIL_REGEX)]);
UserName = new FormControl('', [Validators.required]);
password = new FormControl('', [Validators.required]);
constructor(private router: Router) { }
ngOnInit() {
}
loginUser(e) {
e.preventDefault();
console.log(e);
const username = e.target.elements[0].value;
const password = e.target.elements[1].value;
if (username === 'admin' && password === 'admin') {
// this.user.setUserLoggedIn();
this.router.navigate(['accounts']);
}
}
}
I also have a user service:
import { Injectable } from '#angular/core';
#Injectable()
export class UserService {
private isUserLoggedIn;
public username;
constructor() {
this.isUserLoggedIn = false;
}
setUserLoggedIn() {
this.isUserLoggedIn = true;
this.username = 'admin';
}
getUserLoggedIn() {
return this.isUserLoggedIn;
}
}
I've seen answers regarding Auth and such but i can't seem to sculpt the answers around my code.
How do i hide the Navigation bar on the login page?
I'd appreciate any and all help. Thank you
EDIT
This is the routing file as requested by Dhyey:
import {RouterModule, Routes} from '#angular/router';
import {NgModule} from '#angular/core';
import { LoginComponent } from './login/login.component';
import { AdminComponent } from './Components/admin/admin.component';
import { AccountsComponent } from './Components/accounts/accounts.component';
import { MappingComponent } from './Components/mapping/mapping.component';
const appRoutes: Routes = [
{ path: '',
pathMatch: 'full',
redirectTo: 'login' },
{
path: 'login',
component: LoginComponent
},
{
path: 'admin',
component: AdminComponent
},
{
path: 'accounts',
component: AccountsComponent
},
{
path: 'mapping',
component: MappingComponent
},
];
#NgModule({
imports: [
RouterModule.forRoot(appRoutes)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {}
// export const routingComponents = [MappingComponent, AccountsComponent, AdminComponent, LoginComponent];
EDIT 2
This is the app.component.ts file
import { Component } from '#angular/core';
import {FormControl} from '#angular/forms';
import {HttpModule} from '#angular/http';
import { UserService } from './services/user.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass']
})
export class AppComponent {
title = 'app';
myControl: FormControl = new FormControl();
}
You can check if user is logged in through your getUserLoggedIn method:
First you need to inject UserService in app.component.ts:
constructor(public userService: UserService ) { }
Then in your html:
<app-navbar *ngIf="userService.getUserLoggedIn()">
<router-outlet>
This way only when isUserLoggedIn is true, the app-navbar will be shown
What you can also do is let your menu in app component (this is not the problem), and use a route condition to display it or not.
For that, angular provides ActivatedRoute to get info from the current route url https://angular.io/api/router/ActivatedRoute
Import ActivatedRoute or Route should be fine too
Inject in component
Using constructor :
constructor (private activatedRoute: ActivatedRoute / Route) {
this.currentPage = activatedRoute.url;
}
Then check onInit the route info like below
if (this.curentPage === 'admin') { this.displayMenu = false; }
Finally, use your <div class="menu" *ngIf="! displayMenu">...</div>

Resolving data in angular 2

Introduction:
I have a search-box that let's you select a tv show, which then logs the title of the show you clicked, as well as the id for that show. I define this in my:
landingpage.component
<li (click)="selectShow(list.title)" [routerLink]="['/details', list.id]"
*ngFor="let list of shows"> {{list.show}} </li>
When a li is clicked, it sends the list.id as a parameter to my /details component. selectShow just logs the name (list.title) of the show that was clicked.
My problem:
I cannot seem to figure out how to resolve my list.title value, so that it appears in my /details route in this.route.snapshot.data['title']
My code:
app.routing.module.ts
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { TitleResolver } from './services/title.service';
const routes: Routes = [
{ path: '', component: LandingPage },
{
path: 'details/:id', component: ResultPage,
resolve: {
title: TitleResolver //hopefully contains 'title' data here?
}
}
}
];
resultpage.component.ts
title; //variable we will assign to title
constructor(private route: ActivatedRoute) {}
this.route.data
.subscribe((data: { title: Title}) => {
console.log(title);
});
title.service.ts
// this gives us the name of the clicked show
class ShowService {
fetchTitle(title) {
return title;
}
}
#Injectable()
export class TitleResolver {
constructor(private showservice: ShowService) { }
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> | Promise<any> | any {
return this.showservice.fetchTitle(route.params.title);
}
}
My question
What are the intermediate step(s) that I need to do in order to send the selected list.title value from my landingpage.component data to my app.routing.module.ts, so that I can receive it in my resultpage.component ?

Angular 2 share Service between components in different routes

I have a behaviour in Angular 2 project that i don't know how to solve. I'm using webpack with Angular 2 2.3 (if this helps).
I have a complex project with structure like this:
- index.ts
- app.module.ts
- app.component.ts
- app.routes.ts
- services
- login.service.ts
- +innerapp
- inner.routes.ts
- inner.module.ts
- inner.component.ts
- inner.component.html
- services
-inner.service.ts
- insideinner
- insideinner.component.ts
- insideinner.component.html
- header
- header.component.ts
- header.component.html
- form
- form.component.ts
- form.component.html
When you execute shows login and then route to +innerapp. Inner.component.ts loads inner.services.ts and do a http call for data. A lot of data is moved from server and a let of BehaivorSubjects are initialized inside inner.service.ts.
All works fine, but in a moment user clicks button and loads form.component.ts with a big form. User fills form and click submit, in this moment inner.service is called to add data form. My surprise is inner.service haven't data, it's just initialised.
Code below.
//inner.routes.ts
export const routes = [
{ path: '', children: [
{ path: '', component: InnerComponent },
{ path: 'form', component: FormComponent },
]},
];
inner.module.ts
import { routes } from './inner.routes';
import { InnerComponent } from './inner.component';
import { FormComponent } from './insideinner/form/form.component';
// Services
import { InnerService } from './service/inner.service';
#NgModule({
declarations: [
// Components / Directives/ Pipes
InnerComponent,
FormComponent
],
imports: [
RouterModule.forChild(routes),
],
providers: [
InnerService
]
})
export class InnerModule {
public static routes = routes;
}
inner.component.ts:
#Component({
selector: 'inner',
templateUrl: './inner.component.html'
})
export class InnerComponent implements OnInit {
constructor ( private innerService: innerService ) {
this.innerService.fetchData()
.subscribe(
(response) => {
this.innerService.addData(response.json());
},
(error) => {
alert(error);
}
);
}
services/inner.services.ts
import { Injectable } from '#angular/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { Headers, RequestOptions, Http, Response } from '#angular/http';
#Injectable()
export class InnerService {
// Observable string streams
public readonly data: Observable<string>;
// Observable string sources
private _data: BehaviorSubject<string> = new BehaviorSubject(null);
// private properties
private options: RequestOptions;
constructor( public http: Http ) {
this.data = this._user.asObservable();
// http standard values
let token = localStorage.getItem('token');
let cabs = new Headers({ Authorization: 'Bearer ' + token });
this.options = new RequestOptions({ headers: cabs });
}
// Service message commands
public addData (t: string) {
this._data.next(t);
}
public saveData(t: string) {
return this.http.post(blabla,
{
data: t
},
this.options
).map((res: Response) => {
this.addData(t);
return true;
}).catch(this.handleError);
}
private handleError (error: any) {
//code
}
public fetchData(): Observable<any> {
return this.http.get(blabla, this.options)
.map((res) => { return res.body })
.catch(this.handleError);
}
}
insideinner/form/form.component.ts
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
// services & others
import { InnerService } from '../../services/inner.service';
#Component({
selector: 'add-members',
templateUrl: './form.component.html',
encapsulation: ViewEncapsulation.None
})
export class FormComponent implements OnInit {
constructor(
private fb: FormBuilder,
private innerService: InnerService
) {}
public ngOnInit() {
this.showForm = false;
}
public onSubmit(value: string) {
this.innerService.saveData(value); //Fail here, inner service are without data
}
public showAdd() {
this.showForm = true;
}
}
I read a lot of docs and read here similar problems, but solutions aren't working for me.
EDIT 2017-05-31
I think that is dupe question. I see that problem is related with lazyload in routes. I try this solution:
Angular 2 lazy loaded module - service not singleton
and this solution:
Angular 2 How to make singleton service available to lazy loaded modules
But no one work for me. I want to say that this project is in garbage and I began again with Angular 1.6 but I'm really interested in solve this problem to make future projects.

Angular 2 AuthGuard Service with redirect?

I have an application that I am building that implements CanActivate on the dashboard route. It works fine except on page reload, I check a flag in the user service to see if a user is logged in or not. By default this flag is false which kicks the user out to login. Also on page reload I am trying to fetch user data with a token in localStorage, if fetch is successful, I want them to be able to stay on the dashboard. The problem is that I am seeing a glimpse of login and having to manually redirect them to the dashboard. Is there any way to fix this to where the authGuard doesn't do anything until after it checks the API? Code is here: https://github.com/judsonmusic/tfl
dashboard:
import { Component, ViewChild } from '#angular/core';
import { LoginComponent } from "../login.component";
import { UserService } from "../user.service";
import { SimpleChartComponent } from "../charts/simpleChart.component";
import { AppleChartComponent } from "../charts/appleChart.component";
import { BarChartComponent } from "../charts/barChart.component";
import { DonutChartComponent } from "../charts/donutChart.component";
import { AlertComponent } from 'ng2-bootstrap/ng2-bootstrap';
import { ModalDemoComponent } from "../modals/modalDemoComponent";
import { NgInitHelperComponent } from "../helpers/nginit.helper.component";
import { ModalDirective } from "ng2-bootstrap/ng2-bootstrap";
import { MODAL_DIRECTIVES, BS_VIEW_PROVIDERS } from 'ng2-bootstrap/ng2-bootstrap';
#Component({
selector: 'dashboard',
templateUrl: '/app/components/dashboard/dashboard.component.html',
providers: [UserService, BS_VIEW_PROVIDERS],
directives: [SimpleChartComponent, AppleChartComponent, BarChartComponent, DonutChartComponent, AlertComponent, ModalDemoComponent, NgInitHelperComponent, ModalDirective]
})
export class DashboardComponent {
public areas: any;
constructor() {
this.areas = [
"Spiritual",
"Habits",
"Relationships",
"Emotional",
"Eating Habits",
"Relaxation",
"Exercise",
"Medical",
"Financial",
"Play",
"Work/ Life Balance",
"Home Environment",
"Intellectual Well-being",
"Self Image",
"Work Satisfaction"
]
}
}
Routes:
import { Routes, RouterModule } from '#angular/router';
import { AboutComponent } from './components/about.component';
import { PageNotFoundComponent } from "./components/pageNotFound.component";
import { HomeComponent } from "./components/home.component";
import { DashboardComponent } from "./components/dashboard/dashboard.component";
import { SurveyComponent } from "./components/survey/survey.component";
import { ResourcesComponent } from "./components/resources.component";
import { LogoutComponent } from "./components/logout.component";
import { AuthGuard } from "./components/auth-guard.service";
import { loginRoutes, authProviders } from './login.routing';
import { LoginComponent } from "./components/login.component";
const appRoutes:Routes = [
{ path: '', component: HomeComponent },
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
{ path: 'logout', component: LogoutComponent },
{ path: 'resources', component: ResourcesComponent },
{ path: 'survey', component: SurveyComponent },
{ path: 'about', component: AboutComponent },
{ path: 'login', component: LoginComponent },
{ path: '**', component: PageNotFoundComponent }
];
export const appRoutingProviders: any[] = [
authProviders
];
export const routing = RouterModule.forRoot(appRoutes);
login route:
import { Routes } from '#angular/router';
import { AuthGuard } from './components/auth-guard.service';
import { AuthService } from './components/auth.service';
import { LoginComponent } from './components/login.component';
export const loginRoutes: Routes = [
{ path: 'login', component: LoginComponent }
];
export const authProviders = [
AuthGuard,
AuthService
];
In AuthGuard do the following:
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate() {
if (/*user is logged in*/) {
this.router.navigate(['/dashboard']);
return true;
} else {
this.router.navigate(['/Login']);
}
return false;
}
}
Here's how to correctly handle redirects in a guard by using an UrlTree
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivateChild {
constructor(
private authService: AuthService,
private logger: NGXLogger,
private router: Router
) {}
canActivateChild(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> {
return this.authService.isLoggedIn().pipe(
map(isLoggedIn => {
if (!isLoggedIn) {
return this.router.parseUrl('/login');
}
return true;
})
);
}
}
Big thanks to Angular In Depth for the explanation!
You can now return a UrlTree from an AuthGuard, or a boolean true / false.
Kind of amazed nobody has mentioned this yet! Sorry no example right now, but the idea is pretty simple.
I actually changed my service to this and it works:
import { Injectable } from '#angular/core';
import { CanActivate, Router,
ActivatedRouteSnapshot,
RouterStateSnapshot } from '#angular/router';
import { AuthService } from './auth.service';
import {UserService} from "./user.service";
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router, private userService: UserService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (this.authService.isLoggedIn){
console.log('ATUH GUARD SAYD THEY ARE ALREADY LOGGED IN!');
return true;
}else {
this.userService.getUser().subscribe((user) => {
console.log('AUTH GUARD GETTING USER', user);
if (user._id) {
this.authService.isLoggedIn = true;
// Store the attempted URL for redirecting
this.authService.redirectUrl = state.url;
this.router.navigate(['/dashboard']);
return true;
}else{
console.log('Validation Failed.');
localStorage.clear();
this.router.navigate(['/login']);
return false;
}
}, (error) => {
console.log('There was an error.');
this.router.navigate(['/login']);
return false
});
}
}
}
I solved it like this and used it in my AuthGuard
isLoggedIn(): Observable<boolean> {
return this.afAuth.authState
.pipe(
take(1),
map(user => {
return !!user;
},
() => {
return false;
}
),
tap(loggedIn => {
if (!loggedIn) {
this.router.navigate(['/']);
}
}
));
}
This is what I did for canActivate
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
// ensure the user is properly logged in and the url he's requesting is within his right
if (this.authSvc.getRole().trim().length > 0 && this.authSvc.getToken().trim().length > 0
&& state.url.includes(this.authSvc.getRole().trim())) {
let url: string;
// base on user's role send to different url to check
if (this.authSvc.getRole().trim() === 'client') {
url = ClientAccessRequestUrl;
} else {
url = AdminAccessRequestUrl;
}
return this.http.post<AccessRequestResponse>(url, {
token: this.authSvc.getToken(),
}).pipe(map(response => {
console.log('response is:', response);
// check successful then let go
if (response.reply === true) {
return true;
// unless go to visitor site
} else {
return this.router.createUrlTree(['/visitor']);
}
}));
} else {
return this.router.createUrlTree(['/visitor']);
}
}
The best way to do redirects after authentication is structuring the logic as shown below;
in the AuthGuard,
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
{
// keep the requsted url for redirect after login
let url: string = state.url;
// call the authentication function
var authenticated = this.http.isAuthenticated();
var subject = new Subject<boolean>();
authenticated.subscribe(
(res) => {
//if not authenticated redirect to the login route with the initial route attached as an query param 'returnUrl'
if(!res.successState) {
this.router.navigate(['/login'], {queryParams: {returnUrl: url}});
}else{
// the user is authenticated just go to the requested route
subject.next(res.successState);
}
});
return subject.asObservable();
}
in the login route
loginAction(data: any){
// if the auth works fine the go the route requested before the inconviniences :)
if(data.successState){
// get the query params to see if there was a route requested earlier or they just typed in the login route directly
this.route.queryParams.subscribe(params => {
// console.log(params); //returnUrl
if(params.returnUrl){
// pearse the url to get the path and query params
let urlTree: UrlTree = this.router.parseUrl(params.returnUrl);
let thePathArray : any[] = [];
// populate it with urlTree.root.children.primary.segments[i].path;
for(let i = 0; i < urlTree.root.children.primary.segments.length; i++){
thePathArray.push(urlTree.root.children.primary.segments[i].path);
}
let the_params = urlTree.queryParams;
this.router.navigate(thePathArray, {queryParams: the_params});
}else{
this.router.navigate(['']);
}
});
}else{
// tell them about it and let them try again or reset the password
}
}
That should work perfectly. it will even preserve query params for the initial request.
THANKS

Categories

Resources