简体   繁体   中英

Dynamic Routing with tabs in angular2 4

Today I came across routing problem where I need help from you guys,

Actually, I am having a sidebar in which I have dynamic tree view navigations like below & right side I have 4 tabs default tab1 will be selected in which I am showing the data of active link

Link1
--sublink1
--sublink2
----sub2link1
------sub3link1
Link2
Link3
--sublink1
----sub2link1

Like above I having Nth submenus, for this, I have created a route

{ path: '**', component: tab1}

when I visit Link "link1/sublink2/sub2link1" default it will be a tab1 component

Now my question is if I want to navigate to tab2 component with the same link then how my route url should be.

Am answering my own question I solved it by using QueryParameter below is my approach:

My Routing module will be:

const routes: Routes = [
{
  path: 'parent',
  component: parentComponent,
  children: [
    { path: '', component: allChildComponent},
    { path: '**', component: allChildComponent}
  ]
}
];

So this will accept navigation till N number of tree route like /parent/child/sub_child1/sub_child2/../../.... to navigate just use

this.router.navigate(['/parent/child/sub_child1/sub_child2/../../....']);

Now if we want to pass the parameters to ** route then you need to use query parameter which will look like below while navigating

this.router.navigate(
   ['/parent/child/sub_child1/sub_child2/../../....'],
   {queryParams: 
       {param1: 'value1', param2: 'value2'}
   }
);

And finally, you can read this parameter in the component like below

constructor(private route: ActivatedRoute) { }

ngOnInit() {
  this.sub = this.route.queryParams
    .subscribe(params => {
      console.log(params);
    });
}

You can't achieve this with a catchall route like that without writing some custom logic to save state.

There are two ways you can achieve what you want (showed both examples below).

  1. Use unique routes. This way you can use routerLink and can easily determine which route is active based on the router config. With this approach, you cannot use the ** catchall routes, since it depends on the router.
  2. Use some kind of state, with a mixture of Observables so you can toggle the sidebar from anywhere and determine the active link what way. With this approach, you can use the ** catchall route, since this does not depend on the router.

I will show the two examples below, but before that I should throw in one caveat. This answer is based on no knowledge of your business logic.

What I mean by that is, if your sidebar is data-driven, then it could change how you do things. For instance, I have a similar tree-view sidebar like your example in my app, however mine is database-driven, which means each item has a unique identifier. With that ID, I am able to just use routes like /topic/:topicId/subtopic/:subtopicId and I can easily toggle the active link on my sidebar just by listening to the router events and setting an active link if the topicId currently in the URL equals the a specific topicId on a sidebar link, and so on. This makes it trivial.

Nevertheless, here you go:


Approach #1

With the treeview links below, you can navigate to the following links and the respective link will become active:

  1. /link1
  2. /link1/sublink1
  3. /link1/sublink1/sub2link1
  4. /link1/sublink2
  5. /link2
  6. /link3

Example sidebar:

<ul>
    <li>
        <a [routerLink]="['/link1']" [routerLinkActive]="'active'">Link1</a>

        <ul>
            <li>
                <a [routerLink]="['/link1/sublink1']" [routerLinkActive]="'active'">Sublink1</a>

                <ul>
                    <li>
                        <a [routerLink]="['/link1/sublink1/sub2link1']" [routerLinkActive]="'active'">Sub2link1</a>
                    </li>
                </ul>
            </li>
            <li>
                <a [routerLink]="['/link1/sublink2']" [routerLinkActive]="'active'">Sublink2</a>
            </li>
        </ul>
    </li>
    <li>
        <a [routerLink]="['/link2']" [routerLinkActive]="'active'">Link2</a>
    </li>
    <li>
        <a [routerLink]="['/link3']" [routerLinkActive]="'active'">Link3</a>
    </li>
</ul>

Approach #2

With this approach, instead of using routerLink to navigate, you could have to call a function defined in your component or a service somewhere, and what they function would do is set some state before navigating. When it sets that state, the sidebar would have to be listening for changes (perhaps an Observable ), and the sidebar can toggle the activeness on certain links based on those changes.

Here is a pseudocode example:

Template:

<ul>
    <li>
        <a (click)="toggleSidebar('link1')" [class.active]="activeLink === 'link1'">Link1</a>
    </li>
    <li>
        <a (click)="toggleSidebar('link2')" [class.active]="activeLink === 'link2'">Link2</a>

        <ul>
            <li>
                <a (click)="toggleSidebar('link2/sublink1')" [class.active]="activeLink === 'link2/sublink1'">Link2 Sublink1</a>
            </li>
        </ul>
    </li>
</ul>

Component:

@Component({...})
export class MyComponent implements OnInit {
    activeLink: string;

    constructor(private sidebarService: SideberService) {}

    ngOnInit() {
        this.sidebarService.activeLink$.subscribe((activeLink: string) => {
            this.activeLink = activeLink;
        }));
    }

    toggleSidebar(activeLink: string) {
        this.sidebarService.activeLink.next(activeLink);
    }
}

Service:

@Injectable()
export class SidebarService {
    activeLink: ReplaySubject<string> = new ReplaySubject();
    activeLink$: Observable<string> = this.activeLink.asObservable();
}

Now any time you emit a value into the sidebarService.activeLink stream, the sidebar component (which should be subscribing to that observable) will get the active sidebar link, which you can then use in your template as shown in my example to toggle a certain sidebar link.

If you want the sidebar to default to a certain link, you can switch from a ReplaySubject to using a BehaviorSubject and give it a default value, such as link1 . For example:

@Injectable()
export class SidebarService {
    activeLink: BehaviorSubject<string> = new BehaviorSubject('link1');
    activeLink$: Observable<string> = this.activeLink.asObservable();
}

That way on page load the link1 in the sidebar will be active by default since that is the initial value in the stream.

Now my question is if I want to navigate to tab2 component with the same link then how my route url should be.

That is not possible. You need to save the state of your tabs some where (like in some shared object) or create a new route for each tab.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM