好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Reloading current route in Angular 5 / Angular 6 /

问题: angular 从子状态回到当前的父级状态的时候,父级状态不会重新初始化。

https://github.com/angular-ui/ui-router/issues/2992

 

 

原文: https://medium.com/engineering-on-the-incline/reloading-current-route-on-click-angular-5-1a1bfc740ab2

------------------------------------------------

Back in the days of AngularJS you were able to reload a route even if you were already viewing it. For example, clicking on the home option of a menu when you are already viewing the homepage would refresh the homepage. This was useful for a number of reasons, but most importantly for UX. Users expect to be able to click on a page menu item and have the page re-initialise. Setting up this type of reloading in AngularJS was straightforward. You could invoke  reload()  in  your router and away you went.

When Angular 2 was released this feature was not present in the router and there was no easy way to reload the active route. Many people developed “hacky” techniques such as bouncing through a second route in the  A -> B -> A  sequence, effectively sending you off to a page and back again to trigger the reload. That had the desired effect but was rather inefficient. Another technique that was often suggested was outright reloading the page, which for a single page application is a bad idea.

As of Angular 5.1 there is a supported technique for route reloading. This can now be done using the  onSameUrlNavigation
configuration option as part of the built-in Angular router. Unfortunately, the documentation does not make it very clear on how to implement it, so I have documented my approach below.

The first thing you will need to do is set the option within your  app.routing.ts if you have one, or the file where your app routing is configured in your particular project.

There are two possible values for onSameUrlNavigation  ’reload’  or  false . The default value is  false,  causing nothing to happen when the router is asked to navigate to the active route. We want to set this value to  reload . It is worth noting  reload  does not actually do the work of reloading the route, it only re-triggers events on the router that we then need to hook into.

@ngModule({
imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: ‘reload’})],
exports: [RouterModule],
})

To determine how those events are actually fired, you need to specify the  runGuardsAndResolvers  configuration option on your route. This can take one of three values

paramsChange  — only fire when route params have changed e.g. where the id in  /user/:id  changes

paramsOrQueryParamsChange  — fire when a route param changes or a query param changes. e.g. the  id  or the  limit  property change in  /user/:id/invites?limit=10

always  — Always fire when the route is navigated

We want to specify  always  in this case. An example route definition is shown below.

export const routes: Routes = [
{
path: ‘invites’,
component: InviteComponent,
children: [
{
path: ‘’,
loadChildren: ‘./pages/invites/invites.module#InvitesModule’,
},
],
canActivate: [AuthenticationGuard],
runGuardsAndResolvers: ‘always’,
}
]

With these two changes your router is configured. The next step is to handle the events that your router will produce within one of your components. To do this you will need to import the Router into your component and then hook into the events of interest. In this example, I have hooked into the  NavigationEnd  event which is fired once the router has completed its navigation from one route to another. Due to the way we have configured the app, this will now fire even if you try to navigate to the current route.

export class InviteComponent implements OnInit, OnDestroy{
 // ... your class variables here
navigationSubscription;
 constructor(
// … your declarations here
private router: Router,
) {
// subscribe to the router events - storing the subscription so
// we can unsubscribe later.
   this.navigationSubscription = this.router.events.subscribe((e: any) => {
// If it is a NavigationEnd event re-initalise the component
if (e instanceof NavigationEnd) {
this.initialiseInvites();
}
});
}

initialiseInvites() {
// Set default values and re-fetch any data you need.
}
 ngOnDestroy() {
// avoid memory leaks here by cleaning up after ourselves. If we
// don\'t then we will continue to run our initialiseInvites()
// method on every navigationEnd event.
if (this.navigationSubscription) {
this.navigationSubscription.unsubscribe();
}
}
}

The heavy lifting goes into the  initialiseInvites()  method, this is where you reset properties to their default values and fetch data from services to get the component back to its initial state.

You need to repeat this pattern across each component that you would like to to reload, being sure to add the  runGuardsAndResolvers  option to each route in the routing file.

** Updated 05/03/2018 **

Thanks to  Changyu Geng  and  Dennis de Laat  for pointing this out in the comments.

I have added an unsubscribe handler in the ngOnDestroy hook in the above example.

As  router.events  is global, if we do not unsubscribe our event handler on component destruction then it will continue to fire for every  NavigationEnd event across our application even when we are not on the invites screen. This is less than ideal because

a) We will be running our event handler for invites even if we are on another screen.

b) Every time we navigate back to invites we will resubscribe to the event. This would cause our initialise function to run n times, where n is the number of times we have landed on the invites page.

If you’ve enjoyed this blog, please take a couple of minutes to check out  Gurn  and see how it can make you a more productive developer. ---------------------------------------------------------------------------

Ask Question

2

 

I want to extract  only the last event entry of type NavigationEnd  from router.events. But somehow I can\'t find a proper way to do this. Whatever I try the following code snipped is the only way to get access to these objects of interest.

  let  ne :  any ; 

router . events . filter ( e  =>  e  instanceof   NavigationEnd ) 
    . forEach ( e  =>   { 
      ne  =  e as  NavigationEnd ;                  
    }); 

console . log ( \'target page: \' ,  ne . urlAfterRedirects );                                            

But do I really have to use .forEach() letting it run though all entries and using then the last entry? Isn\'t there a better way to handle the events as a kind of array and then say

  ne  =  arrayOfEvents [ arrayOfEvents . length - 1 ]           

?

I\'m getting crazy about it but it seems that I can\'t see the wood for the trees...

angular   typescript   angular-routing

share edit

edited  Apr 8 \'18 at 21:43

HDJEMAI

4,893 14 43 64

asked  Apr 8 \'18 at 20:19

Lynx 242

4,917 4 18 36

1

Have you looked at using the  last  operator on observables?  reactivex.io/documentation/operators/last.html  –  Daniel W Strimpel   Apr 8 \'18 at 20:29

 

Yes, I tried it. But it unfortunately doesn\'t work in my case as there is no processable return value. –  Lynx 242 Apr 8 \'18 at 20:45  

2

Did you see this article?  toddmotto.com/dynamic-page-titles-angular-2-router-events  –  Vega   Apr 8 \'18 at 20:49

1

That\'s it! This helps a lot. Thank you. –  Lynx 242   Apr 8 \'18 at 21:00

 

If the events observable never completes then  last  never does anything. What do  you  mean by last?  YOU mean the last in the group. But the computer can wait 100 years for the stream to close, and that\'s what  last()  means to RxJS! So I think actually  last()  will NEVER return (it possibly might as the page is being unloaded but that\'s no use). –  Simon_Weaver   Jan 23 at 22:10  

2 Answers

active oldest votes

4

 

Okay, after reading through the posted articles above and rethinking what I really want to achieve I found out that my approach was definitely too complicated. Because actually I only need access to the currently called route. And so I started from scratch and came across this small but very effective solution:

  this . router . events . subscribe ( value  =>   { 
    console . log ( \'current route: \' ,  router . url . toString ()); 
 });                           

Thanks to all who shared a comment in oder to support me! It helped al lot as I was able to tie up loose ends.

share edit

answered  Apr 8 \'18 at 21:16

Lynx 242

4,917 4 18 36

4

PS. There\'s a better way to filter using a \'typeguard\', you then get the proper type without having to assert it again:

  firstChildData$  =   this . router . events . pipe ( 

            filter (( e ):  e is  NavigationEnd   =>  e  instanceof   NavigationEnd ),  

            map ( e  =>   {   // e is now NavigationEnd }                                 

This is actually necessary in strict mode.

查看更多关于Reloading current route in Angular 5 / Angular 6 /的详细内容...

  阅读:36次