Sreehari Ballampalli
Sreehari Ballampalli

Reputation: 3724

How to Refresh a Component in Angular

I am working on an Angular project. I'm struggling with refresh action in a component.

I would like to refresh the router's components on button click. I have refresh button when I click on it the component/router need to be refresh.

I tried window.location.reload() and location.reload() these two are not suitable for my need. Please help if anyone aware of it.

Upvotes: 167

Views: 758159

Answers (27)

Peter
Peter

Reputation: 2267

I notice not all asnwer are specific to the question as for components. Well then show them all, it depends a bit on your needs:

  • Method 1: Full page refresh (heaviest, reloads everything)
  • Method 2: Re-renders components while maintaining state (medium impact)
  • Method 3: Forces update of specific component (lightest)
  • Method 4: Re-runs initialization logic (medium impact)

Generally, Method 2 or 3 are preferred as they provide a better user experience than a full page refresh.

  1. Using Location service (full page refresh):
import { Location } from '@angular/common';

constructor(private location: Location) {}

refresh() {
  this.location.go(this.location.path());
  window.location.reload();
}
  1. Using Router to re-navigate (soft refresh):
import { Router } from '@angular/router';

constructor(private router: Router) {}

refresh() {
  this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => {
    this.router.navigate([this.router.url]);
  });
}
  1. Using ChangeDetectorRef to force component update:
import { ChangeDetectorRef } from '@angular/core';

constructor(private cd: ChangeDetectorRef) {}

refresh() {
  this.cd.detectChanges();
}
  1. Using ngOnInit with a trigger variable:
export class YourComponent {
  refreshTrigger = 0;

  refresh() {
    this.refreshTrigger++;
    this.ngOnInit();
  }
}

And if something needs to be set some var or so and its not GUI related, you might do it in ngafterview. And in some rare cases you move things to constructor logic to be sure its available oince ngOninit fires.

Upvotes: 0

hamdi FAKHFAKH
hamdi FAKHFAKH

Reputation: 11

  refreshPage() {
  // save the current route in constant
    const route = this.router.url; 
  // navigate to other route 
    this.router.navigate(['pages/dashboard'], { skipLocationChange: true 
    }).then(() => {
      // return to the current route
      this.router.navigate([route]); 
});
      }

Upvotes: 1

SergejK
SergejK

Reputation: 109

I know, this is an old question, but from my point of view, still a valid one. I came across the same issue, and none of the previous solutions were satisfying to me. The best solution, from my point of view, was this one:

this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';

unfortunately, this is deprecated: https://angular.io/api/router/Router

After a while of research, I found a practical solution, which is suitable for all my needs and actually works with Angular 17.

Step 1:

Open your app.config.ts and extend your appConfig:

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes,
      withRouterConfig({onSameUrlNavigation: 'reload'}),
    ),
    {
      provide: RouteReuseStrategy,
      useClass: AppRouterReuseStrategy,
    },
  ],
};

Step 2:

Create a new file, called: app.router-reuse-strategy.ts (or something else, doesn't matter) and paste the following content:

export class AppRouterReuseStrategy extends BaseRouteReuseStrategy {
  override shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    window.scrollTo({top: 0, behavior: 'instant'})
    return (future.routeConfig === curr.routeConfig) && !future.data['reloadComponent'];
  }
}

Step 3:

Now you can enable the force reload the component, just by extending your routes

export const routes: Routes = [
  {
    path: '',
    loadChildren: () => import('...').then(m => m.LandingRoutingModule),
  },
  {
    path: 'persons',
    loadChildren: () => import('...').then(m => m.PersonRoutingModule),
    data: {
      'reloadComponent': true
    }
  },
  {
    path: '**',
    component: NotFoundComponent,
  },
];

I hope this helps someone :-)

Upvotes: 5

sankar
sankar

Reputation: 837

Adding this to code to the required component's constructor worked for me.

this.router.routeReuseStrategy.shouldReuseRoute = function () {
  return false;
};

this.mySubscription = this.router.events.subscribe((event) => {
  if (event instanceof NavigationEnd) {
    // Trick the Router into believing it's last link wasn't previously loaded
    this.router.navigated = false;
  }
});

Make sure to unsubscribe from this mySubscription in ngOnDestroy().

ngOnDestroy() {
  this.mySubscription?.unsubscribe();
}

Refer to this thread for more details - https://github.com/angular/angular/issues/13831

Upvotes: 31

AbdealiLoKo
AbdealiLoKo

Reputation: 3357

I came across this when looking for the same problem. And I found there were too many solutions across stackoverflow with similar/tweaked answers.

I believe the answer can be different based on what you're trying to achieve:

  1. Just reload some API on the current component (Similar to calling a simple init() function again)
  2. Tell angular to re-create all components on the page (Services and data saved in stores remain as is)
  3. Reload the guards, resolvers, etc. in Angular (Router related events)
  4. Reload all assets on the page including HTML, CSS, etc. (Browser refresh)

Summarizing the solutions here for easier reference:

Solution 1: Just call ngOnInit()

class MyComponent {
    ngOnInit() {...}

    reinitComponent() {
        this.ngOnInit();
    }
}

Notes:

  • This does not refresh the entire state of the current component - for example variable set in the constructor will not be changed
  • We should not be calling a angular lifecycle hook explicitly
  • This just affects the current component (not all components in page)

Alternate syntax: Move this logic to a separate init() function and call it in both places

class MyComponent {
    ngOnInit() {
        this.init();
    }

    init() { ... }

    reinitComponent() {
        this.init();
    }
}

Solution 2A: Route to another route and return back to current route

import { ActivatedRoute, Router } from '@angular/router';

class MyComponent {
    constructor(private route: ActivateRoute, private router: Router) {}

    refreshPage() {
        this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => {
            this.router.navigate([], { relativeTo: this.route });
        });
}

Notes:

  • Need to have a intermediate route that can be used
  • Sometimes the intermediate route can cause
  • Does not refresh the states of global root injected services (For example if you save currentLoggedInUser in a service)
  • If the intermediate route has a HTML template, it will be shown to the user momentarily
  • Using this you can control how many parent components angular will re-create (For example intermediate route can be ../.. to avoid recreating all components till the top)

Solution 2B: Use Angular RouteReuseStrategy.shouldReuseRoute

This requires 3 steps: Step 1: When configuring router module, use onSameUrlNavigation

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

@NgModule({
    imports: [
        RouterModule.forRoot(routes, {
            onSameUrlNavigation: 'reload',
        })
    ]
}
export class MyRoutingModule {}

Step 2: Change the RouteReuse strategy in Angular so that it does not reuse the component based on a queryParams

import { Injectable, NgModule } from '@angular/core';
import { ActivatedRouteSnapshot, BaseRouteReuseStrategy, RouteReuseStrategy, RouterModule } from '@angular/router';

@Injectable({ providedIn: 'root' })
class CustomRouteReuseStrategy extends BaseRouteReuseStrategy {
    shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return super.shouldReuseRoute(future, curr) && future.queryParams._refreshing !== 'true';
    }
}

@NgModule({
    providers: [
        {
            provide: RouteReuseStrategy,
            useClass: CustomRouteReuseStrategy,
        },
    ],
    imports: [ // This is from Step 1
        RouterModule.forRoot(routes, {
            onSameUrlNavigation: 'reload',
        })
    ]
}
export class MyRoutingModule {}

Step 3: And finally use it in a component

import { ActivatedRouteSnapshot, Router } from '@angular/router';
class MyComponent {
    constructor(private router: Router, private route: ActivatedRoute) {}

    refreshPage() {
        router.navigate([], {
            relativeTo: route,
            queryParamsHandling: 'merge',
            queryParams: { _refreshing: true },
            skipLocationChange: true,
        });
    }

Notes:

  • This will re-create all components on this page

Solution 3: Use angular configs to always run router events on same url

When configuring router module, use onSameUrlNavigation

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

@NgModule({
    imports: [
        RouterModule.forRoot(routes, {
            onSameUrlNavigation: 'reload',
        })
    ]
}
export class MyRoutingModule {}

Notes:

  • This will just call guards/resolvers
  • The component for the route will remain the same and angular will not re-create the components on the page

Solution 4: Tell the browser to refresh the page using Browser APIs

class MyComponent {
    refreshBrowserTab() {
        window.location.reload();
    }

Notes:

  • Reloads the entire page, including all assets like CSS, HTML, JS, images, fonts, etc.

Alternate syntax: Uses Angular's location which can be altered with DI to mock it during tests

import { Location } from '@angular/common';

class MyComponent {
    constructor(private location: Location) {}

    refreshBrowserTab() {
        this.location.reload();
    }

Upvotes: 3

Bullsized
Bullsized

Reputation: 605

Import ActivatedRoute and Router:

import { ActivatedRoute, Router } from '@angular/router';

Then add them to your constructor:

  constructor(
    private route: ActivatedRoute,
    private router: Router,
  ) 

After that in the function that you want the reloading of the page to happen:

   reloadURL(): void {
    this.router.routeReuseStrategy.shouldReuseRoute = () => false;
    this.router.onSameUrlNavigation = 'reload';
    this.router.navigate(['./'], {
      relativeTo: this.route,
    });
  }

It will then reload the very component that you are on, simple as that.

Upvotes: 0

airush
airush

Reputation: 770

private saveRouterStrategyReuseLogic: any;


ngOnInit() {
    // Save logic
    this.saveRouterStrategyReuseLogic = this.router.routeReuseStrategy.shouldReuseRoute;
    this.router.routeReuseStrategy.shouldReuseRoute = (future, curr) => { return false; };
}

ngOnDestroy() {
    this.router.routeReuseStrategy.shouldReuseRoute = 
this.saveRouterStrategyReuseLogic;

}

Upvotes: 0

Tiki
Tiki

Reputation: 105

This worked for me: this.ngOnInit();

Upvotes: 0

Feng Zhang
Feng Zhang

Reputation: 1960

calling ngOnInit() does not work for my complicated component, I end up using this

reloadCurrentRoute() {
    let currentUrl = this.router.url;
    this.router.navigateByUrl('/', {skipLocationChange: true}).then(() => {
        this.router.navigate([currentUrl]);
    });
}

Upvotes: 21

Tombalabomba
Tombalabomba

Reputation: 480

Just change the routeReuseStrategy from the angular Router:

this._router.routeReuseStrategy.shouldReuseRoute = function () {
      return false;
    };

Set the routerproperty "navigated" to false:

this._router.navigated = false;

Then navigate to your component:

this._router.navigate(['routeToYourComponent'])

After that reinstate the old/default routeReuseStrategy:

this._router.routeReuseStrategy.shouldReuseRoute = function (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
          return future.routeConfig === curr.routeConfig;

You can also make a service out of this:

@Injectable({
  providedIn: 'root'
})
export class RouterService {

  constructor(
    private _activatedRoute: ActivatedRoute,
    private _router: Router
  ) { }

  reuseRoutes(reuse: boolean) {
    if (!reuse) {
      this._router.routeReuseStrategy.shouldReuseRoute = function () {
        return false;
      };
    }
    if (reuse) {
      this._router.routeReuseStrategy.shouldReuseRoute = function (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return future.routeConfig === curr.routeConfig;
      };
    }
  }

  async refreshPage(url?: string) {
    this._router.routeReuseStrategy.shouldReuseRoute = function () {
      return false;
    };

    this._router.navigated = false;

    url ? await this._router.navigate([url]) : await this._router.navigate([], { relativeTo: this._activatedRoute });

    this._router.routeReuseStrategy.shouldReuseRoute = function (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
      return future.routeConfig === curr.routeConfig;
    };
  }
}

Upvotes: 2

There are problems with some of the answers before:

  1. one should not call lifecycle methods such as ngOnInit() directly.
  2. re-navigating to some URL is a sub-optimal solution, especially if the component you are dynamically loading is only a small part of a big page

This worked for me (I don't know if it is recommendable or optimal)

  1. In the component to be dynamically loaded, add a public reference to the ChangeDetectorRef.
  2. I assume that the dynamically loaded data has some input data, ("tags" in the example), which is updated after the fact and is creating rendering problems.
  @Input()
  tags: string[] = [];

  constructor(public changeDetector: ChangeDetectorRef )
  1. In the parent component doing the dynamic loading, after having created the child component dynamically and maybe set some data to it, call the created instance's changeDetector.markForCheck() as follows:
  constructor(private vcrf: ViewContainerRef, private cfr: ComponentFactoryResolver, private elementRef: ElementRef) {
  }

  public loadComponent(): void {

    const componentFactory = this.cfr.resolveComponentFactory(TagsListComponent);
    const component = this.container.createComponent(componentFactory);
    component.instance.tags = /* whatever data we want to pass */;
    component.instance.changeDetector.markForCheck();
    ...

loadComponent() is a custom function that could be called, for example, once the parent page scrolls to some position in the parent component, indicating that the dynamic component should be shown.

Upvotes: 3

Juan Castillo
Juan Castillo

Reputation: 76

In my case I needed to reload specific routes (not all in the application), so adding the global setting {onSameUrlNavigation: 'reload'} had no effect, while this.router.routeReuseStrategy.shouldReuseRoute = () => false in the component works but modifies the global settings of the router.

The solution was to save the original router configuration to a variable before changing it in the component's constructor as indicated @abhishek-singh by the first answer of the problem.

private routeReuseStrategy:any;

  constructor(
    private router:Router
  ) {
    this.routeReuseStrategy = this.router.routeReuseStrategy.shouldReuseRoute;
    this.router.routeReuseStrategy.shouldReuseRoute = () => false;
  }

And when exiting the path, remap the original configuration using the OnDestroy hook.

   public ngOnDestroy():void
   {
     this.router.routeReuseStrategy.shouldReuseRoute = this.routeReuseStrategy;
   }

Upvotes: 0

Abhishek Singh
Abhishek Singh

Reputation: 1517

Just follow the below steps:

  1. add {onSameUrlNavigation: 'reload'} as below in your app-routing.modules.ts

@NgModule({
imports: [ RouterModule.forRoot(routes,{onSameUrlNavigation: 'reload'})],
exports: [ RouterModule ]
})

  1. add this.router.routeReuseStrategy.shouldReuseRoute = () => false as below in you component ts file constructor:
constructor(private router: Router) {
      this.router.routeReuseStrategy.shouldReuseRoute = () => false;
  }
  1. Now click the button and call ajax server call, get the desired data from server and replace your global variable in TS file with new data.
  2. call this.router.navigate([/sameRoute]); in the last. replace sameRoute with your route url.

  3. make sure to return false at the end in your refresh button's onClick method, otherwise it will lead to main routing page instead of same page reload.

    These steps will reload your page. :)

For more details: https://medium.com/engineering-on-the-incline/reloading-current-route-on-click-angular-5-1a1bfc740ab2

I am using Angular 12.

Upvotes: 10

Anis KCHAOU
Anis KCHAOU

Reputation: 1104

reload () { this.ngOnInit(); }

Upvotes: -2

Harpreet Singh
Harpreet Singh

Reputation: 329

router.navigate['/path'] will only takes you to the specified path
use router.navigateByUrl('/path')
it reloads the whole page

Upvotes: 0

Harishanker Maurya
Harishanker Maurya

Reputation: 31

html file

<a (click)= "getcoursedetails(obj.Id)" routerLinkActive="active" class="btn btn-danger">Read more...</a>

ts file

  getcoursedetails(id)
  { 
  this._route.navigateByUrl('/RefreshComponent', { skipLocationChange: true }).then(() => {
    this._route.navigate(["course",id]);
  }); 

Upvotes: 0

Vadapalli Chaitu
Vadapalli Chaitu

Reputation: 159

this is little bit out of box and I dont know whether this helps you or not but have yo tried

this.ngOnInit();

its a function so whatever code you have in it will be recalled just like a refresh.

Upvotes: 13

Reza Piri
Reza Piri

Reputation: 115

just do this : (for angular 9)

import { Inject } from '@angular/core';    
import { DOCUMENT } from '@angular/common';

constructor(@Inject(DOCUMENT) private document: Document){ }

someMethode(){ this.document.location.reload(); }

Upvotes: 0

Felix
Felix

Reputation: 4595

One more way without explicit route:

async reload(url: string): Promise<boolean> {
  await this.router.navigateByUrl('.', { skipLocationChange: true });
  return this.router.navigateByUrl(url);
}

Upvotes: 13

Sreehari Ballampalli
Sreehari Ballampalli

Reputation: 3724

After some research and modifying my code as below, the script worked for me. I just added the condition:

this.router.navigateByUrl('/RefreshComponent', { skipLocationChange: true }).then(() => {
    this.router.navigate(['Your actualComponent']);
}); 

Upvotes: 183

Behzad Behzad
Behzad Behzad

Reputation: 11

constructor(private router:Router, private route:ActivatedRoute ) { 
}

onReload(){
 this.router.navigate(['/servers'],{relativeTo:this.route})
}

Upvotes: 1

Julien Rousset
Julien Rousset

Reputation: 11

kdo

// reload page hack methode

push(uri: string) {
    this.location.replaceState(uri) // force replace and no show change
    await this.router.navigate([uri, { "refresh": (new Date).getTime() }]);
    this.location.replaceState(uri) // replace
  }

Upvotes: 1

Lucian Moldovan
Lucian Moldovan

Reputation: 587

Fortunately, if you are using Angular 5.1+, you do not need to implement a hack anymore as native support has been added. You just need to set onSameUrlNavigation to 'reload' in the RouterModule options :

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

More information can be found here: https://medium.com/engineering-on-the-incline/reloading-current-route-on-click-angular-5-1a1bfc740ab2

Upvotes: 27

Bh00shan
Bh00shan

Reputation: 508

In my application i have component and all data is coming from API which i am calling in Component's constructor. There is button by which i am updating my page data. on button click i have save data in back end and refresh data. So to reload/refresh the component - as per my requirement - is only to refresh the data. if this is also your requirement then use the same code written in constructor of component.

Upvotes: 2

Shinoy Babu
Shinoy Babu

Reputation: 1779

Use the this.ngOnInit(); to reload the same component instead reloading the entire page!!

DeleteEmployee(id:number)
  {
    this.employeeService.deleteEmployee(id)
    .subscribe( 
      (data) =>{
        console.log(data);

        this.ngOnInit();

      }),
      err => {
        console.log("Error");
      }   
  }

Upvotes: 116

chirag sorathiya
chirag sorathiya

Reputation: 1243

Other way to refresh (hard way) a page in angular 2 like this it's look like f5

import { Location } from '@angular/common';

constructor(private location: Location) {}

pageRefresh() {
   location.reload();
}

Upvotes: -4

Sajeetharan
Sajeetharan

Reputation: 222722

This can be achieved via a hack, Navigate to some sample component and then navigate to the component that you want to reload.

this.router.navigateByUrl('/SampleComponent', { skipLocationChange: true });
this.router.navigate(["yourLandingComponent"]);

Upvotes: 5

Related Questions