Skip to content

Commit a7cde0f

Browse files
Merge pull request #1 from surveyjs/readme
Update README and code comments
2 parents 61a5af2 + 309154c commit a7cde0f

File tree

7 files changed

+143
-110
lines changed

7 files changed

+143
-110
lines changed

DomainModelsViews/JsonFormGenerator.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ public JSONGeneratorByModelClass(Type type)
1717
{
1818
this.type = type;
1919
}
20+
// Generates a form JSON schema based upon a domain model type.
2021
public dynamic Generate()
2122
{
23+
// Get domain model properties and their metadata
2224
List<PropertyInfo> properties = new List<PropertyInfo>();
2325
this.fillProperties(properties, this.type);
2426
if (properties.Count == 0) return new { };
27+
// Arrays of form pages and elements (questions and panels)
2528
var pages = new List<dynamic>();
2629
var elements = new List<dynamic>();
2730
CustomAttributeData curPageAttr = getFormPageAttribute(properties[0]);
@@ -31,19 +34,23 @@ public dynamic Generate()
3134
if (i > 0)
3235
{
3336
CustomAttributeData pageAttr = getFormPageAttribute(prop);
37+
// If `pageAttr` has a value, it means that the previous page has run out of form elements,
38+
// and we should add it to the `pages` array
3439
if (pageAttr != null)
3540
{
3641
pages.Add(new { title = this.getAttributeValueByProp(curPageAttr, "Title"), elements = elements });
3742
elements = new List<dynamic>();
3843
curPageAttr = pageAttr;
3944
}
4045
}
46+
// Create a form element for the current domain model property
4147
var element = this.createElementByProp(prop);
4248
if(element != null)
4349
{
4450
elements.Add(element);
4551
}
4652
}
53+
// If the `elements` array is not empty, create a form page for its elements. This page will be the last.
4754
if (elements.Count > 0)
4855
{
4956
pages.Add(new { title = this.getAttributeValueByProp(curPageAttr, "Title"), elements = elements });
@@ -61,6 +68,8 @@ private void fillProperties(List<PropertyInfo> properties, Type type)
6168
}
6269
properties.AddRange(type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly));
6370
}
71+
// Creates a form element based upon the metadata of a domain model property.
72+
// You can expand this method to process more attributes.
6473
private dynamic createElementByProp(PropertyInfo prop)
6574
{
6675
var el = new ExpandoObject();
@@ -93,6 +102,7 @@ private dynamic createElementByProp(PropertyInfo prop)
93102
}
94103
return el;
95104
}
105+
// Sets a form element type based upon the type of the passed domain model property.
96106
private void setElementAttrByPropType(ExpandoObject el, PropertyInfo prop)
97107
{
98108
if (this.isGenericListType(prop.PropertyType))
@@ -117,6 +127,7 @@ private void setElementAttrByPropType(ExpandoObject el, PropertyInfo prop)
117127
el.TryAdd("inputType", "number");
118128
}
119129
}
130+
// Sets the type of cells in a Matrix Dynamic question based upon the type of the passed domain model property.
120131
private void setListElementAttrByPropType(ExpandoObject el, PropertyInfo prop)
121132
{
122133
el.TryAdd("type", "matrixdynamic");
@@ -142,6 +153,8 @@ private bool isGenericListType(Type type)
142153
{
143154
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
144155
}
156+
157+
// Sets form element properties based upon `FormFieldAttribute` properties.
145158
private void setElementAttrByFormFieldAttr(ExpandoObject el, CustomAttributeData attr)
146159
{
147160
var choicesByUrl = getAttributeValueByProp(attr, "ChoicesByUrl");

DomainModelsViews/JsonForms.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ public JSONForm(string formName)
2222
public dynamic GetFormJSON()
2323
{
2424
if(!this.IsValid) return null;
25+
// Get a form JSON schema of the current `FormType` from the database
2526
string json = DataStorage.GetForm(this.FormType);
27+
// If the JSON schema is not found, load a pre-generated JSON schema
28+
// from a file in the Data directory
2629
if (string.IsNullOrEmpty(json)) {
2730
json = this.getJsonFromData();
2831
}

README.md

Lines changed: 68 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,68 @@
1-
# SurveyJS as front-end form library for ASP.Net Core app
2-
3-
The examples show how to use SurveyJS Library to edit Domain Models defined as C# classes.
4-
You can edit JSON Form Definition in our SurveyJS Creator and your end-user will see
5-
a different form for editing/filling forms without an application re-building/re-deploying.
6-
7-
There are 3 Domain Classes in this example:
8-
* [JobApplication](/DomainModels/JobApplication.cs)
9-
* [NPSSurvey](/DomainModels/NPSSurvey.cs)
10-
* [PatientAssessment](/DomainModels/PatientAssessment.cs)
11-
12-
## Mock database
13-
To make an example simple, the current implementation doesn't work with a database.
14-
DomainModel objects are stored in the memory by using a singleton [DataStorage](/DomainModels/DataStorage.cs) class.
15-
This singleton stores and retrieve data by class type and object Id (Unique string)
16-
and serialize/deserialize objects by using MS .Net JsonSerializer Serialize/Deserialize functions.
17-
You can change our [DataStorage](/DomainModels/DataStorage.cs) class to write the code that will work with any database of your choice.
18-
As an alternative, you can use an ORM library.
19-
20-
## Form List. Register a new form / DomainModel.
21-
To register a [DomainModelFormAttribute](/Code/FormAttributes.cs) class attribute is added.
22-
It has two parameters: "Name" and "Title".
23-
Here is the example of registering a new form:
24-
````csharp
25-
[DomainModelForm("nps-survey", "NPS Survey (Domain Model without attributes)")]
26-
public class NPSSurvey: DomainModel {
27-
...
28-
}
29-
````
30-
The [DomainModelList](/DomainModels/DomainModelList.cs) can return the list of all registered DomainModel classes (forms)
31-
by calling static function `DomainModelList.GetAllForms();`. It get all classes in the running assembly
32-
and check all classes inherited from "DomainModel" with "DomainModelForm" attribute.
33-
You can get a DomainModel type by form name by calling a static function `DomainModelList.GetTypeByFormName(string formName);`.
34-
35-
## Form Response View to fill out forms
36-
[Form Response View](/Views/Home/FormResponse.cshtml) shows SurveyJS runner to fill out a form.
37-
It uses SurveyJS Library for knockout to render the form. We use it instead of React or Angular or Vue versions, because of simplicity.
38-
The page contains minimim code related to JavaScript framework implementation.
39-
The survey runner shows on loading JSON form definition and survey data, by calling [loadFormAndData](/wwwroot/js/form_api.js) function.
40-
The form (survey) model is setup in [setupSurveyModel](/wwwroot/js/surveyjs.js) function.
41-
Inside this function, on submitting form the [saveFormData](wwwroot/js/form_api.js) function is calling.
42-
43-
## Getting JSON form definition
44-
There are two API server functions [loadform and loadform_and_data](/Controllers/FormController.cs).
45-
They return the JSON form definition by form name and the second function returns a previous entered form response by Id.
46-
The previous entered response data is getting by using our [DataStorage](/DomainModels/DataStorage.cs).
47-
JSON form definition is returning by the following rule. If there is a JSON with the same form name in "Data" directory then the context of this file is returned.
48-
If there is no such file in the directory, then the JSON form definition is generated on fly by DomainModel class corresponded with this form name.
49-
50-
## Generating JSON by DomainModel
51-
By default the JSON form definition is generated based on DomainModel.
52-
[JsonFormGenerator](/DomainModelsViews/JsonFormGenerator.cs) class generates JSON by using .Net reflection API.
53-
Every public writable property in DomainModel is corresponded with a question in Form (Survey) JSON.
54-
Generator class uses property type and property attributes to generate the correct JSON.
55-
You can use .Net standard "Required" and "DisplayAttributes" as well as custom [FormPage and FormField](/Code/FormAttributes.cs) attributes.
56-
You can improve our current JSON generator class by adding new custom attributes or by adding more properties into our attributes.
57-
58-
## Editing generating and existing Form JSONs
59-
You can edit default JSON Form Definition on [EditForm](/Views/Home/EditForm.cshtml) page.
60-
It loads form definition located in "Data" directory or generated by [JsonFormGenerator](/DomainModelsViews/JsonFormGenerator.cs) class
61-
and store changes in [DataStorage](/DomainModels/DataStorage.cs). After changing the JSON Form Definition, Form Response page will use new updated JSON for form editing.
62-
There are two modes for editing JSON Form Definition: admin and content manager.
63-
The first one has no limitation. Admin can add/delete any question/columns and so on.
64-
The mode for content manager doesn't allow to add/delete remove question or column or change question/column type or change their names.
65-
It means that content manager is not able to make form definition incorrect. It can change question title or change question location,
66-
but every question will be corresponded with a correct DomainModel property.
1+
# Generate Forms from Domain Models with SurveyJS
2+
3+
This example demonstrates how to generate forms in JSON format based on strongly-typed domain models and vice versa. Generated forms can be displayed by SurveyJS Form Library and edited in Survey Creator. This solution will be beneficial for content and product managers who regularly create forms and for backend developers who implement domain models based on these forms.
4+
5+
<!-- TODO: Add illustration -->
6+
7+
This application was built using ASP.NET Core. Follow the same instructions if you need to implement this functionality with any other server-side framework.
8+
9+
- [Domain Models and Attributes](#domain-models-and-attributes)
10+
- [Generate Form JSON Schemas from Server-Side Domain Models](#generate-form-json-schemas-from-server-side-domain-models)
11+
- [Display a Form](#display-a-form)
12+
- [Edit JSON Schemas](#edit-json-schemas)
13+
- [Generate Domain Model Code Based on Form JSON Schemas](#generate-domain-model-code-based-on-form-json-schemas)
14+
15+
## Domain Models and Attributes
16+
17+
Domain models are declared in server-side code. You can generate form JSON schemas based on them. Domain model properties become form fields. You can then feed the JSON schemas into SurveyJS Form Library to display a form on your website or in your application. This project contains three sample domain models; each describes an individual form:
18+
19+
- [`JobApplication`](/DomainModels/JobApplication.cs)
20+
- [`NPSSurvey`](/DomainModels/NPSSurvey.cs)
21+
- [`PatientAssessment`](/DomainModels/PatientAssessment.cs)
22+
23+
A domain model and its properties can include [attributes](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/). A form generator uses attribute parameters to generate form JSON schemas. For instance, all domain models have the `DomainModelForm` attribute shown below. Its parameters configure a form's `name` and `title` properties.
24+
25+
```csharp
26+
[DomainModelForm("nps-survey", "NPS Survey (Domain Model without attributes)")]
27+
public class NPSSurvey: DomainModel {
28+
// ...
29+
}
30+
```
31+
32+
A form generator can support predefined .NET attributes, such as [`Required`](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.requiredattribute) or [`Display`](https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.displayattribute), as well as custom attributes, like `DomainModelForm`. The following file shows how this project implements custom attributes: [FormAttributes.cs](/Code/FormAttributes.cs).
33+
34+
You can get a list of all domain models in the running assembly. Call the `DomainModelList.GetAllForms()` static method. It finds all classes that are inherited from `DomainModel` and have the `DomainModelForm` attribute. Refer to the following file for full code: [DomainModelList.cs](/DomainModels/DomainModelList.cs).
35+
36+
## Generate Form JSON Schemas from Server-Side Domain Models
37+
38+
Form JSON schemas are generated based on property types and attributes. Each public writable property in a `DomainModel` class becomes a form field in the form JSON schema. Refer to the [`JSONGeneratorByModelClass`](/DomainModelsViews/JsonFormGenerator.cs#L13) class to view the form generator code. The class implements a `Generate()` method that extracts all properties from a domain model, gets their type and attributes, and uses this information to build a JSON schema. You can extend the form generator's functionality: implement new custom attributes or add more properties to the existing attributes.
39+
40+
## Display a Form
41+
42+
SurveyJS ships with a tool that displays surveys and forms&mdash;[SurveyJS Form Library](https://surveyjs.io/form-library). You no longer need to create a separate page for each form you have in your application. Set up SurveyJS Form Library and assign different JSON schemas to it to display different forms. This tool supports all most popular JavaScript frameworks, including React, Angular, and Vue. However, this project uses the Form Library version for Knockout because it is the easiest version to set up. The [FormResponse.cshtml](/Views/Home/FormResponse.cshtml) file shows the Form Library configuration code.
43+
44+
In this project, SurveyJS Form Library displays JSON schemas that come from different sources. The "NPS Survey" and "Patient Assessment" forms are pre-generated and stored as JSON files in the [Data](/Data/) directory. The "Job Application" form is generated from the `JobApplication` domain model on the fly. If a form JSON schema has been edited, its most recent version is stored in a database (or [database emulator](/DomainModels/DataStorage.cs), as in this application). When Form Library requests a JSON schema of a certain type, the server first searches for the most recently edited schema of that type in the database. If the schema is not found, the server returns a pre-generated schema from one of the JSON files. If a file with a schema of that type is also absent, the server generates the schema on the fly. Refer to the following file to find methods that implement this logic: [JsonForms.cs](/DomainModelsViews/JsonForms.cs).
45+
46+
<!-- TODO: Add illustration -->
47+
48+
## Edit JSON Schemas
49+
50+
JSON schemas can be edited in any text editor, but they are easier to edit in [Survey Creator](https://surveyjs.io/survey-creator). This JavaScript component is a UI form designer by SurveyJS that content and product managers can use to create and modify JSON schemas without writing code. Similarly to SurveyJS Form Library, Survey Creator supports React, Angular, Vue, Knockout, and jQuery and is easy to integrate into your application. The project in this repository sets up the Knockout version (view the [EditForm.cshtml](/Views/Home/EditForm.cshtml) file).
51+
52+
If in your team, more than one person creates and edits forms, you can configure multiple roles with different access rights. For example, you may define two roles: content manager and product manager. Content managers cannot create new forms and can edit only those form properties that do not require server-side code modification. These restrictions ensure the synchronization between domain models and form JSON schemas. Product managers, on the other hand, have unlimited capabilities and can request the backend development team to update domain models on the server. The role separation is implemented at the Survey Creator level. Refer to the [`creatorjs.js`](/wwwroot/js/creatorjs.js) file to see the implementation. You can also view the restricted UI for content managers in the following demo: [Setup for Content Managers](https://surveyjs.io/survey-creator/examples/setup-for-content-manager/).
53+
54+
To make edited schemas available to the users, you do not need to wait until the backend development team rebuilds and redeploys the entire application. Edited schemas are saved in a database, and you can implement the functionality to prioritize them when SurveyJS Form Library loads a schema for display (see the [Display a Form](#display-a-form) section above). As a result, users can use an edited form immediately after the content or product manager modifies it. If the schema modification requires an update of domain models to maintain the model&ndash;schema synchronization, the backend development team may perform this update and rebuild and redeploy the application according to their own schedule.
55+
56+
## Generate Domain Model Code Based on Form JSON Schemas
57+
58+
Previously, you saw how to [implement a form generator](#generate-form-json-schemas-from-server-side-domain-models) that creates JSON schemas for client-side forms based on server-side domain models. You can also implement code that does the opposite&mdash;generates domain model code based on JSON schemas. This capability saves time for backend developers who implement domain models based on JSON schemas created by product managers.
59+
60+
To implement a domain model generator, you need to parse the JSON schema being edited and convert the form fields to model properties. Users can view the generated domain model code under the Domain Model Code tab when they edit a form in Survey Creator as a product manager. View the following demo to see this functionality in action: [Create Domain Models](https://surveyjs.io/survey-creator/examples/create-domain-models/). Refer to the following file for full code: [codegenerator.js](/wwwroot/js/codegenerator.js).
61+
62+
## Useful Links
63+
64+
- [SurveyJS Website](https://surveyjs.io/)
65+
- [Form Library Documentation](https://surveyjs.io/form-library/documentation/overview)
66+
- [Form Library Demos](https://surveyjs.io/form-library/examples/nps-question/)
67+
- [Survey Creator Documentation](https://surveyjs.io/survey-creator/documentation/overview)
68+
- [Survey Creator Demos](https://surveyjs.io/survey-creator/examples/free-nps-survey-template/)

Views/Home/Index.cshtml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@
44
</p>
55
<p>
66
Fill forms:
7-
@for (var i = 0; i < Model.FormList.Count; i++)
8-
{
9-
var form = Model.FormList[i];
10-
<div>@form.Title</div>
11-
<ul>
12-
<li><a href="/Home/[email protected]&id=new">Fill New Form</a></li>
13-
@foreach (var obj in Model.GetObjectsByType(form.Name))
14-
{
15-
<li><a href="/Home/[email protected]&[email protected]">Edit: @obj.ToString()</a></li>
16-
}
17-
</ul>
18-
}
19-
</ul>
7+
@for (var i = 0; i < Model.FormList.Count; i++)
8+
{
9+
var form = Model.FormList[i];
10+
<div>@form.Title</div>
11+
<ul>
12+
<li><a href="/Home/[email protected]&id=new">Fill New Form</a></li>
13+
@foreach (var obj in Model.GetObjectsByType(form.Name))
14+
{
15+
<li><a href="/Home/[email protected]&[email protected]">Edit: @obj.ToString()</a></li>
16+
}
17+
</ul>
18+
}
2019
</p>
2120
<p>
2221
Edit form presentation (as a content manager):

0 commit comments

Comments
 (0)