2

I have this navigation component which sits outside of the router, it makes use of a reusable icon component which relies on the absolute url to point to SVG icons, which is retrieved via getter within the component:

public get absUrl(): string {
    return window.location.href.split('#')[0];
  }

Unfortunately upon redirect the view isn't updated with the new abs url, when triggered from guard.

I've made a workaround with setTimeout but its super hacky -- its purely to demonstrate that there's some delay in the chain that's causing the issue as eventually, the value for window.location.href is correct.

Here's the workaround:

public absUrl = window.location.href.split('#')[0];

  ngAfterViewInit() {
    setTimeout(() => {
      this.absUrl = window.location.href.split('#')[0];
      this.ref.detectChanges();
    }, 200);
  }
  • Only when loading the whole app at a protected route, trying to subsequentially access protected route after loading the app will work fine.
  • This occurs when loading the whole app at a non-existant route and being redirected to /404
  • If I set 0 - 100ms timeout, it will not update in time
  • The component loaded via router-outlet works, the abs url within the icons are updated accurately.

Does anyone have any ideas on how to properly implement this one? My hunch is it has something to do with how the app is loaded the very first time if you're trying to load a protected route.

SebastianG
  • 8,563
  • 8
  • 47
  • 111
  • Why can you not use the `router.url` property? – Poul Kruijt Apr 21 '20 at 12:49
  • @PierreDuc router.url will be lagging behind even more than just using window.location.href , it will not have the updated value to work even in the circumstances where window.location.href works – SebastianG Apr 21 '20 at 12:58

1 Answers1

2

I suppose you can do something like this using the Location service:

public absUrl = window.location.href.split('#')[0];

constructor(location: Location, ref: ChangeDetectorRef) {
  location.onUrlChange((s) => {
    this.absUrl = window.location.href.split('#')[0]);
    ref.detectChanges();
  });
}
Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • you mean this.absUrl = window.location ... and so on? This doesn't work by its own but if I run a change detection cycle right after that, it seems to work more consistently. I will re-test all my use cases without setTimeout to make sure this applies and keep an eye on change detection cycles. It should be more efficient than a straight getter though. Thanks! – SebastianG Apr 21 '20 at 13:04
  • @SebastianG I've updated my answer. I was a bit too hasty I suppose :) Anyways, if you want to prevent using the change detector ref, and you are just using the `absUrl` inside your template, you can think about using an `Observable` combined with the `async` pipe – Poul Kruijt Apr 21 '20 at 13:10
  • using the async pipe will still run changedetection cycles, Its not about calling ref.detectChanges just more being conscious about performance, in any case your solution will be a lot more performant than using a getter which is recalculated with every change detection cycle vs your subscription to Location. It works well in all my use cases with all setTimeouts removed. I will now mark this as the correct solution. Can you also post this to the question I asked a couple of months ago about virtually the same thing? – SebastianG Apr 21 '20 at 13:15
  • @SebastianG if should try to set all your components to `OnPush`, this will be a great performance boost. And yes, I know that `async` does fire its own change detection, but it's managed by angular, so you don't have to worry about it, or clutter your code :) – Poul Kruijt Apr 21 '20 at 13:42
  • been doing that for years but thanks, it's good advice, by the way this is the other thread I mentioned above: https://stackoverflow.com/questions/60580253/child-component-has-wrong-value-for-window-location-href-after-redirect-the – SebastianG Apr 21 '20 at 17:13