1

I am trying to get some data from Apex controller and then change the styling of icons once component is rendered.

Here is the sample code:

HelloWorldApp.app

<aura:application extends="force:slds" controller="HelloWorldApexController">
    <aura:attribute name="accList" type="Account[]"></aura:attribute>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <table aura:id="table-aura-id">
        <thead>
            <tr>
                <th>Col Label</th>
            </tr>
        </thead>
        <tbody>
            <aura:iteration items="{!v.accList}" var="accVar">
                <tr>
                    <td>
                        <lightning:buttonIcon iconName="utility:comments"
                                              size="medium"
                                              name="secondIcon"
                                              aura:id="iconInsideTable"
                                              alternativeText="Hello"
                                              iconClass=""/>
                    </td>
                </tr>
            </aura:iteration>
        </tbody>
    </table>
</aura:application>

HelloWorldAppController.js:

({
    doInit : function(component,event,helper){
        var action = component.get("c.getAccounts");
        action.setParams({
        });
        action.setCallback(this, function(actionResult) {
            console.log('Server call inside doInit finished');
            component.set("v.accList",actionResult.getReturnValue());
        });
        $A.enqueueAction(action);
    }
})

HelloWorldApp.css:

.THIS .bgred{
    background-color:rgb(0,191,255);
}

HelloWorldAppRenderer.js:

({
    afterRender: function(component,helper){
        this.superAfterRender();
        console.log('afterRender event handler is running now');
        var iconCmpList = component.find('iconInsideTable')
        iconCmpList.forEach((iconCmp) => {
            iconCmp.set("v.iconClass","bgred");
        });
    }
})

HelloWorldApexController.cls:

public class HelloWorldApexController {

    @AuraEnabled
    public static List<Account> getAccounts() {
        return [SELECT Id,Name FROM Account LIMIT 2];
    }    

}

This is the error I am getting:

enter image description here

afterRender event handler is running before the doInit method is finished and it is not finding elements. Here is the browser console log:

enter image description here

What do I do to fix this? I want to change css of the elements(conditionally in js due to complex calculations) once the dom is constructed.

5
  • 1
    You should be using CSS as much as practical. There's hardly any reason to use afterRender or rerender. Worst case scenario, consider using the render event instead. It is called each time the DOM is constructed or modified by the framework. You might want to read more in the documentation.
    – sfdcfox
    Commented Jan 9, 2018 at 21:43
  • @sfdcfox I think I needed little push. I have gone through the documentation and found the simple solution.
    – javanoob
    Commented Jan 10, 2018 at 1:45
  • 1
    So javanoob, what was the solution? I need access to elements within a child component, the child component is from a managed package so I cannot use events. I just need to hook into something that fires after all child components have rendered. Commented May 21, 2020 at 19:52
  • @Shane I went wit this(salesforce.stackexchange.com/a/204073/16304) for this particular problem but not sure if it would work for you as you are talking about child components.
    – javanoob
    Commented May 21, 2020 at 19:55
  • Fortunately I was able to just use the rerender() override in my case. None of the other lifecycle events had the child components. But in my case, I only need to access the child record after some user interaction happens. Commented May 21, 2020 at 21:14

3 Answers 3

1

It would be loads easier to write your iconClass as an expression.

If the logic is fairly simple, then you could just write something like this:

iconClass="{!(accVar.SomeField == 'something' ? 'bgred' : 'bggreen')}"

Then the framework will re-run that expression as required and you don't have to handle render at all.

If the logic is a bit more complicated, you can just add a property to the Account object after you get it back from Apex:

({
    doInit : function(component,event,helper){
        var action = component.get("c.getAccounts");
        action.setParams({
        });
        action.setCallback(this, function(actionResult) {
            console.log('Server call inside doInit finished');
            var accList = actionResult.getReturnValue();
            accList.forEach(function(thisAcc) {
              thisAcc.displayClass = /* whatever complex logic */;
            };
            component.set("v.accList",accList);
        });
        $A.enqueueAction(action);
    }
})

Then,

iconClass="{!accVar.displayClass}"
0
1

Obviously iconCmpList has no forEach function. Hence the error.

console.log( iconCmpList ) to verify.

Maybe component.find() just isn't matching the elements or it's a timing issue and the DOM is really not there at a certain moment in time. Both happened to me and I wasn't able to clearify it completely.

I had several issues using afterRerendet and component.find. My current workaround is using jquery instead of component.find (maybe not necessary) and additionaly I'm doing any DOM manipulation in the callback defined in action.setCallback

in the callback I put any DOM tweaks after the last component.set(). The rerendering of the component itself seams to happen synchronously. Caveat emptor: not sure, if this always true. One exception I'm aware of are subcomponents having callbacks on their own. but I doubt that any render function will give you a super late hook after the entire chain of subcompos has finished asynchroniosly their renderes with the last and slowest subcompo, though.

First a was afraid that the rendering might generally happen asynchroniosly. Also working with any render event or with a dedicated renderer sounded way more reasonable than tweaking the DOM in the callback.

But in the callback it works surprisingly well for me.

Using jquery to find elements i had another issue querying for the id attribute. It turned out that salesforce had redered a second invisible version of the dom. Therefore I always use the class attribute to identify my elements. That never faild me.

1
  • Thanks. I also ran into multiple versions of the component when locating elements using DOM selector so I was finding elements relative the element located by this selector document.getElementsByClassName('active oneContent')[0] As this was couple of releases ago, this time I wanted to see if there is a new API to get rid of this hack. Thanks for your time.
    – javanoob
    Commented Jan 10, 2018 at 1:56
1

I started going through the documentation again and found this Handle the render Event

Added the below tag in the component

<aura:handler name="render" value="{!this}" action="{!c.onRender}"/>

And added this function to the controller.js file:

onRender : function(component,event,helper){
        var iconCmpList = component.find('iconInsideTable')
            iconCmpList.forEach((iconCmp) => {
            iconCmp.set("v.iconClass","bgred");
        });
    }

Deleted the Rerender file completely.

With the above changes, it is working fine now.

You must log in to answer this question.

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