When, in the course of human events, you find it necessary to pass data from one route to another in your Angular app, you have a couple of options.

You could include the data as a query parameter. That works well if the info is short and sweet (like an ID, for example).

But what if you have a large, complex object you'd like to pass from one route to the next?

Some people suggest that you use JSON.stringify() to stringify the object and then include it in a request parameter.

But if the object is really large, you could end up with a user-hostile URL.

Nah.

Instead, opt for something called NavigationExtras.

I'll show you how to use that interface to pass objects between routes in this guide.

The State of Affairs

That NavigationExtras interface includes a property called state.

You can use that property to pass objects between routes.

What can you put in that property? Whatever the heck you want.

For my requirements, I'm handling the use case of a user replying to an email. I want to send the contents of the original email to the "compose an email" UI. Then, the application can populate the "To" field with the "From" address of the email that's getting the reply.

Additionally, the app can set the "Subject" field to "Re: " plus the old subject. And it can populate the body of the new email message with the body of the original email message so the recipient can take a look at the context.

To do all of that, I need to pass an Email object between two routes.

And I'll do that using the aforementioned state property from the NavigationExtras interface.

Extra! Extra! Read All About It!

So what is NavigationExtras? It's a cool way to tell Angular how to build or interpret the target URL. You'll use it with a navigation method like navigate() on a Router instance.

That NavigationExtras interface includes several properties. But for the purposes of this guide, you only need to worry about state.

Here's how you implement it:

  reply() {
    let route = '/user/email/compose-email';
    this.router.navigate([route], { state: { currentEmail: this.email } });
  }

In that method, the code starts by specifying the route as a string. 

Then, it uses the injected Router object to navigate to that route.

But pay attention to the navigate() method above. The first parameter is an array containing a single element: the string representation of the route from the previous line.

So what's the second parameter? It's the NavigationExtras object.

Here, the code only sets the state property. Because it doesn't need to set anything else.

The state property is set to an object that also includes only one property: currentEmail. That's a custom property. You can call it whatever you want.

And yes, you can include more than one property in that state object if that turns your crank.

The value of currentEmail is set to an Email object that represents the email the user is currently viewing.

And that's all you need to do on the passing side. When the user replies to an email, that Email object gets sent as the state property of NavigationExtras.

But, like I said, that's on the passing side. What about the receiving side?

For the answer to that question, read on.

A Completed Pass

First thing to keep in mind: if you want to "read" that state property in NavigationExtras, you're going to have to do so in the constructor of the target component.

Yep. This is one of those rare times when you actually need to put code in the constructor.

Otherwise, you'll try to read the current navigation after it's already completed. It will be null.

So here's what I do:

  constructor(private fb: FormBuilder, private emailService: EmailService,
    private userService: UserService, private alertService: AlertService,
    private router: Router) {

    let nav: Navigation = this.router.getCurrentNavigation();

    if (nav.extras && nav.extras.state && nav.extras.state.currentEmail) {
      this.replyToEmail = nav.extras.state.currentEmail as Email;
    }
  }

Not too complicated.

The first thing the code does is get the current navigation from the Router object.

Then, it checks to make sure that current navigation has a NavigationExtras object. That's the nav.extras you see above.

And if it does have extras, then it checks for the state property. 

And if it has state, then it checks for currentEmail.

If that whole if statement evaluates to true, the code retrieves the currentEmail object and sets it as the value of a component-level property called replyToEmail.

As you recall from the passing side, that's an Email object. So the code practices a little bit of type safety with that as Email bit.

And that's all you need to do.

Wrapping It Up

Congratulations! You now know how to pass objects around from one route to the next.

Now it's up to you to use what you've learned here in your own applications. 

Have fun!

Photo by Any Lane from Pexels