---
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: Vanilla JS
source: https://surveyjs.io/survey-creator/examples/order-form-template/vanillajs
index: https://surveyjs.io/survey-creator/examples/overview.md
---

# Order Form Template (Vanilla JS)

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
<!-- Uncomment the following lines to enable Ace Editor in the JSON Editor tab -->
<!-- 
<script src="https://unpkg.com/ace-builds/src-min-noconflict/ace.js"></script>
<script src="https://unpkg.com/ace-builds/src-min-noconflict/ext-searchbox.js"></script>
<script src="https://unpkg.com/ace-builds/src-min-noconflict/theme-clouds_midnight.js"></script>
-->

<div id="surveyCreatorContainer" style="position: absolute; height: 100%; width: 100%"></div>
```

### `src/index.css`

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

### `src/index.js`

```js
import { SurveyCreator } from "survey-creator-js";
import "survey-core/survey.i18n";
import "survey-creator-core/survey-creator-core.i18n";
import { ComponentCollection, Serializer, ItemValue } from "survey-core";
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 SurveyCreator();

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 }
    ]
  }]
};
creator.render("surveyCreatorContainer");
```

### `package.json`

```json
{
  "dependencies": {
    "survey-core": "latest",
    "survey-js-ui": "latest",
    "survey-creator-core": "latest",
    "survey-creator-js": "latest"
  },
  "devDependencies": {
    "react-scripts": "latest"
  }
}
```

## 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)
- [Vue 3](https://surveyjs.io/survey-creator/examples/order-form-template/vue3js.md)
- [jQuery](https://surveyjs.io/survey-creator/examples/order-form-template/jquery.md)
