30001

Angular2 routing between sub-applications in modules

Question:

I'm using Angular 2.1 for a large application that has multiple sub-modules each defining sub-applications organized by feature. The top-level module configures the RouterModule with all child routes for the entire application by importing each sub-app's routes, etc. So from the sub-application's perspective, it routes relative to whatever the top-level application set the route to however the sub-application doesn't explicitly know what its top route actually is, directly.

Some of these sub-applications have summary panels for entities that are more fully defined/controlled in another sub-application. Conceptually the modules are setup like this:

Module 1: Imports Module 2 Routes for Module 1 Component1A App1View (contains Component1A, M2SummaryComponent) Module 2: Routes for Module 2 (one exported as APP2_VIEW_ROUTE, type Route) Component2A Component2B M2SummaryComponent App2View (contains Component2A, Component2B) ...etc.

I'm looking for a design pattern whereby the M2SummaryComponent can be written to link to a route within its own module while it is instantiated in Module 1's App1View without having to hard-code or re-assemble routes somehow, manually.

I had hoped this was a common-enough problem that the Angular team likely made it possible using something like router.navigate([APP2_VIEW_ROUTE, ...other params]), where one can simply pass the route object you used to configure RouterModule pertinent to the desired path. Looking at the source code however this doesn't seem to be an option. Instead we have examples of ActivatedRoute.

The problem faced with ActivatedRoute (this.route below) is that it will be relative to App1View. So trying to use router.navigate(['m2SpecialId'],{relativeTo: this.route}) is a non-starter since it will be relative to the path that got us to App1View, not back into module 2's preferred route to App2View.

The only other, somewhat elegant, route (pun) around these issues as far as I can tell would be to make each sub-application's module be importable as a ModuleWithProviders such that, optionally, one can inform the module of the top-level route applied to its state. Then write up helper functions that assemble URIs specific to that sub-application using its defined routes.

That feels very boiler-plate...like perhaps the solution or design pattern already exists in the framework, hence this question.

<strong>Question:</strong>

Is there a design pattern for using the Angular2 Router that would allow components imported into other modules to cleanly reference their own module's pre-configured routes while maintaining a flexible top-level route definition?

Answer1:

If you want to access your M2Component in M1 via link then you should use RouterModule and do not import module2 in module1.

Inside module1 use

RouterModule.forRoot([ { path: 'module2', loadChildren: 'path-of-module2/module2.module#Module2Module' } ])

and in module2

RouterModule.forChild([ { path: '', component: Module2Component } ])

Now you can open Module2Component from route '/module2'.

Answer2:

Here's the solution I said sounds very boilerplate. It requires that you setup your sub-application modules with a pair of opaque tokens: one for your URI, and the other to guard against re-configuring that URI.

import { NgModule, OpaqueToken, ModuleWithProviders, Optional, SkipSelf, Inject } from '@angular/core'; export const ROOT_URI = new OpaqueToken('ROOT_URI'); export const URI_GUARD = new OpaqueToken('URI_GUARD');

Next, you need to define your NgModule:

@NgModule({ /** ... as before ... */ }) export class SubApp1Module { static configure(uri: string): ModuleWithProviders { return { ngModule: SubApp1Module, providers: [ { provide: ROOT_URI, useValue: uri }, { provide: URI_GUARD, useFactory: provideGuard, deps: [[ Object, new Optional(), new SkipSelf() ]] } ] }; } constructor(@Optional() @Inject(URI_GUARD) guard: any) { /** */ } }

The provideGuard function:

export function provideGuard(something: any): any { if (!something) { return 'guarded'; } else { throw new Error('Do not attempt to configure this module more than once.'); } }

In your module that will be importing this one and configuring the RouterModule routes that ultimately get you into this sub-application, you'll add it to the imports: imports: [ ..., SubApp1Module.configure(SPECIAL_ROOT_URL), ...].

In any Component/Directive that you have in SubApp1Module, you can grab this module's ROOT_URI through injection:

import { Inject } from '@angular/core'; import { ROOT_URI } from './whatever/local/export/location'; // Down at the constructor constructor(@Inject(ROOT_URI) rootUri: string) { console.log('This is my rootUri: ' + rootUri); }

Now, this works and of course you can use @Optional or ? as appropriate to allow for a default value to be injected. This also works for injecting a service or factory that resolves your routes, etc. if you decide to go that route.

In some ways this feels like the Angular Way™ (i.e., do it like this). However as you can see it's also fairly involved <em>just</em> to allow entities within a module to route to within their own module despite being instantiated into a Component in another module without hard-coding paths, etc.

Recommend

  • Jupyter Notebook: no module named pandas
  • URL path parameters vs query parameters in Django
  • Scan installed software on Windows
  • HTML Form posts twice after submitting
  • Where to get the .java files of a netbeans project?
  • How to import Navit into Eclipse and use it in own android project
  • Wrap C++ function using Boost Reflect or another C++ reflection library
  • android Navigation Bar hiding and persantage of usable screen overlap
  • Issue with routerLink directive
  • How do I retrieve the user information of a user authenticated with Apache's mod_ldap?
  • JSR-330 support in Picocontainer : @Inject … @Named(\"xxx)
  • Creating a DropDownList
  • Application level floating views with navigation in Android
  • Who propagate bugfixes across branches (corporate development)?
  • Python pickle not one-to-one: different pickles give same object
  • SAVE attribute needed for Fortran variables when only the C_LOC address is returned to a C program?
  • How do I configure context broker accept post requests from my remote sensor?
  • WPF ICommand CanExecute(): RaiseCanExecuteChanged() or automatic handling via DispatchTimer?
  • How solve “Qt: Untested Windows version 10.0 detected!”
  • How do I access an unhandled exception in an MVC Error view?
  • Rails Find when some params will be blank
  • Google Custom Search with transparent background
  • Scrapy recursive link crawler
  • Counter field in MS Access, how to generate?
  • swift auto completion not working in Xcode6-Beta
  • Javascript Callbacks with Object constructor
  • vba code to select only visible cells in specific column except heading
  • How can I use Kendo UI with Razor?
  • Javascript + PHP Encryption with pidCrypt
  • Websockets service method fails during R startup
  • AT Commands to Send SMS not working in Windows 8.1
  • Rails 2: use form_for to build a form covering multiple objects of the same class
  • Why can't I rebase on to an ancestor of source changesets if on a different branch?
  • How do I configure my settings file to work with unit tests?
  • Qt: Run a script BEFORE make
  • Is it possible to post an object from jquery to bottle.py?
  • Can't mass-assign protected attributes when import data from csv file
  • How to get NHibernate ISession to cache entity not retrieved by primary key
  • Conditional In-Line CSS for IE and Others?
  • To Get the radio button value in ruby on rails