Create wizard using tab in EJ2 TypeScript Tab control

16 Jun 202324 minutes to read

Tab items can be disabled dynamically by passing the index and boolean value with the enableTab method and also passing index or HTML element to select an item from the tab using select method.

In the below Wizard sample, each Tab is integrated with required components to complete the reservation. Each field is provided with validation for all mandatory option to proceed to next tabs. Using Tab item’s template property the components are added into content.

    /* Initializing Tab with header and contents bind to template div for adding other components */
    tabObj = new Tab({ heightAdjustMode: 'None', height: 390, showCloseButton: false,
        selecting: tabSelected,
        items: [
            { header: { 'text': 'New Booking' }, content: '#booking' },
            { header: { 'text': 'Train List' }, content: '#selectTrain', disabled: true },
            { header: { 'text': 'Add Passenger' }, content: '#details', disabled: true },
            { header: { 'text': 'Make Payment' }, content: '#confirm', disabled: true }
        ]
    });
    tabObj.appendTo('#element');

Create the following contents for each tab in the wizard.

  1. Search tab:
    Created with DropDownList to select the source, destination and type of ticket. A DatePicker for choosing the date of journey.
  2. Train tab:
    Based on the selected start and end point, populated Grid with random list of available seats and train list. Initially define the columns and row selected event for validating, after the source and destination chosen update the dataSource for the Grid.
  3. Passenger tab:
    A table with Textbox, Numeric, DropDownList for adding passenger name, age, gender and preferred berth/seat. Add validation on entering passenger details to proceed.
  4. Payment tab:
    Calculate the ticket cost based on location, passenger count and ticket type. Generate data for Grid with passenger details, train number and ticket cost summary.

You can go back on each tab using buttons available in it and tabs are disabled to navigate through tab header click actions. Once you end the wizard all the data get cleared and wizard goes back to starting tab.

import { Dialog } from '@syncfusion/ej2-popups';
import { Tab, SelectEventArgs } from '@syncfusion/ej2-navigations';
import { DatePicker } from '@syncfusion/ej2-calendars';
import { NumericTextBox } from '@syncfusion/ej2-inputs';
import { Grid, RowSelectEventArgs } from '@syncfusion/ej2-grids';
import { DropDownList } from '@syncfusion/ej2-dropdowns';
import { isNullOrUndefined as isNOU } from '@syncfusion/ej2-base';

let tabObj: Tab;
let availTrainGrid: Grid;
let ticketDetailGrid: Grid;
let endPoint: DropDownList;
let journeyDate: DatePicker;
let ticketType: DropDownList;
let startPoint: DropDownList;
let alertDlg: Dialog;
let today : Date = new Date();
let locations: any;
let selectedTrain: any;

let quota: any = [
    { id: '1', text: 'Business Class' },
    { id: '2', text: 'Economy Class' },
    { id: '3', text: 'Common Class' }
];
let gender: any = [
   { id: '1', text: 'Male' },
   { id: '2', text: 'Female' }
];
let berths: any = [
   { id: '1', text: 'Upper' },
   { id: '2', text: 'Lower' },
   { id: '3', text: 'Middle' },
   { id: '4', text: 'Window' },
   { id: '5', text: 'Aisle' }
];
let cities: any = [
    {name: 'Chicago', fare: 300} ,
    {name: 'San Francisco', fare: 125 },
    {name: 'Los Angeles', fare: 175 },
    {name: 'Seattle', fare: 250},
    {name: 'Florida', fare: 150}
];

renderComponents();

function renderComponents(): void {
    /* Initialize Tab with disabled headers for the wizard */
    tabObj = new Tab({ heightAdjustMode: 'None', height: 390, showCloseButton: false,
        selecting: tabSelected,
        items: [
            { header: { 'text': 'New Booking' }, content: '#booking' },
            { header: { 'text': 'Train List' }, content: '#selectTrain', disabled: true },
            { header: { 'text': 'Add Passenger' }, content: '#details', disabled: true },
            { header: { 'text': 'Make Payment' }, content: '#confirm', disabled: true }
        ]
    });
    tabObj.appendTo('#element');
    /* Initialize the components for creating wizard */
    startPoint = new DropDownList({
        width: '100%', dataSource: cities, floatLabelType: 'Auto', placeholder: 'From',
        fields: { text: 'name', value: 'name' }
    });
    startPoint.appendTo('#startPoint');
    endPoint = new DropDownList({
        width: '100%', dataSource: cities, floatLabelType: 'Auto', placeholder: 'To',
        fields: { text: 'name', value: 'name' }
    });
    endPoint.appendTo('#endPoint');
    journeyDate = new DatePicker({
        width: '100%', floatLabelType: 'Auto', placeholder: 'Journey Date', min: new Date(today.getTime()),
        max: new Date(today.getTime() + 60 * 24 * 60 * 60 * 1000),
        focus: () => { journeyDate.show(); }
    });
    journeyDate.appendTo('#journey_date');
    ticketType = new DropDownList({
        dataSource: quota, placeholder: 'Ticket Type', floatLabelType: 'Auto', fields: { text: 'text', value: 'text' }
    });
    ticketType.appendTo('#ticket_type');
    alertDlg = new Dialog({
        header: 'Success', width: 250, isModal: true, visible: false, showCloseIcon: true,
        content: 'Your payment successfully processed',  target: document.getElementById('container'), created: dlgCreated
    });
    alertDlg.appendTo('#alertDialog');
    alertDlg.hide();
    availTrainGrid = new Grid({
        width: '100%',
        columns: [
            { field: 'TrainNo', headerText: 'Train No', width: 120, type: 'number' },
            { field: 'Name', width: 140, headerText: 'Name' },
            { field: 'Departure', headerText: 'Departure', width: 120 },
            { field: 'Arrival', headerText: 'Arrival', width: 140 },
            { field: 'Availability', headerText: 'Availability', width: 140, type: 'number' }
        ],
        rowSelected: trainSelected
    });
    availTrainGrid.appendTo('#availableTrain');
    let age1: NumericTextBox = new NumericTextBox({ min: 1, max: 100, value: 18, format: 'n0', showSpinButton: false });
    age1.appendTo('#pass_age1');
    let age2: NumericTextBox = new NumericTextBox({ min: 1, max: 100, value: 18, format: 'n0', showSpinButton: false });
    age2.appendTo('#pass_age2');
    let age3: NumericTextBox = new NumericTextBox({ min: 1, max: 100, value: 18, format: 'n0', showSpinButton: false });
    age3.appendTo('#pass_age3');
    let gender1: DropDownList = new DropDownList({
        dataSource: gender, text: 'Male', fields: { text: 'text', value: 'text' }
    });
    gender1.appendTo('#pass_gender1');
    let gender2: DropDownList = new DropDownList({
        dataSource: gender, text: 'Male', fields: { text: 'text', value: 'text' }
    });
    gender2.appendTo('#pass_gender2');
    let gender3: DropDownList = new DropDownList({
        dataSource: gender, text: 'Male', fields: { text: 'text', value: 'text' }
    });
    gender3.appendTo('#pass_gender3');
    let berth1: DropDownList = new DropDownList({
        dataSource: berths, placeholder: 'Optional', fields: { text: 'text', value: 'text' }
    });
    berth1.appendTo('#pass_berth1');
    let berth2: DropDownList = new DropDownList({
        dataSource: berths, placeholder: 'Optional', fields: { text: 'text', value: 'text' }
    });
    berth2.appendTo('#pass_berth2');
    let berth3: DropDownList = new DropDownList({
        dataSource: berths, placeholder: 'Optional', fields: { text: 'text', value: 'text' }
    });
    berth3.appendTo('#pass_berth3');
    ticketDetailGrid = new Grid({
        width: '100%',
        columns: [
            { field: 'TrainNo', headerText: 'Train No', width: 120, type: 'number' },
            { field: 'PassName', width: 140, headerText: 'Name' },
            { field: 'Gender', headerText: 'Gender', width: 120 },
            { field: 'Berth', headerText: 'Berth', width: 140 }
        ],
    });
    ticketDetailGrid.appendTo('#ticketDetailGrid');
    document.getElementById('searchNext').onclick = (e: any) => { tabNavigations(e); };
    document.getElementById('bookTickets').onclick = (e: any) => { tabNavigations(e); };
    document.getElementById('confirmTickets').onclick = (e: any) => { tabNavigations(e); };
    document.getElementById('makePayment').onclick = (e: any) => { tabNavigations(e); };
    document.getElementById('goToSearch').onclick = (e: any) => { tabNavigations(e); };
    document.getElementById('goBackToBook').onclick = (e: any) => { tabNavigations(e); };
    document.getElementById('goBackDetails').onclick = (e: any) => { tabNavigations(e); };
}
function tabSelected(e: SelectEventArgs): void {
    if (e.isSwiped) {
        e.cancel = true;
    }
}
function dlgCreated(): void {
  alertDlg.buttons = [{
    buttonModel: { content: 'Ok', isPrimary: true },
      click: (() => {
        alertDlg.hide();
        tabObj.enableTab(0, true);
        tabObj.enableTab(1, false);
        tabObj.enableTab(2, false);
        tabObj.enableTab(3, false);
        tabObj.select(0);
      })
  }];
}
function tabNavigations(args: any): void {
    switch (args.target.id) {
        case 'searchNext':
        /* Validate the Source, Destination, Date and Class chosen and proceed only if all the fields are selected */
            if (!isNOU(startPoint.value) && !isNOU(endPoint.value) &&
                !isNOU(ticketType.value) && !isNOU(journeyDate.value)) {
                if (!isNOU(startPoint.value) && startPoint.value === endPoint.value) {
                    document.getElementById('err1').innerText = '* Arrival point can\'t be same as Departure';
                } else {
                    tabObj.enableTab(0, false);
                    tabObj.enableTab(1, true);
                    filterTrains(args);
                    tabObj.select(1);
                    document.getElementById('err1').innerText = '';
                    document.getElementById('err2').innerText = '';
                }
            } else {
                document.getElementById('err1').innerText = '* Please fill all the details before proceeding';
            }
            break;
        case 'bookTickets':
        /* Based on the selected station generate Grid content to display trains available */
            if (availTrainGrid.getSelectedRecords() === undefined || availTrainGrid.getSelectedRecords().length === 0) {
                document.getElementById('err2').innerText = '* Select your convenient train';
            } else {
                tabObj.enableTab(2, true);
                tabObj.select(2);
                tabObj.enableTab(1, false);
                document.getElementById('err2').innerText = '';
            }
            break;
        case 'confirmTickets':
        /* Get the Passenger details and validate the fields must not be left empty */
            let name: any = document.getElementById('pass_name1');
            let age: any = document.getElementById('pass_age1');
            let gender: any = document.getElementById('pass_gender1');
            if (name.value === '' || age.value === '' || gender.value === '') {
                document.getElementById('err3').innerText = '* Please enter passenger details';
            } else {
                tabObj.enableTab(3, true);
                tabObj.select(3);
                tabObj.enableTab(2, false);
                document.getElementById('err3').innerText = '';
                finalizeDetails(args);
            }
            break;
        case 'makePayment':
            alertDlg.show();
            break;
        case 'goToSearch':
        /* Go back to change class, date or boarding places */
            selectedTrain = [];
            tabObj.enableTab(0, true);
            tabObj.select(0);
            tabObj.enableTab(1, false);
            break;
        case 'goBackToBook':
        /* Change the preferred train chosen already */
            tabObj.enableTab(1, true);
            tabObj.select(1);
            tabObj.enableTab(2, false);
            break;
        case 'goBackDetails':
        /* Update passenger detail before confirming the payment */
            tabObj.enableTab(2, true);
            tabObj.select(2);
            tabObj.enableTab(3, false);
            break;
    }
}
function filterTrains(args: any): void {
    /* Generating trains based on source and destination chosen */
    let result: Object[] = [];
    let fromCity: string = startPoint.text;
    let toCity: string = endPoint.text;
    let count: number = Math.floor((Math.random() * 3) + 2);
    for (let i: number = 0; i < count; i++) {
        let details : any = [];
        details.TrainNo = Math.floor((Math.random() * 20) + 19000);
        details.Name = 'Train ' + i;
        details.Departure = fromCity;
        details.Arrival = toCity;
        details.Availability = Math.floor((Math.random() * 20) + 20);
        result.push(details);
    }
    availTrainGrid.dataSource = result;
}
function finalizeDetails(args: any): void {
    /* Get the passenger details and update table with name and other details for confirmation */
    let reserved: Object[] = [];
    let passCount: any = 0;
    for (let i: number = 1; i <= 3; i++) {
        let name: any = document.getElementById('pass_name' + i);
        let berthSelected: any = document.getElementById('pass_berth' + i);
        let gender: any = document.getElementById('pass_gender' + i);
        if (name.value !== '') {
            let details: any = [];
            let berth: string = berthSelected.value;
            details.TrainNo = selectedTrain.TrainNo.toString();
            details.PassName = name.value;
            details.Gender = gender.value;
            details.Berth = (berth === '') ? 'Any' : berth;
            reserved.push(details);
            passCount++;
        }
        let calcFare: any = 0;
        for (let j: number = 0; j < cities; j++) {
            if (startPoint.value === cities[j].name) {
                calcFare = calcFare + cities[j].fare;
            }
            if (endPoint.value === cities[j].name) {
                calcFare = calcFare + cities[j].fare;
            }
        }
        let displayAmt : any = document.getElementById('amount');
        if (ticketType.value === 'Economy Class') {
            displayAmt.innerText = "Total payable amount: $" + passCount * (300 + calcFare);
        } else if (ticketType.value === 'Business Class') {
            displayAmt.innerText = "Total payable amount: $" + passCount * (500 + calcFare);
        } else if (ticketType.value === 'Common Class') {
            displayAmt.innerText = "Total payable amount: $" + passCount * (150 + calcFare);
        }
    }
    ticketDetailGrid.dataSource = reserved;
}
function trainSelected(args: RowSelectEventArgs): void {
    selectedTrain = args.data;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Essential JS 2 Tab</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 Tab" />
    <meta name="author" content="Syncfusion" />
    <link href="https://cdn.syncfusion.com/ej2/28.1.33/material.css" rel="stylesheet">
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" type="text/css">
    <link href="index.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
    <style>
        .e-content .e-item {
            font-size: 12px;
            margin: 10px;
        }

        #amount {
            text-align: right;
            font-size: 15px;
            padding: 15px 0;
        }

        #passenger-table th {
            text-align: center;
            font-size: 14px;
            font-weight: 400;
            border: 1px solid gainsboro;
        }

        #passenger-table td,
        #passenger-table th {
            padding: 10px;
        }

        #passenger-table td {
            border: 1px solid gainsboro;
        }

        .name-header {
            width: 200px;
        }

        .age-header {
            width: 80px;
        }

        .gender-header {
            width: 120px;
        }

        .type-header {
            width: 150px;
        }

        .btn-container {
            text-align: center;
        }

        .search-item {
            padding-right: 50px;
            padding-bottom: 20px;
        }

        .item-title {
            font-weight: 500;
            padding-top: 10px;
        }

        @media (max-width: 450px) {
            .e-sample-resize-container {
                height: 450px;
            }

            .responsive-align {
                width: 75%;
                margin: 0 auto;
            }

            .search-item {
                padding: 0 0 20px 0;
                width: 100%;
            }
        }

        #err1,
        #err2,
        #err3 {
            font-weight: bold;
            color: red;
            display: block;
            margin-top: 15px;
        }

        .wizard-title {
            font-size: 15px;
        }

        #PassengersList {
            overflow: auto;
        }

        #passenger-table {
            min-width: 500px;
            width: 100%;
        }
    </style>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

<body>
    <div id="loader">LOADING....</div>
    <div id="container">
        <div id="alertDialog"></div>
        <div id="element"></div>
    </div>
    <div id='booking' style='display: none'>
        <div class="wizard-title">Plan your journey</div>
        <div class="responsive-align">
            <div class="row">
                <div class="col-xs-6 col-sm-6 col-lg-6 col-md-6 search-item">
                    <input id="startPoint" class="e-input" type="text">
                </div>
                <div class="col-xs-6 col-sm-6 col-lg-6 col-md-6 search-item">
                    <input id="endPoint" class="e-input" type="text">
                </div>
            </div>
            <div class="row">
                <div class="col-xs-6 col-sm-6 col-lg-6 col-md-6 search-item">
                    <input id="journey_date" class="e-input" type="text">
                </div>
                <div class="col-xs-6 col-sm-6 col-lg-6 col-md-6 search-item">
                    <input id="ticket_type" class="e-input" type="text">
                </div>
            </div>
        </div>
        <div class="btn-container">
            <button id="searchNext" class="e-btn">Search Train</button>
        </div>
        <span id="err1"></span>
    </div>
    <div id="selectTrain" style='display: none'>
        <div class="wizard-title">Select the train from the list </div>
        <div id="availableTrain"></div>
        <br>
        <div class="btn-container">
            <button id="goToSearch" class="e-btn">Back</button>
            <button id="bookTickets" class="e-btn">Continue</button>
        </div>
        <span id="err2"></span>
    </div>
    <div id="details" style='display: none'>
        <div class="details-page wizard-title">Enter the passenger details</div>
        <div id="PassengersList">
            <table id="passenger-table">
                <colgroup>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                    <col>
                </colgroup>
                <thead>
                    <tr>
                        <th class="name-header">Name</th>
                        <th class="age-header">Age</th>
                        <th class="gender-header">Gender</th>
                        <th class="type-header">Berth Preference</th>
                    </tr>
                </thead>
                <tbody>
                    <tr>
                        <td>
                            <input id="pass_name1" class="e-input" type="text" placeholder="Passenger Name">
                        </td>
                        <td>
                            <input id="pass_age1" class="e-input">
                        </td>
                        <td>
                            <input id="pass_gender1" type="text">
                        </td>
                        <td>
                            <input id="pass_berth1" type="text">
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <input id="pass_name2" class="e-input" type="text" placeholder="Passenger Name">
                        </td>
                        <td>
                            <input id="pass_age2" class="e-input">
                        </td>
                        <td>
                            <input id="pass_gender2" type="text">
                        </td>
                        <td>
                            <input id="pass_berth2" type="text">
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <input id="pass_name3" class="e-input" type="text" placeholder="Passenger Name">
                        </td>
                        <td>
                            <input id="pass_age3" class="e-input">
                        </td>
                        <td>
                            <input id="pass_gender3" type="text">
                        </td>
                        <td>
                            <input id="pass_berth3" type="text">
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
        <br>
        <div class="btn-container">
            <button id="goBackToBook" class="e-btn">Back</button>
            <button id="confirmTickets" class="e-btn">Continue</button>
        </div>
        <span id="err3"></span>
    </div>
    <div id='confirm' style='display: none'>
        <div class="tab-title1 wizard-title">Confirm the details and proceed</div>
        <div id="ticketDetailGrid"></div>
        <div id="amount"></div>
        <br>
        <div class="btn-container">
            <button id="goBackDetails" class="e-btn">Back</button>
            <button id="makePayment" class="e-btn">Pay</button>
        </div>
    </div>
</body>

</html>