All docs
V19.1
24.1
The page you are viewing does not exist in version 24.1.
23.2
The page you are viewing does not exist in version 23.2.
23.1
The page you are viewing does not exist in version 23.1.
22.2
The page you are viewing does not exist in version 22.2.
22.1
The page you are viewing does not exist in version 22.1.
21.2
The page you are viewing does not exist in version 21.2.
21.1
The page you are viewing does not exist in version 21.1.
20.2
The page you are viewing does not exist in version 20.2.
20.1
The page you are viewing does not exist in version 20.1.
19.2
19.1
18.2
18.1
17.2
A newer version of this page is available. Switch to the current version.

DevExtreme jQuery - Bind a Lookup Column to a Custom Data Source

In the following code snippet, Author Name is a lookup column bound to a custom data source. The CustomStore loads data from the data source in the raw mode (the data is processed on the client). Data processing in this example includes only sorting, but you can specify other operations using the DataSource options.

jQuery
JavaScript
$(function() {
    var lookupDataSource = {
        store: new DevExpress.data.CustomStore({
            key: "id",
            loadMode: "raw",
            load: function() {
                // Returns an array of objects that have the following structure:
                // { id: 1, name: "John Doe" }
                return $.getJSON("https://mydomain.com/MyDataService/authors/");
            }
        }),
        sort: "name"
    }

    $("#treeListContainer").dxTreeList({
        // ...
        columns: [{
            dataField: "authorId",
            caption: "Author Name",
            lookup: {
                dataSource: lookupDataSource,
                valueExpr: "id", // "id" contains the same values as "authorId"
                displayExpr: "name"
            }
        },
        // ...
        ]
    });
});
Angular
TypeScript
HTML
import { Component, Inject } from "@angular/core";
import { HttpClient, HttpClientModule } from "@angular/common/http";
import { DxTreeListModule } from "devextreme-angular";
import CustomStore from "devextreme/data/custom_store";
import "rxjs/add/operator/toPromise";

@Component({ ... })
export class AppComponent {
    lookupDataSource = {};
    constructor(@Inject(HttpClient) httpClient: HttpClient) {
        this.lookupDataSource = {
            store: new CustomStore({
                key: "id",
                loadMode: "raw",
                load: () => {
                    // Returns an array of objects that have the following structure:
                    // { id: 1, name: "John Doe" }
                    return httpClient.get("https://mydomain.com/MyDataService/authors/")
                        .toPromise();
                }
            }),
            sort: "name"
        };
    }
}
@NgModule({
    imports: [
        // ...
        DxTreeListModule,
        HttpClientModule
    ],
    // ...
})
<dx-tree-list ... >
    <dxi-column
        dataField="authorId"
        caption="Author Name">
        <dxo-lookup
            [dataSource]="lookupDataSource"
            displayExpr="name"
            valueExpr="id"> <!-- "id" contains the same values as "authorId" -->
        </dxo-lookup>
    </dxi-column>
    <!-- ... -->
</dx-tree-list>

The following alternative CustomStore configuration delegates data processing to the server. The loadOptions object passed to the load function contains data processing settings that should be sent to the server with the GET request. In this example, the load function sends only sorting settings because the only specified DataSource option is sort (see the commented out code lines). Note that the CustomStore must also contain a byKey implementation.

jQuery
JavaScript
$(function() {
    var lookupDataSource = {
        store: new DevExpress.data.CustomStore({
            key: "id",
            load: function(loadOptions) {
                var d = $.Deferred(),
                    params = {};
                [
                    "sort", 
                    // "skip",     
                    // "take", 
                    // "filter", 
                    // "searchExpr",
                    // "searchOperation",
                    // "searchValue",
                    // "group"
                ].forEach(function(i) {
                    if(i in loadOptions && isNotEmpty(loadOptions[i])) 
                        params[i] = JSON.stringify(loadOptions[i]);
                });
                $.getJSON("https://mydomain.com/MyDataService/authors/", params)
                    .done(function(result) {
                        d.resolve(result)
                    });
                return d.promise();
            },
            byKey: function(key) {
                return $.getJSON("https://mydomain.com/MyDataService/authors/" + encodeURIComponent(key));
            }
        }),
        sort: "name"
    }
    $("#treeListContainer").dxTreeList({
        // ...
        // The configuration repeats the previous code
        // ...
    });
});
function isNotEmpty(value) {
    return value !== undefined && value !== null && value !== "";
}
Angular
TypeScript
HTML
import { Component, Inject } from "@angular/core";
import { HttpClient, HttpClientModule, HttpParams } from "@angular/common/http";
import { DxTreeListModule } from "devextreme-angular";
import CustomStore from "devextreme/data/custom_store";
import "rxjs/add/operator/toPromise";

@Component({ ... })
export class AppComponent {
    lookupDataSource = {};
    constructor(@Inject(HttpClient) httpClient: HttpClient) {
        function isNotEmpty(value: any): boolean {
            return value !== undefined && value !== null && value !== "";
        }
        this.lookupDataSource = {
            store: new CustomStore({
                key: "id",
                load: (loadOptions) => {
                    let params: HttpParams = new HttpParams();
                    [
                        "sort", 
                        // "skip",     
                        // "take", 
                        // "filter", 
                        // "searchExpr",
                        // "searchOperation",
                        // "searchValue",
                        // "group"
                    ].forEach(function(i) {
                        if(i in loadOptions && isNotEmpty(loadOptions[i])) 
                            params = params.set(i, JSON.stringify(loadOptions[i]));
                    });
                    return httpClient.get("https://mydomain.com/MyDataService/authors/", { params: params })
                        .toPromise()
                        .then(result => {
                            return result;
                        });
                },
                byKey: function(key) {
                    return httpClient.get("https://mydomain.com/MyDataService/authors/" + encodeURIComponent(key))
                        .toPromise();
                }
            }),
            sort: "name"
        }
    }
}
@NgModule({
    imports: [
        // ...
        DxTreeListModule,
        HttpClientModule
    ],
    // ...
})
<dx-tree-list>
    <!-- ... -->
    <!-- The configuration repeats the previous code -->
    <!-- ... -->
</dx-tree-list>
See Also