Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser button navigation for remote app routes #215

Closed
RajathVenkatesh opened this issue Aug 25, 2022 · 8 comments
Closed

Browser button navigation for remote app routes #215

RajathVenkatesh opened this issue Aug 25, 2022 · 8 comments

Comments

@RajathVenkatesh
Copy link

Good Day!

We have two Angular apps where the remote app is used as a MFE web component wrapper in the shell app.

And it looks like multiple router states get created even after sharing the @angular/router in webpack.

This leads to the browser back and forward buttons not working! (The popstate tracking is a bit messed up)

Please can someone help me with a solution or a work around?

@RajathVenkatesh
Copy link
Author

RajathVenkatesh commented Aug 26, 2022

While listening to the router events this is what I have come across :

On the popstate (browser back button clicked to go to home/client-configuration) it looks like the remote app router instance runs an imperative route to (home/my-clients path) which should have never happened (and loads the content of home/my-clients) and meanwhile the shell app router instance tries to run popstate on the right url path.

Is there any way we can connect the two routers to work in sync?

Screenshot 2022-08-26 at 12 58 41 PM

@r1m
Copy link

r1m commented Aug 30, 2022

I made a PR #217 for this, but I found that the only way to get the route correctly handled is to do it during ngOnInit.
Remove appType: 'microfrontend' from you bootstrap call.

export class MfeComponent implements OnInit {
  constructor(private router: Router, private location: Location) {}

  ngOnInit() {
    this.router.initialNavigation();
    void this.router.navigateByUrl(this.location.path(true));
  }
}

@RajathVenkatesh
Copy link
Author

@r1m

So does this mean this change has to be done to each and every component (which has routes) in the remote app ?

@r1m
Copy link

r1m commented Sep 2, 2022

Only on first level components : the ones that are federated. Not inner ones.

@r1m
Copy link

r1m commented Sep 2, 2022

Actually, there are two bugs.

  • back/forward always brings you to the route that was used to display the component (because pop_state listener is not using the current url but a snapshot)
  • deep links for module already loaded are not working because route refresh is hapenning only during module bootstrap

My PR fixes the first one, but my suggestion to move connectRouter in component fixes the second one.

@brunoalod
Copy link

@manfredsteyer Is this actually solved? Are there any docs about this issue?

@RajathVenkatesh
Copy link
Author

Not really. @brunoalod

Look at issue 451

@nanodm
Copy link

nanodm commented Aug 9, 2024

Hey there @RajathVenkatesh

Thanks to your PR and some hours reading angular/router and mf-tools plugin, I came to a possible solution.

First, I removed appType: 'microfrontend' from bootstrap call as suggested by @r1m .

And then I added this inside AppComponent (first level component):

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

export class AppComponent implements OnInit, OnDestroy {
    private nonRouterCurrentEntryChangeSubscription?: SubscriptionLike;
    
    customInitialNavigation() {
        const url = `${location.pathname.substring(1)}`;
        this.router.navigateByUrl(url);
    
        this.nonRouterCurrentEntryChangeSubscription ??= this.location.subscribe(event => {
          if (event['type'] === 'popstate') {
            const routeBelongsThisRouter = event.url!.startsWith('/search'); // here goes the url you use in startsWith() on shell *
            if (routeBelongsThisRouter) {
              this.router.navigateByUrl(event['url']!, { state: event.state });
            }
          }
        })
      }
    
    ngOnInit() {
        this.customInitialNavigation();
        // ...
    }
}

The first two lines were exctracted from your PR and correspond to the initial navigation (but fixed) made by connectRouter() from mf-tools.

The nonRouterCurrentEntryChangeSubscription idea came from reading @angular/router's setUpLocationChangeListener() method, used by initialNavigation() from @angular/router too.
I tried using those two methods, but they kept the subscription alive all through the browser session because mfe's AppModule keeps loaded, and so does RouterModule, even if you navigate out of it to shell or other mfe's. But AppComponent gets destroyed when you navigate out of mfe, so I can unsubscribe from that subscription, knowing that if I come back to the mfe, it will be created again:

ngOnDestroy(): void {
    if (this.nonRouterCurrentEntryChangeSubscription) {
      this.nonRouterCurrentEntryChangeSubscription.unsubscribe();
      this.nonRouterCurrentEntryChangeSubscription = undefined;
    }
}

This is also part of @angular/router's dispose() method

  • I use that const routeBelongThisRouter because if I popped back from mfe to shell, it was doing one extra navigation to the mfe base URL, because mfe Router doesn't know shell's routes, being 'search' the URL used to load the application as MFE.

Hope this helps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants