Exploring the world of tabbed dynamic routing in Angular 2 version 4

Today I encountered a routing issue that requires assistance from all of you.

Currently, I have a sidebar with dynamic tree view navigations on the left and 4 tabs on the right. By default, tab1 is selected to display data related to the active link.

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

As shown above, there are numerous submenus for which I have created the following route:

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

If I visit "link1/sublink2/sub2link1", by default it displays the tab1 component.

Now, my question is how should my route URL be constructed if I want to navigate to the tab2 component with the same link?

Answer №1

After figuring out the solution to my question, I utilized the QueryParameter method. Here is how I approached it:

In my Routing module, I structured it as follows:

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

This setup allows for navigation through multiple levels of tree routes such as

/parent/child/sub_child1/sub_child2/../../....
. To navigate, simply use

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

If you need to pass parameters to the ** route, you can do so by including query parameters in the navigation like this

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

Finally, to access and read these parameters within the component, you can implement it like this

constructor(private route: ActivatedRoute) { }

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

Answer №2

To achieve your goal, you will need to implement some custom logic to save and manage the state effectively.

There are two approaches you can take to accomplish this task, outlined below:

  1. Utilize unique routes: By using routerLink, you can easily determine the active route based on the router configuration. This method does not support the use of catchall routes like ** due to its reliance on the router settings.
  2. Implement a state management system with Observables to toggle the sidebar from any part of your application and track the active links accordingly. With this method, you can utilize the ** catchall route independently of the router configuration.

Please note that the examples provided assume no prior knowledge of your specific business logic.

If your sidebar is data-driven, the implementation may vary. For instance, if your sidebar items have unique identifiers, you can dynamically set active links based on the URL parameters. In such cases, handling routes like

/topic/:topicId/subtopic/:subtopicId
becomes simplified by leveraging router events and matching IDs for seamless toggling.


Approach #1

The following treeview links demonstrate navigation behavior and respective link activation:

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

Sample sidebar structure:

<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

This method involves triggering a function within your component or service to establish and update a particular state before navigating. By monitoring these changes through an Observable, the sidebar can dynamically adjust active links based on these interactions.

Here's a conceptual 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();
}

By emitting values into sidebarService.activeLink, the sidebar component can react to these changes and reflect them as active links in the user interface.

If you wish to initialize the sidebar with a default active link, consider using a BehaviorSubject with a predefined value like link1. Here's how it can be implemented:

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

This way, upon loading the page, the sidebar will display link1 as the default active link due to the initial stream value.

Answer №3

I'm curious about how to navigate to the tab2 component using the same link. What should my route URL look like for this scenario?

Unfortunately, navigating directly to tab2 with the same link is not feasible. One option would be to save the state of your tabs in a shared object, or you could create separate routes for each tab.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to generate a type that generates a dot notation of nested class properties as string literals?

In relation to the AWS SDK, there are various clients with namespaces and properties within each one. The library exports AWS, containing clients like DynamoDB and ACM. The DynamoDB client has a property named DocumentClient, while ACM has a property call ...

The continuous re-rendering is being triggered by the Async/Await Function

I am facing an issue with fetching data from the backend using axios. The function is returning a Promise and each time I call it, my component keeps rendering continuously. Below is the code snippet: import { useState } from "react"; import Ax ...

Learning how to effectively incorporate the spread operator with TypeScript's utility type `Parameters` is a valuable skill to

I have implemented a higher order function that caches the result of a function when it is called with the same parameters. This functionality makes use of the Parameters utility type to create a function with identical signature that passes arguments to t ...

What is the method for defining a constant data type with a class property data type in typescript?

I've been working on developing a nestjs API and have been using classes to define my entities. For instance, I have created a Customer entity as shown below: export class Customer { id: number; name: string; } Now, while working on my Custom ...

Assign a value to a file input in Angular 6

I'm currently working with Angular 6 and I have a requirement to retrieve an image that is dropped into a div element and assign it as the value of an input type="file" within a form. The process involves the user dropping an image into the designate ...

Why is my RxJS timer not waiting for the specified time?

I'm diving into the world of RxJS and trying to grasp its concepts. During some testing, I encountered a puzzling issue that has me stumped. Below is the snippet in question : let item = { id: 1, name: 'chair' }; const asyncItem = timer(20 ...

Angular unit testing using Jasmin Karma encountered an error stating: "Unable to locate the name 'google'."

codeSnippet.js:- getAutocompleteResults(inputElement: ElementRef) { const autocomplete = new google.maps.places.Autocomplete(inputElement.nativeElement, { ... }); google.maps.event.addListener(autocomplete, 'place_changed&a ...

Create a recursive CSS style for an angular template and its container

I am struggling with styling CSS inside an ng-container that is used in a tree recursive call to display a tree hierarchy. <ul> <ng-template #recursiveList let-list> <li *ngFor="let item of list" [selected]="isSelected"> <sp ...

Sending an image in the body of an HTTP request using Angular 7

I'm currently in the process of developing an Angular application that captures an image from a webcam every second and sends it to a REST API endpoint for analysis. To achieve this, I have implemented an interval observable that captures video from t ...

Ways to specify a setter for a current object property in JavaScript

Looking to define a setter for an existing object property in JavaScript ES6? Currently, the value is directly assigned as true, but I'm interested in achieving the same using a setter. Here's a snippet of HTML: <form #Form="ngForm" novalida ...

Is it possible to construct an Angular project without altering the file size limit?

The budget of 40.00 kB fell short by 60.66 kB for a total of 80.66 kB. I've been grappling with the issue of exceeding the max file limit for anyComponentStyle. While working with Material UI and utilizing indigo-pink.css in my component scss, I&apo ...

Using JavaScript to dynamically calculate the sum of selected column values in Angular Datatables

I have a table set up where, if a checkbox is checked, the amounts are automatically summed and displayed at the top. However, I am encountering issues with the code below as it is not providing the exact sum values. Can anyone suggest a solution to this p ...

Angular - The confirmDialog from Primeng is appearing hidden behind the modal from ng-bootstrap

I am currently utilizing the primeng and ng-bootstrap components to develop an angular-based website. In order to collect data, I have implemented a form within an ng-bootstrap modal. However, upon clicking the "save" button on the modal, the primeng conf ...

How to implement angular 2 ngIf with observables?

My service is simple - it fetches either a 200 or 401 status code from the api/authenticate URL. auth.service.ts @Injectable() export class AuthService { constructor(private http: Http) { } authenticateUser(): Observable<any> { ...

What is the procedure for accessing a namespace when declaring it globally?

Website Project Background Currently, I am working on a simple website where users can update their pictures. To achieve this functionality, I am utilizing the Multer library along with Express in Typescript. Encountered Issue I am facing a challenge re ...

Obtain the file path relative to the project directory from a Typescript module that has been compiled to JavaScript

My directory structure is as follows: - project |- build |- src |- index.ts |- file.txt The typescript code is compiled to the build directory and executed from there. I am seeking a dependable method to access file.txt from the compiled module without ...

I'm curious if there's a method to ensure that the content within a mat-card stays responsive

So I'm working with this mat-card: <mat-card> <mat-card-content> <div *ngFor="let siteSource of siteSources | paginate: { itemsPerPage: 5, currentPage: page};"> <site-details [site]='siteSource'></s ...

Angular 11 along with RxJS does not support the combineLatest method in the specified type

Hey there, I'm currently working on utilizing the combineLatest operator to merge two streams in Angular, but I keep encountering an error message stating that "combineLatest does not exist on type". I've attempted to move the code into a .pipe() ...

Property element does not exist in this basic TypeScript project

I'm diving into my initial TypeScript project and encountering an issue with the "style". I attempted to utilize style!, create an if(changeBackgroundColor){}, but without success. let changeBackgroundColor = document.querySelectorAll('[data-styl ...

There was an issue encountered when creating the class: The parameters provided do not correspond to any valid call target signature

I am facing an issue with my code. Here is the scenario: export class MyClass { public name:string; public addr:string; constructor() {} } I have imported MyClass and trying to use it like this: import { MyClass } from './MyClass' ...