We're in the process of constructing custom components that extend the native question types, as per the recommended approach here: https://surveyjs.io/form-library/documentation/customize-question-types/create-custom-widgets. For each question type, we intend to expand functionality by introducing boolean questions and comments and more in the future.
Our goal is to create a nested JSON structure where answers to the boolean questions and comments are appropriately organized under the corresponding parent questions. How can we achieve this nesting?
We do not wish to create composite question types.
Codeimport { CustomWidgetCollection, Serializer, Question } from "survey-core"
export const loadConformanceType = () => {}
CustomWidgetCollection.Instance.add({
name: "conformance",
isFit: function (element: any) {
const isSurveyQuestion = element instanceof Question
const isConformantEnabled = !!element.conformant
return isSurveyQuestion && isConformantEnabled
},
init() {
Serializer.addProperty("question", {
name: "conformant:boolean",
default: false,
category: "general",
})
Serializer.addProperty("question", {
name: "critical:boolean",
default: false,
category: "general",
})
},
isDefaultRender: true,
afterRender: function (question: any, el: any) {
const mainDiv = document.createElement("div")
const conformanceToggle = `
<div class="sd-question__header sd-element__header sd-question__header--location-top sd-element__header--location-top" style="margin-top: 20px;">
<h5 class="sd-title sd-element__title sd-question__title sd-question__title--answer">
<span class="sv-string-viewer">Conformant?</span>
</h5>
</div>
<div class="sv_qcbc sv_qbln sd-scrollable-container sd-boolean-root">
<label class="sd-boolean sd-boolean--allowhover sd-boolean--checked" id=\"conf-toggle-${question.name}\">
<input
type="checkbox"
class="sd-boolean__control sd-visuallyhidden"
aria-required="false"
aria-invalid="false"
value="true"
data-rendered="r"
/>
<div class="sd-boolean__thumb-ghost">
<span
class="sd-boolean__label sd-checkbox__label--disabled sd-boolean__label--false"
><span class="sv-string-viewer">No</span></span
>
</div>
<div class="sd-boolean__switch">
<span class="sd-boolean__thumb"
><span class="sd-boolean__thumb-text"
><span class="sv-string-viewer">Yes</span></span
></span
>
</div>
<div class="sd-boolean__thumb-ghost">
<span class="sd-boolean__label sd-boolean__label--true">
<span class="sv-string-viewer">Yes</span>
</span>
</div>
</label>
</div>
`
const commentTitle = `
<h5 class="sd-title sd-element__title sd-question__title sd-question__title--answer" style="margin-top: 30px; margin-bottom: 15px;">
<span class="sv-string-viewer">Please provide your comment:</span>
</h5>
`
const CommentTextarea = `<textarea id=\"comment-${question.name}\" class="sd-input sd-comment sd-selectbase__other" placeholder="" aria-required="false" aria-label="question1" style="resize: both;"></textarea>`
const html = `
<div>
${conformanceToggle}
${commentTitle}
${CommentTextarea}
</div>
`
mainDiv.innerHTML = html
mainDiv.style.marginTop = "10px"
mainDiv.style.padding = "3px"
el.append(mainDiv)
mainDiv.style.display = !question.conformant ? "none" : ""
// question.survey.setValue(`conformance-${question.name}`, "Yes")
document
.querySelector(`#conf-toggle-${question.name}`)
?.addEventListener("click", (event: any) => {
event.preventDefault()
event.stopPropagation()
// @ts-ignore
let thumbTextEl = document.querySelector(
`#conf-toggle-${question.name} .sd-boolean__thumb-text .sv-string-viewer`
)
let labelEl = document.querySelector(`#conf-toggle-${question.name}`)
let inputEl = document.querySelector(
`#conf-toggle-${question.name} input`
)
let labelFalse = document.querySelector(
`#conf-toggle-${question.name} .sd-boolean__label--false`
)
let labelTrue = document.querySelector(
`#conf-toggle-${question.name} .sd-boolean__label--true`
)
if (thumbTextEl?.innerHTML === "No") {
labelEl?.classList.add("sd-boolean--checked")
thumbTextEl.innerHTML = "Yes"
// @ts-ignore
inputEl.value = "false"
labelFalse?.classList.remove("sd-checkbox__label--disabled")
labelTrue?.classList.add("sd-checkbox__label--disabled")
question.survey.setValue(`${question.name}-conformance-toggle`, "Yes")
} else if (thumbTextEl?.innerHTML === "Yes") {
labelEl?.classList.remove("sd-boolean--checked")
thumbTextEl.innerHTML = "No"
// @ts-ignore
inputEl.value = "true"
labelFalse?.classList.add("sd-checkbox__label--disabled")
labelTrue?.classList.remove("sd-checkbox__label--disabled")
question.survey.setValue(`${question.name}-conformance-toggle`, "No")
}
})
let confomanceAnswer
let commentContent
document
.querySelector(`#yes-${question.name}`)
?.addEventListener("click", (event: any) => {
confomanceAnswer = "Yes"
question.survey.setValue(
`${question.name}-conformance-toggle`,
confomanceAnswer
)
})
document
.querySelector(`#no-${question.name}`)
?.addEventListener("click", (event: any) => {
confomanceAnswer = "No"
question.survey.setValue(
`${question.name}-conformance-toggle`,
confomanceAnswer
)
})
document
.querySelector(`#comment-${question.name}`)
?.addEventListener("change", (event: any) => {
commentContent = event.target.value
question.survey.setValue(
`${question.name}-conformance-comment`,
commentContent
)
console.log(question.survey)
})
},
})