Angular Lookup - Getting Started

Drop-down editors allow users to navigate through a list of items, select one or multiple items, and search through the list. To learn how to choose a DevExtreme drop-down editor and for more details about the component's features, refer to the following article: How to Choose a Drop-Down Editor.

jQuery
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
Angular
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
Vue
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.
React
NOTE
Before you start the tutorial, ensure DevExtreme is installed in your application.

Lookup is a component that allows users to search and select an item in the drop-down list.

This tutorial shows how to add a Lookup component to your application, bind it to data, and configure its core features.

Each section in this tutorial describes a single configuration step. You can also find the full code in the following GitHub repository: getting-started-with-lookup.

Create a Lookup Component

jQuery

Add DevExtreme to your jQuery application and use the following code to create a Lookup component:

index.js
index.html
$(function() {
    $("#lookup").dxLookup({ });
});
<html>
    <head>
        <!-- ... -->
        <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
        <link rel="stylesheet" href="https://cdn3.devexpress.com/jslib/23.2.11/css/dx.light.css">
        <script type="text/javascript" src="https://cdn3.devexpress.com/jslib/23.2.11/js/dx.all.js"></script>
        <script type="text/javascript" src="index.js"></script>
    </head>
    <body>
        <div id="lookup"></div>
    </body>
</html>
Angular

Add DevExtreme to your Angular application and use the following code to create a Lookup component:

app.component.html
app.component.ts
app.module.ts
<dx-lookup></dx-lookup>
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {

}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

import { DxLookupModule } from 'devextreme-angular';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        DxLookupModule
    ],
    providers: [ ],
    bootstrap: [AppComponent]
})
export class AppModule { }
Vue

Add DevExtreme to your Vue application and use the following code to create a Lookup component:

App.vue
<template>
    <DxLookup>
    </DxLookup>
</template>

<script>
import 'devextreme/dist/css/dx.light.css';
import { DxLookup } from 'devextreme-vue/lookup';

export default {
    components: {
        DxLookup
    }
}
</script>
React

Add DevExtreme to your React application and use the following code to create a Lookup component:

App.js
import React from 'react';

import 'devextreme/dist/css/dx.light.css';

import { Lookup } from 'devextreme-react/lookup';

function App() {
    return (
        <Lookup>
        </Lookup>
    );
}

export default App;

Bind Lookup to Data

Lookup can load data from different data source types. To use a local array, assign it to the dataSource property. If array elements are objects, specify the fields that supply Lookup's value (valueExpr) and displayed value (displayExpr). For information on other data source types, refer to the following articles:

jQuery
index.js
index.html
data.js
$(function() {
    $("#lookup").dxLookup({
        dataSource: new DevExpress.data.DataSource({ 
            store: employeesTasks, 
            key: "ID"
        }),
        valueExpr: "ID",
        displayExpr: "Subject"
    });
});
<head>
     <!-- ... -->
    <script type="text/javascript" src="data.js"></script>
</head>
const employeesTasks = [
    {
        ID: 1,
        Assignee: "Mr. John Heart",
        Subject: "Choose between PPO and HMO Health Plan",
        disabled: true
    }, {
        ID: 2,
        Assignee: "Mr. John Heart",
        Subject: "Google AdWords Strategy"
    }, {
        ID: 3,
        Assignee: "Mr. John Heart",
        Subject: "New Brochures"
    }, {
        ID: 4,
        Assignee: "Mr. John Heart",
        Subject: "Update NDA Agreement"
    }, {
        ID: 5,
        Assignee: "Mr. John Heart",
        Subject: "Review Product Recall Report by Engineering Team"
    }, {
        ID: 6,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Update Personnel Files"
    }, {
        ID: 7,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Review Health Insurance Options Under the Affordable Care Act"
    }, {
        ID: 8,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Non-Compete Agreements"
    }, {
        ID: 9,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Give Final Approval for Refunds"
    }, {
        ID: 10,
        Assignee: "Mr. Robert Reagan",
        Subject: "Deliver R&D Plans for 2013"
    }, {
        ID: 11,
        Assignee: "Mr. Robert Reagan",
        Subject: "Decide on Mobile Devices to Use in the Field"
    }, {
        ID: 12,
        Assignee: "Mr. Robert Reagan",
        Subject: "Try New Touch-Enabled WinForms Apps"
    }, {
        ID: 13,
        Assignee: "Mr. Robert Reagan",
        Subject: "Approval on Converting to New HDMI Specification"
    }, {
        ID: 14,
        Assignee: "Ms. Greta Sims",
        Subject: "Approve Hiring of John Jeffers"
    }, {
        ID: 15,
        Assignee: "Ms. Greta Sims",
        Subject: "Update Employee Files with New NDA"
    }, {
        ID: 16,
        Assignee: "Ms. Greta Sims",
        Subject: "Provide New Health Insurance Docs"
    }
];
Angular
app.component.html
app.component.ts
app.service.ts
<dx-lookup
    [dataSource]="dataSource"
    valueExpr="ID"
    displayExpr="Subject">
</dx-lookup>
import { Component } from '@angular/core';
import { AppService, Task } from './app.service';
import DataSource from 'devextreme/data/data_source';

// ...
export class AppComponent {
    employeesTasks: Task[];
    dataSource: DataSource;

    constructor(service: AppService) {
        this.employeesTasks = service.getTasks();
        this.dataSource = new DataSource({
            store: this.employeesTasks,
            key: "ID"
        });
    }
}
import { Injectable } from '@angular/core';

export class Task {
    ID: number;
    Assignee: string;
    Subject: string;
    disabled?: boolean;
}

const employeesTasks: Task[] = [
    {
        ID: 1,
        Assignee: "Mr. John Heart",
        Subject: "Choose between PPO and HMO Health Plan",
        disabled: true
    }, {
        ID: 2,
        Assignee: "Mr. John Heart",
        Subject: "Google AdWords Strategy"
    }, {
        ID: 3,
        Assignee: "Mr. John Heart",
        Subject: "New Brochures"
    }, {
        ID: 4,
        Assignee: "Mr. John Heart",
        Subject: "Update NDA Agreement"
    }, {
        ID: 5,
        Assignee: "Mr. John Heart",
        Subject: "Review Product Recall Report by Engineering Team"
    }, {
        ID: 6,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Update Personnel Files"
    }, {
        ID: 7,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Review Health Insurance Options Under the Affordable Care Act"
    }, {
        ID: 8,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Non-Compete Agreements"
    }, {
        ID: 9,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Give Final Approval for Refunds"
    }, {
        ID: 10,
        Assignee: "Mr. Robert Reagan",
        Subject: "Deliver R&D Plans for 2013"
    }, {
        ID: 11,
        Assignee: "Mr. Robert Reagan",
        Subject: "Decide on Mobile Devices to Use in the Field"
    }, {
        ID: 12,
        Assignee: "Mr. Robert Reagan",
        Subject: "Try New Touch-Enabled WinForms Apps"
    }, {
        ID: 13,
        Assignee: "Mr. Robert Reagan",
        Subject: "Approval on Converting to New HDMI Specification"
    }, {
        ID: 14,
        Assignee: "Ms. Greta Sims",
        Subject: "Approve Hiring of John Jeffers"
    }, {
        ID: 15,
        Assignee: "Ms. Greta Sims",
        Subject: "Update Employee Files with New NDA"
    }, {
        ID: 16,
        Assignee: "Ms. Greta Sims",
        Subject: "Provide New Health Insurance Docs"
    }
];

@Injectable()
export class AppService {
    getTasks(): Task[] {
        return employeesTasks;
    }
}
Vue
App.vue
data.js
<template>
    <DxLookup
        :data-source="dataSource"
        value-expr="ID"
        display-expr="Subject"
    />
</template>

<script>
// ...
import DataSource from 'devextreme/data/data_source';
import { employeesTasks } from './data';

const dataSource = new DataSource({
    store: employeesTasks,
    key: "ID"
});

export default {
    components: {
        DxLookup
    },
    data() {
        return {
            dataSource
        }
    }
}
</script>
export const employeesTasks = [
    {
        ID: 1,
        Assignee: "Mr. John Heart",
        Subject: "Choose between PPO and HMO Health Plan",
        disabled: true
    }, {
        ID: 2,
        Assignee: "Mr. John Heart",
        Subject: "Google AdWords Strategy"
    }, {
        ID: 3,
        Assignee: "Mr. John Heart",
        Subject: "New Brochures"
    }, {
        ID: 4,
        Assignee: "Mr. John Heart",
        Subject: "Update NDA Agreement"
    }, {
        ID: 5,
        Assignee: "Mr. John Heart",
        Subject: "Review Product Recall Report by Engineering Team"
    }, {
        ID: 6,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Update Personnel Files"
    }, {
        ID: 7,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Review Health Insurance Options Under the Affordable Care Act"
    }, {
        ID: 8,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Non-Compete Agreements"
    }, {
        ID: 9,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Give Final Approval for Refunds"
    }, {
        ID: 10,
        Assignee: "Mr. Robert Reagan",
        Subject: "Deliver R&D Plans for 2013"
    }, {
        ID: 11,
        Assignee: "Mr. Robert Reagan",
        Subject: "Decide on Mobile Devices to Use in the Field"
    }, {
        ID: 12,
        Assignee: "Mr. Robert Reagan",
        Subject: "Try New Touch-Enabled WinForms Apps"
    }, {
        ID: 13,
        Assignee: "Mr. Robert Reagan",
        Subject: "Approval on Converting to New HDMI Specification"
    }, {
        ID: 14,
        Assignee: "Ms. Greta Sims",
        Subject: "Approve Hiring of John Jeffers"
    }, {
        ID: 15,
        Assignee: "Ms. Greta Sims",
        Subject: "Update Employee Files with New NDA"
    }, {
        ID: 16,
        Assignee: "Ms. Greta Sims",
        Subject: "Provide New Health Insurance Docs"
    }
];
React
App.js
data.js
// ...
import DataSource from 'devextreme/data/data_source';
import { employeesTasks } from './data';

const dataSource = new DataSource({
    store: employeesTasks,
    key: "ID"
});

function App() {
    return (
        <Lookup
            dataSource={dataSource}
            valueExpr="ID"
            displayExpr="Subject"
        />
    ); 
}

export default App;
export const employeesTasks = [
    {
        ID: 1,
        Assignee: "Mr. John Heart",
        Subject: "Choose between PPO and HMO Health Plan",
        disabled: true
    }, {
        ID: 2,
        Assignee: "Mr. John Heart",
        Subject: "Google AdWords Strategy"
    }, {
        ID: 3,
        Assignee: "Mr. John Heart",
        Subject: "New Brochures"
    }, {
        ID: 4,
        Assignee: "Mr. John Heart",
        Subject: "Update NDA Agreement"
    }, {
        ID: 5,
        Assignee: "Mr. John Heart",
        Subject: "Review Product Recall Report by Engineering Team"
    }, {
        ID: 6,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Update Personnel Files"
    }, {
        ID: 7,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Review Health Insurance Options Under the Affordable Care Act"
    }, {
        ID: 8,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Non-Compete Agreements"
    }, {
        ID: 9,
        Assignee: "Mrs. Olivia Peyton",
        Subject: "Give Final Approval for Refunds"
    }, {
        ID: 10,
        Assignee: "Mr. Robert Reagan",
        Subject: "Deliver R&D Plans for 2013"
    }, {
        ID: 11,
        Assignee: "Mr. Robert Reagan",
        Subject: "Decide on Mobile Devices to Use in the Field"
    }, {
        ID: 12,
        Assignee: "Mr. Robert Reagan",
        Subject: "Try New Touch-Enabled WinForms Apps"
    }, {
        ID: 13,
        Assignee: "Mr. Robert Reagan",
        Subject: "Approval on Converting to New HDMI Specification"
    }, {
        ID: 14,
        Assignee: "Ms. Greta Sims",
        Subject: "Approve Hiring of John Jeffers"
    }, {
        ID: 15,
        Assignee: "Ms. Greta Sims",
        Subject: "Update Employee Files with New NDA"
    }, {
        ID: 16,
        Assignee: "Ms. Greta Sims",
        Subject: "Provide New Health Insurance Docs"
    }
];

Configure Search

Lookup allows users to search through the drop-down list. To configure the search, set the following properties:

  • searchMode: "contains" | "startswith"
    Specifies the comparison operation used to search Lookup items.

  • searchExpr
    Specifies data fields or an expression against which the component compares the search criteria.

  • minSearchLength
    Specifies the minimum number of characters that users should enter into the text box to begin a search.

  • showDataBeforeSearch
    Specifies whether the UI component displays unfiltered values until users types the number of characters that exceed the minSearchLength property value.

To disable the search, set the searchEnabled property to false.

In the following code, the searchMode property is set to "contains". This mode allows users to find items that contain the search value. The searchExpr property is set to an array of two data fields to searches both fields. The minSearchLength property indicates that the search begins only when users type two or more characters. The showDataBeforeSearch property is set to true, and the component shows items before the search starts.

jQuery
index.js
$(function() {
    $("#lookup").dxLookup({
        // ...
        searchMode: "contains",
        searchExpr: ['Assignee', 'Subject'],
        minSearchLength: 2,
        showDataBeforeSearch: true
    });
});
Angular
app.component.html
<dx-lookup ...
    searchMode="contains"
    [searchExpr]="['Assignee', 'Subject']"
    [minSearchLength]="2"
    [showDataBeforeSearch]="true">
</dx-lookup>
Vue
App.vue
<template>
    <DxLookup ...
        search-mode="contains"
        :search-expr="['Assignee', 'Subject']"
        :min-search-length="2"
        :show-data-before-search="true"
    />
</template>

<script>
    // ...
</script>
React
App.js
// ...
const searchExpression = ['Assignee', 'Subject'];

function App() {
    return (
        <Lookup ...
            searchMode="contains"
            searchExpr={searchExpression}
            minSearchLength={2}
            showDataBeforeSearch={true}
        />
    );
}
export default App;

Handle the Value Change Event

Implement the onValueChanged handler to perform an action when a user selects an item. In the code below, this function logs the IDs of the currently and previously selected items.

jQuery
index.js
$(function() {
    $("#lookup").dxLookup({
        // ...
        onValueChanged: function(e) {
            console.log(e.value);
            console.log(e.previousValue);
        },
    });
});
Angular
app.component.html
app.component.ts
<dx-lookup ...
    (onValueChanged)="onValueChanged($event)">
</dx-lookup>
import { Component } from '@angular/core';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    // ...
    onValueChanged(e) {
        console.log(e.previousValue);
        console.log(e.value);
    }
}
Vue
App.vue
<template>
    <DxLookup ...
        @value-changed="onValueChanged"
    />
</template>

<script>
export default {
    // ...
    methods: {
        onValueChanged(e) {
            console.log(e.previousValue);
            console.log(e.value);
        }
    }
}
</script>
React
App.js
import React, { useCallback } from 'react';

function App() { 
    const onValueChanged = useCallback((e) => {
        console.log(e.previousValue);
        console.log(e.value);
    }, []);

    return (
        <Lookup ...
            onValueChanged={onValueChanged}
        />
    );
}

export default App;

Group Data

You can group data by category in Lookup. To implement this, we use data from the previous steps with the DataSource component that allows you to sort, filter, select, and group data.

Use the DataSource's group property to specify the data field to group by and enable Lookup's grouped property to notify the component that the data source is grouped.

jQuery
index.js
$(function() {
    $("#lookup").dxLookup({
        dataSource: new DevExpress.data.DataSource({ 
            // ...
            group: "Assignee"
        }),
        // ...
        grouped: true
    });
});
Angular
app.component.html
app.component.ts
<dx-lookup ...
    [grouped]="true">
</dx-lookup>
// ...
export class AppComponent {
    // ...
    constructor(service: Service) {
        // ...
        this.dataSource = new DataSource({
            // ...
            group: "Assignee"
        });
    }
}
Vue
App.vue
<template>
    <DxLookup ...
        :grouped="true"
    />
</template>

<script>
    // ...
    const dataSource = new DataSource({
        // ...
        group: "Assignee"
    });
</script>
React
App.js
// ...
const dataSource = new DataSource({
    // ...
    group: "Assignee"
});

function App() {
    return (
        <Lookup ...
            grouped={true}
        />
    );
}
export default App;

To customize the appearance of group titles, use groupTemplate.

jQuery
index.js
$(function() {
    $("#lookup").dxLookup({
        // ... 
        groupTemplate: function (data, index, element) {
            return data.key + " (" + data.items.length + " tasks)";
        },
    });
});
Angular
app.component.html
<dx-lookup ...
    groupTemplate="listGroup">
    <!-- ... -->
    <div *dxTemplate="let data of 'listGroup'">
        {{ data.key + " (" + data.items.length + " tasks)" }}
    </div>
</dx-lookup>
Vue
App.vue
<template>
    <DxLookup ...
        group-template="group-list">
        <template #group-list="{ data }">
            {{ data.key + " (" + data.items.length + " tasks)" }}
        </template>
    </DxLookup>
</template>

<script>
    // ...
</script>
React
App.js
// ...

const renderListGroup = (data) => {        
    return (
        <div>{ data.key + " (" + data.items.length + " tasks)" }</div>
    );
}

function App() {
    return (
        <Lookup ...
            groupRender={renderListGroup}
        />
    );
}
export default App;

Enable Pagination

Pagination allows Lookup to render items in batches. When data is remote, the component can also load items in batches if the server can partition data. Follow the steps below to enable this functionality:

  1. Set the DataSource's paginate property to true.

  2. Use the DataSource's pageSize property to specify the number of visible items on one page. If you group data, the pageSize specifies the number of groups.

Lookup renders the next page when users scroll the drop-down list to the bottom or when they click the "Next" button. To specify this behavior, set the Lookup's pageLoadMode property to "nextButton". Additionally, you can specify the nextButtonText property if you want to rename the "Next" button.

jQuery
index.js
$(function() {
    $("#lookup").dxLookup({
        dataSource: new DevExpress.data.DataSource({ 
            // ...
            paginate: true,
            pageSize: 2
        }),
        // ...
        pageLoadMode: "nextButton",
        nextButtonText: "More"
    });
});
Angular
app.component.html
app.component.ts
<dx-lookup ...
    pageLoadMode="nextButton"
    nextButtonText="More">
</dx-lookup>
// ...
export class AppComponent {
    // ...
    constructor(service: AppService) {
        // ...
        this.dataSource = new DataSource({
            // ...
            paginate: true,
            pageSize: 2
        });
    }
}
Vue
App.vue
<template>
    <DxLookup ...
        page-load-mode="nextButton"
        next-button-text="More"
    />
</template>

<script>
    // ...
    const dataSource = new DataSource({
        // ...
        paginate: true,
        pageSize: 2
    });
</script>
React
App.js
// ...
const dataSource = new DataSource({
    // ...
    paginate: true,
    pageSize: 2
});

function App() {
    return (
        <Lookup ...
            pageLoadMode="nextButton"
            nextButtonText="More"
        />
    );
}
export default App;

Customize Item Appearance

To customize item appearance, use itemTemplate.

jQuery
index.js
$(function() {
    $("#lookup").dxLookup({
        // ...
        itemTemplate: function (itemData, itemIndex, itemElement) {
            return itemData.disabled ? '\u274C ' + itemData.Subject : '\u2705 ' + itemData.Subject;
        }
    });
});
Angular
app.component.html
<dx-lookup ...
    itemTemplate="listItem">
    <!-- ... -->
    <div *dxTemplate="let itemData of 'listItem'">
        {{ itemData.disabled ? '\u274C ' + itemData.Subject : '\u2705 ' + itemData.Subject }}
    </div>
</dx-lookup>
Vue
App.vue
<template>
    <DxLookup ...
        item-template="list-item">
        <template #list-item="{ data: itemData }">
            {{ itemData.disabled ? '\u274C ' + itemData.Subject : '\u2705 ' + itemData.Subject }}
        </template>
    </DxLookup>
</template>

<script>
    // ...
</script>
React
App.js
// ...
const renderListItem = (data) => {
    return (
        <div>{ data.disabled ? '\u274C ' + data.Subject : '\u2705 ' + data.Subject }</div>
    );
}

function App() {
    return (
        <Lookup ...
            itemRender={renderListItem}
        />
    );
}
export default App;

Customize the Drop-Down Menu

On desktops and iOS devices, the Lookup's drop-down menu is the Popover UI component; on other devices, it is the Popup component.

To customize Popup or Popover, use the dropDownOptions object. For example, the following code closes Lookup when users click outside and removes the item list title:

jQuery
index.js
$(function() {
    $("#lookupContainer").dxLookup({
        // ...
        dropDownOptions: {
            hideOnOutsideClick: true,
            showTitle: false
        }
    });
});
Angular
app.component.html
<dx-lookup>
    <dxo-drop-down-options
        [hideOnOutsideClick]="true"
        [showTitle]="false">
    </dxo-drop-down-options>
</dx-lookup>
Vue
App.vue
<template>
    <DxLookup>
        <DxDropDownOptions
            :hide-on-outside-click="true"
            :show-title="false"
        />
    </DxLookup>
</template>

<script>
// ...

import { DxLookup, DxDropDownOptions } from 'devextreme-vue/lookup';

export default {
    components: {
        DxLookup,
        DxDropDownOptions
    },
    data() {
        return {
            // ...
        };
    }
}
</script>
React
App.js
import { Lookup, DropDownOptions } from 'devextreme-react/lookup';

function App() {
    return (
        <Lookup>
            <DropDownOptions
                hideOnOutsideClick={true}
                showTitle={false}
            />
        </Lookup>
    );
}
export default App;

You have configured basic Lookup features. For more information about this UI component and examples, refer to the following resources: