---
title: Limit Question Instances and Panel Nesting
product: Survey Creator
description: Survey Creator allows you to have complete control over the types of questions users can add to a form and how many questions of each type a user can include within a single form. Survey elements that support nesting, such as Panels and Dynamic Panels, can be configured to either prevent users from adding certain question types or limit how many times they can be added. This JavaScript form builder demo illustrates how to restrict nesting options and control the use of specific question types.
framework: Angular
source: https://surveyjs.io/survey-creator/examples/limit-number-of-survey-elements/angular
index: https://surveyjs.io/survey-creator/examples/overview.md
---

# Limit Question Instances and Panel Nesting (Angular)

When dealing with complex forms, you may wish to restrict the number of elements authors can insert, nesting depth for panels, and acceptable form structures. This example illustrates how to enforce such design constraints in Survey Creator: for example, allow just one Signature question per survey and restrict nesting of panels and matrices.

## Limit a Question Type to a Single Instance

To restrict survey authors from adding more than one instance of a specific question type, handle the [`onAllowAddElement`](https://surveyjs.io/survey-creator/documentation/api-reference/survey-creator#onAllowAddElement) event.

The following code ensures that only one Signature field can be added:

```js
const SIGNATURE_QUESTION_TYPE = "signaturepad";

function hasSignatureQuestion(surveyModel) {
  const questions = surveyModel.getAllQuestions(true, true, true);
  return questions.some(q => q.getType() === SIGNATURE_QUESTION_TYPE);
}

creator.onAllowAddElement.add((_, options) => {
  if (options.name === SIGNATURE_QUESTION_TYPE) {
    // Allow adding only if no Signature question exists
    options.allow = !hasSignatureQuestion(creator.survey);
  }
});
```

## Restrict Nested Panels and Question Types

To maintain a clear and manageable survey structure, you can limit how deeply panels can nest and which element types can appear inside them. Use the following configuration options:

- [`maxPanelNestingLevel`](https://surveyjs.io/survey-creator/documentation/api-reference/survey-creator#maxPanelNestingLevel): `number`\
Sets the maximum depth for nested Panels and Dynamic Panels.

- [`forbiddenNestedElements`](https://surveyjs.io/survey-creator/documentation/api-reference/survey-creator#forbiddenNestedElements): `{ panel: string[]; paneldynamic: string[]; }`\
Specifies which element types cannot appear inside Panels and Dynamic Panels.

The example below applies these restrictions:

- Panels cannot contain Dynamic Panels.
- Dynamic Panels cannot contain Panels, other Dynamic Panels, Multi-Select Matrices, or Dynamic Matrices.
- Panels and Dynamic Panels are limited to two levels of nesting.

```js
const creatorOptions = {
  maxPanelNestingLevel: 2,
  forbiddenNestedElements: {
    panel: ["paneldynamic"],
    paneldynamic: ["panel", "paneldynamic", "matrixdropdown", "matrixdynamic"]
  }
}
const creator = new SurveyCreator.SurveyCreator(creatorOptions);

// In modular applications:
import { SurveyCreatorModel } from "survey-creator-core";
const creator = new SurveyCreatorModel(creatorOptions);
```

## 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 = {
  "pages": [
    {
      "name": "page1",
      "elements": [
        {
          "type": "panel",
          "name": "panel",
          "title": "Panel",
          "description": "Allows up to two levels of nesting and cannot contain Dynamic Panels."
        },
        {
          "type": "paneldynamic",
          "name": "dynamic-panel",
          "title": "Dynamic Panel",
          "description": "Allows up to two levels of nesting and cannot contain Panels, other Dynamic Panels, Multi-Select Matrices, or Dynamic Matrices."
        },
        {
          "type": "signaturepad",
          "name": "signature",
          "title": "Signature",
          "description": "Only one Signature field per survey is allowed."
        }
      ]
    }
  ]
};
```

### `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 { 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

@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({
            maxPanelNestingLevel: 2,
            forbiddenNestedElements: {
                panel: ["paneldynamic"],
                paneldynamic: ["panel", "paneldynamic", "matrixdropdown", "matrixdynamic"]
            }
        });
        const SIGNATURE_QUESTION_TYPE = "signaturepad";
        
        function hasSignatureQuestion(surveyModel) {
            const questions = surveyModel.getAllQuestions(true, true, true);
            return questions.some(q => q.getType() === SIGNATURE_QUESTION_TYPE);
        }
        
        creator.onAllowAddElement.add((_, options) => {
            if (options.name === SIGNATURE_QUESTION_TYPE) {
                // Allow adding only if no other Signature question exists
                options.allow = !hasSignatureQuestion(creator.survey);
            }
        });
        
        creator.JSON = surveyJSON;
        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/limit-number-of-survey-elements/reactjs.md)
- [Vue 3](https://surveyjs.io/survey-creator/examples/limit-number-of-survey-elements/vue3js.md)
- [jQuery](https://surveyjs.io/survey-creator/examples/limit-number-of-survey-elements/jquery.md)
- [Vanilla JS](https://surveyjs.io/survey-creator/examples/limit-number-of-survey-elements/vanillajs.md)
