Hey guys, in this post
we are going to see how to create tabs dynamically in which each tab will
contain data from each component. There are many ways to create tab structure,
in this post we are going to focus on eagerly loaded component, which means all
components that will act as a tab will be loaded in advance when the tab holder
component is loaded. Let’s do it step by step.
If you want lazy loading to load the tabs, please visit this post click here.
If you want lazy loading to load the tabs, please visit this post click here.
[at the bottom of the post you will get the link to download the project]
[ If you are preparing for an interview please click here - angular interview questions and answers ]
[ If you are preparing for an interview please click here - angular interview questions and answers ]
At very first, install
angular material and angular CDK to support material.
D:\Angular-DemoApps\Angular 6 - Dynamic
tab implementation with Multiple component (Component as a tab, Eager
loading)> npm install --save @angular/material @angular/cdk
npm WARN @angular/animations@7.1.4 requires
a peer of @angular/core@7.1.4 but none is installed. You must install peer
dependencies yourself.
npm WARN bootstrap@4.2.1 requires a peer
of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies
yourself.
npm WARN bootstrap@4.2.1 requires a peer
of popper.js@^1.14.6 but none is installed. You must install peer dependencies
yourself.
npm WARN @angular/cdk@8.1.1 requires a
peer of @angular/core@^8.0.0 || ^9.0.0-0 but none is installed. You must
install peer dependencies yourself.
npm WARN @angular/cdk@8.1.1 requires a
peer of @angular/common@^8.0.0 || ^9.0.0-0 but none is installed. You must
install peer dependencies yourself.
npm WARN @angular/material@8.1.1
requires a peer of @angular/animations@^8.0.0 || ^9.0.0-0 but none is installed.
You must install peer dependencies yourself.
npm WARN @angular/material@8.1.1
requires a peer of @angular/core@^8.0.0 || ^9.0.0-0 but none is installed. You
must install peer dependencies yourself.
npm WARN @angular/material@8.1.1
requires a peer of @angular/common@^8.0.0 || ^9.0.0-0 but none is installed.
You must install peer dependencies yourself.
npm WARN @angular/material@8.1.1
requires a peer of @angular/forms@^8.0.0 || ^9.0.0-0 but none is installed. You
must install peer dependencies yourself.
npm WARN optional SKIPPING OPTIONAL
DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL
DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted
{"os":"darwin","arch":"any"} (current:
{"os":"win32","arch":"x64"})
+ @angular/cdk@8.1.1
+ @angular/material@8.1.1
updated 2 packages and audited 40183
packages in 19.079s
found 607 vulnerabilities (2 low, 5
moderate, 600 high)
run `npm audit fix` to fix them, or `npm audit` for details
PS D:\Angular-DemoApps\Angular 6 - Dynamic
tab implementation with Multiple component (Component as a tab, Eager
loading)>
After completing the installation we are going to use our previous login and registration pages(pages
that we use in all our posts), That will be loaded into tab or will act as a
tab.
Following are the login
and registration component firstpage and secondpage component respectively.
first-page.component.html
<div
class="container">
<form
#frm="ngForm"
id="frm"><br><br>
<div
class="row">
<div
class="col-lg-4"></div>
<div
class="col-lg-6">
<h3>Login</h3>
<table>
<thead>
<th>
Login Name
</th>
<th>
<input
class="form-control"
type="text"
[(ngModel)]="user.userName"
name="userName"
required>
</th>
</thead>
<tbody>
<tr>
<th>
Password
</th>
<td><input
class="form-control"
type="password"
[(ngModel)]="user.password"
name="password"></td>
</tr>
<tr>
<td>
</td>
<td>
<button
class="btn
btn-primary">Login</button>
<button
class="btn
btn-light" (click)="registerMe();">Register
Me</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</div>
first-page.component.ts
import
{ Component, OnInit,
HostListener, ViewChild
} from '@angular/core';
import
{ Router } from
'@angular/router';
import
{ User } from
'../user/user';
import
{ HomepageComponent
} from '../homepage/homepage.component';
@Component({
selector: 'app-first-page',
templateUrl: './first-page.component.html',
styleUrls: ['./first-page.component.css']
})
export
class FirstPageComponent
implements OnInit
{
user: User;
constructor(private
homePagereferance:
HomepageComponent)
{ }
ngOnInit() {
this.user
= new User();
}
registerMe() {
this.homePagereferance.openTab('second
page', 'secondpage',
undefined);
}
}
second-page.components.html
<div
class="container">
<div
class="row">
<div
class="col-lg-4"></div>
<div
class="col-lg-6">
<form
action=""
#frm="ngForm">
<h3>Registration</h3>
<table>
<tr>
<td>User
name</td>
<td><input
[(ngModel)]="user.userName"
class="form-control"
type="text"
name="userName"></td>
</tr>
<tr>
<td>password</td>
<td><input
[(ngModel)]="user.password"
class="form-control"
type="password"
name="password"></td>
</tr>
<tr>
<td>confirm
password</td>
<td><input
class="form-control"
type="password"
name="password"></td>
</tr>
<tr>
<td>email</td>
<td><input
[(ngModel)]="user.email"
class="form-control"
type="text"
name="email"></td>
</tr>
<tr>
<td>contact
No</td>
<td><input
[(ngModel)]="user.contactNo"
class="form-control"
type="text"
name="contactNo"></td>
</tr>
<tr>
<td>address</td>
<td><input
[(ngModel)]="user.address"
class="form-control"
type="text"
name="address"></td>
</tr>
<tr>
<td></td>
<td><button
class="btn
btn-light">Cancel</button> <button
class="btn
btn-primary">save</button></td>
</tr>
</table>
</form>
</div>
</div>
</div>
second-page.component.ts
import
{ Component, OnInit,
ViewChild } from
'@angular/core';
import
{ User } from
'../user/user';
@Component({
selector: 'app-second-page',
templateUrl: './second-page.component.html',
styleUrls: ['./second-page.component.css']
})
export
class SecondPageComponent
implements OnInit
{
user: User;
constructor() { }
ngOnInit() {
this.user
= new User();
}
}
Above are the components
containing simple code to show login and registration page respectively, which
will act as a tab.
But these components cannot
act as a tab directly. Let’s make them capable to become a tab.
We are going to make a new component which will act as a wrapper to all component who want to become a
tab.
In tab-wrapper component
we are simply giving the selector of all components that will act as a tab
of that will be contained in the tab.
tabId is the variable accepted
as input from outside of a component will decide which selector is going to
be used means tabId will decide in any particular tab, which component will be
shown.
tab-wrapper.component.html
<app-first-page
*ngIf="tabId
== 'firstpage'"></app-first-page>
<app-second-page
*ngIf="tabId
== 'secondpage'"></app-second-page>
tab-wrapper.component.ts
import
{ Component, OnInit,
Input } from
'@angular/core';
@Component({
selector: 'app-tab-wrapper',
templateUrl: './tab-wrapper.component.html',
styleUrls: ['./tab-wrapper.component.scss'],
})
export
class TabWrapperComponent
implements OnInit
{
@Input("tabId")
tabId: string;
constructor() { }
ngOnInit() { }
}
After creating tab-wrapper
component, we are ready to implement our home page where tabs are going to play
the game. Let’s create a home page.
Here we have used the tabs from the material, but
material tab doesn’t allow us to handle other things like close the tab, so to
achieve that we have divided tab in two-parts, header, and the contents.
In the header part, we have used navigation and bound
an array of tabs which will show the headers of tabs. In the content part, we have used
tab-wrapper component which will act as a wrapper component for all other
components to become a tab.
homepage.component.html
<div
class="container
no-gutters">
<div
class="row">
<div
class="col-lg-12">
<h2>Welcome
to dynamic tab with multiple component demo</h2>
</div>
<div
class="col-lg-12">
<a
class="btn
btn-outline-light" (click)="openLogin()">open
login
component in
tab</a>
<a
class="btn
btn-outline-light" (click)="openReg()">open
registeration
component tab</a>
<a
class="btn
btn-outline-light" (click)="duplicate()">
Duplicate</a>
</div>
</div>
<div
class="row
no-gutters">
<nav
mat-tab-nav-bar mainTabs
class="scroll
mainTabs">
<a
*ngFor="let
link of navLinks; let i = index" mat-tab-link
class="tab-font"
(click)="activate(i);"
[active]="activeLinkIndex
== i" title="{{link.label}}">
<span>{{link.label}}</span>
<span
(click)="closeTab(i)"
class="closeIco">
<img
src=""
alt="X">
</span>
</a>
</nav>
<mat-tab-group
[selectedIndex]="activeLinkIndex"
class="col-lg-12">
<mat-tab
*ngFor="let
link of navLinks; let i = index" label="{{link.label}}"
(click)="activeLinkIndex
= i;">
<app-tab-wrapper
[tabId]="link.link"></app-tab-wrapper>
</mat-tab>
</mat-tab-group>
</div>
</div>
To
show our designed header we have made original header of mat-tab hidden.
homepage.component.css
/deep/
.mat-tab-header {
display: none!important;
}
Now
real business comes into existence, here we have declared one array which will be
used for maintaining tab-list and managed our logic to add a tab, remove the tab and
maintain selected tab as well. You can understand it easily by looking at code.
homepage.component.ts
import
{ Component, OnInit
} from '@angular/core';
@Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export
class HomepageComponent
implements OnInit
{
activeLinkIndex = -1;
navLinks: any[];
link: any;
counter: number
= 0;
constructor() { }
ngOnInit() {
this.navLinks
= [];
}
openLogin() {
let tabTitle
= "First Page";
let tabId
= "firstpage";
let param
= undefined;
this.openTab(tabTitle,
tabId, undefined);
}
openReg() {
let tabTitle
= "Second Page";
let tabId
= "secondpage";
let param
= undefined;
this.openTab(tabTitle,
tabId, undefined);
}
duplicate() {
this.counter++;
let tabTitle
= "duplicate "
+ this.counter;
let tabId
= "firstpage";
let param
= undefined;
this.openTab(tabTitle,
tabId, "allow
duplicate");
}
public openTab(heading:
String, route:
String, parameter:
any) {
console.log("openTab
in layout... IN");
var res;
var navFlag
= false;
var tabLength
= this.navLinks.length;
if (parameter
== "allow duplicate")
{
navFlag = false;
} else {
for (var
i = 0;
i < this.navLinks.length;
i++) {
if
(this.navLinks[i].link
== route) {
navFlag
= true;
if
(!this.navLinks[i].parameter)
{
this.activeLinkIndex
= i;
} else
{
this.activeLinkIndex
= i;
}
break;
} else
{
navFlag
= false;
}
}
}
if (navFlag
== false) {
this.navLinks.push({
label:
heading,
link:
route,
index:
tabLength,
parameter:
parameter
});
if (parameter
== "allow duplicate")
{
this.activeLinkIndex
= this.navLinks.length;
} else {
this.activeLinkIndex
= tabLength;
}
}
console.log("openTab
in layout... OUT");
}
closeTab(index:
number) {
console.log("closeTab
in layout component... IN");
var
maxTabIndex;
var res;
if (this.activeLinkIndex
== index) {
maxTabIndex = this.navLinks.length
- 1;
if (maxTabIndex
== 0) {
this.navLinks.splice(index,
1);
} else if
(index < maxTabIndex)
{
this.navLinks.splice(index,
1);
this.activeLinkIndex
= index;
} else if
(index == maxTabIndex)
{
this.navLinks.splice(index,
1);
maxTabIndex
= this.navLinks.length
- 1;
this.activeLinkIndex
= maxTabIndex;
} else {
}
} else if
(this.activeLinkIndex
> index) {
this.navLinks.splice(index,
1);
this.activeLinkIndex
= this.activeLinkIndex
- 1;
} else {
this.navLinks.splice(index,
1);
}
if (this.navLinks.length
<= 0) {
}
console.log("closeTab
in layout component... OUT");
}
closeOtherTabs(index:
number) {
console.log("closeOtherTabs
in layout component... IN");
this.link
= this.navLinks[index];
this.navLinks
= [];
this.navLinks.push(this.link);
this.activeLinkIndex
= 0;
this.link
= undefined;
console.log("closeOtherTabs
in layout component... OUT");
}
closeAllTabs() {
console.log("closeAllTabs
in layout component... IN");
this.activeLinkIndex
= -1;
this.navLinks
= [];
console.log("closeAllTabs
in layout component... OUT");
}
activate(index:
number) {
console.log("activate
in layout component... IN");
if (!this.navLinks[index].parameter)
{
this.activeLinkIndex
= index;
} else {
this.activeLinkIndex
= index;
}
console.log("activate
in layout component... OUT");
}
}
And that’s it,
we have successfully
created dynamic tab logic into our angular project.
Note: this is the logic
where we have used selectors to load the tab content, so it is eagerly loaded
structure. And the main advantage of this structure is, we don’t need to do anything to preserve the state of each tab content, we don’t need to save the state of
each tab.
Hope you like it.
Here is the link to download the project[download]
Comments
Post a Comment