Vue TagBox - Custom Sources
To bind DevExtreme TagBox to a custom data source, implement a CustomStore. You can use the following extensions to configure CustomStore and implement server-side data processing to bind DevExtreme components to Web API and MongoDB services:
If these extensions are not suitable for your data source, follow the instructions below to configure the CustomStore manually.
The CustomSource's configuration differs depending on whether data is processed on the client or server. In the former case, switch the CustomStore to the raw mode and load all data from the server using the load function as shown in the following example:
jQuery
$("#tagBoxContainer").dxTagBox({
dataSource: new DevExpress.data.DataSource({
store: new DevExpress.data.CustomStore({
key: "ID",
loadMode: "raw",
load: function () {
return $.getJSON('https://mydomain.com/MyDataService');
}
}),
paginate: false,
})
});Angular
import { Component } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { DataSource, CustomStore } from "devextreme-angular/common/data";
import { lastValueFrom } from "rxjs";
import { DxTagBoxComponent } from "devextreme-angular/ui/tag-box";
@Component({
imports: [DxTagBoxComponent],
// ...
})
export class AppComponent {
tagBoxDataSource: DataSource;
constructor(httpClient: HttpClient) {
this.tagBoxDataSource = new DataSource({
store: new CustomStore({
key: "ID",
loadMode: "raw",
load: () => {
return lastValueFrom(httpClient.get('https://mydomain.com/MyDataService'));
}
}),
paginate: false,
});
}
}
<dx-tag-box
[dataSource]="tagBoxDataSource"
></dx-tag-box>Vue
<template>
<DxTagBox :data-source="tagBoxDataSource" />
</template>
<script setup lang="ts">
import { DxTagBox } from "devextreme-vue/tag-box";
import { DataSource, CustomStore } from "devextreme-vue/common/data";
import "whatwg-fetch";
function handleErrors(response) {
if (!response.ok)
throw Error(response.statusText);
return response;
}
const tagBoxDataSource = new DataSource({
store: new CustomStore({
key: "ID",
loadMode: "raw",
load: () => {
return fetch("https://mydomain.com/MyDataService")
.then(handleErrors);
}
}),
paginate: false,
});
</script>React
import React from "react";
import { TagBox } from "devextreme-react/tag-box";
import { DataSource, CustomStore } from "devextreme-react/common/data";
import "whatwg-fetch";
function handleErrors(response) {
if (!response.ok)
throw Error(response.statusText);
return response;
}
const tagBoxDataSource = new DataSource({
store: new CustomStore({
key: "ID",
loadMode: "raw",
load: () => {
return fetch("https://mydomain.com/MyDataService")
.then(handleErrors);
}
}),
paginate: false,
});
function App() {
return (
<TagBox dataSource={tagBoxDataSource} />
);
}In the latter case, use the CustomStore's load function to send data processing settings to the server. These settings are passed as a parameter to the load function and depend on the operations (paging, filtering, sorting, etc.) that you have enabled in the DataSource. The following settings are relevant for the TagBox:
Paging settings: take, skip, requireTotalCount
Present if paginate is true and pageSize is set in the DataSource. The requireTotalCount setting appears when the TagBox's selectAllMode is "allPages".Sorting settings: sort
Present if the DataSource's sort property is set.Filtering settings: filter
Present if the value property is specified at design time, searching is enabled in the UI component, or the DataSource's filter property is set.Searching settings: searchExpr, searchOperation, and searchValue
Present if corresponding properties are set in the DataSource.Grouping settings: group
Present if the DataSource's group property is set.
After receiving these settings, the server should apply them to data and send back an object with the following structure:
{
data: [{
key: "Group 1",
items: [ ... ] // result data objects
},
...
],
totalCount: 100
}If the group setting is absent, the object structure is different:
{
data: [ ... ], // result data objects
totalCount: 100
}If the TagBox allows a user to add custom items, you should also implement the insert method. Below is a generalized CustomStore configuration for the TagBox UI component.
jQuery
$(function() {
$("#tagBoxContainer").dxTagBox({
dataSource: new DevExpress.data.DataSource({
key: "ID",
load: function(loadOptions) {
const d = $.Deferred(),
params = {};
[
"skip",
"take",
"sort",
"filter",
"searchExpr",
"searchOperation",
"searchValue",
"group",
"requireTotalCount"
].forEach(function(i) {
if(i in loadOptions && isNotEmpty(loadOptions[i]))
params[i] = JSON.stringify(loadOptions[i]);
});
$.getJSON("http://mydomain.com/MyDataService", params)
.done(function(result) {
// Here, you can perform operations unsupported by the server
d.resolve(result.data, {
totalCount: result.totalCount
});
});
return d.promise();
},
insert: function(values) {
return $.ajax({
url: "http://mydomain.com/MyDataService/",
method: "POST",
data: values
})
}
})
});
});
function isNotEmpty(value) {
return value !== undefined && value !== null && value !== "";
}Angular
<dx-tag-box ...
[dataSource]="tagBoxData">
</dx-tag-box>
import { ..., Inject } from "@angular/core";
import { HttpClient, HttpClientModule, HttpParams } from "@angular/common/http";
import { DxTagBoxModule } from "devextreme-angular";
import DataSource from "devextreme/data/data_source";
import CustomStore from "devextreme/data/custom_store";
import { lastValueFrom } from 'rxjs';
// ...
export class AppComponent {
tagBoxData: DataSource = {};
constructor(@Inject(HttpClient) httpClient: HttpClient) {
function isNotEmpty(value: any): boolean {
return value !== undefined && value !== null && value !== "";
}
this.tagBoxData = new DataSource({
store: new CustomStore({
key: "ID",
load: (loadOptions) => {
let params: HttpParams = new HttpParams();
[
"skip",
"take",
"sort",
"filter",
"searchExpr",
"searchOperation",
"searchValue",
"group",
"requireTotalCount"
].forEach(function(i) {
if(i in loadOptions && isNotEmpty(loadOptions[i]))
params = params.set(i, JSON.stringify(loadOptions[i]));
});
return lastValueFrom(httpClient.get("http://mydomain.com/MyDataService", { params: params }))
.then(result => {
// Here, you can perform operations unsupported by the server
return {
data: result.data,
totalCount: result.totalCount
};
});
},
insert: function(values) {
return lastValueFrom(httpClient.post('http://mydomain.com/MyDataService', values));
}
})
});
}
}
@NgModule({
imports: [
// ...
DxTagBoxModule,
HttpClientModule
],
// ...
})Vue
<template>
<DxTagBox ...
:data-source="dataSource" />
</template>
<script>
import DxTagBox from "devextreme-vue/tag-box";
import CustomStore from "devextreme/data/custom_store";
import 'whatwg-fetch';
// ...
function isNotEmpty(value) {
return value !== undefined && value !== null && value !== "";
}
function handleErrors(response) {
if (!response.ok)
throw Error(response.statusText);
return response;
}
const tagBoxDataSource = {
store: new CustomStore({
key: "ID",
load: (loadOptions) => {
let params = "?";
[
"skip",
"take",
"sort",
"filter",
"searchExpr",
"searchOperation",
"searchValue",
"group",
"requireTotalCount"
].forEach(function(i) {
if(i in loadOptions && isNotEmpty(loadOptions[i]))
params += `${i}=${JSON.stringify(loadOptions[i])}&`;
});
params = params.slice(0, -1);
return fetch(`https://mydomain.com/MyDataService${params}`)
.then(handleErrors)
.then(response => response.json())
.then((result) => {
return {
data: result.data,
totalCount: result.totalCount
}
});
},
insert: (values) => {
return fetch("https://mydomain.com/MyDataService", {
method: "POST",
body: JSON.stringify(values),
headers: {
'Content-Type': 'application/json'
}
}).then(handleErrors);
}
})
}
export default {
// ...
data() {
return {
dataSource: tagBoxDataSource
};
},
components: {
// ...
DxTagBox
}
}
</script>React
import React from "react";
import TagBox from "devextreme-react/tag-box";
import CustomStore from "devextreme/data/custom_store";
import 'whatwg-fetch';
// ...
function isNotEmpty(value) {
return value !== undefined && value !== null && value !== "";
}
function handleErrors(response) {
if (!response.ok)
throw Error(response.statusText);
return response;
}
const tagBoxDataSource = {
store: new CustomStore({
key: "ID",
load: (loadOptions) => {
let params = "?";
[
"skip",
"take",
"sort",
"filter",
"searchExpr",
"searchOperation",
"searchValue",
"group",
"requireTotalCount"
].forEach(function(i) {
if(i in loadOptions && isNotEmpty(loadOptions[i]))
params += `${i}=${JSON.stringify(loadOptions[i])}&`;
});
params = params.slice(0, -1);
return fetch(`https://mydomain.com/MyDataService${params}`)
.then(handleErrors)
.then(response => response.json())
.then((result) => {
return {
data: result.data,
totalCount: result.totalCount
}
});
},
insert: (values) => {
return fetch("https://mydomain.com/MyDataService", {
method: "POST",
body: JSON.stringify(values),
headers: {
'Content-Type': 'application/json'
}
}).then(handleErrors);
}
})
}
export default function App() {
return (
<TagBox ...
dataSource={tagBoxDataSource}>
</TagBox>
);
}