0

I am trying to build a custom Activity Timeline using lwc. I want to have a button to expand all activity nodes and collapse all activity nodes on click . This is similar to expand all button in standard Activity Timeline.

The issue which i am facing is when click on ExpandAll button on parent , i am calling the child method . After that only the first node is getting expand

Before clicking

enter image description here

After clicking "Expand All" button

enter image description here

How to make all the nodes to expand when button is clicked.

Below is my code.

Parent HTML

    <lightning-card class="slds-m-top_medium">
        <template if:true={timelineGroups}>
  <lightning-button variant="base" label="Expand All" title="Expand All" onclick={handleClick}
                class="slds-m-left_x-small"></lightning-button>
            <lightning-accordion allow-multiple-sections-open active-section-name={activeSections}>
                <template for:each={timelineGroups} for:item="activityGroup">
                    <lightning-accordion-section name={activityGroup.sectionName} label={activityGroup.name}
                        key={activityGroup.items}>
                        <ul class="slds-timeline">
                            <template for:each={activityGroup.items} for:item="item">
                                <c-activitylineitem key={item} item={item} isexpanded="true"></c-activitylineitem>
                            </template>
                        </ul>
                    </lightning-accordion-section>
                </template>
            </lightning-accordion>
        </template>
    </lightning-card>

Parent JS -

@track activeSections = [];
    @track timelineGroups;
    connectedCallback() {
        getActivityTimeline({ recordId: this.rowdetails.idtoupdate, includeChildren: false })
            .then(result => {
                console.log(result)
                var timelineGroups = result;
                var activeSections = [];
                timelineGroups.forEach(function (timelineGroup, index) {
                    var sectionName = 'Section' + index;
                    activeSections.push(sectionName);
                    timelineGroup.sectionName = sectionName;
                });
                this.activeSections = activeSections;
                this.timelineGroups = timelineGroups;
            })
            .catch(error => {
                this.isLoaded = false;
                this.error = error;
                this.showToast(this.error, 'Error')
            });

    }

handleClick(e) {
    this.template.querySelector('c-activitylineitem').expandAllNode();
}

Child HTML

 <template>
    <li>
        <div class={itemStyle}>
            <span class="slds-assistive-text">{item.activityTimelineType}</span>
            <div class="slds-media">
                <div class="slds-media__figure">
                    <button class="slds-button slds-button_icon" title={title} onclick={toggleActivityDetail}>
                        <lightning-icon icon-name="utility:switch" size="x-small"
                            class="slds-button__icon slds-timeline__details-action-icon">
                        </lightning-icon>
                    </button>
                </div>
            </div>
        </div>
    </li>
</template>

Child JS

  get itemStyle() {
        return this.showDetails ? "slds-timeline__item_expandable slds-is-open" : "slds-timeline__item_expandable";
    }
      @api
        expandAllNode() {
            this.showDetails = !this.showDetails;
        }
2

You need to get all children and expand them. Note that we have to copy the NodeList in order to iterate over it, so we use a quick array copy. Here's what that looks like:

handleClick(e) {
    [...this.template.querySelectorAll('c-activitylineitem')]
      .forEach(node => node.expandAllNode());
}

Note that querySelector only returns the first matching item, which is why we need to use querySelectorAll.

Note: I would recommend allowing a way to be explicit about setting the expansion value, otherwise you have a Toggle All instead of an Expand All, as your button suggests:

handleClick(e) {
    [...this.template.querySelectorAll('c-activitylineitem')]
      .forEach(node => node.setExpanded(true));
}

And for your setExpanded method:

  @api
    setExpanded(value) {
        this.showDetails = value;
    }

That said, you should consider just creating an attribute at the parent level:

<template for:each={activityGroup.items} for:item="item">
    <c-activitylineitem key={item} item={item} isexpanded={item.isExpanded}></c-activitylineitem>
</template>

And:

getActivityTimeline({recordId: this.rowdetails.idtoupdate, includeChildren: false })
.then(result => {
    this.timelineGroups = result.map((v,i) => ({...v, sectionName: `Section${i}`, isExpanded: true}));
    this.activeSections = this.timelineGroups.map((v) => v.sectionName);
})
.catch(error => {
    this.error = error;
    this.showToast(this.error, 'Error');
});

Which you can then toggle in the parent:

handleClick() {
  this.timelineGroups = [...this.timelineGroups].map(v => ({...v, isExpanded: true }));
}

It's generally better to manipulate data at the parent level rather than expose methods on the child directly.

1
  • thank u sfdcfox..
    – hellraizer
    Nov 1 '21 at 16:34

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.