0

I've been building a dynamic forms app for our support community experience using LWC that are nested and driven by custom object data, and conditionally render based on the configuration data and the customer responses. The LWCs nesting consists of wrapper components for selection, and then form components that correspond to custom objects from top to bottom: form, sections, questions, and question options, with the ability to recursively nest the question and question option templates for conditional subquestions. Most of the work for structuring the data is done in the .js and the apex is primarily used just for simple CRUD. It took awhile to find what seemed to be the right combination of connectedCallback vs @wire calls to produce the desired rendering and retaining of user input, but during testing, beta-users identified an issue that has appeared to be OS specific, as we have tested multiple browsers on Mac, and multiple browsers on Windows, and the issue is only present on Windows. When a form is selected and renders, space for questions render, but questions don't render, even though there is no error in the debug logs or the console, and when inspecting the elements, the question data and html are present. In the rendered space, no text is selectable, and there doesn't seem to be any css causing it to be hidden.

This is the primary function from section.js that sets the attributes for the questions:

async setQuestionAttributesImp(){
        await getSectionQuestions({ sectionId: myself.sectionId, responseId: myself.masterResponseId}).then((result) => {
            if(result && result.constructor === Array)
            {
                myself.sectionQuestions = result;
                if(myself.sectionQuestions.length > 0)
                {
                    let loadedData = myself.sectionQuestions;
                    for(let key in loadedData) {
                        if (loadedData.hasOwnProperty(key)) { 
                            let tempQuestionType = {'QuestionSet': false};
                            let tempLoadedOptions = [];
                            if(loadedData[key].Question_Type__c !== undefined)
                            {
                                tempQuestionType[loadedData[key].Question_Type__c.replace(/\s/g, '')] = true;
                            }
                            if(loadedData[key].Question_Options__r !== undefined)
                            {
                                let optionData = loadedData[key].Question_Options__r;
                                for(let optionKey in optionData) {
                                    if (optionData.hasOwnProperty(optionKey)) {
                                        let tempOptionType = {};
                                        if(optionData[optionKey].RecordType.Name !== undefined)
                                        {
                                            tempOptionType[optionData[optionKey].RecordType.Name.replace(/\s/g, '')] = true;
                                        }
                                        tempLoadedOptions.push({option:optionData[optionKey], key:optionKey, optionType: tempOptionType, masterResponseId: myself.masterResponseId});
                                    }
                                }
                            }
                            if(loadedData[key].hasOwnProperty('Client_Form_Question_Responses__r') && loadedData[key].Client_Form_Question_Responses__r.length > 0)
                            {
                                loadedData[key].Client_Form_Question_Responses__r[0].Selected_Option__c
                            }
                            myself.loadedQuestions.push({question:loadedData[key], key:key, questionType: tempQuestionType, qOptions: tempLoadedOptions, masterResponseId: myself.masterResponseId});
                            if(myself.editMode)
                            {
                                myself.isEnableSave = true;
                            }
                        }
                    }
                    if(myself.loadedQuestions.length > 0)
                    {
                        myself.hasLoadedQuestions = true;
                    }
                }
            }
        })
        .catch(error => {
            myself.error = error;
            console.log(error);
        });
    };

This is the question iteration from the section.html:

<template for:each={loadedQuestions} for:item="indexedQuestion">
                <div key={indexedQuestion.key} class="slds-m-left_medium slds-m-right_medium">
                    <template if:false={indexedQuestion.questionType.QuestionSet}>
                        <p>Should have rendered a question</p>
                        <c-cf-question-template onquestionoptionvaluechange={handleOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} master-response-id={indexedQuestion.masterResponseId} render-rich-text-fields={indexedQuestion.question.Render_Rich_Text__c} edit-mode={editMode}></c-cf-question-template>
                    </template>
                    <template if:true={indexedQuestion.questionType.QuestionSet}>
                        <c-cf-question-set-option-template onquestionoptionvaluechange={handleOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} question-set-options={indexedQuestion.qOptions} master-response-id={indexedQuestion.masterResponseId} edit-mode={editMode}></c-cf-question-set-option-template>
                    </template>
                    <br key={indexedQuestion.key}>
                </div>
            </template>

Here is the primary function for setting attributes in the question.js:

@wire(getQuestionOptions, { questionId: '$questionId' , responseId: '$masterResponseId'})
    setOptionAttributes(questionOptions){
        myself = this;
        myself.loadedOptions = [];
        myself.questionOptions = questionOptions;
        if(myself.questionOptions.hasOwnProperty('data') && myself.questionOptions.data.constructor === Array)
        {
            let loadedData = myself.questionOptions.data;
            for(let key in loadedData) {
                if (loadedData.hasOwnProperty(key)) { 
                    let tempOptionType = {};
                    if(loadedData[key].hasOwnProperty('RecordType') && loadedData[key].RecordType.hasOwnProperty('Name'))
                    {
                        tempOptionType[loadedData[key].RecordType.Name.replace(/\s/g, '')] = true;
                    }
                    let tempOption = {label: loadedData[key].Label__c, option:loadedData[key], key:key, optionKey: key, optionKey: key, optionType: tempOptionType, optionId: loadedData[key].Id, questionId: myself.questionId, masterResponseId: myself.masterResponseId};
                    if(loadedData[key].hasOwnProperty('Client_Form_Question_Responses__r') && loadedData[key].Client_Form_Question_Responses__r.length > 0)
                    {
                        tempOption['optionResponseId'] = loadedData[key].Client_Form_Question_Responses__r[0].Id;
                        tempOption['optionResponseUrl'] = '/' + loadedData[key].Client_Form_Question_Responses__r[0].Id;
                        tempOption['optionResponseLastModBy'] = loadedData[key].Client_Form_Question_Responses__r[0].LastModifiedBy.Name;
                        tempOption['optionResponseLastModById'] = loadedData[key].Client_Form_Question_Responses__r[0].LastModifiedById;
                        tempOption['optionResponseLastModDate'] = loadedData[key].Client_Form_Question_Responses__r[0].LastModifiedDate;
                        if (tempOptionType.hasOwnProperty('Number')) {
                            tempOption['optionResponseValue'] = loadedData[key].Client_Form_Question_Responses__r[0].Number_Value__c;
                        }
                        else if (tempOptionType.hasOwnProperty('Text')) {
                            tempOption['optionResponseValue'] = loadedData[key].Client_Form_Question_Responses__r[0].Text_Value__c;
                        }
                        else if (tempOptionType.hasOwnProperty('Checkbox')) {
                            tempOption['optionResponseValue'] = loadedData[key].Client_Form_Question_Responses__r[0].Checkbox_Value__c;
                        }
                        myself.optionResponses[loadedData[key].Client_Form_Question_Responses__r[0].Id] = loadedData[key].Client_Form_Question_Responses__r[0];
                    }
                    if(loadedData[key].hasOwnProperty('Sub_Questions__r') && loadedData[key].Sub_Questions__r.length > 0)
                    {
                        tempOption['renderSubQuestions'] = false;
                        if (tempOptionType.hasOwnProperty('Number')) {
                            tempOption['requiredSubQuestionValue'] = loadedData[key].Number_Value__c;
                            if(tempOption.hasOwnProperty('optionResponseValue') && tempOption.requiredSubQuestionValue === tempOption.optionResponseValue)
                            {
                                tempOption['renderSubQuestions'] = true;
                            }
                        }
                        else if (tempOptionType.hasOwnProperty('Text')) {
                            tempOption['requiredSubQuestionValue'] = loadedData[key].Text_Value__c;
                            if(tempOption.hasOwnProperty('optionResponseValue') && tempOption.requiredSubQuestionValue === tempOption.optionResponseValue)
                            {
                                tempOption['renderSubQuestions'] = true;
                            }
                        }
                        else if (tempOptionType.hasOwnProperty('Checkbox')) {
                            tempOption['requiredSubQuestionValue'] = true;
                            if(tempOption.hasOwnProperty('optionResponseValue') && tempOption.requiredSubQuestionValue === tempOption.optionResponseValue)
                            {
                                tempOption['renderSubQuestions'] = true;
                            }
                        }
                        else if (tempOptionType.hasOwnProperty('PicklistValue')) {
                            tempOption['requiredSubQuestionValue'] = loadedData[key].Id;
                            if(myself.selectedOption && tempOption.optionId === myself.selectedOption)
                            {
                                tempOption['renderSubQuestions'] = true;
                            }
                        }
                        let subQuestions = loadedData[key].Sub_Questions__r;
                        let tempSubQuestions = [];
                        for(let key in subQuestions) {
                            if (subQuestions.hasOwnProperty(key)) { 
                                let tempQuestionType = {};
                                if(subQuestions[key].hasOwnProperty('Question_Type__c') && subQuestions[key].Question_Type__c)
                                {
                                    tempQuestionType[subQuestions[key].Question_Type__c.replace(/\s/g, '')] = true;
                                }
                                tempSubQuestions.push({question:subQuestions[key], key:key, questionType: tempQuestionType, masterResponseId: myself.masterResponseId});
                            }
                        }
                        tempOption['optionSubQuestions'] = tempSubQuestions;
                    }
                    myself.loadedOptions.push(tempOption);
                }
            }
            if(myself.loadedOptions.length > 0)
            {
                myself.hasLoadedOptions = true;
            }
        }
    }

Here is the question.html:

<template>
        <p>Should be rendering whole question</p>
        <p>{hasLoadedOptions}</p>
                <template if:false={renderDecisionMatrixView}>
            <template if:false={renderOptionsOnly}>
                <lightning-record-view-form record-id={questionId} object-api-name="Client_Form_Question__c">
                    <lightning-output-field field-name="Question_Text__c" variant="label-hidden"></lightning-output-field>
                    <template if:true={renderRichTextFields}>
                        <lightning-output-field field-name="Header_Text__c" variant="label-hidden"></lightning-output-field>
                        <lightning-output-field field-name="Help_Text__c" variant="label-hidden"></lightning-output-field>
                    </template>
                </lightning-record-view-form>
            </template>
                        <template if:false={renderLabelsOnly}>
                <template if:true={hasLoadedOptions}>
                    <template if:true={questionType.Picklist}>
                        <c-cf-picklist-option-template onoptionvaluechange={handleOptionValueChange} question-id={question.Id} question-options={loadedOptions} master-response-id={masterResponseId} selected-value={selectedOption} question-response-id={selectedOptionResponseId} edit-mode={editMode}></c-cf-picklist-option-template>
                        <br/>
                        <template for:each={loadedOptions} for:item="indexedOption">
                            <template if:true={indexedOption.renderSubQuestions}>
                                <template for:each={indexedOption.optionSubQuestions} for:item="indexedQuestion">
                                    <template if:false={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} master-response-id={indexedQuestion.masterResponseId} render-rich-text-fields={indexedQuestion.question.Render_Rich_Text__c} edit-mode={editMode}></c-cf-question-template>
                                    </template>
                                    <template if:true={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-set-option-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} question-set-options={indexedQuestion.qOptions} master-response-id={indexedQuestion.masterResponseId} edit-mode={editMode}></c-cf-question-set-option-template>
                                    </template>
                                </template>
                            </template>
                        </template>
                    </template>
                    <template if:true={questionType.Number}>
                        <template for:each={loadedOptions} for:item="indexedOption">
                            <c-cf-number-option-template onoptionvaluechange={handleOptionValueChange} key={indexedOption.key} question-id={questionId} option-id={indexedOption.option.Id} option-key={indexedOption.key} option-label={indexedOption.label} question-option={indexedOption.option} master-response-id={indexedOption.masterResponseId} option-response-id={indexedOption.optionResponseId} option-response-value={indexedOption.optionResponseValue} option-number-format={indexedOption.option.Number_Formatter__c} edit-mode={editMode}></c-cf-number-option-template>
                            <br key={indexedOption.key}>
                            <template if:true={indexedOption.renderSubQuestions}>
                                <template for:each={indexedOption.optionSubQuestions} for:item="indexedQuestion">
                                    <template if:false={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} master-response-id={indexedQuestion.masterResponseId} render-rich-text-fields={indexedQuestion.question.Render_Rich_Text__c} edit-mode={editMode}></c-cf-question-template>
                                    </template>
                                    <template if:true={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-set-option-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} question-set-options={indexedQuestion.qOptions} master-response-id={indexedQuestion.masterResponseId} edit-mode={editMode}></c-cf-question-set-option-template>
                                    </template>
                                </template>
                            </template>
                        </template>
                    </template>

                    <template if:true={questionType.FreeText}>
                        <template for:each={loadedOptions} for:item="indexedOption">
                            <c-cf-text-option-template onoptionvaluechange={handleOptionValueChange} key={indexedOption.key} question-id={questionId} option-id={indexedOption.option.Id} option-key={indexedOption.key} option-label={indexedOption.label}  question-option={indexedOption.option} master-response-id={indexedOption.masterResponseId} option-response-id={indexedOption.optionResponseId} option-response-value={indexedOption.optionResponseValue} edit-mode={editMode}></c-cf-text-option-template>
                            <br key={indexedOption.key}>
                            <template if:true={indexedOption.renderSubQuestions}>
                                <template for:each={indexedOption.optionSubQuestions} for:item="indexedQuestion">
                                    <template if:false={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} master-response-id={indexedQuestion.masterResponseId} render-rich-text-fields={indexedQuestion.question.Render_Rich_Text__c} edit-mode={editMode}></c-cf-question-template>
                                    </template>
                                    <template if:true={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-set-option-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} question-set-options={indexedQuestion.qOptions} master-response-id={indexedQuestion.masterResponseId} edit-mode={editMode}></c-cf-question-set-option-template>
                                    </template>
                                </template>
                            </template>
                        </template>
                    </template>

                    <template if:true={questionType.Checkbox}>
                        <template for:each={loadedOptions} for:item="indexedOption">
                            <c-cf-checkbox-option-template onoptionvaluechange={handleOptionValueChange} key={indexedOption.key} question-id={questionId} option-id={indexedOption.option.Id} option-key={indexedOption.key} option-label={indexedOption.label} question-option={indexedOption.option} master-response-id={indexedOption.masterResponseId} option-response-id={indexedOption.optionResponseId} option-response-value={indexedOption.optionResponseValue} render-label={indexedOption.option.Render_Label__c} edit-mode={editMode}></c-cf-checkbox-option-template>
                            <br key={indexedOption.key}>
                            <template if:true={indexedOption.renderSubQuestions}>
                                <template for:each={indexedOption.optionSubQuestions} for:item="indexedQuestion">
                                    <template if:false={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} master-response-id={indexedQuestion.masterResponseId} render-rich-text-fields={indexedQuestion.question.Render_Rich_Text__c} edit-mode={editMode}></c-cf-question-template>
                                    </template>
                                    <template if:true={indexedQuestion.questionType.QuestionSet}>
                                        <c-cf-question-set-option-template onquestionoptionvaluechange={handleSubOptionValueChange} key={indexedQuestion.key} question-id={indexedQuestion.question.Id} question={indexedQuestion.question} question-type={indexedQuestion.questionType} question-set-options={indexedQuestion.qOptions} master-response-id={indexedQuestion.masterResponseId} edit-mode={editMode}></c-cf-question-set-option-template>
                                    </template>
                                </template>
                            </template>
                        </template>
                    </template>
                </template>

            </template>
        </template>
</template>

Edit-

This is the App running on Mac: Running on Mac Mac with Console

This is the App running on Windows: Running on Windows Windows with Console

1
  • Could you include a screenshot or short screen cast? This is a lot of code to dig through, and I doubt that it's really the OS at fault here. You can also use we components.dev to try and replicate the issue.
    – sfdcfox
    Sep 20, 2022 at 20:52

1 Answer 1

1

Apparently it was the z-index. One of the LWCs had CSS that set the z-index to -1, which rendered fine on Mac, but not on Windows.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .