Backend API
<template>
<div>
<div class="long-title"><h3>Personal details</h3></div>
<div id="form-container">
<DxForm
id="form"
:col-count="2"
:form-data="employee"
>
<DxGroupItem
caption-template="infoGroupCaption"
caption="System Information"
>
<DxSimpleItem
data-field="ID"
/>
<DxGroupItem
caption-template="userDataGroupCaption"
caption="Main Information"
>
<DxSimpleItem
data-field="FirstName"
/>
<DxSimpleItem
data-field="LastName"
/>
<DxSimpleItem
data-field="HireDate"
/>
<DxSimpleItem
data-field="Position"
/>
<DxSimpleItem
data-field="OfficeNo"
/>
</DxGroupItem>
</DxGroupItem>
<DxGroupItem
caption-template="personalDataGroupCaption"
caption="Personal Data"
>
<DxSimpleItem
data-field="BirthDate"
/>
<DxGroupItem
caption-template="addressGroupCaption"
caption="Home Address"
>
<DxSimpleItem
data-field="Address"
/>
<DxSimpleItem
data-field="City"
/>
<DxSimpleItem
data-field="State"
/>
<DxSimpleItem
data-field="Zipcode"
/>
</DxGroupItem>
</DxGroupItem>
<DxGroupItem
caption-template="contactInfoGroupCaption"
caption="Contact Information"
>
<DxTabbedItem>
<DxTabPanelOptions
:defer-rendering="false"
/>
<DxTab
title="Phone"
>
<DxSimpleItem
data-field="Phone"
/>
</DxTab>
<DxTab
title="Skype"
>
<DxSimpleItem
data-field="Skype"
/>
</DxTab>
<DxTab
title="Email"
>
<DxSimpleItem
data-field="Email"
/>
</DxTab>
</DxTabbedItem>
</DxGroupItem>
<template #userDataGroupCaption="{ data }">
<GroupCaption
:data="data"
icon="user"
/>
</template>
<template #personalDataGroupCaption="{ data }">
<GroupCaption
:data="data"
icon="card"
/>
</template>
<template #contactInfoGroupCaption="{ data }">
<GroupCaption
:data="data"
icon="tel"
/>
</template>
<template #addressGroupCaption="{ data }">
<GroupCaption
:data="data"
icon="home"
/>
</template>
<template #infoGroupCaption="{ data }">
<GroupCaption
:data="data"
/>
</template>
</DxForm>
</div>
</div>
</template>
<script setup lang="ts">
import {
DxForm, DxSimpleItem, DxGroupItem, DxTabbedItem, DxTabPanelOptions, DxTab,
} from 'devextreme-vue/form';
import service from './data.ts';
import GroupCaption from './GroupCaption.vue';
import 'devextreme-vue/text-area';
const employee = service.getEmployee();
</script>
<style scoped>
#form-container {
margin: 10px;
}
.long-title h3 {
font-size: 24px;
text-align: center;
line-height: 2em;
}
.dx-form-group-custom-caption .dx-icon {
color: var(--dx-color-spin-icon);
}
.dx-form-group-custom-caption span {
line-height: 1;
}
</style>
<template>
<i :class="`dx-icon dx-icon-${icon}`"/><span>{{ data.caption }}</span>
</template>
<script setup lang="ts">
withDefaults(defineProps<{
data?: Record<string, any>
icon?: string
}>(), {
data: () => ({} as Record<string, any>),
icon: 'info',
});
</script>
window.exports = window.exports || {};
window.config = {
transpiler: 'plugin-babel',
meta: {
'*.vue': {
loader: 'vue-loader',
},
'*.ts': {
loader: 'demo-ts-loader',
},
'*.svg': {
loader: 'svg-loader',
},
'devextreme/time_zone_utils.js': {
'esModule': true,
},
'devextreme/localization.js': {
'esModule': true,
},
'devextreme/viz/palette.js': {
'esModule': true,
},
'openai': {
'esModule': true,
},
},
paths: {
'project:': '../../../../',
'npm:': 'https://cdn.jsdelivr.net/npm/',
'bundles:': '../../../../bundles/',
'externals:': '../../../../bundles/externals/',
},
map: {
'vue': 'npm:vue@3.4.27/dist/vue.esm-browser.js',
'@vue/shared': 'npm:@vue/shared@3.4.27/dist/shared.cjs.prod.js',
'vue-loader': 'npm:dx-systemjs-vue-browser@1.1.2/index.js',
'demo-ts-loader': 'project:utils/demo-ts-loader.js',
'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js',
'svg-loader': 'project:utils/svg-loader.js',
'mitt': 'npm:mitt/dist/mitt.umd.js',
'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js',
'luxon': 'npm:luxon@3.4.4/build/global/luxon.min.js',
'es6-object-assign': 'npm:es6-object-assign',
'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs',
'devextreme-vue': 'npm:devextreme-vue@link:../../packages/devextreme-vue/npm/cjs',
'devextreme-quill': 'npm:devextreme-quill@1.7.6/dist/dx-quill.min.js',
'devexpress-diagram': 'npm:devexpress-diagram@2.2.24/dist/dx-diagram.js',
'devexpress-gantt': 'npm:devexpress-gantt@4.1.64/dist/dx-gantt.js',
'inferno': 'npm:inferno@8.2.3/dist/inferno.min.js',
'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js',
'inferno-create-element': 'npm:inferno-create-element@8.2.3/dist/inferno-create-element.min.js',
'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js',
'inferno-hydrate': 'npm:inferno-hydrate/dist/inferno-hydrate.min.js',
'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js',
'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js',
'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js',
'@preact/signals-core': 'npm:@preact/signals-core@1.8.0/dist/signals-core.min.js',
'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js',
'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js',
// Prettier
'prettier/standalone': 'npm:prettier@2.8.8/standalone.js',
'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js',
},
packages: {
'devextreme-vue': {
main: 'index.js',
},
'devextreme-vue/common': {
main: 'index.js',
},
'devextreme': {
defaultExtension: 'js',
},
'devextreme/events/utils': {
main: 'index',
},
'devextreme/common/core/events/utils': {
main: 'index',
},
'devextreme/events': {
main: 'index',
},
'es6-object-assign': {
main: './index.js',
defaultExtension: 'js',
},
},
packageConfigPaths: [
'npm:@devextreme/*/package.json',
],
babelOptions: {
sourceMaps: false,
stage0: true,
},
};
System.config(window.config);
// eslint-disable-next-line
const useTgzInCSB = ['openai'];
const employee = {
ID: 1,
FirstName: 'John',
LastName: 'Heart',
CompanyName: 'Super Mart of the West',
Position: 'CEO',
OfficeNo: '901',
BirthDate: new Date(1964, 2, 16),
HireDate: new Date(1995, 0, 15),
Address: '351 S Hill St.',
City: 'Los Angeles',
State: 'CA',
Zipcode: '90013',
Phone: '+1(213) 555-9392',
Email: 'jheart@dx-email.com',
Skype: 'jheart_DX_skype',
};
export default {
getEmployee() {
return employee;
},
};
import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');
<!DOCTYPE html>
<html lang="en">
<head>
<title>DevExtreme Demo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/25.1.7/css/dx.light.css" />
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" />
<script src="https://cdn.jsdelivr.net/npm/typescript@5.4.5/lib/typescript.js"></script>
<script type="module">
import * as vueCompilerSFC from "https://cdn.jsdelivr.net/npm/@vue/compiler-sfc@3.4.27/dist/compiler-sfc.esm-browser.js";
window.vueCompilerSFC = vueCompilerSFC;
</script>
<script src="https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@0.21.3/dist/system.js"></script>
<script type="text/javascript" src="config.js"></script>
<script type="text/javascript">
System.import("./index.ts");
</script>
</head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"> </div>
</div>
</body>
</html>
To create a group item, assign "group" to the itemType property. To specify a group's title, use the caption property. To replace a group's title with custom content, implement a caption template. This demo shows three groups. The Personal Data group is nested in the System Information group.
To create a tabbed item, assign "tabbed" to the itemType property. This demo shows a tabbed item nested in the Contact Information group. The Form uses the TabPanel component to display tabs. You can specify the TabPanel's properties in the tabPanelOptions object. This demo disables the deferRendering property to render TabPanel's content immediately.