DEV Community

SurveyJS for _SurveyJS

Posted on • Edited on

4

Add Survey Creator / Form Builder to Your Angular Application

Last year, the SurveyJS Team has released a major update of the Survey Creator component for all supported platforms, including Angular. In addition to a refined UI and new features, this update brought significant changes in the architecture. Survey Creator for Angular no longer depends on non-Angular JavaScript widgets; it is now a composition of true Angular components. This article shows how to integrate the new Survey Creator into your Angular app.

Survey Creator for Angular

We will implement a simple application that displays a list of surveys stored in a database. Users can create new surveys and edit/delete existing surveys.

Task 1: Add Survey Creator to your Angular app

Step 1: Install Survey Creator

Open CodeSandbox and create a new Angular application. Add the following packages to Dependencies:

  • survey-creator-angular - Survey Creator rendering code for Angular
  • survey-creator-core - Platform-independent Survey Creator code
  • survey-angular-ui - SurveyJS Form Library rendering code for Angular
  • survey-core - Platform-independent SurveyJS Form Library code
  • @angular/cdk - Angular Component Dev Kit

If you want to add Survey Creator to a real-world application, use the following command to install the survey-creator-angular and @angular/cdk packages. Other packages will be installed automatically as peer dependencies:

npm install survey-creator-angular@latest @angular/cd --save
Enter fullscreen mode Exit fullscreen mode

As a result, your package.json should contain the following dependencies:

{
"dependencies": {
"@angular/cdk": "15.0.4",
"survey-angular-ui": "latest",
"survey-core": "latest",
"survey-creator-angular": "latest",
"survey-creator-core": "latest",
}
}
view raw package.json hosted with ❤ by GitHub

Step 2: Configure Styles

Reference Survey Creator and SurveyJS Form Library style sheets in the angular-cli.json file. Refer to the resulting CodeSandbox for a code example.

In a real-world application, open the angular.json file and reference the style sheets in it:

{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"projects": {
"project-name": {
"projectType": "application",
"architect": {
"build": {
"options": {
"styles": [
"src/styles.css",
"node_modules/survey-core/defaultV2.min.css",
"node_modules/survey-creator-core/survey-creator-core.min.css"
]
}
}
}
}
}
}
view raw angular.json hosted with ❤ by GitHub

Step 3: Create a custom component that renders the Survey Creator

We can name the custom component CreatorWidgetComponent. If you use Angular CLI, run the following command to generate the component:

ng generate component creator-widget
Enter fullscreen mode Exit fullscreen mode

In CodeSandbox, you have to create component files manually: creator-widget.component.html and creator-widget.component.ts.

Open the creator-widget.component.ts file and import SurveyCreatorModel from the survey-creator-core package. Instantiate the SurveyCreatorModel and assign the instance to a component prop.

import { Component, OnInit } from "@angular/core";
import { SurveyCreatorModel } from "survey-creator-core";
@Component({
templateUrl: "./creator-widget.component.html",
selector: "survey-creator-widget"
})
export class CreatorWidgetComponent implements OnInit {
creator!: SurveyCreatorModel;
ngOnInit() {
this.creator = new SurveyCreatorModel({ showLogicTab: false });
}
}

Open the creator-widget.component.html file and pass the SurveyCreatorModel instance to the model attribute of the <survey-creator> element.

<survey-creator [model]="creator"></survey-creator>

Step 4: Render the custom component

Before you render Survey Creator, you need to import the module that integrates it with Angular. Open your NgModule class (usually resides in the app.module.ts file), import the SurveyCreatorModule from survey-creator-angular, and list it in the imports array. In addition, import our custom component and add it to the declarations array.

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { SurveyCreatorModule } from "survey-creator-angular";
import { AppComponent } from "./app.component";
import { CreatorWidgetComponent } from "./components/creator-widget.component";
@NgModule({
declarations: [AppComponent, CreatorWidgetComponent],
imports: [BrowserModule, SurveyCreatorModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
view raw app.module.ts hosted with ❤ by GitHub

To render the custom component that contains Survey Creator, add the <survey-creator-widget> element to app.component.html.

<survey-creator-widget></survey-creator-widget>

If you do everything right, the Survey Creator will be shown in the CodeSandbox preview:

Survey Creator for Angular

Task 2: Show a survey list that supports CRUD operations and set up Angular routing

This task bears no relation to SurveyJS functionality. We need to get the list of surveys from a database, allow users to create a new survey, and change the name and JSON definition of an existing survey. Unfortunately, in real-world apps, you have to repeat these steps for every application. If you are familiar with them, you can skip this section and go directly to Task 3.

Step 1: Implement asynchronous functions that work with the database

To simplify the code and let you modify data locally in your browser, we will use the browser’s local storage and emulate asynchronous calls using the setTimeout function. We will put all our data-related functions into the WebDataService.ts file. In the code below, this file contains only function signatures for brevity. Refer to the resulting CodeSandbox for a full code listing.

// Get the survey list. Each object contains the following properties: `id`, `name`, and `json`
export function getSurveyItems(onCallback);
// Create a new survey and return it
export function createSurvey(onCallback);
// Delete a survey by `id` and return the updated survey list
export function deleteSurvey(id, onCallback);
// Get a survey JSON definition by survey `id`
export function getSurveyJSON(id, onCallback);
// Get a survey name by survey `id`
export function getSurveyName(id, onCallback);
// Set a survey JSON definition by survey `id`
export function saveSurveyJSON(id, json, onCallback);
// Set a survey name by survey `id`
export function saveSurveyName(id, name, onCallback);

Step 2: Render the survey list

Create a SurveyListComponent that will render the list of surveys, allow users to add a new survey and edit/delete existing surveys. It will navigate to the following path for editing surveys: /editsurvey?id=surveyId.

Edit Surveys

<div class="survey-list">
<ul *ngFor="let item of items">
<li key={item.id}>
<a [routerLink]="['/editsurvey']" [queryParams]="{ id: item.id }">{{item.name}}</a>
<a class="edit-link" [routerLink]="['editsurvey']" [queryParams]="{ id: item.id }">
<button type="button" class="btn btn--primary">Edit</button>
</a>
<button type="button" class="btn btn--danger" (click)="removeSurvey(item.id)">Remove</button>
</li>
</ul>
<button class="add-btn" type="button" (click)="addNewSurvey()">Add New Survey</button>
</div>
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import { createSurvey, deleteSurvey, getSurveyItems } from "../WebDataService";
interface SurveyListItem {
name: string;
id: number;
}
@Component({
templateUrl: "./survey-list.component.html",
selector: "survey-list"
})
export class SurveyListComponent {
constructor(private router: Router) {}
public items: Array<SurveyListItem> = []
public addNewSurvey() {
createSurvey((newItem) => {
this.router.navigate(["/editsurvey"], { queryParams: { id: newItem.id.toString() }});
});
}
public removeSurvey(id: number) {
deleteSurvey(id, (currentItems) => {
this.items = currentItems;
});
}
ngOnInit() {
getSurveyItems((currentItems) => {
this.items = currentItems;
});
}
}

Step 3: Set up Angular routing

Add the app-routing.module.ts file to your project and specify routes within it.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CreatorWidgetComponent } from './components/creator-widget.component';
import { SurveyListComponent } from './components/survey-list.component';
const routes: Routes = [
{ path: "", component: SurveyListComponent },
{ path: "editsurvey", component: CreatorWidgetComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

Task 3: Load and save survey JSON definitions

We need a survey ID to load and save the survey JSON definition. We can get the ID from the id query parameter. All that’s left to do is to call the getSurveyJSON and saveSurveyJSON functions from the WebDataService.ts file.

Note that Survey Creator has an isAutoSave property. If you enable it, the saveSurveyFunc callback is called on every change. The callback has two parameters: a numeric saveNo and a callback function. saveNo is an incremental number. Since web services are asynchronous by their nature, older changes may come after more recent changes. It means that if you saved the change #152 on the server, you can simply ignore changes #151 and below. After getting a confirmation from the server, you can use the callback parameter and call it as callback(saveNo, true) in case of a success or callback(saveNo, false) in case server could not save the data for some reason. In both cases, Survey Creator will show notifications.

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Serializer } from "survey-core";
import { SurveyCreatorModel } from "survey-creator-core";
import { getSurveyJSON, saveSurveyJSON } from "../WebDataService";
@Component({
templateUrl: "./creator-widget.component.html",
selector: "creator-widget"
})
export class CreatorWidgetComponent implements OnInit {
creator!: SurveyCreatorModel;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
const id: number = Number.parseInt(this.route.snapshot.queryParams["id"]);
this.creator = new SurveyCreatorModel({ showLogicTab: false });
// Enable auto save
this.creator.isAutoSave = true;
// A function executed to save the survey definition
this.creator.saveSurveyFunc = (saveNo: number, callback: (saveNo: number, arg: boolean) => void) => {
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
saveSurveyJSON(id, this.creator.JSON, () => {
callback(saveNo, true);
});
};
// Load the survey definition
getSurveyJSON(id, (json: any) => {
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
this.creator.JSON = json;
});
}
}

Task 4: Change the survey name

You can implement different UIs to allow users to change the survey name. For example, users can edit the name in a survey list. The second option is to display a text input for editing the survey name below the Survey Creator. Another solution is to add a survey property that users can modify in the Survey Creator’s Property Grid (see the example).

In this article, we take the survey name from the survey title and save a record about it in the database. The record has three fields: id, name, and json.

Survey Title

Step 1: Set the survey title

You can do it in code as follows: creator.survey.title = "yourValue";. Do it within the onInit hook after loading the survey JSON definition.

Step 2: Update the survey name in the database

Set the name field in the database record when the survey title property is changed. You can use the creator.onModified event for this purpose.

Step 3: Make the survey title property required

You should prevent end users from emptying the survey title because the survey name in the database cannot be empty. There are several ways of doing it, but the easiest one is to find the needed property and set its isRequired attribute to true.

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Serializer } from "survey-core";
import { SurveyCreatorModel } from "survey-creator-core";
import {
getSurveyJSON,
getSurveyName,
saveSurveyJSON,
saveSurveyName
} from "../WebDataService";
// Make survey title required. End users cannot make it empty in designer or property grid
Serializer.findProperty("survey", "title").isRequired = true;
@Component({
templateUrl: "./creator-widget.component.html",
selector: "creator-widget"
})
export class CreatorWidgetComponent implements OnInit {
creator!: SurveyCreatorModel;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
const id: number = Number.parseInt(this.route.snapshot.queryParams["id"]);
// Create a Survey Creator instance
this.creator = new SurveyCreatorModel({ showLogicTab: false });
// Enable auto save
this.creator.isAutoSave = true;
// A function executed to save the survey definition
this.creator.saveSurveyFunc = (
saveNo: number,
callback: (saveNo: number, arg: boolean) => void
) => {
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
saveSurveyJSON(id, this.creator.JSON, () => {
callback(saveNo, true);
});
};
this.creator.onModified.add((_, options) => {
// We are interested in property changes only
if (options.type === "PROPERTY_CHANGED") {
// Update the survey name in the database when the survey title is changed
if (
options.name === "title" &&
!!options.target &&
options.target.getType() === "survey"
) {
saveSurveyName(id, options.newValue);
}
}
});
// Load survey definition
getSurveyJSON(id, (json: any) => {
// Save the survey title to prevent it from being overwritten
const prevTitle = this.creator.survey.title;
// You can use `this.creator.text` as an alternative to `this.creator.JSON`
this.creator.JSON = json;
if (!!prevTitle) {
this.creator.survey.title = prevTitle;
}
});
getSurveyName(id, (name: string) => {
this.creator.survey.title = name;
});
}
}

Conclusion

You have learnt how to add our Survey Creator component into your Angular application and save survey JSON definitions in a database. It is not a complete survey service. Missing capabilities include showing surveys to end users, gathering survey responses, and presenting them in a table or dashboard. Leave a comment below if you want us to continue implementing the service in our future articles.

About SurveyJS Project

SurveyJS Project includes four open-source JavaScript Libraries:

  • Form Library — A free and open-source MIT-licensed JavaScript library that lets you design dynamic, data-driven, multi-language survey forms and run them in your web application using a variety of front-end technologies. Available for free under the MIT license.

  • Survey Creator — A GUI-based no-code survey builder that allows easy drag-and-drop form creation even for non-tech savvy users. Requires a commercial developer license.

  • Dashboard — Simplifies survey data analysis with interactive and customizable charts and tables. Visualize your insights with the survey data dashboard and analyze survey results in one view. Requires a commercial developer license.

  • PDF Generator — Allows you to save an unlimited number of custom-built survey forms to PDF (both new and filled-in), and generate fillable PDF forms to automate your form workflow and go paperless. Requires a commercial developer license.
    To learn more about SurveyJS Project, visit our website: surveyjs.io.

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay