14

Let's say we have two routes Dashboard and Profile. Dashboard has dynamic tabs feature like Google spreadsheet. I want to do some interactions(build charts, visualizing some data) creating tabs in the Dashboard. Now, if I route to Profile and then route back to Dashboard, I want to see what was before in those tabs in Dashboard. It means, I want to maintain the state in the client side. AFAIK while routing between components, it recreates components. Is it possible to make spreadsheet like application while using angular 2 routing? I need to use routing because in my application I have to use LazyLoading feature.

So what should be the idea? I am new to angular 2.

pd farhad
  • 6,352
  • 2
  • 28
  • 47
  • I think it's possible from routing. I've read this blog, but haven't myself tried to implement such advanced routing you seek. https://vsavkin.com/angular-2-router-d9e30599f9ea#.2o8b488po – DNRN Sep 29 '16 at 06:22
  • I read the blog and also the new book of vsavkin. But it's way too complicated for me to understand the concept. That's why I am seeking a better and easier understanding :/ – pd farhad Sep 29 '16 at 06:43

2 Answers2

16

Currently components are reused only when only route parameters change while staying on the same route.

If the route is changed, event when the new route adds the same component, the component is recreated.

The preferred workaround is to keep the model in a shared service that stays alive during route changes and use the data from this service to restore the previous state of the component.

It was mentioned that there are plans to support custom reuse strategies with the router but no timeline when this will become available.

Update

Support for custom reuse strategy was added to Angular2.

See also

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • But as per my requirements, I need to create multiple tabs under `Dashboard`(it might be even 100 tabs like google spreadsheet).So what should I do? Can you shine some light on `RouterState`? – pd farhad Sep 29 '16 at 06:51
  • I don't think `RouterState` is related. You might want to look aux-routes and don't navigate away from the current route to show `Profile` but instead show it as a modal or similar. Aux routes support still seemed a bit limited to me (didn't experiment too much yet with them though). – Günter Zöchbauer Sep 29 '16 at 06:57
  • I mentioned `Profile` as an example :/ but my application is like, build interactions in the tab like google spreadsheet and store those tabs in `Dashboard`. And I need LazyLoading. My requirement is complex one and I am having problems with the initial architecture :( – pd farhad Sep 29 '16 at 07:03
  • You might ignore that limitation for now and hope the Angular team will provide a solution eventually like mentioned in https://github.com/angular/angular/issues/7757#issuecomment-236737846 – Günter Zöchbauer Sep 29 '16 at 07:09
  • 4
    You can also replace the `` component with a custom one, that doesn't remove specific components, but only hides them instead. – Günter Zöchbauer Sep 29 '16 at 07:10
  • https://github.com/angular/angular/blob/0614c8c99d6680c3db3fac012ed45ff5c49b5a59/modules/%40angular/router/src/directives/router_outlet.ts isn't very complex. – Günter Zöchbauer Sep 29 '16 at 07:11
2

Thanks to the example provided by @Günter Zöchbauer, for my own understanding I decided to distill a minimal example.

See it in action

In Summary:

First I implemented a RouteReuseStrategy:

export class CustomReuseStrategy implements RouteReuseStrategy {

    handlers: {[key: string]: DetachedRouteHandle} = {};

    calcKey(route: ActivatedRouteSnapshot) {
        let next = route;
        let url = '';
        while(next) {
            if (next.url) {
                url = next.url.join('/');
            }
            next = next.firstChild;
        }
        return url;
    }

    shouldDetach(route: ActivatedRouteSnapshot): boolean {
        return true;
    }

    store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
        this.handlers[this.calcKey(route)] = handle;
    }

    shouldAttach(route: ActivatedRouteSnapshot): boolean {
        return !!route.routeConfig && !!this.handlers[this.calcKey(route)];
    }

    retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
        if (!route.routeConfig) { return null; }
        return this.handlers[this.calcKey(route)];
    }

    shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
        return this.calcKey(curr) === this.calcKey(future);
    }
}

Within my AppModule, I added a new provider and configured some routes:

export const ROUTES: Routes = [
  {
    path: 'one',
    component: OneComponent
  },
  {
    path: 'two',
    component: TwoComponent
  },
];

@NgModule({
  ...
  imports: [... RouterModule.forRoot(ROUTES)...]
  providers: [...{ provide: RouteReuseStrategy, useClass: CustomReuseStrategy }...],
  ...
})
export class AppModule { }

Finally, I defined some components as follows:

@Component({
  selector: 'my-app',
  template: `
    <a [routerLink]="['/one']" >Route One</a><br>
    <a [routerLink]="['/two']" >Route Two</a><br>
    <router-outlet></router-outlet>
  `
})
export class AppComponent  {}

@Component({
  selector: 'app-one',
  template: `
  Route One currently showing<br>
  <input type="text" placeholder="enter some text">
  `,
})
export class OneComponent {}

@Component({
  selector: 'app-two',
  template: `
  Route Two currently showing<br>
  <input type="text" placeholder="enter some text">
  `,
})
export class TwoComponent {}
Stephen Paul
  • 37,253
  • 15
  • 92
  • 74