-
Notifications
You must be signed in to change notification settings - Fork 0
Front End
This page explains how the front end code is working, how to launch, deploy the app, and the useful links that can help the development of new features. This wiki is based on the commit a675c9be1672534e0d560d6e8ae2afb5b1b91815.
The front end is a web application, that can be launched in a browser independently of a back end. In that case, the functionalities are limited, since it needs a back end from which it needs to receive questions to display.
The Front End is built with :
- React, used to structure the code and build the user interface. More precisely, the front was created with create-react-app.
- survey-react, a library, designed to work with React, which allows to create surveys. It is the backbone of the web application.
- Bootstrap, for the CSS design of the application.
Those are the 3 main libraries used for the project. To be able to understand and improve the existing code, a minimal knowledge about react and surveyjs is mandatory.
The Front End was developed on Ubuntu 18. Working on other Linux distribution should work as well, and even Windows. But if you want to work with the back end as well, which will probably will be the case, you must use Linux, because of lighttp.
In conclusion, to be sure that everything works as expected, use Ubuntu.
To set up the working environment of the front end, you must have npm. If you want to make tests by using the available mocked back end, you need Nodejs to launch it.
The easiest thing to do is to go there : Install Nodejs. It is not recommended to use apt get install npm since another package with this name exists, and it leads to errors.
Those commands must be launched in surveysystem/front. They require npm.
- npm install : use it the first time you cloned the repository. This command will install the needed packages for the project
- npm start : use it to launch locally the front end. Then go to http://localhost:3000/ to see the app. However, with the current version of the front end and back end, using this command will not allow the front and the back to communicate, because of CORS. It is still possible to use this command if you use the mocked back end. This command is useful because the code is compiled on the go, and the modifications you do in the code are automatically displayed in the browser : you win a lot of time when you test new features.
- npm build : this command creates a production-optimised code, and stores it in surveysystem/front/build/. The back end uses this folder to send to clients the application when they connect to it.
If you want to launch the surveysystem as a whole(backend+frontend), you have to launch the executable ./testrun, in surveysystem/backend/> This is currently the command that allows to have a fully working production version of the system. You have to launch it again everytime you make a change in the code.
If you want to see what the front end looks like, you can quickly deploy a local-working version of it. However, don't look at the code, because it heavily changed since then. Here are the steps to make it work :
- git clone https://github.com/gardners/surveysystem.git
- cd surveysytem/mocked_back
- npm run start
- in a new console/tab, cd surveysystem/front
- npm run
- in your browser, go to http://localhost/survey/10 (in fact, you can put any number or string after survey/)
Few points to know :
- always answer (even partially) a question. If you don't do it, the survey will "crash".
- the mocked back end will select 5 random questions from the 14 available, and send them to the front. After that, it will send a stop message, finishing the survey. To launch the survey again, type 'rs' (reset) in the console of mocked_back, then refresh the browser page.
the source code is found in surveysystem/front/src/, by following these rules :
- since it is a React project, Components (kind of 'classes') are used. They are located in the /components folder. There is a unique component for each element and page displayed in the browser. The most important one is FlexibleSurvey.js, which contains the code of the survey.
- in config/config.js, you can find the configurable variables of the project, such as the server url/port, the definition of api calls, and so on.
- the api/ folder contains a javascript object, which allows to request the back end.
- the customQuestions/ folder contains the custom questions developed for the needs of the project. For the moment, only the goelocationQuestion is working.
- If you want to complete the survey #id, you have to go to http://localhost/survey/.
- It will create a new instance of the survey, identified by a session ID in the back end.
- This session ID is used to identify a user, and the instance (with the already asked questions, user's answers, etc.) of the survey. It is used by the front end at every API call.
- When the session ID is known, the front end asks the first question to display
- The server answers him, and the front displays it to the user.
- The front end retrieves the user's answer, and then sends the answer to the server.
- The back end processes the answers, and sends the next question to ask to the front, which displays it
- The loop repeats.
The idea of this project is to make the system and the user interface as simple and easy to understand as possible. In the UI, only one question is displayed per page, it is what allows the survey to be simple. But of course, many questions can be displayed in the same page.
Those are 2 notions used by surveyjs.
- A questions, is, as its name suggests, the question that is displayed to the user. It contains a title, an ID, an input text, or a button, etc. For more info about this object, go here.
- A page is a container that holds as many questions as wanted. A page has 2 buttons : Previous and Next. When the user is at the first page of the survey, the Previous button isn't showed. When the user arrives at the last page, the Next button is replaced by the 'Complete' button. More info about a Page are available here
As mentioned before, the Complete buttons appears at the last page of the survey. By clicking on it, the survey ends. The problem is that the current page showed to the user is always the last (since the next question is generated only when the user submits the answer to the current answer).
So, it means that with the default behaviour of surveyjs, a user can never go further than the first question. To fix that, this is what is done :
- The Continue button is always displayed to the user.
- But its text is changed from 'Complete' to 'Next'
- Also, until the server says otherwise (it says it when there are no more questions to display), pressing on the Complete button doesn't complete the survey (the functionality is disabled)
- Instead, a function (called onNextButtonPressed) overrides the default behaviour, by sending to the server the answer from the current question, and fetching from it the next question to display.
- This behaviour is stopped when the server send an empty list of questions. In this case, the user can complete the survey.
The first function to be called when the survey is loaded is init(). It does the following :
- diplays a loading spinner, until the following actions are not finished
- it asks the back end to create a new session of a survey identified by the id, located in the url, in this form : http://localhost/survey/id
- when the server answer, it extracts the session id generated by the server, and stores it to use it later in other requests. The session id identifies a user and a survey.
- it then configures the survey : it add event listeners to Next and Previous buttons, loads the custom properties and custom questions, custom styles, etc
- it then asks the server the next question to display
- when it receives it, it processes it (see below for more info)
When the server sends the next question to display, it sends it in a JSON format. Before adding it to the survey, the JSON must be processed.
- First, the quesiton is deserialized with deserialize(). The question, in a JSON format, is converted into a javascript object, which can be read by surveyjs.
- With isThereAnotherQuestion(), it checks that the server didn't send an empty HTTP request. If it happens, it means that there are no more questions to display, and that the survey can be completed.
- If there are still questions to display, they are added and displayed in the survey with the setNextQuestionOfSurvey() method.
When the question, sent by the server, is converted into an object, it is put into an array (named questions) in the index equals to the id of the question. This array is used by the setNextQuestionOfSurvey(), it access to the desired index and add the question located there at the end of the survey.
Note : processNextQuestionReceived() and setNextQuestionOfSurvey() want an array of questions as parameter. When multiple questions are provided, they will be displayed at the same time, in the same page.
When the user goes back in the survey by pressing the 'Previous' button, the page that the user left must be destroyed. This page, and the questions inside are not relevant anymore since the question order will (probably) change after the user changes his answer from the previous question. This is what the function goBackToPreviousStep() does, and is called when the previous button is pressed.
The variable tmpSurvey is often used in the code. It is a local variable (instantiated each time inside of a function), and is used to make all the needed changes on this.state.survey. The this.state.survey variable is a part of the Component state, so you can only modify it in the this.setState() function. For practical reasons, at the beginning of a function working with the this.state.survey variable, the tmpSurvey is created. The function works with tmpSurvey all the time, and at the end, with a single setState(), this.state.survey takes the value of tmpSurvey.
A user has the possibility to stop the survey halfway, and then resume it later. It is possible thanks to localStorage :
- At the beginning of a new survey, the application checks if there is an existing session ID for the asked survey ID.
- If yes, the previous state is recovered and displayed to the user. The user will start the survey at the same step he interrupted it.
- Each time the user presses the next button, the localstorage is updated.
- When the user presses the complete button, the localstorage is deleted.
Most of the features available in the front application use the API of surveyjs. The documentation of Surveyjs is huge, so here are some links to centralize all the information :
- Library Overview : a quick overview of surveyjs, with basic examples of what the library can do. Its a good place to start
- API documentation : a in-depth documentation of the available API calls
- GitHub issue page : a lot of information can be found in the issues, on GitHub. The developers are extremely reactive and helpful, if you struggle on something, you can create a new issue with a relevant description and example.
The front end is only displaying the information that the back end is sending to it. The data is sent between back and front through API calls, in HTTP requests. For the moment, only GET requests are used, even to send data to the server. In the future, it will be changed into more adequate HTTP requests.
- /surveyapi/newsession?surveyid=id : asks the server to create a new instance of a survey (identified by its id). The server returns a session ID if the survey exists.
- /surveyapi/nextquestion?sessionid=id : asks the server the next question to display. The server sends a JSON, containing an array (called questions) that contains the next questions to display. Here is an example of what can be received :
{"next_questions": [{"id": "question1", "name": "question1", "title": "What is the answer to question 1?", "title_text": "What is the answer to question 1?", "type": "text"}]}
-
/surveyapi/updateanswer?sessionid=id&answer=answer : it adds or updates (if an answer already exists) the for the session identified by sessionid. This call is fired when the user submits an answer. The answer must be formatted in CSV. The CSV formatting is covered later in this wiki. The server answers with the next questions to display, in the same way as /nextquestion. It allows to not make an additional HTTP request to ask the next question to display.
-
/surveyapi/delanswer?sessionid=id&questionid=questionid : it allows to delete a question from the server. This call is fired when the user presses the next button to modify a previous answer. Even if it changes nothing in the behaviour of the front end app, this call must be made to acknowledge the back end that the user went back in the survey.
To implement these API calls, the library axios is used. It allows to easily create HTTP request and configure it as much as wanted.
To improve code readability, all AJAX requests are made with Premises, await and async. If you are not familiar with these concepts, here is some tutorials : https://javascript.info/promise-basics and https://javascript.info/async-await
While testing in dev environment, you can open the console of your browser and see the logs. All the important steps are logged. In addition, you can install React Dev Tool which allows you to debug Components state.
A survey is represented in JSON, as a list of questions. Here is an example of a survey :
{
"questions": [
{
"id" : 14,
"name": "name",
"type": "geolocation",
"title": "Please give us your geolocation :",
"placeHolder": "Type your geolocation..."
},
{
"id" : 0,
"name": "name",
"type": "text",
"title": "Please enter your name:",
"placeHolder": "Vlad"
}, {
"id" : 1,
"name": "birthdate",
"type": "text",
"inputType": "date",
"title": "Your birthdate:"
}, {
"id" : 2,
"name": "color",
"type": "text",
"inputType": "color",
"title": "Your favorite color:"
}, {
"id" : 3,
"name": "email",
"type": "text",
"inputType": "email",
"title": "Your e-mail:",
"placeHolder": "example@example.org",
"validators": [
{
"type": "email"
}
]
}
]
}A complete example of a survey, with different question type, can be found here.
A question, as a survey, is represented in JSON. The representation is defined by the developers of surveyjs developers. Here's an example, this is a question which asks for an email :
{
"id" : 3,
"name": "email",
"type": "text",
"inputType": "email",
"title": "Your e-mail:",
"placeHolder": "example@example.org",
"validators": [
{
"type": "email"
}
]
}The properties of the json are easy to understand :
- id : the id that identifies the question between the front and back. This property is special, because it is not natively supported by surveyjs. It will be discussed a bit later in the wiki. The id property is mandatory for each question. Without it, the whole system cannot work.
- name : it is the 'id' used by surveyjs. It must be unique, so a possible way is to have the same value for the id and the name.
- type : this one defines what will be the 'pattern' of the question : a text, a matrix,... All the available types can be found here. It is also possible to add custom question type, this will be discussed later in the wiki.
- inputType : the inputType property is specific to the 'text' type. You can ask for an email, a color, a date, etc. The complete list of available inputTypes can be found here .
- title : the text which will be displayed to the user. It is the question itself.
- placeHolder : allows to pre-fill with a place holder a input text. This filed is not mandatory.
- validators : surveyjs allows you to define what is a valid answer, in the question declaration, through validators. In this example, the validator will test that the answer has an email format. If not, a pop up will appear to the user, telling him that there is an error. Validators are not currently used in this project. Using them will allow to increase the number of features available for the system. For example, if you want to limit a number between two value, you should use validators. Click here for more info about validators.
Here is only a simple overview of what it is possible to do with surveyjs. Their JSON formatting is really rich.
As said before, surveyjs allows a lot of customisation with the available properties in their JSON. However, if you want to add new properties, you have to declare them in the config.js file. Without it, the front end will crash. This behaviour will be corrected by this issue.
Currently, the id property is a custom one, surveyjs use the 'name' property to identify a question. But to allow a working communication between the back and front, the property 'id' is needed.
So, always put an id in the JSON definition of a question
The particularity of the front end app is that it sends answers to the back end not in JSON, but CSV. For that, a serializer exists to do that. However, for the moment, it works only with basic answers.
The CSV contains values separated by colons (':'). The first value is the question ID, and the other ones are the possible answers. If the answer is text, the answer must be put in the 2nd value. If it is a number, it must be put in the 3rd value. And etc. A more detailed description about how it works can be found in the comment above serializeToCSV() function.
The serialization itslef is not difficult, it is how to determine the type of the answer that is most 'tricky'. Currently, this part is incomplete, and works with only number and text types.
It is possible to create a new type of question, not natively supported by surveyjs. An example already exists, it is the geolocation question, found in front/customQuestions/ folder. If you want to create your own question(called widget in surveyjs), here is a good template with which to start.
Also, surveyjs implements other popular question types, implemented by other members of GitHub. In the current version of FlexibleSurvey, it is not supported. If you want to use them, here is a good place to start.
To create a new survey, you need a JSON file to define it. The root element of this JSON is 'questions', which is an array of questions. Here is an example of how it looks.
To create this JSON, you have 2 ways :
- Write it yourself from scratch, thanks to the examples and documentation.
- Use Survey Builder. It is a tool provided by SurveyJS team, that allows you to create and customise your survey with a Graphic Interface. When all the questions are done, you can copy them from the JSON window, and paste then in your own JSON file.
⚠️ You have to copy only the question objects, not the whole JSON, which also contains the pages and other stuff.