#Implement a router scope

6 messages · Page 1 of 1 (latest)

earnest marsh
#

One of my websites shows tables of data. The data set may change and so i want to provide access to a previous version of the data. My idea is to add a router scope for example
If those are the routes on the page

  • /foo
  • /bar
    i want them also be available as
  • /v/:version/foo
  • /v/:version/bar

That is relatively easy to achieve by just having a v/:version route node

export const ROUTES: Routes = [
  // ... common routes here

  {
    // default application behavior
    path: '',
    children: routes, 
  },
  {
    // versioned scope
    path: 'v/:version',
    children: routes,
  },
]

Now, the inner components use routerLink to link between pages. I want the links to be scoped without implementing a custom routerLink directive. My approach is to override the Router service so that it always produces Url Trees relative to its activated route.

@Injectable()
export class ScopedRouter extends Router {
  private scopeRoute = inject(ActivatedRoute)
  override createUrlTree(commands: any[], navigationExtras?: UrlCreationOptions): UrlTree {
    navigationExtras = navigationExtras || {}
    avigationExtras.relativeTo = navigationExtras.relativeTo || this.scopeRoute
    return super.createUrlTree(commands, navigationExtras)
  }
}

That service is then provided at the versioned route node.

The links are generated as expected. However, navigation does not work because of the following error

core.mjs:10572 ERROR Error: Uncaught (in promise): Error: NG04013: Cannot activate an already activated outlet
Error: NG04013: Cannot activate an already activated outlet
    at RouterOutlet.activateWith (router.mjs:2472:19)
    at ActivateRoutes.activateRoutes (router.mjs:3037:40)

Besides that, it feels to be a dirty approach. Is there any other technique that can be applied to solve this? Preferably without having to change the implementation of inner components.

steel crystal
#

This error message indicates that an outlet is being activated more than once.

#

This can happen if there are multiple router outlets in the template, and the same route is being activated in both outlets.

#

So in this code, you should put the name on router outlet.
export const ROUTES: Routes = [
// ... common routes here

{
// default application behavior
path: '',
children: routes,
outlet:'main'
},
{
// versioned scope
path: 'v/:version',
children: routes,
outlet:'version'
},
]

earnest marsh
#

i see, thank you. havent thought about named outlets. Need to play around with the idea, maybe even solvable without overriding the Router service.

earnest marsh