2

According to Salesforce documentation,

The afterRender() method is called to enable you to interact with the DOM tree after the framework’s rendering service has inserted DOM elements

I have a Lightning component and I want to add some class to an element after the component has completed its load. But this doesn't work, the class doesn't get added to the element. Here is my code.

({
    afterRender: function(cmp, helper) {
        this.superAfterRender();
        var lst_officeElements = document.getElementsByClassName('myMenuItem');
        lst_officeElements[0].className += " slds-active";
    }
})

When I use a timer though, with JavaScript function window.setTimeout() with 2 seconds delay, it works.

Can anyone help me with this? Is my understanding of the afterRender() method correct? I'm expecting this method to do stuff on the DOM ounce my component has completely loaded its content (DOM), kind of an equivalent of the jQuery function .ready().

Here is my Markup:

<aura:component controller="OfficeCtrl" implements="flexipage:availableForAllPageTypes" access="global"> 
    <aura:handler name="init" action="{!c.doInit}" value="{!this}" />
    <aura:registerEvent name="OfficeEvent" type="c:OfficeEvent" />
    <aura:attribute name="offices" type="Office__c[]" />
    <aura:attribute name="officesMap" type="Map" />
    <div class="slds-tabs--default">
        <ul class="slds-tabs--default__nav" role="tablist">
            <aura:iteration items="{!v.offices}" var="myOffice">
                <li class="myMenuItem slds-tabs--default__item slds-text-title--caps" title="{!myOffice.Name}" role="presentation" id="{!myOffice.Id}" onclick="{!c.selectOffice}">
                    <a class="slds-tabs--default__link" role="tab" aria-selected="false">
                        {!myOffice.Name}
                    </a>
                </li>
            </aura:iteration>
        </ul>
    </div>
</aura:component>

Here is the rest of the code of my component (it is not really related to the problem I'm facing though). Here is the controller:

({
    doInit : function(cmp, event, helper) {
        let getOfficesAction = cmp.get("c.getOffices");
        getOfficesAction.setCallback(this, function(response) {
            let state = response.getState();
            if (cmp.isValid() && state === "SUCCESS") {
                let lst_offices = response.getReturnValue();
                if (lst_offices.length == 0) {
                    return;
                }

                let map_offices = {};
                let size = lst_offices.length;
                for (let i = 0; i < size; i++) {
                    map_offices[lst_offices[i].Id] = lst_offices[i];
                }

                cmp.set("v.offices", lst_offices); 
                cmp.set("v.officesMap", map_offices); 

                let officeEvent = $A.get("e.c:OfficeEvent");
                officeEvent.setParams({"office": lst_offices[0]});
                officeEvent.fire();
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        $A.enqueueAction(getOfficesAction);
    },

    selectOffice: function(cmp, event, helper) {
        let map_offices = cmp.get("v.officesMap");
        let lst_offices = cmp.get("v.offices");
        let currentOfficeId = event.currentTarget.getAttribute("id");
        helper.selectOfficeHlp(lst_offices, map_offices, currentOfficeId);
    }
})

And here is the helper:

({
    selectOfficeHlp: function(p_offices, p_officesMap, p_currentOfficeId) {
        if (p_offices.length == 0) {
            return;
        }

        p_offices.map(function(myOffice) {
            document.getElementById(myOffice.Id).className = "myMenuItem slds-tabs--default__item slds-text-title--caps";
        });

        $A.util.addClass(document.getElementById(p_currentOfficeId), 'slds-active');
        this.fireOfficeEvent(p_officesMap[p_currentOfficeId]);
    },

    fireOfficeEvent: function(p_office) {
        let officeEvent = $A.get("e.c:OfficeEvent");
        officeEvent.setParams({"office": p_office});
        officeEvent.fire();
    }
})

Thanks a lot

  • 1
    Can you post the rest of your code as well? – Robert Sussland Dec 2 '16 at 23:39
  • OK, can you publish the rest of your code -- e.g. controller? – Robert Sussland Dec 3 '16 at 0:49
  • It is not really related to the problem I'm facing, but I will update my question. – Riadh Mankai Dec 3 '16 at 0:52
  • i am also looking for the solution. did you find one. if not can you please give your code with time delay. – ajay rawat Apr 6 at 3:03
1

Second try here...

For adding class Names I'd suggest you use $A.util.addClass(), the component has a chance to rerender a lot and it will clobber your changes if you don't set it on the component as well

$A.util.addClass(cmp, "slds-active");

You could also try using getElement() on the component

var element = cmp.getElement().classList.add("slds-active");
  • Thanks for the suggestion. The ret variable returns undefined (I displayed its content with console.log) – Riadh Mankai Dec 2 '16 at 23:54
  • 1
    You are right, thats only for superRender and superRerender. – Kris Gray Dec 2 '16 at 23:55
  • With the first one, I got the same result as before. With the second one, I got an Aura error: Cannot read property 'classList' of undefined – Riadh Mankai Dec 3 '16 at 0:07
  • How is the element getting output? Whats the markup for your cmp file? – Kris Gray Dec 3 '16 at 0:16
  • I updated the question to include the markup. It is a very simple component. I just want to add slds-active class after component load, so the first element of my list is automatically underlined in UI – Riadh Mankai Dec 3 '16 at 0:23
0

OK, afterRender only fires after the render function is called -- when your component is first rendered. It fires once. Then your object will load and the afterRender will not fire again. Rerender will fire, so try putting this into a rerender instead.

Or if you want put it in a helper and call it from both renderers to handle the situation in which the object is loaded into your component by a parent in addition to being loaded from the server async. That would cover all cases.

Also, please do not search the whole document. Scope your query down to the element in your component. If someone uses the same classname in another component, you will be modifying their style. Once Locker is out everywhere, this type of encapsulation will be done for you, but in the meantime, please scope down your queries.

  • Thanks. I put it in both afterRender() and rerender(). It didn't work. – Riadh Mankai Dec 3 '16 at 1:35
  • Yeah you're right, good point (for the styling scoping). But I'm still not able to do what I wanted to do: changing component style after the component has completly finished rendering the Dom – Riadh Mankai Dec 3 '16 at 2:20
  • What happens when you put some debugger statements into your custom rerender? E.g. is your DOM element visible to the rerender? – Robert Sussland Dec 3 '16 at 2:55
  • I tried that. The element is undefined. It means that at that particular time, the element is not rendered yet. I tried the output on the afterRender() and the rerender() methods – Riadh Mankai Dec 3 '16 at 4:07

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.