1

I have written a custom LWC with some nested accordians. I have a function that expands all similarly named child accordion sections in the each of the parent accordion sections. I was able to get it to work in the Local Dev Server, but when I use it in an org it does not work. I am assuming because of Locker Service.

Nested Accordians

This method is where I find my issue (see full code below).

expandThisCardOnAll(event) {
        var competitorList = this.allCompetitors;
        var cardType = event.target.value;
        var allAccordianElements = this.template.querySelectorAll('lightning-accordion');

        var populateFirstAccordian = true; // this is a workaround that assumes the main accordian will always be first

        allAccordianElements.forEach(function(thisAccordian) {
            if(populateFirstAccordian){
                populateFirstAccordian = false;
                thisAccordian.activeSectionName = competitorList;
                // 
            }
            else {
                thisAccordian.activeSectionName = cardType;
            }
        });

    }

When I am in the dev server, and I log allAccordianElements to console it returns

[objectNodeList]

, but when I am in the org, it returns:

SecureNodeList: [object NodeList]{ key: {"namespace":"c"} }

I tried to access the elements through the wrapper, but no luck. I read some posts that say that it is not possible, but perhaps there is a way to access using a querySelector that I am not aware of.

Here is the HTML file...

<template>
    <div class="slds-box slds-box_xx-small">
        <div class="slds-clearfix">
            <div class="slds-float_left slds-m-around_x-small">
                <div class="slds-text-heading_medium">Competitive Intelligence Battlecards</div>
            </div>
            <div class="slds-float_right slds-m-around_x-small">
                <lightning-button-group>
                    <lightning-button label="Collapse Competitors" onclick={collapseAllCompetitors}>
                    </lightning-button>
                    <lightning-button label="Collapse All" onclick={collapseAll}></lightning-button>
                    <lightning-button label="Refresh Data" onclick={refreshData}>
                    </lightning-button>
                </lightning-button-group>

                <lightning-button label="Open client" value={homepageClient} onclick={navigateToWebPage} variant="brand"
                    class="slds-var-m-left_x-small">
                </lightning-button>
            </div>
        </div>

        <template if:true={competitors}>
            <lightning-accordion allow-multiple-sections-open active-section-name={expandedCompetitorSections}>
                <template for:each={competitors} for:item="competitor">
                    <div key={competitor.Id} class="slds-box slds-box_xx-small slds-m-around_x-small">
                        <lightning-accordion-section key={competitor.Id} name={competitor.Name} label={competitor.Name}
                            class="slds-box_xx-small">

                            <lightning-button-menu slot="actions" alternative-text="Show menu" icon-size="x-small"
                                menu-alignment="right">
                                <lightning-menu-item value={competitor.Name} label="Email this Competitor Card"
                                    icon-name="utility:open" onclick={emailContent}>
                                </lightning-menu-item>
                                <lightning-menu-item value={competitor.Client__c} label="View Competitor in Client"
                                    icon-name="utility:open" onclick={navigateToWebPage}>
                                </lightning-menu-item>
                                <lightning-menu-item value={competitor.Client__c} label="Edit Competitor in Client"
                                    icon-name="utility:edit" onclick={navigateToWebPage}>
                                </lightning-menu-item>
                                <lightning-menu-item value="CollapseAll" label="Collapse All"
                                    icon-name="utility:collapse_all" onclick={collapseAll}></lightning-menu-item>
                            </lightning-button-menu>

                            <div class="slds-box slds-box_xx-small">
                                <lightning-card key={competitor.Id} title={competitor.Full_Company_Name__c}
                                    icon-name="custom:custom63">
                                    <div class="slds-card__body slds-card__body_inner">

                                        <p key={competitor.Id}>
                                            <lightning-formatted-url value={competitor.Company_Website__c}
                                                tooltip="Go to competitor website" label={competitor.Company_Website__c}
                                                target="_blank">
                                            </lightning-formatted-url>
                                        </p>
                                        <p key={competitor.Id}>{competitor.Summary__c}</p>
                                    </div>
                                </lightning-card>
                            </div>

                            <lightning-accordion allow-multiple-sections-open>
                                <template for:each={competitor.Battlecards__r} for:item="battlecard">

                                    <lightning-accordion-section key={battlecard.Id} name={battlecard.Name}
                                        label={battlecard.Name}>

                                        <lightning-button-menu slot="actions" alternative-text="Show menu"
                                            icon-size="x-small" menu-alignment="right">
                                            <lightning-menu-item value={battlecard.Name} label="Email this Battlecard"
                                                icon-name="utility:email" onclick={emailContent}></lightning-menu-item>
                                            <lightning-menu-item value={battlecard.Client__c} label="View in Client"
                                                icon-name="utility:open" onclick={navigateToWebPage}>
                                            </lightning-menu-item>
                                            <lightning-menu-item value={battlecard.Client__c} label="Edit in Client"
                                                icon-name="utility:edit" onclick={navigateToWebPage}>
                                            </lightning-menu-item>
                                            <lightning-menu-item value={battlecard.Name} label="Open this Battlecard for All"
                                                icon-name="utility:expand_all" onclick={expandThisBattlecardOnAll}>
                                            </lightning-menu-item>
                                            <lightning-menu-item value="CollapseAll" label="Collapse All"
                                                icon-name="utility:collapse_all" onclick={collapseAll}>
                                            </lightning-menu-item>
                                        </lightning-button-menu>

                                        <div class="slds-box slds-theme_shade">
                                            <p key={battlecard.Id}>{battlecard.Title__c}</p>
                                            <p key={battlecard.Id}>{battlecard.Subtitle__c}</p>
                                            <p key={battlecard.Id}>{battlecard.Summary__c}</p>
                                            <lightning-formatted-rich-text value={battlecard.Content__c}>
                                            </lightning-formatted-rich-text>
                                        </div>
                                    </lightning-accordion-section>

                                </template>
                            </lightning-accordion>
                        </lightning-accordion-section>
                    </div>
                </template>
            </lightning-accordion>
        </template>

        <template if:true={error}>
            <c-error-panel errors={error}></c-error-panel>
        </template>
    </div>
</template>

Here is the related JS controller...

import { LightningElement, api, track, wire } from 'lwc';
import getAccountCompetitors from '@salesforce/apex/AccountCompetitorController.getAccountCompetitors';
import { refreshApex } from '@salesforce/apex';
import { NavigationMixin } from 'lightning/navigation';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class Battlecards extends NavigationMixin(LightningElement) {

    @api recordId;
    // used for testing in LDS
    testRecordId = '0014T000002zOYaQAM';

    defualtClientLink = 'https://google.com';

    @track competitors;
    @track error;
    @track refreshes = 0;

    allCompetitors = [];

    /** Wired Apex result so it can be refreshed programmatically */
    wiredCompetitorResult;

    // wire the Apex method to the competitors property
    // using testRecordId for testing purposes in LDS
    @wire(getAccountCompetitors, { accountId: '$testRecordId' })
    wiredCompetitors(result) {
        this.wiredCompetitorResult = result;
        if(result.data) {
            this.competitors = result.data;
            this.error = undefined;

            // capture all the Competitor Sections for opening all at once
            this.competitors.forEach(competitor => {
                this.allCompetitors.push(competitor.Name);

            });
        }
        else if (result.error){
            this.error = result.error;
            this.competitors = undefined;
        }
    }

    refreshData() {
        this.refreshes++;
        return refreshApex(this.wiredCompetitorResult);
    }

    collapseAllCompetitors() {
        var allCompetitors = this.template.querySelector('lightning-accordion');
        allCompetitors.activeSectionName = "";
        return null;
    }

    collapseAll() {
        var all = this.template.querySelectorAll('lightning-accordion');
        all.forEach(function(thisAccordian) {
            thisAccordian.activeSectionName = "";
        });
    }

    expandThisBattlecardOnAll2() {
        var all = this.template.querySelectorAll('lightning-accordion');
        all.forEach(function(thisAccordian) {
            thisAccordian.activeSectionName = "";
        });
    }

    expandThisBattlecardOnAll(event) {
        var competitorList = this.allCompetitors;
        var battlecardType = event.target.value;
        var allAccordianElements = this.template.querySelectorAll('lightning-accordion');

        var populateFirstAccordian = true; // this is a workaround that assume the main accordian will always be first

        allAccordianElements.forEach(function(thisAccordian) {
            if(populateFirstAccordian){
                populateFirstAccordian = false;
                thisAccordian.activeSectionName = competitorList;
                // 
            }
            else {
                thisAccordian.activeSectionName = battlecardType;
            }
        });

    }

    emailContent(event) {
        var clientIdentifier = event.target.value;
        var emailType = event.target.label;
        var messageString;
        var emailAddress = 'someperson@client.com';

        // TODO - call out to client email system
        //          need to define the value in the button to be something relevant to the client server
        //          need to get the current users email address

        // build message
        if(!emailType){
            messageString = 'client data has been sent';
        }
        else if (emailType === 'Email this Competitor Card') {
            messageString = `The client data for ${clientIdentifier} has been sent to ${emailAddress}`;
        }
        else if (emailType === 'Email this Battlecard') {
            messageString = `The client data for this ${clientIdentifier} bBattlecard has been sent to ${emailAddress}`;
        }

        const toastEvent = new ShowToastEvent({
            title: 'Email Sent',
            message: messageString,
            variant: 'success',
        });
        this.dispatchEvent(toastEvent);
    }

    navigateToWebPage(event) {
        // get the incoming Url
        var targetURL = event.target.value;

        // check if there is a value to use
        if(!targetURL){
            targetURL = this.defualtClientLink;
        }

        // Navigate to a URL
        this[NavigationMixin.Navigate]({
            type: 'standard__webPage',
            attributes: {
                url: targetURL
            }
        },
        false // true - Replaces the current page in your browser history with the Url
        //              but causes the originating page to navigate to App Builder ???
      );
    }

}

Any idea how I can access these elements, with Locker Service active?

  • 3
    Could you share a code sample so it can be reproduced ? This is due to locker for sure. – Mohith Shrivastava Oct 19 '19 at 4:14
  • 1
    @MohithShrivastava sorry about that, I have included the code to allow reproduction. I also did a few formatting edits for clarity... – Kurt Fairfield Oct 21 '19 at 23:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.