RoundtableJS provides a robust system for handling and storing survey data. This includes managing responses, survey metadata, and methods to access and manipulate this data.

Data Structure

The Survey class maintains two main data structures:

  1. responses: An array that stores all question responses.
  2. surveyDetails: An object that stores metadata about the survey.

Storing Responses

When a user answers a question:

  1. The response is collected via the collectPageData method.
  2. Each response is stored in the responses array using the updateData method.
  3. A timestamp is added to each response.
updateData(elementData) {
    this.responses.push({
        ...elementData,
        responseTimestamp: new Date().toISOString()
    });
}

Response Format

Each response in the responses array has the following structure:

{
  id: 'question_id',
  type: 'question_type',
  response: 'user_response',
  responded: true,
  responseTimestamp: '2023-07-13T12:34:56.789Z',
  // Additional question-specific data
}

Accessing Data

RoundtableJS provides several methods to access stored data:

getResponse
function

Retrieves the most recent response for a specific question.

Parameters:

  • questionId (string): The ID of the question

Returns:

  • The response value for the specified question or null if not found
const age = survey.getResponse('age_question');
responded
function

Checks if a specific question has been answered.

Parameters:

  • questionId (string): The ID of the question

Returns:

  • boolean: True if the question has been answered, false otherwise
const hasAnswered = survey.responded('favorite_color');
getAllResponses
function

Returns the entire responses array.

Returns:

  • array: An array containing all responses
const allResponses = survey.getAllResponses();
getAllSurveyData
function

Returns an object containing all survey details and responses.

Returns:

  • object: An object with surveyDetails and responses properties
const surveyData = survey.getAllSurveyData();

Survey Metadata

Survey metadata is stored in the surveyDetails object. This includes:

  • startTime: When the survey began
  • endTime: When the survey was completed
  • Custom metadata passed during survey initialization

You can set and get survey details using:

setSurveyDetail
function

Sets a custom survey detail.

Parameters:

  • key (string): The key for the survey detail
  • value (any): The value to set for the survey detail
survey.setSurveyDetail('condition', 'experimental');
getSurveyDetail
function

Retrieves a custom survey detail.

Parameters:

  • key (string): The key of the survey detail to retrieve

Returns:

  • The value of the specified survey detail
const condition = survey.getSurveyDetail('condition');

Completing the Survey

When the survey is finished:

  1. A completion message is displayed.
  2. The endTime is recorded in surveyDetails.
  3. The complete survey data is logged to the console.
finishSurvey
function

Completes the survey and displays a finishing message.

Parameters:

  • options (object): An object containing the finishing options
    • message (string): The message to display upon survey completion
survey.finishSurvey({ message: 'Thank you for completing the survey!' });

Data Validation

RoundtableJS includes built-in validation for survey responses:

  • The validateCurrentPage method checks the validity of all elements on the current page.
  • Each question type has its own validate method that can be customized.
  • Validation errors are displayed to the user, preventing progression until resolved.
async validateCurrentPage() {
    let isValid = true;
    for (const element of this.currentPage.elements) {
        if (typeof element.validate === 'function') {
            const elementValid = await element.validate(true);
            if (!elementValid) {
                isValid = false;
            }
        }
    }
    return isValid;
}

Custom Validation

You can implement custom validation for your elements by overriding the validate method when creating custom question types. This allows you to define specific validation rules for each element type.

Here’s an example of how to implement custom validation for a rating question:

class RatingQuestion extends Element {
    // ... other methods ...

    validate(showError = false) {
        const isValid = this.data.response !== undefined && this.data.response >= 1 && this.data.response <= this.maxRating;
        if (showError && !isValid) {
            this.showValidationError(`Please select a rating between 1 and ${this.maxRating}`);
        }
        return isValid;
    }
}

In this example:

  1. The validate method checks if a response has been given (this.data.response !== undefined) and if it’s within the valid range (>= 1 and <= this.maxRating).
  2. If showError is true and the validation fails, it displays an error message using this.showValidationError().
  3. The method returns a boolean indicating whether the validation passed or failed.

You can implement similar custom validation for any element type by extending the Element class and overriding the validate method.

Implementing Complex Validation

For more complex validation scenarios, you can implement additional methods in your custom element class:

class ComplexQuestion extends Element {
    // ... other methods ...

    validate(showError = false) {
        const isValidFormat = this.validateFormat();
        const isValidContent = this.validateContent();
        const isValid = isValidFormat && isValidContent;

        if (showError && !isValid) {
            if (!isValidFormat) {
                this.showValidationError('Invalid format. Please check your input.');
            } else if (!isValidContent) {
                this.showValidationError('Invalid content. Please review your answer.');
            }
        }

        return isValid;
    }

    validateFormat() {
        // Implement format validation logic
        // Return true if format is valid, false otherwise
    }

    validateContent() {
        // Implement content validation logic
        // Return true if content is valid, false otherwise
    }
}

This approach allows you to break down complex validation into smaller, more manageable pieces, making your code more readable and maintainable.

Remember to call super.validate(showError) if you want to retain the base validation logic from the parent class.

By implementing custom validation, you can ensure that the data collected in your survey meets your specific requirements and quality standards.