Why is my user ID undefined when passing it into my URL? - javascript

I am building a profile page and trying to get the authenticated user data to display there. My API call works with their id, and it works on the front end if I manually enter the id into the url.
But when I try to navigate to the profile from the navbar, I receive a
400 Bad Request for URL: http://localhost:3000/users/undefined
What I can assume right now is that it's an asynchrony issue. My profile page calls the user data, but that user data isn't available in my nav component. And it seems as though I need to pass in my id param into my profile [routerLink] if I want to navigate correctly. Since my user data isn't available in my nav component, it has nothing to pass.
Is there a better approach to this? Should I be using an event emitter?
Fairly new to Angular - help much appreciated!
Profile Component
import { Component, OnInit, Input } from '#angular/core';
import { AuthService } from '.././services/auth.service';
import { UserService } from '.././services/user.service'
import { Router, ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css'],
providers: [UserService]
})
export class ProfileComponent implements OnInit {
currentUser;
isAuth: boolean;
constructor(
private session: AuthService,
private router: Router,
private userService: UserService,
private route: ActivatedRoute
) {
this.session.isAuth
.subscribe((isAuth: boolean) => {
// user will be false if logged out
// or user object if logged in.
this.isAuth = isAuth;
});
if (this.session.token) {
this.isAuth = true;
console.log(this.session);
} else {
this.isAuth = false;
}
}
ngOnInit() {
this.route.params.subscribe(params => {
this.getUserDetails(params['id']);
});
}
getUserDetails(id) {
this.userService.get(id)
.subscribe((user) => {
this.currentUser = user;
console.log(this.currentUser);
});
}
}
Nav Template
Where I'm navigating to my profile page.
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">bnb</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li *ngIf="!isAuth"><a [routerLink]="['login']">Login</a></li>
<li *ngIf="isAuth"><a [routerLink]="['profile']"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Profile</a></li>
<li *ngIf="isAuth"><a (click)="logout()">Logout</a></li>
<li *ngIf="!isAuth"><a [routerLink]="['signup']">Signup</a></li>
</ul>
</div>
</div>
</nav>
Nav Component
import { Component, OnInit, Input } from '#angular/core';
import { AuthService } from '.././services/auth.service';
import { UserService } from '.././services/user.service';
import { Router, ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
isAuth: boolean;
currentUser: any;
constructor(
private session: AuthService,
private userService: UserService,
private router: Router,
private route: ActivatedRoute
) {
this.currentUser = JSON.parse(localStorage.getItem("User"))
console.log("USER",this.currentUser) //Currently returns Null
console.log(this.session)
this.session.isAuth
.subscribe((isAuth: boolean) => {
// user will be false if logged out
// or user object if logged in.
this.isAuth = isAuth;
});
if (this.session.token) {
this.isAuth = true;
} else {
this.isAuth = false;
}
}
ngOnInit() {
}
logout() {
this.session.logout();
}
}
Router
import { Routes } from '#angular/router';
import { LoginComponent } from '../login/login.component';
import { SignupComponent } from '../signup/signup.component';
import { HomeComponent } from '../home/home.component';
import { RentalListingsComponent } from '../rental-listings/rental-listings.component';
import { SingleRentalComponent } from '../rental-listings/single-rental/single-rental.component';
import { ProfileComponent } from '../profile/profile.component'
import { AuthService } from '../services/auth.service';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'login', component: LoginComponent },
{ path: 'signup', component: SignupComponent },
{ path: 'rentals', component: RentalListingsComponent },
{ path: 'listing', component: SingleRentalComponent },
{ path: 'profile/:id', component: ProfileComponent, canActivate: [AuthService] } <--profile path. I know I have to match my url paths, but don't know how to do this from the navbar.
// { path: 'home', component: HomeComponent, canActivate: [AuthService] },
{ path: '**', redirectTo: '' }
];

Thanks for providing the detail. Somewhere you need to subscribe to 'after login' or 'authentication' event, grab the user profile JSON, and save it to localstorage so you can use it anywhere you want. If you can't hook in or subscribe to one of these, then do it imperatively somewhere convenient in your code. Find out what call you can make to fetch the entire user JSON and save it as follows...
Check out my AuthService init() below. First line is this.authProvider.on('authenticated', this.onAuth);. Whatever authentication service API you are using should provide a way for you to specify a callback (providing the login token) whenever someone logs in. The onAuth callback function saves the token in localstorage and then fetchProfile(...){...} makes another call to the authentication service API to get the whole JSON user profile using the token just received this.user.getProfile(idToken, this.onProfile);. For example, I use Auth0 in projects, and my call to Auth0 API looks like this.lock.getProfile(idToken, this.onProfile); but I replaced that with an example of what your call might look like this.user.getProfile(idToken, this.onProfile); So use whatever your API uses replace in fetchProfile. Then the onProfile callback saves the entire JSON profile in a single key in local storage using this.localStorage.set('profile', profile); Then you can get it any time by calling this.localStorage.get('profile').
Do not provide UserService through the lazy-loaded ProfileComponent. That creates a separate branch on the dependency injection tree you might not want. See https://angular-2-training-book.rangle.io/handout/modules/shared-modules-di.html Import the UserService in a top-level module like AppModule or SharedModule and provide it there. No need to export it if it's in AppModule.
app.module.ts
...
#NgModule({
imports: [
...
UserService,
...
]
providers: [
...
UserService,
...
]
Handle Auth related stuff in Auth, not Profile. Profile seems visual/implementation-specific (e.g. it has a template). Here is a code snippet example.
auth.service.ts
#Injectable()
export class Auth {
userProfile: UserProfile;
constructor(
...
private localStorage: LocalStorageService,
private router: Router,
private user: UserService,
private authProvider: ...
...
) {
}
init() {
this.authProvider.on('authenticated', this.onAuth);
// Set userProfile attribute if already saved profile
this.userProfile = this.localStorage.get('profile');
setTimeout(() => { // let AppComponent listener initialize
this.localStorage.set('profile', this.userProfile);
}, 0);
}
}
onAuth = (authResult: AuthResult) => {
this.localStorage.set('id_token', authResult.idToken);
this.fetchProfile(authResult.idToken);
}
// Save current route for redirect url
login() {
this.localStorage.set('redirect_url', this.router.url);
this.authProvider.show({initialScreen: 'login'});
};
// Check if user is logged in.
authenticated() {
// Check if unexpired token.
// Searches for item in localStorage with key == 'id_token'
return this.authProvider.tokenNotExpired();
};
logout() {
this.router.navigateByUrl('');
this.userProfile = undefined; // do before localstorage
this.localStorage.remove('id_token');
this.localStorage.remove('profile');
};
fetchProfile(idToken: string) {
this.user.getProfile(idToken, this.onProfile);
}
/**
* On profile event callback.
* Save profile to LocalStorage.
* Redirect to url if present in LocalStorage.
*/
onProfile = (error: any, profile: UserProfile) => {
if (error) {
console.log(error);
return;
}
this.userProfile = profile;
this.localStorage.set('profile', profile);
// Redirect if there is a saved url to do so.
const redirectUrl: string = this.localStorage.get('redirect_url');
if (redirectUrl !== undefined) {
this.router.navigateByUrl(redirectUrl);
this.localStorage.remove('redirect_url');
}
}
Interact with localStorage through a LocalStorageService and subscribe to changes as follows.
localstorage.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class LocalStorageService {
[key:string]: any;
/**
* define your localstorage variables here as observables
*/
id_token$ = new Subject();
redirect_url$ = new Subject();
profile$ = new Subject();
customer$ = new Subject();
set(key: string, value: any) {
this[key + '$'].next(value); // this will make sure to tell every subscriber about the change.
localStorage.setItem(key, JSON.stringify(value));
}
get(key: string) {
const value = localStorage.getItem(key);
return value && JSON.parse(value);
}
remove(key: string) {
this[key + '$'].next(undefined);
localStorage.removeItem(key);
}
}
Don't do so much in constructor. Example:
app.component.ts
export class AppComponent implements OnDestroy, OnInit {
webRobot: boolean = false;
private profileSub: any;
private customerSub: any;
private subscriptionSub: any;
constructor(
private analyticsService: AnalyticsService,
private auth: Auth,
private localStorage: LocalStorageService,
) {
}
ngOnInit(): void {
this.init();
}
init() {
this.auth.init(this.webRobot);
this.analytics.init(this.webRobot);
if (!this.webRobot) {
// subscribe to authed profile changes
this.profileSub =
this.localStorage.profile$.subscribe(this.onProfile);
// Subscribe to changes to Stripe customer
this.customerSub =
this.localStorage.customer$.subscribe(this.onCustomer);
}
// always delete active subscribes on destroy
ngOnDestroy() {
this.profileSub.unsubscribe();
this.customerSub.unsubscribe();
}
onProfile = (profile: UserProfile) => {
// ...do stuff
}
onCustomer= (profile: Customer) => {
// ...do stuff
}

In your profile route configuration, it is expecting the id query param
{ path: 'profile/:id', component: ProfileComponent, canActivate: [AuthService] }
<--profile path.
I know I have to match my url paths,
but don't know how to do this from the navbar.
but your navbar link is not passing the id value
<li *ngIf="isAuth"><a [routerLink]="['profile']"><span class="glyphic
you need to do something like this in your navbar
<li *ngIf="isAuth"><a [routerLink]="['profile/user.id']">

Related

How to access queryParams in a Resolver Angular

I Need to implement 'search' by passing queryParams through route from the search component to the userList component (example. /search-result?user="Alfred"). Before loading the userList component, i need to make an API call using the queryParams in the userList resolver but the query params keeps showing undefined.
Search Component
search(searchTerm: string) {
if (searchTerm) {
this.router.navigate(['search-result'], { queryParams: { user: searchTerm } });
}
}
UserList Resolver
export class UserResolver implements Resolve<User[]> {
constructor(private userService: UserService, private route: ActivatedRoute) { }
resolve(): Observable<User[]> {
const searchTerm: string = this.route.snapshot.queryParams['user'];
console.log(searchTerm); //Logs Undefined
return this.userService.getUsers(searchTerm);
}
}
On latest versions of Angular you can get the ActivatedRouteSnapshot on the resolver function.
export class UserResolver implements Resolve<User[]> {
constructor(private userService: UserService, private route: ActivatedRoute) { }
resolve(**route: ActivatedRouteSnapshot**): Observable<User[]> {
**console.log(route.queryParams)**
return this.userService.getUsers(searchTerm);
}
}
Maybe the resolve function is running before the queryParams are populated in the url. Try doing it in an Rxjs way.
import { filter, map, switchMap, tap } from 'rxjs/operators';
...
export class UserResolver implements Resolve<User[]> {
constructor(private userService: UserService, private route: ActivatedRoute) { }
resolve(): Observable<User[]> {
return this.route.queryParams.pipe(
tap(params => console.log(`Params: ${params}`)),
// wait until params has user in it
filter(params => !!params['user']),
tap(params => console.log('after filter')),
// extract the value of the user param
map(params => params['user']),
// switch to a new observable stream once we know the searchTerm
switchMap(searchTerm => this.userService.getUsers(searchTerm)),
);
}
}
Edit
Use the tap operator to debug the stream. See what the log is and make sure console.log(Params: ${params}) has the user params.
Edit2
Try
this.router.navigateByUrl(`/search-result?user=${searchTerm}`);
, I am thinking there is something wrong with how you navigate.
Edit 3
I am thinking queryParams can only be read when the component itself loads and not at the run time of the route resolvers because it is saying, I need to go to the route of search-result, give me the data before I go to search-result and it is independent of the queryParams. To fix this, I followed this guide (https://blog.thoughtram.io/angular/2016/10/10/resolving-route-data-in-angular-2.html).
1.) In app-routing-module.ts, change the registration of the path to:
{ path: 'search-result/:user', component: UserListComponent, resolve: { users: UserResolver } },
Now the user will be the parameter we are after in the URL.
2.) In search.component.ts, change search to:
search(searchTerm: string) {
if (searchTerm) {
this.router.navigate([`search-result/${searchTerm}`]);
}
}
3.) In user-resolver.service.ts, change it to this:
#Injectable({
providedIn: 'root'
})
export class UserResolver implements Resolve<User[]> {
constructor(private userService: UserService) { }
resolve(route: ActivatedRouteSnapshot): Observable<User[]> {
const searchTerm: string = route.paramMap.get('user');
return this.userService.getUsers(searchTerm);
}
}
I when console logging searchTerm, it is the accurate value. Thanks for providing the StackBlitz, it helped you and me.

Navigate in Angular 7 without adding parameter to URL

I want to navigate between two routes in Angular 7 with posting data between them. But I don;t want to show those parameter in URL. How to do it in proper way?
at this moment I am strugging with something like this:
this.router.navigate(['/my-new-route', {data1: 'test', test2: 2323, test: 'AAAAAAA'}]);
and it change my url to
http://localhost:4200/my-new-route;data1=test;test2=2323;test=AAAAAAA
how to do it to cancel those data from url:
http://localhost:4200/my-new-route
Edit:
My case:
/form - route with some form
/options - route with some data
on /form route - users have some form with empty fields to fill manually
but on /options page there is some preset configuration, when user choose one is navigated to /form and fields are fill autmatically
when they move back to another page and back again to /form - should see empty form. Only link from /options to /form should fill those fields.
You can create a service and share it between both the components (the one that you're moving from, and the one that you're moving to).
Declare all the parameters that you want to pass to the URL, in the service, and before the router.navigate([]), set the values for parameters in the service.
You can access those parameters from the other component with that service.
Example:
SharedService
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class SharedService {
data1;
test2;
test;
}
Component1
import { SharedService } from 'location';
import { Router } from '#angular/router';
...
constructor(private _sharedService: SharedService,
private _router: Router) { }
...
this._sharedService.data1 = 'test'
this._sharedService.test2 = 2323;
this._sharedService.test = 'AAAAAAAA';
this._router.navigate(['/my-new-route']);
...
Component2
import { SharedService } from 'location';
...
private test2;
private test;
private data1;
constructor(private _sharedService: SharedService){ }
ngOnInit() {
this.data1 = this._sharedService.data1;
this.test2 = this._sharedService.test2;
this.test = this._sharedService.test;
...
}
There are few ways to do it.
Try 1 :
this.router.navigate(['/some-url'], { queryParams: filter, skipLocationChange: true});
Try 2 :
We can use this work around instead by using EventEmitter and BehaviorSubject with a shared service
In component 1:
this.router.navigate(['url']).then(()=>
this.service.emmiter.emit(data)
)
In service :
emmiter : EventEmitter = new EventEmitter();
In component 2: inside constructor
this.service.emmiter.subscribe();
another solution for passing information from one route to another without touching the query params is via the state field of NavigationExtras (as of Angular 7.2+)
something along these lines
// Publish
<a
[routerLink]="['/studies', study.id]"
[state]="{ highlight: true }">
{{study.title}}
</a>
// Subscribe
constructor(private route: ActivatedRoute, ...) {
}
public highlight: boolean;
public ngOnInit() {
...
this.route.paramMap
.pipe(map(() => window.history.state))
.subscribe(state => {
this.highlight = state && state.highlight;
});
...
}
// Alternative
constructor(private router: Router, ...) {
}
public highlight: boolean;
public ngOnInit() {
...
this.router.events.pipe(
filter(e => e instanceof NavigationStart),
map(() => this.router.getCurrentNavigation().extras.state)
)
.subscribe(state => {
this.highlight = state && state.highlight;
})
...
}
pass value through "state" key from which you want to naviagte to next component:
//From where we Navigate
import {ActivatedRoute, NavigationExtras, Router} from "#angular/router";
export class MainPageComponent {
constructor(public router:Router) {}
navWithExtraValue () {
const navigationExtras: NavigationExtras = {
state: {
editMode: true
},
};
}
}
//In constructor where we Navigated
constructor(public router:Router,
public route:ActivatedRoute){
this.route.queryParams.subscribe(data=> {
if (this.router.getCurrentNavigation().extras.state) {
this.editMode = this.router.getCurrentNavigation().extras.state.editMode;
}
});
We don't see these value in url

How to update navigation bar after routing on some scenario in angular2

I have a bootstrap navbar, on the right side of navigation bar, i have some links like login,logout, register
I put it on my app.component.html.ts
<div class="navbar-collapse collapse">
// Here i check if user is authenticated, display : Hello abc#gmail.com
<ul *ngIf="user" class="nav navbar-nav navbar-right">
//code in here
</ul>
// If user is not authenticated, display Login - Register
<ul *ngIf="!user" class="nav navbar-nav navbar-right">
<li><a routerLink="/register" id="registerLink">Register</a></li>
<li><a routerLink="/login" id="loginLink">Log in</a></li>
</ul>
In login.component.ts i call my Authen.Service.ts to get token that is store on localStorage
import { UrlConstants } from './core/common/url.constants';
import { LoggedInUser } from './core/domain/loggedin.user';
import { SystemConstants } from './core/common/system.constants';
#Component({
selector: 'app-login',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
public user: any;
private isLoggedIn = false;
loginUser(valid: boolean) {
this.loading = true;
if (valid) {
const userData = {
username: this.form.controls.username.value,
password: this.form.controls.password.value
}
this._authenService.login(userData.username, userData.password).subscribe(data => {
this.user = JSON.parse(localStorage.getItem(SystemConstants.CURRENT_USER));
// If success redirect to Home view
this._router.navigate([UrlConstants.HOME]);
}, error => {
this.loading = false;
});
}
}
ngOnInit() {
}
}
Here is my Authen.Service.ts
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import 'rxjs/add/operator/map';
import { SystemConstants } from '../common/system.constants';
import { LoggedInUser } from '../domain/loggedin.user';
#Injectable()
export class AuthenService {
constructor(private _http: Http) {
}
login(username: string, password: string) {
let body = "userName=" + encodeURIComponent(username) +
"&password=" + encodeURIComponent(password) +
"&grant_type=password";
let headers = new Headers();
headers.append("Content-Type", "application/x-www-form-urlencoded");
let options = new RequestOptions({ headers: headers });
return this._http.post(SystemConstants.BASE_API + '/api/oauth/token', body, options).map((response: Response) => {
let user: LoggedInUser = response.json();
if (user && user.access_token) {
localStorage.removeItem(SystemConstants.CURRENT_USER);
localStorage.setItem(SystemConstants.CURRENT_USER, JSON.stringify(user));
}
});
}
logout() {
localStorage.removeItem(SystemConstants.CURRENT_USER);
}
isUserAuthenticated(): boolean {
let user = localStorage.getItem(SystemConstants.CURRENT_USER);
if (user != null) {
return true;
}
else
return false;
}
Here is my app.component.ts
export class AppComponent implements OnInit {
// the user object got from localStore
ngOnInit() {
this.user = JSON.parse(localStorage.getItem(SystemConstants.CURRENT_USER));
console.log(this.user);
}
}
The problem i got is i cant update the navbar to change in right state (It still work, i have the token but i have to refresh the whole page to update the nav bar)
How can i update the navigation bar in angular way? Thanks
As i understood your problem it is: How to hide "login" link located on main component after user signed himself in
I can think about solution like following:
Inside your AuthService you can add public boolean member "isLoggedIn":
#Injectable()
export class AuthService {
isLoggedIn = false;
}
You can share this service between components
Inside login component you can set isLoggedIn to true after successful login
login(){
this.auth.isLoggedIn = true
}
In your app.component you can subscribe to NavigationEnd event of the router :
export class AppComponent {
constructor(
private router: Router, private auth:AuthService){}
ngOnInit() {
this.router.events.subscribe(event => {
if (event.constructor.name === "NavigationEnd") {
this.isLoggedin = this.auth.isLoggedIn;
}
})
}
}
And then, in app component template you can show "login" menu with *ngIf="!isLoggedin"
here is plunker
hope it helps...

Angular 2 shared service to pass data to component-to-component

I am trying to pass the string value of this.title from my LandingPage.component to my ResultPage.component.
I retrieve the list.show value, and send it to my TitleService in like so in my:
landingpage.component.html
<ol>
<li (click)="selectShow(list.show)" [routerLink]="['/details', list.id]" *ngFor="let list of shows">{{list.show}}
</li>
</ol>
landingpage.component.ts
import { TitleService } from '../../services/title.service';
constructor(private TitleService: TitleService) {}
selectShow(show) {
this.TitleService.fetchTitle(show)
}
The above sends the list.show value to my:
title.service.ts
// this gives us the name of the clicked show, which we send to TitleResolver
#Injectable()
export class TitleService {
fetchTitle(title) {
console.log("title is " + title); // this outputs correctly
return title;
}
}
And here is how I manage the routing in my:
app-routing.module.ts
import { TitleService } from './services/title.service';
const routes: Routes = [
{ path: '', component: LandingPage },
{
path: 'details/:id', component: ResultPage
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [TitleService]
})
My question
Once I receive the title.show value in my service component, I'm unsure how to then send it to my receiving component (resultpage.component)
How can I send my title value from my service to my ResultPage.component?
Make the title a public property of the service like this:
// this gives us the name of the clicked show, which we send to TitleResolver
#Injectable()
export class TitleService {
selectedTitle: string;
fetchTitle(title) {
console.log("title is " + title); // this outputs correctly
this.selectedTitle = title;
return title; // No need to return it.
}
}
Then any other component can inject this service and access this.titleService.selectedTitle
In title.service.ts you can declare a variable called title and have setter and getter:
title: string ="";
// replace fetchTitle with setTitle
// remember to change it in the component too
setTitle(title) {
this.title = title;
}
getTitle() {
return this.title;
}
Then, when ResultPage.component is initialized, call getTitle() from TitleService and set the result to a variable declared in the component.
Here's an example of sharing data via shared services.
Separation of concerns... Your landing page is used to select the list item and navigate to the result page. Let it do just that and only that. Let the ResultPage.component do the rest. Note: Other answers recommend storing the value of the last title in the TitleService. It's not a good idea to store state in a service. Then TitleService cannot be used as a generic way to get any title separate from your current navigation, without side effects.
Remove (click) event. Add 'show' as a QueryParam.
landingpage.component.html
<li [routerLink]="['/details', list.id]"
[queryParams]="{show: list.show}"
*ngFor="let list of shows">
{{list.show}}
</li>
Subscribe to router params and queryparams to get the id and show.
resultpage.component.ts
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { TitleService } from '../../services/title.service';
#Component({
...
})
export class ResultPageComponent implements OnInit, OnDestroy {
itemId: string;
show: string;
subParams: any; // infinite Observable to be unsubscribed
subQueryParams: any; // infinite Observable to be unsubscribed
constructor(
...
private TitleService: TitleService,
protected route: ActivatedRoute,
protected router: Router,
...
) {}
ngOnInit() {
this.subParams = this.route.params.subscribe(this.onParams);
this.subQueryParams = this.route.queryParams(this.onQueryParams);
}
ngOnDestroy() {
// Delete active subscribes on destroy
this.subParams.unsubscribe();
this.subQueryParams.unsubscribe();
}
onParams = (params: any) => {
this.itemId = params['id'];
}
onQueryParams = (data: any) => {
this.show = data.show;
if(this.show) {
this.TitleService.fetchTitle(this.show)
}
}

angular2 - Pass value from parent route to child route

I have a route called home and it has three child routes, documents, mail and trash. In the home route component it has a variable called 'user'. I know there are a few ways of passing info between parent and child components highlighted here, but how am I suppose to pass info between parent/child routes.
{ path: 'home', component: HomeComponent, children: [
{ path: 'documents', component: DocumentsComponent },
{ path: 'mail', component: MailComponent },
{ path: 'trash', component: TrashComponent },
]
},
Service
import { Injectable } from '#angular/core';
#Injectable()
export class HomeService {
// Mock user, for testing
myUser = {name:"John", loggedIn:true};
// Is Super Admin
isLogged():boolean {
if(this.myUser.role == true){
return true ;
}
return false ;
}
}
Component
constructor(public router: Router, public http: Http, private homeService: HomeService) {
}
isLogged(){
return this.homeService.isLogged();
}
Template
<div class="side-nav fixed" >
<li style="list-style: none">
<img alt="avatar" class="circle valign profile-image" height="64" src=
"../images/avatar.jpg" width="64">
<div class="right profile-name">
<!-- Value not changing even with service -->
{{myUser.role}}
</div>
</li>
You may use a common service to pass data like explained in the Angular Documentation
Basically you may create a Service which will have a user object, which can be updated once your parent route gets loaded or with some action on parent component.
UserService
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class UserService {
// Observable user
user = new Subject<string>();
}
And then when the child route component gets loaded you may retrieve the value from the Service.
HomeComponent
#Component({
...
})
export class HomeComponent{
...
constructor(private userService:UserService ){}
someMethod = () =>{
this.userService.user.next(<pass user object>);
}
}
MailComponent
#Component({
...
})
export class HomeComponent{
...
constructor(private userService:UserService ){
this.userService.user.subscribe(userChanged);
}
userChanged = (user) => {
// Do stuff with user
}
}
Service object will be same instance in child if you add the provider in the parent.
Check out :- https://angular.io/docs/ts/latest/guide/router.html#!#link-parameters-array
You can pass data while changing routes on click as :-
<a [routerLink]="['/crisis-center', { foo: myVar }]">Crisis Center</a>

Categories