I want to pass an id with url using Router in Angular. And i have to fetch that value on another page. Like I have a student list. On click of edit button belongs to particular student on the list. The id of that student pass to edit student details page. After fetching that studentId I want show existing details in the input fields.
So How i can do this?
this is my example path
{ path: 'school-students-list', component: studentsListPageComponent },
{ path: 'edit-student-details', component: studentEdit },
Give the route a parameter like this:
{ path: 'edit-student-details/:id', component: studentEdit }
And then use ActivatedRoute in that component to access the route parameters. Import it from #angular/router
import { ActivatedRoute, Params } from '#angular/router';
import { Subscription } from 'rxjs/Subscription';
Inject this into the constructor
private routeSub: Subscription;
constructor(private route: ActivatedRoute) {}
On init, subscribe to route parameters
ngOnInit(): void {
this.routeSub = this.route.params.subscribe((params: Params): void => {
const id = params['id'];
});
}
Always a good idea to unsubscribe on destroy
ngOnDestroy(): void {
this.routeSub.unsubscribe();
}
And to link to this page, passing in their member ID, do this on your student listings page:
<a [routerLink]="['/edit-student-details', memberId]">Edit</a>
To navigate via a component method, inject Router from #angular/router and then when you want to navigate, use this.router.navigate(['/edit-student-details', memberId]);
Import ActivatedRoute from #angular/router
import { ActivatedRoute, Router } from '#angular/router';
Access parameter from constructor:
constructor(private route: ActivatedRoute) {
let id = +this.route.snapshot.params['id'];
// use the id here
}
Related
I have a component which needs to show the data in the grid on the component/page Load and when a button is clicked from parent component it needs refresh the grid with new data. My component is like below
export class TjlShipdateFilterComponent implements DoCheck {
tljShipDate: ShipDateFilterModel[];
constructor(private psService: ProjectShipmentService) {
}
ngDoCheck() {
// this data is from the service, trying to get it on Page load
}
#Input() filter: ShipDateFilterModel[];
//Load or refresh the data from parent when the button clicked from parent component
ngOnChanges(changes: SimpleChanges) {
}
The ngOnChanges works fine, it gets the data from the parent component and displays when the button is clicked from the parent component. But on load of the page/component the grid it doesn't show anything and says this.psService.tDate; is undefined.
Below is the service where I get the tDate
export class ProjectShipmentService {
......
constructor(service: DataService, private activatedRoute: ActivatedRoute) {
service.get<ShipDateFilterModel[]>(this.entityUrl).subscribe(x => this.tDate = x);
}
I am unsure what am I missing here. How can I achieve this scenario
It happened because when the component is loaded, the request in your service may not completed and the data may not return yet, that why tDate is undefined, try subscribe to it inside your component, also use ngOnInit() instead of ngDoCheck().
In your service:
tDate: Observable<ShipDateFilterModel[]>
constructor(service: DataService, private activatedRoute: ActivatedRoute) {
...
this.tDate = service.get<ShipDateFilterModel[]>(this.entityUrl)
}
In your component:
export class TjlShipdateFilterComponent implements OnInit, OnChanges {
tljShipDate: ShipDateFilterModel[];
constructor(private psService: ProjectShipmentService) {
}
ngOnInit() {
// this data is from the service, trying to get it on Page load
this.psService.tDate.subsribe(x => this.tljShipDate = x);
}
#Input() filter: ShipDateFilterModel[];
//Load or refresh the data from parent when the button clicked from parent component
ngOnChanges(changes: SimpleChanges) {
if (changes.filter && changes.filter.currentValue)
{
this.tljShipDate = this.filter;
}
}
}
You have a couple options here.
NgOnInit will run when the component is created, before it is rendered. This is the most common way to load data on component initialization.
If you need the data even before the component is initialized, then you may need to utilize a Resolver.
Here's an example:
import { Injectable } from '#angular/core'
import { HttpService } from 'services/http.service'
import { Resolve } from '#angular/router'
import { ActivatedRouteSnapshot } from '#angular/router'
#Injectable()
export class DataResolver implements Resolve<any> {
constructor(private http: HttpService) { }
resolve(route: ActivatedRouteSnapshot) {
return this.http.getData(route.params.id);
}
}
Then, in your route config:
{
path: 'data/:id',
component: DataComponent,
resolve: { data: DataResolver }
}
The inclusion of the ActivatedRouteSnapshot is optional, you only need it if you're using route data, like params.
Edit:
Looking at your example closer, is it possible that the ngDoCheck is firing before the psService subscription does?
Using Angular 7.x, I want to change my parent header component accordingly to the child routes and if they're activated or not. So in my case
AppComponent
Feature Module <= detect changes here
Child Components of feature module
So my routing is very simple, the app-routing.module just contains the loading of the feature module with loadChildren, nothing fancy here.
Then this feature module contains the routes for the child components. There is one parentComponent, we call it ParentComponent which contains the router-outlet. There is also some headers that I want to change accordingly to the childs.
SO i have two child components: create, and ':id' (detail page). When I trigger these routes I need the parent component to just change their text content accordingly. So for example when the create page is triggered I want to add the header: "Create new item", and for the :id page I want to show "Detail page".
Now, I have figured out I need to subscribe to the router events or on the activatedRoute (or snapshot). I'm at a loss here so I don't really know what to do here.
My parent component looks like this now:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
#Component({
selector: 'parent-component',
templateUrl: './parent.component.html',
})
export class ParentComponent implements OnInit {
title = 'Parent Title';
// Check if it is possible to change the title according to the route
subtitle = 'Parent subtitle';
constructor(private readonly router: Router, private readonly route: ActivatedRoute) {}
ngOnInit(): void {
this.route.url.subscribe(data => {
if (this.route.snapshot.firstChild) {
console.log('yes', this.route.snapshot.firstChild);
// Change header with a if/else or switch case here
} else {
// Display standard text
console.log('no');
}
});
}
}
this is the output of the console.logs, notice in my real application the parent is named 'timesheets'.
Is there maybe another solution for this? Maybe a service, but all of the information is basically in the route already, so I'm trying to figure out if this is the way to go for my problem.
You can listen NavigationEnd events in ParentComponent or (I think) even better you can use a title service.
Solution 1:
In ParentComponent
import {NavigationEnd, Router} from '#angular/router';
import {filter} from 'rxjs/operators';
...
constructor(private router: Router, private readonly route: ActivatedRoute) {
this.subscribeRouterEvents();
}
subscribeRouterEvents = () => {
this.router.events.pipe(
filter(e => e instanceof NavigationEnd)
).subscribe(() => {
this.title = this.route.snapshot.data['title'];
// Assuming your route is like:
// {path: 'path', component: MyComponent, data: { title: 'Page Title'}}
});
Solution 2:
Using TitleService. Create a service that holds the page title, subscribe to title from ParentComponent and send new title to service from ChildComponent.
TitleService
#Injectable({
providedIn: 'root'
})
export class TitleService {
private defaultTitle = 'Page Title';
private titleSubject: BehaviorSubject<string> = new BehaviorSubject(this.defaultTitle);
public title: Observable<string>;
constructor(private titleService: Title) {
this.title = this.titleSubject.asObservable();
}
public setTitle(title: string) {
this.titleSubject.next(title);
}
}
ParentComponent
pageTitle = 'Page Title';
constructor(private titleService: TitleService) {}
ngOnInit(){
this.titleService.title.subscribe(value => this.pageTitle = value);
}
ChildComponent
pageTitle = 'Child Component Title';
constructor(private titleService: TitleService) {}
ngOnInit(){
this.titleService.setTitle(this.pageTitle);
}
You can try setting the title for a child as part of route like this.
const routes: Routes =[
{
path: 'create',
component: SomeComponent,
data : {header_title : 'some title'}
},
];
ngOnInit() {
this.title = this.route.data.subscribe(x => console.log(x));
}
and get the title in the child component and set that as header title using a service.
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
So when a post is clicked I do this which sends me to another page with the postId in the router:
this.router.navigate(['/annotation', postId]);
This navigates me to the annotations page where only that single post will be shown. In order for this to work, I need to get the postId which is now in the router link:
http://localhost:4200/annotation/5b3f83b86633e59b673b4a4f
How can I get that id: 5b3f83b86633e59b673b4a4f from the router and put it into my TS file. I want this id to only load posts with this ID.
Can anyone point me in the right direction to be able to grab the link http://localhost:4200/annotation/5b3f83b86633e59b673b4a4f take of everything and only get the ID at the end and store that in my TS file.
Sorry, I'm new to angular/web dev hence why I'm asking, many thanks in advance for your time.
You can read params of activated route via params observable, subscribe on it and you will get access to route params:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'selector',
template: ``,
})
export class LoanDetailsPage implements OnInit, OnDestroy {
private paramsSubscription$: Subscription;
constructor(private _route: ActivatedRoute) {}
ngOnInit() {
this.paramsSubscription$ = this._route.params.subscribe(params => {
console.log(params); // Full params object
console.log(params.get('paramName')); // The value of "paramName" parameter
});
}
ngOnDestroy() {
this.paramsSubscription$.unsubscribe();
}
}
PS: Don't forget to unsubscribe() in OnDestroy lifecycle hook.
You have to inject the ActivatedRoute service and subscribe to the paramMap:
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// subscribe to the parameters observable
this.route.paramMap.subscribe(params => {
this.foo = params.get('paramName');
});
}
Try in the component which you load something like:
id: string;
ngOnInit() {
this.id = this.route.snapshot.paramMap.get('postId');
}
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)
}
}