What is a proper way to preserve parameters after navigating back? - javascript

In my Angular app, I have list and details pages and I want to lkeep the pageIndex value before navigating to details page. There is a Back button in the details page and I can return the list page by clicking on that button. However, I want to get the pageIndex value when navigating back to the list page and let the user open the page where he/she was before. For example I navigate 3rd page on the list and click details. At this stage I set the pageIndex to 3 and then navigate to details. Then by clicking the Back button I can navigate back to the list page, but I need to retrieve the pageIndex as 3.
So, what is an elegant way to fix this problem in Angular 10+?
list-componnet
details(id) {
this.router.navigate(['/details'], { state: { id: id } }); // I pass id value of the record
}
details-componnet
constructor(private router: Router) {
this.id = this.router.getCurrentNavigation().extras.state.id;
}
back() {
this._location.back();
}

Just write a simple example to make it work, I use the sessionStorage and router together, use router to show your the routing module, actually you can just use sessionStorage only, and wrapper it in a servive. Then you can retrieve the pageIndex anywhere.
And if you want to use router only, the pageIndex paramater will be place in both list and detail component, since this two component all need to use this value, in list component you will need pageIndex to set data-table, in detail component you need this value to pass to list component when redirect back triggered.
The routing module like below:
import { NgModule } from "#angular/core";
import { Routes, RouterModule } from "#angular/router";
import { ListComponent } from "./list/list.component";
import { DetailComponent } from "./detail/detail.component";
const routes: Routes = [
{ path: "", redirectTo: "list", pathMatch: "full" },
{
path: "list/:pageIndex=1",
component: ListComponent,
pathMatch: "full"
},
{
path: "detail/:id",
component: DetailComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Here you can navigate to list page from detail page use:
this.router.navigate(["/list/" + pageIndex]);
And then in list page's ngOnInit method to set current pageIndex to your data-table. Here is the demo: https://stackblitz.com/edit/angular-ivy-5vmteg?file=src/app/list/list.component.ts

Use sessionStorage, a listService or router queryParams to keep track of the current pageIndex.
I´d advocate for queryParams as it seems most logical and you can also link directly to specific page.
constructor(
private route: ActivatedRoute,
) { }
// Access the queryParam in list component
// Ie /list?pageIndex=4
this.route.queryParams.subscribe(params => {
// Do something with params.pageIndex if it exists
}
);
I´d also consider to change the way you handle routing to the details. If the route to the list is /list then route to the details should be /list/<listid> so you can link directly to the details if needed.
You can access the listId parameter as below but note it must also be specified as parameter in the router definition.
// Router definition
{ path: 'list/', component: ListComponent},
{ path: 'list/:listId', component: ListIdComponent}
// Access the listId in the details component
this.route.params.subscribe(param=> {
// Do somthing with param.listId
});

Related

How to reuse route on query params change, but not on path params change in Angular?

Say I have 2 routes '/users' and /users/:id. First one renders UserListComponent and second UserViewComponent.
I want to re-render component when navigating from /users/1 to /users/2. And of course if I navigate from /users to /users/1 and vice versa.
But I DON'T want to re-render component if I navigate from /users/1?tab=contacts to /users/1?tab=accounts.
Is there a way to configure router like this for entire application?
--
Update:
I'm importing RouterModule in AppRoutingModule like this:
RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })
I'm using Angular 12
The default behavior of the Angular router is to preserve the current component when the URL matches the current route.
This behavior can be changed by using the onSameUrlNavigation option:
Define what the router should do if it receives a navigation request
to the current URL. Default is ignore, which causes the router ignores
the navigation. This can disable features such as a "refresh" button.
Use this option to configure the behavior when navigating to the
current URL. Default is 'ignore'.
Unfortunately, this option is not fine-grained enough to allow reload for path params and ignore for query params.
So you have to subscribe both to the query params and the path params changes with something like this:
constructor(route: ActivatedRoute) { }
ngOnInit() {
this.renderLogic();
this.route.params.subscribe(() => this.renderLogic());
this.route.queryParams.subscribe(() => this.renderLogic());
}
renderLogic() {
// ...
}
As far as I know, #Guerric P is correct, you can't completely re-render the component selectively like this, at least not without some trickery like subscribing to each event and then possibly blocking it for one scenario and not the other. Feel free to try something like that, but below is an alternative if you make use of resolvers to fetch your data.
What you can do is use runGuardsAndResolvers in your route configuration like so:
const routes = [{
path: 'team/:id',
component: Team,
children: [{
path: 'user/:name',
component: User
}],
runGuardsAndResolvers: 'pathParamsChange',
resolvers: {...},
canActivate: [...]
}]
This will, as the name suggests, run your guard resolver logic again. If you fetch data using resolvers and pass it into your components, you can update what your component displays only when the path or params change.

How to access protected routes / disable Auth Guard by knowing auth status

scenario :
Here i am building an application in which currently i have a page called products-info where a url is sent to the customer and on clicking on the link he can directly open that particular product page and see the info and the same page will be there in application.
issue :
Here i am protecting the routes using Auth guards so without logging, a user cannot access the page .If i sent the same page url to user via email he should be able to view only the page.
So my application has:
login page
products page
products-info page
Normally if a user logs in, this page will be visible but when an admin sends a url as mobiledot.com/products-info to user's email, user clicks on that and application don't want to login and it doesn't want to show any logout or other pages info only on that specific page. below is my code :
router.module.ts
const routes: Routes = [
{ path: '', component: LoginComponent },
{ path: 'main/:role', component: MainComponent, canActivate: [RouteGuard] },
{ path: 'admin', component: AdminComponent},
{ path: 'user', component: userComponent,canActivate: [RouteGuard]}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [RouteGuard]
})
export class AppRoutingModule { }
auth guard
#Injectable()
export class RouteGuard implements CanActivate {
constructor(private service: accessService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
const isAllowed = this.service.getAuthenticated();
if (!isAllowed) {
this.router.navigate(['/']);
}
return isAllowed;
}
}
So i also thought about some thing like if user logs into application. My router module is:
if(user loginin){
{ path: 'main/:role', component: MainComponent, canActivate: [RouteGuard] },
}
else {
{ path: 'main/:token', component: MainComponent },
ex: www.mobiledot.com/product-info?toke="ssdsdsdsdSDSD"
}
Is it possible or do we have any other way?
In short, if admin sent the same page url in application which is protected by auth guards to the user via email then user will click on the link and open the page and it should not ask for login.
There is another problem which is about stored token in localstorage. so before moving, do we have to clear that token and place a new one so that it will not redirect to main page?
You mean that products-info should be visible for registered users, as they can receive the corresponding link inside their email while they may be unauthorized (perhaps by token expiration) so you need a solution to cover it.
First of all, this is a bad practice to reveal sth like token as Url parameter. So avoid it.
Second, Please try to employ right solution instead of changing basic behaviors in your App. AuthGuard designed to decide if a route can be activated or not. It uses browser storage and in case of token expiration, You can only refresh it before expiration, otherwise user has to login again.
My solution is creating sth like a ticket id which can be appended to the sent link (like mobiledot.com/products-info/928f5f8b663571b7f3d829921c9079939c2d0319). You can make it one-time valid or more for users. store it in database and validate it when a user uses. In this way, there's no need to employ AuthGuard just control content permission on server-side.
As you have a route guard , you need a case where it's should return true even you are not logged in.
So, simply you can use the approach you have think of www.mobiledot.com/product-info?toke="ssdsdsdsdSDSD".
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
// check if there is a query param , like token in the url and url is your without token one.
if(queryParamToken && urlpath === 'view'){
// as you have token hit the api to check if required to check token is a valid one.
or can simply return true as per your need / requirement
-- checkout this link if you need referece
} else {
const isAllowed = this.service.getAuthenticated();
if (!isAllowed) {
this.router.navigate(['/']);
}
return isAllowed;
}
}
Verify token with API in Angular8 canActivate method of AuthGuard -- checkout this link if you need reference

Angular load url when using a router

Im using Angular 7 + router. For example, my home page is localhost:4200, one of my router's url is localhost:4200/route and I have an API end point at localhost:4200/api/foo.
I'm trying to let the browser load the api end point from both locations. If I put an anchor with href pointing to the API end point, both anchor link works perfectly. However, I need to do it programmatically and I have tried all of the following methods
window.open("localhost:4200/api/foo","_self")
window.location.replace('localhost:4200/api/foo');
window.location.href = 'localhost:4200/api/foo';
They all works on the home page but if I'm in the router page, doing so will get me to the home page.
Any help is greatly appreciated!
To be specific, I have a spring boot server with url patterns like /api/*. All other urls are handled by angular. I want the browser to load localhost:4200/api/foo, which is directly handled by a get request defined in the server
Demo:
My nav bar is a component and it stays the same regardless of the router.
The code behind that button click is below. Note that the first time I click it under some Angular routed url, it loads the home page, not google.com
onLogIn() {
window.open('https://www.google.com',"_self");
}
Routing.ts file
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { IndexComponent } from "./index/index.component";
import { MicroserviceComponent } from "./microservice/microservice.component";
const routes: Routes = [
{ path: '', component: IndexComponent},
{ path: 'microservice/:id', component: MicroserviceComponent}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
add pathMatch to your empty route, it's missing tht's why you're redirected to the home component
const routes: Routes = [
{ path: '', component: IndexComponent, pathMatch:'full'},
{ path: 'microservice/:id', component: MicroserviceComponent}
];

Refresh data on vue

This may have been asked but I have not be able to find a solution. I have a index page that loads a left nav vue. On that view is a typeahead input with names. When a name is selected a function is called and and a unique value is passed as the pmid_list
this.$router.push({ name: 'About', params: { pmid_list: item.PMID_Include } }
This works fine the first time because the About vue is loaded and the function is called with the pmid_list value. Every name works fine if I refresh the page between calls. If I don't refresh the correct pmid_list (parameter) is sent to the router but the router decides to send the old one if the vue component has not changed.
From what I have read it is a router issue but I can't figure out how to force it to refresh.
export default new Router({
mode: 'history',
routes: [
{
path: '/about/:pmid_list',
name: 'About',
component: About,
props: {default: true}
}
The About component is being cached.
One thing to note when using routes with params is that when the user navigates from /user/foo to /user/bar, the same component instance will be reused. Since both routes render the same component, this is more efficient than destroying the old instance and then creating a new one. However, this also means that the lifecycle hooks of the component will not be called.
Dynamic Route Matching
As shown in the documentation, you should use a watcher to react to parameter changes:
watch: {
'$route' (to, from) {
// react to route changes...
}
}
You can try to watch your route changes.
watch:{
'$route.params.pmid_list': function (pmid_list) {
//your logic here
}
},

share data between two angular component

I am angular 2.0 beginner and wondered if anyone can help me with passing data from one component to another.
My project is made up of three parts, a login window component, a table component and center component to handle routing. when the users log in in the login component, a http.post request will be sent to server to authenticate. if the credentials are authentic, a json containing users' information will be returned from the server . The page will also be routed to a table component showing the name of this user and his/her other info.
these two component are on the same page using router-outlet.
Here is my app.component.ts:
import { Component } from '#angular/core';
#Component({
selector: 'td-app',
template: `
<router-outlet></router-outlet>
`
})
export class AppComponent {
}
app.module.ts:
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
JsonpModule,
RouterModule.forRoot([
{
path: 'login',
component: LoginComponent
},{
path: 'table',
component: TableComponent
},{
path: '',
redirectTo: '/login',
pathMatch: 'full'
}
])
],
Here is the data I wanna pass into another component. the postUser method is doing http.post and an json string will return. I wanna pass this json data to my next component. I was thinking using promise to make sure the data is assigned to the variable userInfoPack and then pass userInfoPack to next component's template.
login.component.ts:
postUser(body: User): Promise<any>{
return Promise.resolve( this.getUser(body))
.then(response => this.userInfoPack = response );
}
getUser(body: User){
this.userJson = JSON.stringify(body);
this.loginservice.postUser(this.userJson)
.subscribe(response => this.userInfo= response);
return this.userInfo; //subscribe method ends here
}
this is the template I wanna pass data into. I want to use #input
to pass the data but I dont know how.
table.component.ts:
template: `
<h2>Hi, {{userInfoPack}}</h2>
`
Please help me with this, thank you!
Also is there any way that we can route the page to another component, since i use a button to route the page and send http.post request to authenticate account at the same time.
<button type= "button" class="btn btn-primary btn-lg"
(click)= "postUser(user)" style=" position: relative;
left: 90%;" routerLink= '/whoison'>
Login
</button>
I don't how to route the component after the credentials are sent and authenticated. (can we use routeLink in a function instead of a directive? )
Any suggestion or advice will be helpful, thank you!
Best solution would be create a sharing service to maintain your data. I had a similar challenge in one of my Angular application. Created a sharingService directive/class and injected/imported into both controller. Now due to singleton nature of services once you update data from one controller it could be fetched/read from other controller.
A good example with Angular 2 :
Bootstrapp your shared service into your application:
bootstrap(AppComponent, [ SharedDataService ]);
Do not add it again in providers attribute of your components. This way you will have a single instance of the service for the whole application.
#Component({
selector : "selectorName",
// providers : [SharedDataService ], //---- not needed
template : `I'm MyComponent and shared data is: {{data}}`,
})
export class MyComponent implements OnInit{
(...)
}
I found an easy way to do this.
We can use local storage if you only want to share string variable.
e.g.
app.component.ts
localStorage.set('name', 'yeehaah');
other.component.ts
var name = localStorage.get('name');
This way, you can access the name variable 'yeehaah' in 'other.component' from 'app.component'
Welcome to elaborate on this!

Categories