9

I have a component basically consisting of 8 <aura:iteration>s. For each <aura:iteration> I have a dedicated attribute. So 8 aura:attributes are controlling 8 iterators. The types of all attributes are all lists of SObjects. For now all 8 attributes are set at the same time in the js-controller.

<aura:attribute name="Products"             type="object[]"             default="" />
<aura:attribute name="Battlecards"          type="object[]"             default="" />
<aura:attribute name="FAQAssignments"       type="object[]"             default="" />
<aura:attribute name="RequestTests"         type="object[]"             default="" />
<aura:attribute name="Testresults"          type="object[]"             default="" />
<aura:attribute name="Parameters"           type="object[]"             default="" />
<aura:attribute name="Trainings"            type="object[]"             default="" />
<aura:attribute name="Documents"            type="object[]"             default="" />

As observed also by others, massive <aura:iteration>s might lead to slow rendering. For some given reasons, I can't reduce the rendered data (e.g. by lazyloading).

What worked for me to speed things up from >10 seconds to some hundreds of milliseconds was to do the rendering via DOM manipulation on my own. I let the <aura:iteration>s only insert some skeletal HTML and then I use JS to do the rest (e.g. several picklist with lots of options are created on the fly). Lets call this post-process. Now everything is lightning-fast. I hate this approach, but I haven't anything better yet.

But one quirk is still there: I do my custom rendering still too often (namely 8 times instead of only once).

The problem seems to be that each iteration and it's invocation via attribute change causes a separate rereder-cycle. As said, currently all attributes are set at one time (synchronously). My post-process would best only run once, after the very last update is done and the last iterator has been rendered.

No I'm looking for something like a final "all-re-rendering-done" event or mechanism.

Idea (probably working)

It would possibly re-render only once, if I used only one composite attribute consisting of a list of the 8 SObject[]. Then I could set only this MASTER-attribute once instead of the 8 separate attributes.

But this would complicate things. Now the iterations are flat. They would become nested: top-level MASTER-attribute, sub-iterations on the SObject[]. Hardly viable because even the skeletal structure is too different and better kept separated.

Are there alternatives?

So I would prefer to keep them lose. Is there any mechanism to

  • either set multiple attributes at once?
  • or to combine multiple attribute updates into one rendering-cycle?
  • or to defer rendering so it renders only once?

Some more in-deep rendering questions:

  • is the rendering for synchronously set attributes also executed synchronously by the framework?
  • is there a way to tell inside the re-renderer or at the render-event which attribute update has caused the rendering?

If both would be possible, I could tell apart the last attribute from the others and render only then.

Any other ideas?

What events I have tried so far?

My post-process is omitted and only console.logs are shown. Each log is a candidate for post-process-invocation. Here is my renderer JS:

({
    render: function(cmp, hlp) {
        console.log('*** renderer:render'); 
        var ret = this.superRender();
        return ret;
    },
    rerender        : function(cmp, hlp) {
        console.log('*** renderer:rerender'); 
    },
})

And some handlers in the markup

<aura:handler name="render"                 action="{!c.render}"        value="{!this}"/>
<aura:handler event="aura:doneRendering"    action="{!c.auraDoneRendering}" />
<aura:handler event="aura:waiting"          action="{!c.auraWaiting}"/>
<aura:handler event="aura:doneWaiting"      action="{!c.auraDoneWaiting}"/>     

Handeled like that

render : function(cmp, evt, hlp) { 
    console.log('### event:render' ); 
},

auraDoneRendering : function(cmp, evt, hlp) { 
    console.log('### event:auraDoneRendering' ); 
},

auraWaiting : function(cmp, evt, hlp) { 
    console.log('### event:auraWaiting' ); 
},

auraDoneWaiting : function(cmp, evt, hlp) {
    console.log('### event:auraDoneWaiting' ); 
},

Now if I look into my console.log I got this for the initial rendering

COMPO.js: ### event:auraDoneWaiting 
COMPO.js: *** renderer:render 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraDoneWaiting 
COMPO.js: ### event:auraDoneWaiting 
COMPO.js: ### event:auraDoneWaiting 

And this on each re-render

COMPO.js: ### event:auraDoneRendering 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraDoneWaiting 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: *** renderer:rerender 
COMPO.js: ### event:render 
COMPO.js: ### event:auraDoneRendering 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraDoneWaiting 
COMPO.js: ### event:auraDoneWaiting 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraDoneWaiting 
COMPO.js: ### event:auraDoneWaiting 
COMPO.js: ### event:auraWaiting 
COMPO.js: ### event:auraDoneWaiting
  • Are there any relationships that exist between your objects? Knowing if there are any relationships that exist (or that could be artificially created in a logical manner) would be helpful. – crmprogdev Oct 1 '17 at 14:39
  • I would say the easiest solution would be to defer rendering via a flag that you check in your renderer and rerenderer. If it's false then don't render/rerender. Prior to setting the attributes in your large data structure, you'd clear the flag and set it again when you are done. – Caspar Harmer Oct 1 '17 at 16:40
  • @CasparHarmer how to defer rendering via a custom attribute or anyhow? In my experience setting a aura:attribute in the controller or help enforces a synchronous rerendering immediately. So the knowledge about a way of suppression is the answer to this question. – Uwe Heim Oct 9 '17 at 20:17
  • Yes but couldn't you about the render in the render function bad in the value of the flag? – Caspar Harmer Oct 9 '17 at 20:19
  • @CasparHarmer I think what you mean is to do my postprocess conditionally based on the attribute value, right? That indeed could work. All that leads me to something else: move the postprocess directly into the controller/helper. It seems that rerendering happens only after attribute change (and only synchronously) - what I need to know: are there other things which might invoke rerendering - or if rerendering might happen asynchroniosly, too? – Uwe Heim Oct 9 '17 at 20:38
1

You can't do it for iterations because iterations need to build themselves when their data changes.

You can delay it, as I'll explain below, but it most likely won't work for iterations.


Try this.

Put all your data on a state attribute.

<aura:attribute name="state" type="Map" default="{}" />

Then, edit the raw state object

var state = cmp.get("v.state");
state.Products = [...];
state.Battlecards = [...];
state.RequestTests.push('new');

Finally, when you're ready to rerender everything, do a set call.

cmp.set("v.state", state);

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.