---
title: Order Form Template
product: Survey Creator
description: Choose from a collection of free ready-made templates of most common web form sections, e.g., an order form or contact info. The elements ship with predefined properties for easy and fast integration into your JavaScript app.
framework: Vue 3
source: https://surveyjs.io/survey-creator/examples/order-form-template/vue3js
index: https://surveyjs.io/survey-creator/examples/overview.md
---

# Order Form Template (Vue 3)

The Order Table question type collects details about ordered items. This demo shows how to create an order table using either Multi-Select Matrix or Dynamic Matrix. Multi-Select Matrix displays a complete list of available products and allows users to select quantities for the items they want to purchase. This implementation is ideal for shorter product lists. Dynamic Matrix lets users choose items from a drop-down menu, which makes it suitable for longer product lists. To implement any or both of these order tables in your application, create a specialized question type.

[Create Specialized Question Types](https://surveyjs.io/form-library/documentation/customize-question-types/create-specialized-question-types (linkStyle))

## Files

### `public/index.html`

```html
<div id="app" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0;"></div>
```

### `src/App.vue`

```html
<template>
    <SurveyCreatorComponent :model="creator" />
</template>
<script setup lang="ts">
    import { SurveyCreatorModel } from "survey-creator-core";
    import { SurveyCreatorComponent } from "survey-creator-vue";
    import { ComponentCollection, Serializer, ItemValue } from "survey-core";
    import "survey-core/survey.i18n";
    import "survey-creator-core/survey-creator-core.i18n";
    import "survey-core/survey-core.css";
    import "survey-creator-core/survey-creator-core.css";
    import "./index.css";
    import SurveyTheme from "survey-core/themes";
    import { registerCreatorTheme } from "survey-creator-core";

    registerCreatorTheme(SurveyTheme); // Add predefined Survey Creator UI themes

    function setupCustomProperties (className) {
        // Add a custom `price` property to the `itemvalue` class
        if (!Serializer.findProperty("itemvalue", "price")) {
            Serializer.addProperty("itemvalue", {
                name: "price",
                type: "number",
                default: 0,
                // Hide the property for built-in select-based questions (Dropdown, Checkboxes, Radio Button Group)
                visible: false
            });
        }
    
        // Create a custom `ordertableitem` class derived from `itemvalue`
        // to hide the `text`, `visibleIf`, and `enableIf` properties and show the `price` property
        if (!Serializer.findClass("ordertableitem")) {
            Serializer.addClass(
                "ordertableitem",
                [
                    { name: "text", visible: false },
                    { name: "visibleIf", visible: false },
                    { name: "enableIf", visible: false },
                    { name: "price", visible: true }
                ],
                (json) => new ItemValue(json, null, "ordertableitem"),
                "itemvalue"
            );
        }
    
        // Add a custom `orderItems` property to the `ordertable` class
        if (!Serializer.findProperty(className, "orderItems")) {
            Serializer.addProperty(className, {
                name: "orderItems",
                type: "ordertableitem[]",
                category: "general",
                visibleIndex: 3
            });
        }
    }
    
    // Add the Order Table question type
    ComponentCollection.Instance.add({
        name: "ordertable",
        title: "Order Table",
        defaultQuestionTitle: "Order Table",
        questionJSON: {
            type: "matrixdropdown",
            columns: [{
                name: "price",
                title: "Price",
                cellType: "expression",
                displayStyle: "currency"
            }, {
                name: "qty",
                title: "Quantity",
                cellType: "text",
                inputType: "number",
                defaultValue: 0,
                min: 0,
                textUpdateMode: "onTyping"
            }, {
                name: "total",
                title: "Total",
                cellType: "expression",
                displayStyle: "currency",
                expression: "{row.qty} * {row.price}",
                totalType: "sum",
                totalDisplayStyle: "currency"
            }]
        },
        onInit() {
            setupCustomProperties("ordertable");
        },
        onLoaded (question) {
            this.updateRowsAndValues(question);
        },
        onPropertyChanged (question, propertyName) {
            // Update rows and default values each time an order item is added or removed
            if (propertyName === "orderItems") {
                this.updateRowsAndValues(question);
            }
        },
        onItemValuePropertyChanged (question, { name, propertyName }) {
            if (propertyName === "orderItems") {
                // Update rows and default values each time the name (`value`) of an order item is changed
                if (name === "value") {
                    this.updateRowsAndValues(question);
                }
                // Update default values each time the price of an order item is changed
                if (name === "price") {
                    this.setDefaultValues(question);
                }
            }
        },
        // Helper functions
        buildRows (question) {
            var rows = [];
            for (let i = 0; i < question.orderItems.length; i++) {
                const item = question.orderItems[i];
                if (!!item.value) {
                    rows.push(item.value);
                }
            }
            question.contentQuestion.rows = rows;
        },
        setDefaultValues (question) {
            var defaultValue = question.contentQuestion.defaultValue || { };
            for (let i = 0; i < question.orderItems.length; i++) {
                const item = question.orderItems[i];
                if (!!item.value && !!item.price) {
                    defaultValue[item.value] ? 
                        defaultValue[item.value]["price"] = item.price : 
                        defaultValue[item.value] = { price: item.price, qty: 0, total: 0 };
                }
            }
            question.contentQuestion.defaultValue = defaultValue;
        },
        updateRowsAndValues (question) {
            this.buildRows(question);
            this.setDefaultValues(question);
        }
    });
    
    // Add a Dynamic Order Table question type
    ComponentCollection.Instance.add({
        name: "ordertabledynamic",
        title: "Dynamic Order Table",
        defaultQuestionTitle: "Dynamic Order Table",
        questionJSON: {
            type: "matrixdynamic",
            rowCount: 1,
            addRowText: "Add Item",
            columns: [{
                name: "item",
                title: "Item",
                cellType: "dropdown",
                isRequired: true
            }, {
                name: "price",
                title: "Price",
                cellType: "expression",
                displayStyle: "currency",
                defaultValue: 0
            }, {
                name: "qty",
                title: "Quantity",
                cellType: "text",
                inputType: "number",
                defaultValue: 1,
                min: 0,
                textUpdateMode: "onTyping"
            }, {
                name: "total",
                title: "Total",
                cellType: "expression",
                displayStyle: "currency",
                expression: "{row.qty} * {row.price}",
                totalType: "sum",
                totalDisplayStyle: "currency"
            }],
        },
        onInit() {
            setupCustomProperties("ordertabledynamic");
        },
        onLoaded (question) {
            // Update the Price column value when a user selects an order item in the Item column
            question.survey.onMatrixCellValueChanged.add((_, options) => {
                if (options.columnName === "item") {
                    const itemQ = options.row.getQuestionByColumnName("item");
                    const priceQ = options.row.getQuestionByColumnName("price");
                    if (!!itemQ && !!priceQ) {
                        priceQ.value = !!itemQ.selectedItem ? itemQ.selectedItem.price : 0;
                    }
                }
            });
            this.updateItemsColumn(question);
        },
        onPropertyChanged (question, propertyName) {
            // Update the Item column dropdown each time an order item is added or removed
            if (propertyName === "orderItems") {
                this.updateItemsColumn(question);
            }
        },
        onItemValuePropertyChanged (question, { propertyName }) {
            // Update the Item column dropdown each time an order item is changed
            if (propertyName === "orderItems") {
                this.updateItemsColumn(question);
            }
        },
        // A helper function
        updateItemsColumn (question) {
            question.contentQuestion.getColumnByName("item").choices = question.orderItems;
        }
    });
    const creator = new SurveyCreatorModel();
    creator.toolbox.defineCategories([{
      category: "Custom Items",
      items: [
          "ordertable",
          "ordertabledynamic",
      ]
    }], true);
    
    creator.JSON = {
      elements: [{
          type: "ordertable",
          name: "question1",
          orderItems: [
            { value: "Steak", price: 27 },
            { value: "Salmon", price: 22 },
            { value: "Beer", price: 5 }
          ]
      }, {
        type: "ordertabledynamic",
        name: "question2",
        orderItems: [
          { value: "Steak", price: 27 },
          { value: "Salmon", price: 22 },
          { value: "Beer", price: 5 }
        ]
      }]
    };
</script>
```

### `src/index.css`

```css
/* You can add your custom CSS here. */
```

### `src/main.ts`

```ts
import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);
app.mount("#app");
```

### `src/shims-vue.d.ts`

```ts
/* eslint-disable */
declare module "*.vue" {
    import type { DefineComponent } from "vue"
    const component: DefineComponent<{}, {}, any>
    export default component
}
```

### `.eslintrc.js`

```js
module.exports = {
    root: true,
    env: {
        node: true
    },
    extends: [
        "plugin:vue/vue3-essential",
        "eslint:recommended",
        "@vue/typescript/recommended",
        "@vue/prettier",
        "@vue/prettier/@typescript-eslint"
    ],
    parserOptions: {
        ecmaVersion: 2020
    },
    rules: {
        "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
        "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
    },
    overrides: [
        {
            files: [
                "**/__tests__/*.{j,t}s?(x)",
                "**/tests/unit/**/*.spec.{j,t}s?(x)"
            ],
            env: {
                jest: true
            }
        }
    ]
};
```

### `babel.config.js`

```js
module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"]
};
```

### `package.json`

```json
{
  "name": "surveyjs-library-vue3",
  "version": "0.1.0",
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "tslib": "2.6.1",
    "vue": "^3.4.1",
    "survey-core": "latest",
    "survey-vue3-ui": "latest",
    "survey-creator-core": "latest",
    "survey-creator-vue": "latest",
    "vue-router": "^4.0.0-0",
    "vuex": "^4.0.0-0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^2.33.0",
    "@typescript-eslint/parser": "^2.33.0",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-pwa": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-typescript": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^5.0.2",
    "@vue/test-utils": "^2.0.0-0",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.1.3",
    "eslint-plugin-vue": "^7.0.0-0",
    "node-sass": "^4.12.0",
    "prettier": "^1.19.1",
    "sass-loader": "^8.0.2",
    "typescript": "~3.9.3"
  }
}
```

### `tsconfig.json`

```json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env",
      "jest"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

## Other Frameworks

- [Angular](https://surveyjs.io/survey-creator/examples/order-form-template/angular.md)
- [React](https://surveyjs.io/survey-creator/examples/order-form-template/reactjs.md)
- [jQuery](https://surveyjs.io/survey-creator/examples/order-form-template/jquery.md)
- [Vanilla JS](https://surveyjs.io/survey-creator/examples/order-form-template/vanillajs.md)
