In my previous article I showed how to create tabs using Angular 4 routing and Angular Material 2’s md-tab-nav-bar
and md-tab-link
.
Yesterday afternoon I added a new tab to the same code. Under this new tab, I have a drop down which has list of products that a user can add. When user selects a product from the drop down, it will display the add product form underneath, for the selected product. The UI looks similar to the below example that I created for this article:
My application structure layout:
Here is what my application structure looks like:
src |--css | |--ndevre-material-theme.css | |--index.html |--styles.css | |--app |-- app.component.css |-- app.component.html |-- app.component.ts |-- app.routing.module.ts |-- app.module.ts |-- products |-- product 1 |-- product-1.component.html |-- product-1.component.css |-- product-1.component.ts |-- product 2 |-- product-2.component.html |-- product-2.component.css |-- product-2.component.ts |-- product 3 |-- product-3.component.html |-- product-3.component.css |-- product-3.component.ts |-- product 4 |-- product-4.component.html |-- product-4.component.css |-- product-4.component.ts |-- product 5 |-- product-5.component.html |-- product-5.component.css |-- product-5.component.ts |-- new-product |-- new-product.component.html |-- new-product.component.css |-- new-product.component.ts |-- product 1 |-- product-1.component.html |-- product-1.component.css |-- product-1.component.ts |-- product 2 |-- product-2.component.html |-- product-2.component.css |-- product-2.component.ts |-- product 3 |-- product-3.component.html |-- product-3.component.css |-- product-3.component.ts |-- product 4 |-- product-4.component.html |-- product-4.component.css |-- product-4.component.ts |-- product 5 |-- product-5.component.html |-- product-5.component.css |-- product-5.component.ts
Problem
After making changes to the code to get the new functionality which I described above, I came across an issue. When I select a product from the drop down, it displays the form correctly but the tab loses its focus. Here is the screenshot of the issue:
Here is the code that will reproduce the issue: Code reproducing problem.
Issue Details
The reason it loses the focus from the tab is because,
When the tab is selected, our route is http://localhost:4040/product1
, http://localhost:4040/product2
, etc.
When the tab to add new product is clicked/selected, the route is http://localhost:4040/newproduct
.
Now, when the child route is selected, the route becomes http://localhost:4040/newproduct/addproduct1
, http://localhost:4040/newproduct/addproduct2
, etc. Therefore, our current active route becomes addproduct1, addproduct2, etc. and value for activeLinkIndex
is set to the index of addproduct1, addproduct2, etc. It does not honour the newproduct in the full route path.
Solution
To fix this issue, I tried online help and referred to the Angular and Angular Material documentation. None were helpful. After few hours of tinkering this morning, I found the solution myself. I had to make few changes to my code that are shown below.
You can download the complete working code from here: Fixed Issue Code
app.component.ts
In my app.component.ts
, I modified the ngOnInit()
function to the below.
In my code previous to this latest change the activeLinkIndex
was set to the currently selected route. So if the child route addproduct1, addproduct2, etc. was selected, the index value for activeLinkIndex
will be that of addproduct1, addproduct2, etc. The below code is now setting the value for activeLinkIndex
to the index of newproduct.
I also added a new function getActiveClass(indexOfRouteLink)
, which accepts index of the routeLink
of the currently selected tab. This function takes the index of the tab passed to it and sets the CSS class for that tab to mat-tab-label-active
.
ngOnInit(): void { this.router.events.subscribe((res) => { this.activeLinkIndex = this.routeLinks.indexOf(this.routeLinks.find(tab => tab.link === './' + this.router.url.split('/')[1])); }); } getActiveClass(indexOfRouteLink) { let tabsclass: string = 'mat-tab-link'; if (this.activeLinkIndex === indexOfRouteLink) { tabsclass = 'mat-tab-link mat-tab-label-active'; } return tabsclass; }
app.component.html
In my app.component.html
, I modified my code to call the new function getActiveClass()
on [class] property. I pass the index i
of the currently selected tab, to the function getActiveClass(i)
.
<nav md-tab-nav-bar > <a md-tab-link *ngFor="let routeLink of routeLinks; let i = index;" [routerLink]="routeLink.link" routerLinkActive #rla="routerLinkActive" [active]="activeLinkIndex === i" (click)="activeLinkIndex = i" [routerLinkActiveOptions]="{exact: true}" [class] = getActiveClass(i) > {{routeLink.label}} </a> </nav>
That’s it. This resolved my issue of tab losing its focus. Below is the screenshot after I fixed the issue.
I hope this article helps someone. Please feel free to leave comments below. Please don’t hesitate to provide alternate solutions or correct anything that I might have done wrong in my code examples in this article.
Thank you for reading my blog.