Search code examples
angularurl-routingsingle-page-applicationangular-routing

Keeping route coherent when navigating between components


I have a router connecting several components. Each component can modify the route so that its state can be preserved (copy paste to someone etc.).

Example :

  1. /route/mycomp
  2. play with some field
  3. route becomes /route/mycomp;field=value
  4. click on /route/othercomp, route adapts
  5. click on /route/mycomp
  6. route becomes route/mycomp

I would like it to remain /route/mycomp;field=value instead, since the component itself did not change.

What I tried :

  • changing my RouteReuseStrategy implementation
  • saving url in each component after each filter action
  • changing the DOM

Any ideas ?

Angular 2.3.1.


Solution

  • This lecture helped :

    How to implement RouteReuseStrategy shouldDetach for specific routes in Angular 2

    import { RouteReuseStrategy } from '@angular/router'
    import { DetachedRouteHandle } from '@angular/router'
    import { ActivatedRouteSnapshot } from '@angular/router'
    
    /**
     * Basic implementation comes from @manfredsteyer.
     * We extended it to add functionality described in current issue.
     */
    export class CustomReuseStrategy implements RouteReuseStrategy {
    
      tab: string = null
      handlers: {[key: string]: any/*DetachedRouteHandle*/} = {}
    
      /// Should detach and not destroy: true (always)
      shouldDetach(route: ActivatedRouteSnapshot): boolean {
    
        return true
    
      }
    
      /// If detached, store
      store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    
        this.handlers[route.routeConfig.path] = {
          handle: handle,
          params: route.params
        }
    
      }
    
      /// Should reuse existing component: if avalaible
      shouldAttach(route: ActivatedRouteSnapshot): boolean {
    
        // Avoid multiple calls
        this.tab = route.routeConfig.path // current tab
    
        // the double exclamation mark (!!) casts to actual boolean
        return !!route.routeConfig && !!this.handlers[route.routeConfig.path]
    
      }
    
      /// Should we retrieve component and parameters
      retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    
        if (!route.routeConfig || !this.handlers[route.routeConfig.path])
          return null
    
        let handler = this.handlers[route.routeConfig.path]
        let sentParameters = route.url.slice(-1)[0].parameters
        let parameters = null
    
        // Send back params (only if current tab, last retrieve call)
        if (this.tab === route.routeConfig.path) {
          // Which parameters to send to the retrieved component ?
    
          // If you send parameters from one panel to another
          // (like clicking on a button to access a related object in another panel),
          // then use them
          if (Object.keys(sentParameters).length)
            parameters = sentParameters
          // if you don't, use the old ones (cf. **Expected behavior**)
          else
            parameters = handler.params
    
          // urlParse() is in each panel component (inheritance is your friend)
          // and can do some specific parameters name and/or value parsing
          // before injecting them into said component
          handler.handle.componentRef.instance.urlParse(parameters)
        }
    
        return handler.handle
    
      }
    
      // I'm not too sure about this part
      /// Should we load/retrieve other component: if path is different
      //  @todo /!\ if routeConfig is null, say "yes"
      shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    
        // @todo: strange case with null/null values
        return future.routeConfig === curr.routeConfig
    
      }
    
    }
    

    (Copy paste of github.)