---
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: Angular
source: https://surveyjs.io/survey-creator/examples/smart-search-input/angular
index: https://surveyjs.io/survey-creator/examples/overview.md
---

# Smart Search Input (Angular)

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

### `src/app/components/creator.component.css`

```css
/* You can define custom CSS rules here */
```

### `src/app/components/creator.component.html`

```html
<div style="position: fixed; top: 0; bottom: 0; right: 0; left: 0;">
    <survey-creator [model]="model"></survey-creator>
</div>
```

### `src/app/components/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/components/creator.component.ts`

```ts
import { Component, OnInit } from "@angular/core";
import { SurveyCreatorModel } from "survey-creator-core";
import "survey-core/survey.i18n";
import "survey-creator-core/survey-creator-core.i18n";
import { ComponentCollection, Serializer } from "survey-core";
import { surveyJSON } from "./survey_json";
import "survey-core/survey-core.css";
import "survey-creator-core/survey-creator-core.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)
}
@Component({
    // tslint:disable-next-line:component-selector
    selector: "component-survey-creator",
    templateUrl: "./creator.component.html",
    styleUrls: ["./creator.component.css"]
})
export class SurveyCreatorComponent implements OnInit {
    model: SurveyCreatorModel;
    ngOnInit() {
        const creator = new SurveyCreatorModel();
        creator.JSON = surveyJSON;
        creator.activeTab = "test";
        this.model = creator;
    }
}
```

### `src/app/app.component.html`

```html
<component-survey-creator></component-survey-creator>
```

### `src/app/app.component.ts`

```ts
import { Component } from "@angular/core";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html"
})
export class AppComponent {
    title = "CodeSandbox";
}
```

### `src/app/app.module.ts`

```ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppComponent } from "./app.component";
import { SurveyCreatorModule } from "survey-creator-angular";
import { SurveyCreatorComponent } from "./components/creator.component";

@NgModule({
    declarations: [AppComponent, SurveyCreatorComponent],
    imports: [BrowserModule, SurveyCreatorModule],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }
```

### `src/environments/environment.prod.ts`

```ts
export const environment = {
    production: true
};
```

### `src/environments/environment.ts`

```ts
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.

export const environment = {
    production: false
};
```

### `src/index.html`

```html
<app-root></app-root>
```

### `src/main.ts`

```ts
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch(err => console.log(err));
```

### `src/polyfills.ts`

```ts
/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install classlist.js`.

/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';

/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import "core-js/proposals/reflect-metadata";

/**
 * Required to support Web Animations `@angular/platform-browser/animations`.
 * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
 **/
// import 'web-animations-js';  // Run `npm install web-animations-js`.

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import "zone.js/dist/zone"; // Included with Angular CLI.

/***************************************************************************************************
 * APPLICATION IMPORTS
 */
```

### `src/styles.css`

```css
/* You can add global styles to this file and import other style files */
```

### `src/typings.d.ts`

```ts
/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
    id: string;
}
```

### `.angular-cli.json`

```json
{
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [ "assets", "favicon.ico" ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "prefix": "app",
      "styles": [ "styles.css"  ],
      "scripts": [  ],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ]
}
```

### `_tsconfig.json`

```json
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "resolveJsonModule": true,
    "target": "es2015",
    "module": "es2020",
    "lib": [
      "es2018",
      "dom"
    ]
  }
}
```

### `package.json`

```json
{
  "name": "surveyjs-angular",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --prod",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "14.1.1",
    "@angular/cdk": "14.1.1",
    "@angular/common": "14.1.1",
    "@angular/compiler": "14.1.1",
    "@angular/core": "14.1.1",
    "@angular/forms": "14.1.1",
    "@angular/platform-browser": "14.1.1",
    "@angular/platform-browser-dynamic": "14.1.1",
    "@angular/router": "14.1.1",
    "core-js": "3.6.4",
    "rxjs": "6.5.4",
    "survey-angular-ui": "latest",
    "survey-creator-core": "latest",
    "survey-core": "latest",
    "survey-creator-angular": "latest",
    "tslib": "1.13.0",
    "zone.js": "0.11.7"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~13.0.0",
    "@angular/cli": "~13.0.0",
    "@types/jasmine": "3.6.3",
    "@types/jasminewd2": "2.0.8",
    "@types/node": "14.14.28",
    "codelyzer": "6.0.1",
    "jasmine-core": "3.6.0",
    "jasmine-spec-reporter": "6.0.0",
    "karma": "6.1.1",
    "karma-chrome-launcher": "3.1.0",
    "karma-coverage-istanbul-reporter": "3.0.3",
    "karma-jasmine": "4.0.1",
    "karma-jasmine-html-reporter": "1.5.4",
    "protractor": "7.0.0",
    "ts-node": "9.1.1",
    "tslint": "~6.1.3",
    "typescript": "4.1.5"
  },
  "keywords": [ "angular", "surveyjs" ],
  "description": "SurveyJS-Angular example project"
}
```

## Other Frameworks

- [React](https://surveyjs.io/survey-creator/examples/smart-search-input/reactjs.md)
- [Vue 3](https://surveyjs.io/survey-creator/examples/smart-search-input/vue3js.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)
