Search results

Data source binding and Custom menu items

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.

Source
Preview
index.tsx
index.html
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { enableRipple } from '@syncfusion/ej2-base';
import { MenuComponent, FieldSettingsModel } from '@syncfusion/ej2-react-navigations';

enableRipple(true);

export default class App extends React.Component<{}, {}> {
    public dataSource:  { [key: string]: Object }[] = [
    {
        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
    public menuFields: FieldSettingsModel = {
        text: ['continent', 'country', 'language'],
        children: ['countries', 'languages']
    };

render() {
        return (
            <MenuComponent items={this.dataSource} fields={this.menuFields}></MenuComponent>
        );
    }
}

ReactDom.render(<App />,document.getElementById('element'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React ContextMenu</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet" />
    <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>
</head>

<body>
    <div id='element'>
        <div id='loader'>Loading....</div>
    </div>
</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.

Source
Preview
index.tsx
index.html
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { enableRipple } from '@syncfusion/ej2-base';
import { DataManager, Query, ODataAdaptor, ReturnOption } from '@syncfusion/ej2-data';
import { MenuComponent, FieldSettingsModel } from '@syncfusion/ej2-react-navigations';

const SERVICE_URI: string = 'https://js.syncfusion.com/demos/ejServices/Wcf/Northwind.svc/';

enableRipple(true);

export default class App extends React.Component<{}, {}> {
    menuItems: { [key: string]: Object }[] = [];

    // Menu fields definition.
    menuFields: FieldSettingsModel = {
        text: ['FirstName', 'ShipName'],
        children: ['Orders']
    };

    componentWillMount(): void {
        // Getting remote data using DataManager.
        new DataManager({ url: SERVICE_URI, adaptor: new ODataAdaptor, crossDomain: true })
        .executeQuery(
            new Query().from('Employees').take(5).hierarchy(
            new Query()
            .foreignKey('EmployeeID')
            .from('Orders').take(13),
            function() {
                return [1,2,3,4,5]
            }
        ))
        .then((e: ReturnOption) => {
            this.menuItems = e.result as { [key: string]: Object }[];
        });
    }

    render() {
        return (
            <div>
                {
                    this.menuItems.length  ?
                    <MenuComponent items={this.menuItems} fields={this.menuFields}></MenuComponent> : ''
                }
            </div>
        );
    }
}

ReactDom.render(<App />,document.getElementById('element'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React ContextMenu</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet" />
    <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>
</head>

<body>
    <div id='element'>
        <div id='loader'>Loading....</div>
    </div>
</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.

Source
Preview
index.tsx
index.html
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { enableRipple } from '@syncfusion/ej2-base';
import { MenuComponent, FieldSettingsModel } from '@syncfusion/ej2-react-navigations';

enableRipple(true);

export default class App extends React.Component<{}, {}> {
    //Menu datasource
    public data:  { [key: string]: Object }[] = [
        { 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
    public menuFields: FieldSettingsModel = {
        itemId:'id',
        text: 'text',
        parentId: 'pId'
    };

    render() {
        return (
            <MenuComponent items={this.data} fields={this.menuFields}></MenuComponent>
        );
    }
}

ReactDom.render(<App />,document.getElementById('element'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React ContextMenu</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet" />
    <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>
</head>

<body>
    <div id='element'>
        <div id='loader'>Loading....</div>
    </div>
</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.

Source
Preview
index.tsx
index.html
index.css
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { enableRipple } from '@syncfusion/ej2-base';
import { MenuComponent, FieldSettingsModel } from '@syncfusion/ej2-react-navigations';

enableRipple(false);

export default class App extends React.Component<{}, {}> {
    // Menu data definition
    public data:  { [key: string]: Object }[] = [
        {
            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' }
    ];

    public menuTemplate(data: any): JSX.Element {
        return (
            data.category ? <span>{data.category}</span> :
                (data.value && data.url) ?
                    <div className='e-avatar e-avatar-small image' style={{ backgroundImage: 'url(https://ej2.syncfusion.com/react/demos/src/menu/images/' + data.url + '.png)' }}>{data.value}</div> :
                    data.support ?
                        <ul>
                            {
                                data.support.map((supp) => <li>
                                    {supp.value}
                                    {
                                        supp.count ? <span className='e-badge e-badge-success'>{supp.count}</span> : ""
                                    }
                                </li>)
                            }
                        </ul> :
                        <div tabIndex={0} className="e-card">
                            <div className="e-card-header">
                                <div className="e-card-header-caption">
                                    <div className="e-card-header-title">About Us</div>
                                </div>
                            </div>
                            <div className="e-card-content">
                                {data.about.value}
                            </div>
                            <div className="e-card-actions">
                                <button className="e-btn e-outline">
                                    Read More
                    </button>
                            </div>
                        </div>
        );
    }

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

    render() {
        return (
            <div className="menu-section">
                <MenuComponent items={this.data} fields={this.menuFields} template={this.menuTemplate}></MenuComponent>
            </div>
        );
    }
}

ReactDom.render(<App />,document.getElementById('element'));
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Syncfusion React ContextMenu</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Essential JS 2 for React Components" />
    <meta name="author" content="Syncfusion" />
    <link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-cards/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-notifications/styles/material.css" rel="stylesheet" />
    <link href="//cdn.syncfusion.com/ej2/ej2-layouts/styles/material.css" rel="stylesheet" />
    <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>
</head>

<body>
    <div id='element'>
        <div id='loader'>Loading....</div>
    </div>
</body>

</html>
#loader {
	color: #008cff;
	height: 40px;
	left: 45%;
	position: absolute;
	top: 45%;
	width: 30%;
}

.menu-section {
    margin-top: 30px;
    text-align: center;
}

/**
* Common customization 
*/
.e-bigger .e-menu-wrapper.e-control ul.e-ul .e-menu-item,
.e-menu-wrapper.e-control ul.e-ul .e-menu-item {
    display: flex;
    height: auto;
    padding: 0;
}

.e-bigger .e-menu-wrapper.e-control ul.e-ul,
.e-menu-wrapper.e-control ul.e-ul {
    padding: 0;
}

/**
* Avatar customization
*/
.e-bigger .e-menu-wrapper ul .e-menu-item .e-avatar {
    background-size: auto;
    text-indent: 40px;
}

.e-menu-wrapper ul .e-menu-item .e-avatar {
    background-color: inherit;
    background-position: 0;
    background-size: 25px;
    color: inherit;
    font-size: inherit;
    height: inherit;
    justify-content: left;
    margin: 0 10px;
    width: 100%;
    text-indent: 35px;
}

/**
* Badge customization
*/
.e-menu-wrapper ul .e-menu-item ul li {
    padding: 0 10px;
}

.e-bigger .e-menu-wrapper ul .e-menu-item ul li .e-badge {        
    margin: 18px 0px 0px 10px;
}

.e-menu-wrapper ul .e-menu-item ul li .e-badge {
    float: right;
    margin: 13px 0px 0px 10px;
}

.e-menu-wrapper ul .e-menu-item ul li:hover {
    background-color: #eee;
}

.fabric .e-menu-wrapper ul .e-menu-item ul li:hover {
    background-color: #eaeaea;
}

.bootstrap .e-menu-wrapper ul .e-menu-item ul li:hover {
    background-color: #e6e6e6;
}

.highcontrast .e-menu-wrapper ul .e-menu-item ul li:hover {
    background-color: #685708;
}

/**
* Card customization
*/
.e-bigger .e-menu-wrapper ul.e-ul .e-menu-item .e-card {        
    width: 320px;
}

.e-menu-wrapper ul.e-ul .e-menu-item .e-card {        
    width: 290px;
    font-size: inherit;
    cursor: default;
    background-color: inherit;
    border-color: transparent;
}

.e-menu-wrapper ul.e-ul .e-menu-item .e-card .e-card-content {
    white-space: normal;
    color: inherit;
    padding-top: 0;
    text-align: justify;
    font-size: inherit;
}

.e-bigger .e-menu-wrapper ul.e-ul .e-menu-item .e-card .cb-icons {
    width: 40px;
    font-size: 40px;
    height: 40px;
}

.e-menu-wrapper ul.e-ul .e-menu-item .e-card .cb-icons {
    width: 30px;
    font-size: 30px;
    height: 30px;
}

.e-menu-wrapper ul.e-ul .e-menu-item .e-card .e-card-btn {
    background-color: inherit;
}

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

See Also