Listening for Angular 2 router events

How can I detect state changes in Angular 2 router?

In Angular 1.x, I used the following event:

$rootScope.$on('$stateChangeStart',
    function(event,toState,toParams,fromState,fromParams, options){ ... })

In Angular 2, using the window.addEventListener("hashchange") eventlistener does not work as expected when changing state programmatically; it only triggers on browser history back function.

To listen for state changes, use the router.subscribe() function like this:

import {Injectable} from 'angular2/core';
import {Router} from 'angular2/router';

@Injectable()
export class SubscribeService {
constructor (private _router: Router) {
this._router.subscribe(val => {
console.info(val, '<-- subscribe func');
})
}
}

Inject the service into a component that is initialized through routing:

import {Component} from 'angular2/core';
import {Router} from 'angular2/router';

@Component({
selector: 'main',
templateUrl: '../templates/main.html',
providers: [SubscribeService]
})
export class MainComponent {
constructor (private subscribeService: SubscribeService) {}
}

I have injected this service into other components as well, but when I change state, the console.info() in the service doesn't seem to be working. What could I be doing wrong?

Answer №1

Setting up a new router

constructor(router:Router) {
  router.events.subscribe(event:Event => {
    if(event instanceof NavigationStart) {
    }
    // NavigationEnd
    // NavigationCancel
    // NavigationError
    // RoutesRecognized
  });
}

Implementing with the old router

Using dependency injection to subscribe to route change events

import {Router} from 'angular2/router';

class MyComponent {
  constructor(router:Router) {
    router.subscribe(...)
  }
}

A Friendly Reminder

When using the new router, make sure to import NavigationStart from the @angular/router module

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

Otherwise, the instanceof check will fail and you'll see an error message saying NavigationStart is not defined.

For more information

Answer №2

Filtering events can be done using the filter() method.

Instead of simply using

filter(e => e is NavigationEnd)
, a better approach is to include a 'type guard' in the filter() function like this:

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

This setup includes two components:

  • e is NavigationEnd defines the assertion for the function (this TypeScript syntax does not carry over to JavaScript)
  • e instanceof NavigationEnd actually checks the type at runtime

The benefit of this method is that downstream operators, such as map, will recognize the type as

NavigationEnd</code instead of just <code>Event
without the type guard.

If you only need to verify one event type, this is the most efficient way to do so. It also seems to be required in strict mode to avoid compiler issues.

https://i.stack.imgur.com/uZiNj.png

Answer №3

To check for the type of an event, you can employ the instanceof method just like what was mentioned by @GünterZöchbauer

this.router.events.subscribe(event => {
  if(event instanceof NavigationStart) {
    // perform a specific action...
  }
}

Alternatively, you have the option to take a more cautious approach, however, keep in mind that the constructor name may be altered while the function is still operational!

this.router.events.subscribe(event => {
  if(event.constructor.name === "NavigationStart") {
    // perform a specific action...
  }
});

Answer №4

Directly from the official documentation

import {Event, RouterEvent, Router, NavigationEnd} from '@angular/router';

this.router.events.pipe(
  filter((e: any): e is RouterEvent => e instanceof RouterEvent)
).subscribe((evt: RouterEvent) => {
  if (evt instanceof NavigationEnd) {
    console.log(evt.url)
  }
})

While the docs suggest using the code filter((e: Event), I recommend using filter((e: any) to avoid linting errors in WebStorm.

Answer №5

const { BrowserRouter as Router, Route, Switch } = 'react-router-dom';
constructor(props) {
  super(props);
  
  this.routeEvent(this.props.history);

}
routeEvent(history){
  history.listen((location, action) => {
    console.log('Location:', location.pathname);
    console.log('Action:', action);
  });
}

Answer №6

The events in the Angular 2 router have various classes, and when subscribed to the router.events observable, you can receive instances of NavigationEnd, NavigationCancel, NavigationError, or NavigationStart. The one that will actually initiate a routing update is NavigationEnd.

It's best to avoid using instanceof or event.constructor.name because minification can cause class names to be mangled which may lead to incorrect behavior.

Instead, you can utilize the isActive function of the router. You can find more information about it here: https://angular.io/docs/ts/latest/api/router/index/Router-class.html

this.routerEventSubscription = this._router.events.subscribe((event: any) => {
  if (this._router.isActive(events.url, false)) { 
    // Returns true if the url route is active
  }
}

Answer №7

To enable route tracing in Angular 2, navigate to the file "app.modules.ts" and locate the 'imports' section.

RouterModule.forRoot(
      appRoutes,
      { 
         enableTracing: true
      }
)

When 'enableTracing' is set to true, routeEvents will be displayed in the console. Conversely, setting it to false will hide routeEvents in the console.

Answer №8

Using @myangularpluginsite, simplifying this task has been made much easier...

All you need to do is extend the RouteAware class and create a method called on<EventType>():

import { Component                                        } from '@angular/core';
import { NavigationStart, NavigationEnd, RoutesRecognized } from '@angular/router';
import { RouteAware                                       } from '@myangularpluginsite/router-x';

@Component({
    selector   : 'app-demo',
    templateUrl: './demo.component.html',
    styleUrls  : ['./demo.component.css']
})
export class DemoComponent extends RouteAware
{
    // ✨ You can handle any router event with a handler method.
    // Check https://angular.io/guide/router#router-events for a full list of Angular's router events.
    
    // ✨ Use `this.router` to access the router
    // ✨ Use `this.route` to access the activated route
    // ✨ Use `this.componentBus` to access the RouterOutletComponentBus service

    protected onNavigationStart(event: NavigationStart): void
    {
        console.log(`Navigation started for: ${event.url}`);
    }

    protected onRoutesRecognized(event: RoutesRecognized): void
    {
        console.log('Recognized routes.');
    }
    
    protected onNavigationEnd(event: NavigationEnd): void
    {
        console.log(`Navigation ended for: ${event.url}`);
    }
}

For more information, refer to this answer:

Answer №9

For a comprehensive view of all state changes, it is recommended to extend the default RouterOutlet and incorporate your own logic in the 'activate' and 'deactivate' handlers.

import {Directive} from 'angular2/core';
import {Router, RouterOutlet, ComponentInstruction} from 'angular2/router';

@Directive({
  selector: 'router-outlet'
})

export class MyOwnRouterOutlet extends RouterOutlet {
  ...

  activate() {
    console.log('Greetings from the updated router outlet!');
  }
}

Adapted from the example titled 'Custom Router Outlet' available at: https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in-depth/

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adding a trailing slash to the URL in an Express server causes it to be appended

I've encountered a strange issue with my express server. Whenever I try to use a specific URL route with the word 'bind,' an extra '/' is automatically added to it. This behavior isn't happening with other URLs that I've ...

Exploring the Fusion of Different Styles in Material-UI Using React

I have two different styles that I use in my code. One style is specific to certain components, while the other style is global and used across various components. For example, consider the following file tree: index.tsx -App.tsx -globalConstants.ts In ...

Enable the feature for users to upload images to a specific folder within the Chrome extension without the need for

I need to implement a feature in my Chrome extension that allows users to upload images directly to a specific folder named "upload" without needing a submit button. <form action="/upload"> <input type="file" name="myimages" accept="image/*"> ...

I am unable to utilize autocomplete with types that are automatically generated by Prisma

Currently, I am working on a project utilizing Next and Prisma. Within my schema.prisma file, there are various models defined, such as the one shown below: model Barbershop { id String @id @default(uuid()) name String address String ...

What is the reason behind TypeScript rejecting the syntax of checkbox input elements?

When trying to use the following checkbox in TypeScript, I encountered a warning: <input type="checkbox" onClick={(event: React.MouseEvent<HTMLInputElement>) => { setIsTOSAccepted(event.target.checked); }} defaultChecked={ ...

The signal property 'ɵunwrapWritableSignal' is not found on the specified type 'typeof import/node_modules/@angular/core/index")'

Despite attempting the solutions provided in previous threads, none of them have been successful for me. Can someone please lend a hand with this issue? https://i.stack.imgur.com/sGRsn.png ...

failure to render updated content after modification of variable

I am facing an issue with triggering a function in the component: componentA.ts html = 'hey'; this.onElementSelected(r => this.change()); public change() { console.log(this.html); if (this.html === 'hey&ap ...

How can you notify a component, via a service, that an event has occurred using Subject or BehaviorSubject?

For my Angular 10 application, I created a service to facilitate communication between components: export class CommunicationService { private messageSubject = new Subject<Message>(); sendMessage(code: MessageCode, data?: any) { this.messag ...

Implementing a Collapse and Expand All feature within an Accordion Component

Hey there! I've been attempting to implement a Collapse All feature on my accordion but am having trouble figuring it out. The resource I've been referencing is this one. I've searched around and noticed that this accordion setup is a bit d ...

When attempting to perform conditional rendering in React using a stateless functional component, I encounter an error stating "Unexpected token, expected ,"

Here is the code snippet: 'use strict' import React from 'react' import { connect } from 'react-redux' import { Panel, Col, Row, Well, Button } from 'react-bootstrap' const Cart = ({ cart }) => { const cartI ...

Tips for effectively managing loading state within redux toolkit crud operations

Seeking guidance on efficiently managing the loading state in redux-toolkit. Within my slice, I have functionalities to create a post, delete a post, and fetch all posts. It appears that each operation requires handling a loading state. For instance, disp ...

Select multiple rows by checking the checkboxes and select a single row by clicking on it in the MUI DataGrid

I am currently utilizing the MUI DataGrid version 4 component. The desired functionalities are as follows: Allow multiple selections from the checkbox in the Data Grid (if the user selects multiple rows using the checkbox). Prevent multiple selections fr ...

Route Handler 13 is encountering difficulties in retrieving data from the body in the (app/api/auth) endpoint

Whenever I attempt to retrieve the body from the new export async function POST( req: Request), it seems to come through as a stream instead of the expected content type. The route handler can be found in api/auth/signup See folder layout image export asyn ...

Transferring data between various stages of the user interface

As a newcomer to angularJs, I find myself facing an issue with two forms existing in different UI states (URLs) labeled as Step 1 and Step 2. The process requires filling up Step 1 and moving to the next step by clicking the NEXT button, which then leads t ...

I am looking to efficiently store various pieces of data in a database by utilizing a singular variable through JS, PHP, and AJAX for streamlined processing and management

I am not very familiar with the technical jargon in programming, so please bear with me if my question is a bit unclear. To provide more clarity, I have shared the code that I have already written. I will elaborate on the issue after presenting the code: ...

What is the best way to insert a button at the end of the last row in the first column and also at the

I am working on a JavaScript project that involves creating a table. My goal is to dynamically add buttons after the last row of the first column and also at the top of the last column. for (var i = 0; i < responseData.length; i++) { fo ...

Getting the value of a variable within the scope of AngularJS can be achieved by utilizing

I have an ng-repeat directive in my code that displays slides. Here is a snippet of the data: slides = [{ src: "/sikiosk/slidedata/Global/NA/USNS/Cafeteria/5000_24.jpg", interval: 5000 }, { src: "/sikiosk/slidedata/Global/NA/USNS/Cafeteria/5000_login-regi ...

What methods can I utilize to expand the qix color library with personalized manipulation features?

Utilizing the qix color library, my goal is to create specific custom manipulation functions for use in a theme. The approach I am taking looks something like this: import Color from 'color'; const primary = Color.rgb(34, 150, 168); const get ...

How can I use JavaScript fetch to retrieve data from a JSON file based on a specific value?

I am looking to extract specific values from a JSON array based on the ID of elements in HTML. How can I achieve this? [ { "product": "gill", "link": "x.com", "thumbnail": "gill.jpg ...

I'm perplexed as to why my array remains empty despite assigning a value to it in my controller. (Just to clarify, I am working with AngularJS, not Angular)

I spent a whole day debugging this issue without any luck. Issue: this.gridOptions.data = this.allTemplatesFromClassificationRepo ; **this.allTemplatesFromClassificationRepo ** remains an empty array. I have already called the activate() function to assig ...