Data source binding and custom menu items in EJ2 JavaScript Menu control

29 Jul 202324 minutes to read

Data binding

The Menu supports data source bindings such as array of JavaScript objects that can be structured as either hierarchical or self-referential data.

Hierarchical data

The Menu can be populated with hierarchical data source by assigning it to the items property, and the fields with corresponding keys can be mapped to the fields property.

JSON data

The Menu can generate its menu items through an array of complex data source by mapping fields from the fields property.

ej.base.enableRipple(true);

//Menu datasource
var dataSource = [
    {
        continent: 'Asia',
        countries: [
            {
                country: 'China',
                languages: [
                    { language: 'Chinese' },
                    { language: 'Cantonese' }
                ]
            },
            {
                country: 'India',
                languages: [
                    { language: 'English' },
                    { language: 'Hindi' },
                    { language: 'Tamil' }
                ]
            },
            {
                country: 'Japan',
                languages: [
                    { language: 'Japanese' }
                ]
            }
        ]
    },
    {
        continent: 'Africa',
        countries: [
            {
                country: 'Nigeria',
                languages: [
                    { language: 'English' },
                    { language: 'Hausa' }
                ]
            },
            {
                country: 'Egypt',
                languages: [
                    { language: 'Arabic' }
                ]
            },
            {
                country: 'South Africa',
                languages: [
                    { language: 'Tswana' },
                    { language: 'Swati' }
                ]
            }
        ]
    },
    {
        continent: 'North America',
        countries: [
            {
                country: 'Canada',
                languages: [
                    { language: 'French' }
                ]
            },
            {
                country: 'Mexico',
                languages: [
                    { language: 'Spanish' }
                ]
            },
            {
                country: 'USA',
                languages: [
                    { language: 'English' }
                ]
            }
        ]
    },
    {
        continent: 'South America',
        countries: [
            {
                country: 'Brazil',
                languages: [
                    { language: 'Portuguese' }
                ]
            },
            {
                country: 'Colombia',
                languages: [
                    { language: 'Spanish' }
                ]
            },
            {
                country: 'Argentina',
                languages: [
                    { language: 'Spanish' }
                ]
            }
        ]
    },
    {
        continent: 'Oceania',
        countries: [
            {
                country: 'Australia'
            },
            {
                country: 'New Zealand'
            },
            {
                country: 'Samoa'
            },
        ]
    }
];

//Menu fields definition
var menuFields = {
    text: ['continent', 'country', 'language'],
    children: ['countries', 'languages']
};

// Initialize Menu component.
var menuObj = new ej.navigations.Menu({ items: dataSource, fields: menuFields }, '#menu');
<!DOCTYPE html><html lang="en"><head>
    <title>Essential JS 2</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="description" content="Essential JS 2">
    <meta name="author" content="Syncfusion">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-base/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-popups/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-navigations/styles/material.css" rel="stylesheet">

    <!--style reference from app-->
    <link href="styles.css" rel="stylesheet">

    <!--system js reference and configuration-->
    
    
<script src="https://cdn.syncfusion.com/ej2/27.2.2/dist/ej2.min.js" type="text/javascript"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    
    <div id="container">
        <div class="control-section">
            <ul id="menu"></ul>
        </div>
    </div>



<script>
var ele = document.getElementById('container');
if(ele) {
  ele.style.visibility = "visible";
}   
      </script>
<script src="index.js" type="text/javascript"></script>
</body></html>

Data Service

In application level, remote data binding can be achieved using DataManager. To create Menu, assign items property with resultant data from callback function.

The following example displays five employees’ FirstName from Employees table and ShipName details from Orders table of the Northwind Data Service.

ej.base.enableRipple(true);

var SERVICE_URI = 'https://services.odata.org/V4/Northwind/Northwind.svc/';

var menuFields = {
    text: ['FirstName','ShipName'],
    children: ['Orders']
};

new ej.data.DataManager({ url: SERVICE_URI, adaptor: new ej.data.ODataV4Adaptor(), crossDomain: true })
.executeQuery(
new ej.data.Query().from('Employees').take(5).hierarchy(
    new ej.data.Query()
    .foreignKey('EmployeeID')
    .from('Orders').take(13),
    function() {
        return [1,2,3,4,5]
    }
))
.then((e) => {
    new ej.navigations.Menu({ items: e.result, fields: menuFields  }, '#menu');
});
<!DOCTYPE html><html lang="en"><head>
    <title>Essential JS 2</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="description" content="Essential JS 2">
    <meta name="author" content="Syncfusion">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-base/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-popups/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-navigations/styles/material.css" rel="stylesheet">

    <!--style reference from app-->
    <link href="styles.css" rel="stylesheet">

    <!--system js reference and configuration-->
    
    
<script src="https://cdn.syncfusion.com/ej2/27.2.2/dist/ej2.min.js" type="text/javascript"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    
    <div id="container">
        <div class="control-section">
            <ul id="menu"></ul>
        </div>
    </div>



<script>
var ele = document.getElementById('container');
if(ele) {
  ele.style.visibility = "visible";
}   
      </script>
<script src="index.js" type="text/javascript"></script>
</body></html>

Self-referential data

Menu can be populated from self-referential data structure that contains array of JSON objects with parentId mapping.

In the following example, the id, pId, and text columns from self-referential data have been mapped to the itemId, parentId, and text fields, respectively.

ej.base.enableRipple(true);

//Menu datasource
var data = [
    { id: 'parent1', text: 'Events' },
    { id: 'parent2', text: 'Movies' },
    { id: 'parent3', text: 'Directory' },
    { id: 'parent4', text: 'Queries', pId: null },
    { id: 'parent5', text: 'Services', pId: null },

    { id: 'parent6', text: 'Conferences', pId: 'parent1' },
    { id: 'parent7', text: 'Music', pId: 'parent1' },
    { id: 'parent8', text: 'Workshops', pId: 'parent1' },

    { id: 'parent9', text: 'Now Showing', pId: 'parent2' },
    { id: 'parent10', text: 'Coming Soon', pId: 'parent2' },

    { id: 'parent10', text: 'Media Gallery', pId: 'parent3' },
    { id: 'parent11', text: 'Newsletters', pId: 'parent3' },

    { id: 'parent12', text: 'Our Policy', pId: 'parent4' },
    { id: 'parent13', text: 'Site Map', pId: 'parent4' },
    { id: 'parent14', text: 'Pop', pId: 'parent7' },
    { id: 'parent15', text: 'Folk', pId: 'parent7' },
    { id: 'parent16', text: 'Classical', pId: 'parent7' }
];

//Menu fields definition
var menuFields = {
    itemId:'id',
    text: 'text',
    parentId: 'pId'
};

// Initialize Menu component.
var menuObj = new ej.navigations.Menu({ items: data, fields: menuFields }, '#menu');
<!DOCTYPE html><html lang="en"><head>
    <title>Essential JS 2</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="description" content="Essential JS 2">
    <meta name="author" content="Syncfusion">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-base/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-popups/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-navigations/styles/material.css" rel="stylesheet">

    <!--style reference from app-->
    <link href="styles.css" rel="stylesheet">

    <!--system js reference and configuration-->
    
    
<script src="https://cdn.syncfusion.com/ej2/27.2.2/dist/ej2.min.js" type="text/javascript"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    
    <div id="container">
        <div class="control-section">
            <ul id="menu"></ul>
        </div>
    </div>



<script>
var ele = document.getElementById('container');
if(ele) {
  ele.style.visibility = "visible";
}   
      </script>
<script src="index.js" type="text/javascript"></script>
</body></html>

HTML element

The Menu can be initialized using <UL> element that contains a collection of <LI> elements. A <LI> item acts as a menu item of the Menu, and the sub <UL> element inside the <LI> element acts as a sub menu item of its preceding menu item.

ej.base.enableRipple(true);

// Initialize Menu component.
var menuObj = new ej.navigations.Menu(null, '#menu');
<!DOCTYPE html><html lang="en"><head>
    <title>Essential JS 2</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="description" content="Essential JS 2">
    <meta name="author" content="Syncfusion">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-base/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-popups/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-navigations/styles/material.css" rel="stylesheet">

    <!--style reference from app-->
    <link href="styles.css" rel="stylesheet">

    <!--system js reference and configuration-->
    
    
<script src="https://cdn.syncfusion.com/ej2/27.2.2/dist/ej2.min.js" type="text/javascript"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    
    <div id="container">
        <ul id="menu">
            <li id="leafy_salad"><a>Leafy</a>
                <ul>
                    <li>Broccoli</li>
                    <li>Cabbage</li>
                    <li>Spinach</li>
                </ul>
            </li>
            <li id="beans"><a>Beans</a>
                <ul>
                    <li>Chickpea</li>
                    <li>Green bean</li>
                    <li>Horse gram</li>
                </ul>
            </li>
            <li id="bulb_stem"><a>Bulb and Stem</a>
                <ul>
                    <li>Pearl onion</li>
                    <li>Garlic</li>
                    <li>Shallot</li>
                    <li>Lotus root</li>
                </ul>
            </li>
            <li id="root_tuberous"><a>Root</a>
                <ul>
                    <li>Beetroot</li>
                    <li>Carrot</li>
                    <li>Ginger</li>
                    <li>Turmeric</li>
                </ul>
            </li>
            <li id="pod"><a>Podded</a>
                <ul>
                    <li>American groundnut</li>
                    <li>Drumstick</li>
                    <li>Horse gram</li>
                    <li>Peanut</li>
                </ul>
            </li>
        </ul>
    </div>



<script>
var ele = document.getElementById('container');
if(ele) {
  ele.style.visibility = "visible";
}   
      </script>
<script src="index.js" type="text/javascript"></script>
</body></html>

Custom menu items

The Menu can be customized using Essential JS2 Template engine to render the elements.

To customize menu items in your application, set your customized template string to the template property. In the following example, the menu has been rendered with customized menu items.

ej.base.enableRipple(false);

//Menu data definition
var data = [
    {
        category: 'Products',
        options: [
            { value: 'JavaScript', url: 'javascript' },
            { value: 'Angular', url: 'angular' },
            { value: 'ASP.NET Core', url: 'core' },
            { value: 'ASP.NET MVC', url: 'mvc' }
        ]
    },
    {
        category: 'Services',
        options: [
            {
                support: [
                    { value: 'Application Development', count: '1200+' },
                    { value: 'Maintenance & Support', count: '3700+' },
                    { value: 'Quality Assurance' },
                    { value: 'Cloud Integration', count: '900+' }
                ]
            }
        ]
    },
    {
        category: 'About Us',
        options: [
            {
                about: {
                    value: "We are on a mission to provide world-class best software solutions for web, mobile and desktop platforms. Around 900+ applications are desgined and delivered to our customers to make digital & strengthen their businesses."
                }
            }
        ]
    },
    { category: 'Careers' },
    { category: 'Sign In' }
];

//Menu fields definition 
var menuFields  = {
    text: ['category', 'value'],
    children: ['options']
};

//Menu initialization
var menuObj = new ej.navigations.Menu({
    items: data,
    fields: menuFields,
    template: '#menuTemplate'
}, '#menu');
<!DOCTYPE html><html lang="en"><head>
    <title>Essential JS 2</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="description" content="Essential JS 2">
    <meta name="author" content="Syncfusion">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-base/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-popups/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-navigations/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-buttons/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-inputs/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-dropdowns/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-cards/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-notifications/styles/material.css" rel="stylesheet">
    <link href="https://cdn.syncfusion.com/ej2/27.2.2/ej2-layouts/styles/material.css" rel="stylesheet">

    <!--style reference from app-->
    <link href="styles.css" rel="stylesheet">

    <!--system js reference and configuration-->
    
    
<script src="https://cdn.syncfusion.com/ej2/27.2.2/dist/ej2.min.js" type="text/javascript"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
    
    <div id="container">
        <div class="menu-section">
            <ul id="menu"></ul>
        </div>
    </div>
        <script id="menuTemplate" type="text/x-template">
        ${if(category)}
            ${category}
        ${else if (value && url)}
            <div class="e-avatar e-avatar-small image" style="background-image: url(${url}.png);">${value}</div>
        ${else if (support)}
            <ul>
                ${for(val of support)}
                <li>
                    ${val.value}
                    ${if(val.count)}
                        <span class='e-badge e-badge-success'>${val.count}</span>
                    ${/if}
                </li>
                ${/for}
            </ul>
        ${else}        
        <div tabindex="0" class="e-card">
            <div class="e-card-header">
                <div class="e-card-header-caption">
                    <div class="e-card-header-title">About Us</div>
                </div>
            </div>
            <div class="e-card-content">
                ${about.value}
            </div>
            <div class="e-card-actions">
                <button class="e-btn e-outline">
                    Read More
                </button>
            </div>
        </div>
        ${/if}
    </script>



<script>
var ele = document.getElementById('container');
if(ele) {
  ele.style.visibility = "visible";
}   
      </script>
<script src="index.js" type="text/javascript"></script>
</body></html>

To prevent sub menu closing, set args.cancel to true in beforeClose event.

See Also