---
title: Smart Search Input
product: Survey Creator
description: Discover how our Intelligent Auto-Complete Input and Smart Search Input components streamline data entry for industry and occupation codes by providing real-time, user-friendly suggestions, ensuring accurate and standardized data capture.
framework: Vue 3
source: https://surveyjs.io/survey-creator/examples/smart-search-input/vue3js
index: https://surveyjs.io/survey-creator/examples/overview.md
---

# Smart Search Input (Vue 3)

Smart search is an advanced search functionality that uses personalization, context understanding, auto-suggestions, and other techniques to improve the accuracy and relevance of search results. With SurveyJS, you can implement smart search using a custom composite component. This component allows you to combine multiple built-in question types into a single element and implement custom logic to handle user input. In this demo, the custom component finds industry and occupation codes based on free-form text input from users. Industry and occupation data is provided by the <a href="https://wwwn.cdc.gov/NIOCCS/default.html" target="_blank">NIOCCS Industry and Occupation Coding Web API</a>.

To create a custom smart search component, follow the steps below:

1. Create a custom SurveyJS component.                    
Define a configuration object that implements the [`ICustomQuestionTypeConfiguration`](https://surveyjs.io/form-library/documentation/api-reference/icustomquestiontypeconfiguration) interface. Within this object, specify a unique [`name`](https://surveyjs.io/form-library/documentation/api-reference/icustomquestiontypeconfiguration#name) used to identify your component in code, a user-friendly [`title`](https://surveyjs.io/form-library/documentation/api-reference/icustomquestiontypeconfiguration#title), and a [`defaultQuestionTitle`](https://surveyjs.io/form-library/documentation/api-reference/icustomquestiontypeconfiguration#defaultQuestionTitle) to use for questions created using this component.

1. Configure input fields.          
To specify the input fields that component contains, populate the [`elementsJSON`](https://surveyjs.io/form-library/documentation/api-reference/icustomquestiontypeconfiguration#elementsJSON) array with objects. Add one or more text input fields, which will act as search boxes, and radio button groups, which will be populated with autocomplete suggestions.

1. Update autocomplete suggestions.         
Each time users change the search box value, you need to update the autocomplete suggestions. To configure this behavior, implement the [`onValueChanged`](https://surveyjs.io/form-library/documentation/api-reference/icustomquestiontypeconfiguration#onValueChanged) function.

1. Limit the maximum number of suggestions.        
A custom SurveyJS component can have its own properties that help you implement custom behavior. To add a component property, call the `Serializer.addProperty(className, propMeta)` method. Pass the `name` from step 1 as `className` and an object with property settings as `propMeta`. Call this method within the [`onInit`](https://surveyjs.io/form-library/documentation/api-reference/icustomquestiontypeconfiguration#onInit) function to add the property when the component is initialized. In this demo, we add a `maxSuggestionCount` property that specifies the maximum number of suggestions. 

1. Register the SurveyJS component.         
To make your SurveyJS component recognizable by Form Library and Survey Creator, add its configuration object to the `ComponentCollection`. Refer to the code listing for a full code example.

## Files

### `public/index.html`

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

### `src/survey_json.js`

```js
export const surveyJSON = {
  "title": "Employment and Work Environment Survey",
  "pages": [
    {
      "name": "employmentAndWorkEnvironment",
      "elements": [
        {
          "type": "occupation-industry-autocomplete",
          "name": "industryAndOccupation",
          "maxSuggestionCount": 4
        }
      ]
    }
  ]
}
```

### `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 } from "survey-core";
    import { surveyJSON } from "./survey_json";
    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

    const QUESTION_TYPE_OCCUPATION = "occupation-industry-autocomplete";
    
    // Step 1: Create a custom SurveyJS component
    const smartSearchComponent = {
        name: QUESTION_TYPE_OCCUPATION,
        title: "Industry and Occupation",
        defaultQuestionTitle: "Industry and Occupation",
        // Industry information will be fetched from https://wwwn.cdc.gov/nioccs/IOCode
        industrySuggestedChoices: [],
        // Occupation information will be fetched from https://wwwn.cdc.gov/nioccs/IOCode
        occupationSuggestedChoices: [],
        // Step 2: Configure input fields
        elementsJSON: [
            {
                type: "text",
                name: "userInputIndustry",
                title: "Describe your industry or business (do not enter a company name)",
                description: "For example: community hospital, elementary education, insurance agency, automobile manufacturing, apple orchard.",
                textUpdateMode: "onTyping",
                placeholder: "Enter your industry"
            },
            {
                type: "text",
                name: "userInputOccupation",
                title: "Describe your occupation",
                description: "For example: emergency room nurse, second-grade teacher, office manager, spot welder, fruit grower.",
                textUpdateMode: "onTyping",
                placeholder: "Enter your occupation"
            },
            {
                type: "radiogroup",
                name: "industryCode",
                title: "Please select an industry",
                hideIfChoicesEmpty: true
            },
            {
                type: "radiogroup",
                name: "occupationCode",
                title: "Please select an occupation",
                hideIfChoicesEmpty: true
            },
            {
                type: "text",
                name: "details",
                clearIfInvisible: "none",
                visible: false
            },
        ],
        // Step 3: Update autocomplete suggestions when users change the search box value
        onValueChanged(question, name, newValue) {
            if (name === "userInputOccupation" || name === "userInputIndustry") {
                const { industry, occupation } = this.maybeRefetch(question);
                this.getIndustryCodeQuestion(question).isRequired = !!industry || !!occupation;
                this.getOccupationCodeQuestion(question).isRequired = !!industry || !!occupation;
                this.getIndustryCodeQuestion(question).visible = !!industry && !!occupation;
                this.getOccupationCodeQuestion(question).visible = !!industry && !!occupation;
            }
            if (name === "industryCode" && !!newValue) {
                const code = this.getIndustryCodeQuestion(question).value;
                const selectedIndustry = this.industrySuggestedChoices.find(
                    (industry) => industry.NAICSCode === code
                );
                const answer = {
                    ...this.getDetailsQuestion(question).value,
                    Industry: selectedIndustry,
                };
                this.getDetailsQuestion(question).value = answer;
            }
            if (name === "occupationCode" && !!newValue) {
                const code = this.getOccupationCodeQuestion(question).value;
                const selectedOccupation = this.occupationSuggestedChoices.find(
                    (occupation) => occupation.SOCCode === code
                );
                const answer = {
                    ...this.getDetailsQuestion(question).value,
                    Occupation: selectedOccupation,
                };
                this.getDetailsQuestion(question).value = answer;
            }
            return newValue;
        },
        sendRequest(question, industry, occupation) {
            const baseUrl = "https://wwwn.cdc.gov/nioccs/IOCode";
            const params = {
                c: 2,
                u: 0,
                v: 18,
                n: question.maxSuggestionCount + 1,
                t: "json",
                i: industry,
                o: occupation,
            };
            const queryString = Object.keys(params)
                .map(
                    (key) => encodeURIComponent(key) + "=" + encodeURIComponent(params[key])
                )
                .join("&");
            const url = `${baseUrl}?${queryString}`;
    
            fetch(url)
                .then((response) => response.json())
                .then((data) => {
                    this.industrySuggestedChoices = data.Industry;
                    this.occupationSuggestedChoices = data.Occupation;
                    this.updateIndustryChoices(
                        question,
                        data.Industry.map((item) => ({
                            Code: item.NAICSCode,
                            Title: item.NAICSTitle,
                        }))
                    );
                    this.updateOccupationChoices(
                        question,
                        data.Occupation.map((item) => ({
                            Code: item.SOCCode,
                            Title: item.SOCTitle,
                        }))
                    );
                });
        },
        maybeRefetch(question) {
            const industry = this.getUserInputIndustryQuestion(question).value;
            const occupation = this.getUserInputOccupationQuestion(question).value;
            if (industry && occupation) {
                this.sendRequest(question, industry, occupation);
            }
            return { industry, occupation };
        },
        updateOccupationChoices(question, data) {
            this.updateChoices(this.getOccupationCodeQuestion(question), data, question.maxSuggestionCount);
        },
        updateIndustryChoices(question, data) {
            this.updateChoices(this.getIndustryCodeQuestion(question), data, question.maxSuggestionCount);
        },
        updateChoices(codeQuestion, data, maxChoices) {
            if (codeQuestion) {
                const choices = [];
                const prevValue = codeQuestion.value;
                let hasValue = false;
                data.forEach((item) => {
                    if (item.Title !== "Insufficient Information" && choices.length < maxChoices) {
                        hasValue = hasValue || (prevValue && prevValue === item.Code);
                        choices.push({ value: item.Code, text: item.Title });
                    }
                });
                choices.push({ value: "-", text: "None of the above" });
                codeQuestion.choices = choices;
                if (prevValue && !hasValue) {
                    codeQuestion.clearValue();
                }
            }
        },
        getUserInputIndustryQuestion(question) {
            return this.getQuestionByName(question, "userInputIndustry");
        },
        getUserInputOccupationQuestion(question) {
            return this.getQuestionByName(question, "userInputOccupation");
        },
        getIndustryCodeQuestion(question) {
            return this.getQuestionByName(question, "industryCode");
        },
        getOccupationCodeQuestion(question) {
            return this.getQuestionByName(question, "occupationCode");
        },
        getDetailsQuestion(question) {
            return this.getQuestionByName(question, "details");
        },
        getQuestionByName(question, name) {
            return question.contentPanel.getQuestionByName(name);
        },
        // Step 4: Limit the maximum number of suggestions
        onInit() {
            Serializer.addProperty(QUESTION_TYPE_OCCUPATION, {
                name: "maxSuggestionCount:number",
                default: 3,
                choices: [1, 2, 3, 4, 5, 6, 7],
                category: "general",
                visibleIndex: 2,
            });
        },
        onValueSet(question) {
            this.maybeRefetch(question);
            this.updateChoicesOnSetValue(
                this.getOccupationCodeQuestion(question),
                this.getDetailsQuestion(question).value?.Occupation?.SOCTitle
            );
            this.updateChoicesOnSetValue(
                this.getIndustryCodeQuestion(question),
                this.getDetailsQuestion(question).value?.Industry?.NAICSTitle
            );
        },
        updateChoicesOnSetValue(codeQuestion, titleValue) {
            if (codeQuestion.choices.length === 0 && titleValue) {
                codeQuestion.choices = [{ value: codeQuestion.value, text: titleValue }];
            }
        }
    }
    
    // Step 5: Register the SurveyJS component
    if (!ComponentCollection.Instance.getCustomQuestionByName(QUESTION_TYPE_OCCUPATION)) {
        ComponentCollection.Instance.add(smartSearchComponent)
    }
    const creator = new SurveyCreatorModel();
    creator.JSON = surveyJSON;
    creator.activeTab = "test";
</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/smart-search-input/angular.md)
- [React](https://surveyjs.io/survey-creator/examples/smart-search-input/reactjs.md)
- [jQuery](https://surveyjs.io/survey-creator/examples/smart-search-input/jquery.md)
- [Vanilla JS](https://surveyjs.io/survey-creator/examples/smart-search-input/vanillajs.md)
