survey-library
survey-library copied to clipboard
Implementation of SurveyJS for developing own Field app - lessons learned?
Dear community,
For developing our own field application, we are planning to use Survey JS as an extern module to create our own checklists, forms, instructions, etc. We have done a market analysis and the Survey JS product seems to meet our needs.
But before we proceed further, we would be curious to hear the experience of other customers who are using Survey JS as an open-source library to develop their own forms as part of an integrated field application. By forms, we mean digital instructions as well as checklist and report templates. We would like to hear about your paint points, watch-out and recommendations of implementing SurveyJS within a custom field application so that we can benefit from your experience and not make the same mistakes.
Thanks in advance, Valérie
Here's some of the pain points that I've found after 8 weeks of working with SurveyJS.
Survey Module
UI framework integration
The supported frontend frameworks (Vue, React, Angular, Knockout) are but a thin wrapper over a monolithic core.
You might as well use the vanilla javascript (jQuery) version of the survey module because the framework integration doesn't get more idiomatic than importing and rendering a custom <survey>
element. For example:
<Survey :survey="survey" />
The SurveyModel
model isn't reactive so the framework sees as an opaque blob of unchanging data. You can't use slots or nested elements to customize the presentation of the survey module on your page. I'll explain more about this later.
Event handling
Events aren't fired using the framework's own event emitter. This means you can't use your framework's event handling idioms. For example, as far as I know, you won't be able to do this:
<Survey :survey="survey" @complete="doStuff" />
Instead you'll have to keep a hold of your SurveyModel
and register your method as a callback:
survey.onComplete.add(this.doStuff);
Events emitted by lower components don't bubble up, so implementing anything more complicated than that is going to be a slog.
Customization
Theming
Custom CSS rules are injected through a JSON object and appended to existing rules through string concatenation. It's a bit how ya goin'.
Even if you kept the class names untouched and wrote your own CSS rules you'd quickly end up with a large reset stylesheet and loads of !important
declarations. To complicate things further, there's inline styling sprinkled throughout the library.
Layout
Customizing the layout of the survey is an abysmal experience. Because you can't nest anything inside the <Survey>
component, or use named slots, you'll have to flip the <Survey>
component inside out and hope there are enough events to replicate the native functionality using your own surrounding components. For example:
<page>
<navigation>
<button @click="survey.prevPage">Previous</button>
<button @click="survey.nextPage">Next</button>
</navigation>
<Survey :survey="survey" />
<progress-bar :survey="survey" />
<button @click="saveSurvey">Save</button>
</page>
Custom components
There is support for custom widgets but it is also lackluster. Custom widgets can be used to inject small elements into an existing question type, override the rendering of a native question type, or introduce a completely new question type.
However, anything that needs a sophisticated rendering which could be easily implemented as a custom framework component can't be done using a custom widget, or at least it's undocumented. I've recently hit this problem myself as I was replacing one of the native components with my own Vue cmponent, you can read more about this endeavor in #4665.
Did I mention inline styling yet? There's inline styling too.
Quality of native components
I believe that the native SurveyJS components aren't that well built. Take the file
component for example.
The native file
question doesn't have a clean and definite data-flow so it can basically treat a server response as either a blob of data or a file identifier. In the latter case, you must declare a callback to pull the entire file, or files, every time the component is (re)mounted to generate a preview.
Besides being a waste of bandwidth on both the server and client's side, the lack of a proper data-flow with file identification makes it impossible to properly track file attachments. Somebody else also noticed this behaviour in #4189 and #1978. An option to upload files on demand would partly mitigate this problem.
Disabling the preview mode, however, breaks the rendering of the selected files which forces you to re-implement the file listing capability. Recall that you can't pass a framework component, so this ends up being a bunch of markup inside a string inside a javascript file. This behavior is reported in #4000 and it's being advertised as a feature.
I couldn't find an existing bug report for the issue where onDownloadFiles
gets triggered a multiple of N times after uploading many files but one of the N requests fails. There's also this bug in 1.9.42.
I haven't used question types more complicated than boolean
, comment
, checkbox
and radiogroup
so I can't speak for the quality of other components. But the file
question type is a complete mess.
Performance
The survey module suffers from some serious performance issues. I'm still working through Firefox profiles to find the root cause, but some larger surveys (~200 questions, one question per page using questionsOnPageMode = "questionPerPage"
) can easily freeze the browser for ~10-20 seconds.
Survey Creator Module
The survey creator module is slow and crashes the browser on a 32GB machine. Mind you, this is happening on the offocial surveyjs.io website demo, with two questions. There's no recovering from this, once the survey creator starts spinning the browser freezes and must be reopened.
Some options that open a modal are unusable because the modal rendered off screen making them unusable. As much as I wanted to use it, there's now way I can justify the expense. I'll have to build my own.
@metaturso
-
Perfomance issue If you send us your JSON, we will look. In most cases SurveyJS has a good performance. We have JSON with 30k lines of code in our performance tests. Of course, there will be always a scenario where the performance is bad. Commonly, developers send us JSON and we optimize it for the next minor version. In your case, 200 questions with “question per page” mode, this can be an issue. We can check it. If you send us JSON it would be better.
-
Regarding Survey Creator. Again, if you show us how you crash our Creator in a browser, we would love to fix these issues. We have a lot of 2e2 tests and the product is used by many companies. There should be something that make a browser unstable.
-
Rendering for different plaforms We have vue2 rendering as well as react and we are working on Angular. Vue rendering is located here. Vue3 is in our plans. I do not see any reason to duplicate business logic for every platform and it is easy to test platform independent code via unit tests. Currently, our version for React can be used as a lego constructor in SurveyJS Library and SurveyJS Creator V2 for React. The most react components in the Library and in Creator can be replaced by custom react components. We will have the same for Angular soon and later for Vue3.
Looking forward for your JSON and instructions to crash the Creator. We are here to fix issues.
Thank you, Andrew
@metaturso Forgot to mention the file question. Let's review your scenario. The most problems with file components that developers have, it can't live without talking to the server. You have to upload and sometimes download file(s) into/from your server. I would suggest to create the separate issue regarding this.
Thank you, Andrew
@metaturso Here is the example with over 200 questions including matrices. JSON is about 6k lines. questionsOnPageMode is "questionPerPage". The survey model loading time is less then 1sec in my Chrome. Feel free to fork it, modify and post a new link here. Probably there is something in your JSON/code that kills SurveyJS Library performance.
Thank you, Andrew
Hi @andrewtelnov thanks for your timely response and thanks again for taking the time to read this long piece of feedback.
Survey Creator
Rendering and performance issues
I shall try to recreate the freezing example with Survey Creator 1.9.43 on the legacy page. It doesn't happen on the live demo.
Using the Survey Creator is going to save me a sizeable amount of work. Hopefully the performance and presentation problems aren't related.
I need the Survey Creator to exist under an application header without being forced to write a whole reset stylesheet or show the creator on a dedicated blank page. The broken presentation may well be caused by a clash of CSS styles inside my project.
Survey Library
Monolithic root
We have vue2 rendering as well as react and we are working on Angular. Vue rendering is located here. [Currently,] our version for React can be used as a lego constructor in SurveyJS Library and SurveyJS Creator V2 for React.
Are you suggesting that there's a disparity of features between the SurveyJS React and Vue implementations? I'm afraind I don't understand what you mean about the duplication and the lego constructor with React. Nevertheless, I believe this is outside the scope of this discussion.
My original point was that SurveyJS library already duplicates code to expose a monolithic framework-dependant <Survey>
component that's seemingly impossible to customize from the application code. This is why I brought Vue's slots into the conversation.
I don't know what the approach is called, but frameworks such as Vuetify do this extensively. Most notably with <v-card>
and its children, and the <v-dialog>
component. For the sake of the argument let's call this "modularity".
Using this "modular" approach, a developer can nest markup inside the root component and override slots to customize parts or the entire structure of the component. Using slot props to bring event emitters and data into the overriding slot's scope.
I took the time to prepare 4 working examples showing a possible "modular" survey component implemented in Vue2:
<!--
NOTE: This is a Vue pseudo-code sample.
I'm positive that Angular, Ract and whatnot have corresponding constructs.
-->
<survey title="Survey 4">
<template v-slot="{prevPage, nextPage, save, title}">
<header>
<h1 v-text="title" />
<button @click="prevPage">
Prev Page
</button>
<button @click="nextPage">
Next Page
</button>
</header>
<main>
Custom page
</main>
<footer>
Custom footer
<button @click="save">
Save
</button>
</footer>
</template>
</survey>
I only wish that SurveyJS took advantage of each framework's "modularity" constructs and idioms to provide developers with ways to rearrange, omit, or customize the presentation of its hierarchy of components.
Granted, it's still possible to customize the current component by switching some parts off, adding a component outside the survey and using events to keep them in sync, however, this is far from convenient or even idiomatic of each framework implementation.
Custom question types
I couldn't find anywhere in the SurveyJS documentation the VueJS equivalent to this React example showing how to register a React component as a custom question type.
Is there a document detailing how to shape a custom Vue component to behave like a SurveyJS question? If not, I'm happy to figure this out by myself but I'll need a few pointers. Perhaps this is a more in-depth technical conversation that we should continue in #4665.
Sample JSON structure
Here's a sample of JSON structure that can cause SurveyJS to slow the browser down
It's not the whole thing, just a hand-written excerpt.
It's rendered in questionPerPage
mode. The only appreciable difference from the code examples is that the SurveyJS module is rendered inside a <router-view />
component.
{
"name": "urn:survey:1",
"title": "Survey",
"pages": [
{
"title": "Section 1",
"name": "urn:survey:1:section:1",
"elements": [
{
"name": "urn:survey:1:section:1:question:1",
"title": "Question 1",
"type": "comment"
}
]
},
{
"title": "Section 2",
"name": "urn:survey:1:section:2",
"elements": [
{
"name": "urn:survey:1:section:2:question:2",
"title": "Question 2",
"type": "comment"
},
{
"name": "urn:survey:1:section:2:question:3",
"title": "Question 3",
"type": "comment"
},
{
"name": "urn:survey:1:section:2:question:4",
"title": "Question 4",
"type": "boolean"
},
{
"name": "urn:survey:1:section:2:question:5",
"title": "Question 5",
"type": "boolean"
},
{
"name": "urn:survey:1:section:2:question:6",
"title": "Question 6",
"type": "boolean"
},
{
"name": "urn:survey:1:section:2:question:7",
"title": "Question 7",
"type": "boolean"
},
]
},
{
"title": "Section 3",
"name": "urn:survey:1:section:3",
"elements": [
{
"name": "urn:survey:1:section:3:question:8",
"title": "Question 8",
"type": "boolean"
},
{
"name": "urn:survey:1:section:3:question:9",
"title": "Question 9",
"type": "comment"
},
{
"name": "urn:survey:1:section:3:question:10",
"title": "Question 10",
"type": "comment"
},
{
"name": "urn:survey:1:section:3:question:11",
"title": "Question 11",
"type": "comment"
},
{
"name": "urn:survey:1:section:3:question:12",
"title": "Question 12",
"type": "comment"
},
{
"name": "urn:survey:1:section:3:question:13",
"title": "Question 13",
"type": "comment"
},
{
"name": "urn:survey:1:section:3:question:14",
"title": "Question 14",
"type": "comment"
}
]
},
{
"title": "Section 4",
"name": "urn:survey:1:section:4",
"elements": [
{
"name": "urn:survey:1:section:4:question:15",
"title": "Question 15",
"type": "comment"
},
{
"name": "urn:survey:1:section:4:question:16",
"title": "Question 16",
"type": "comment"
},
{
"name": "urn:survey:1:section:4:question:17",
"title": "Question 17",
"type": "comment"
},
{
"name": "urn:survey:1:section:4:question:18",
"title": "Question 18",
"type": "comment"
},
{
"name": "urn:survey:1:section:4:question:19",
"title": "Question 19",
"type": "comment"
},
{
"name": "urn:survey:1:section:4:question:20",
"title": "Question 20",
"type": "comment"
},
{
"name": "urn:survey:1:section:4:question:21",
"title": "Question 21",
"type": "comment"
}
]
}
]
}