4

Good afternoon everyone,

I have a question regarding the use of the $A.createComponents() Lightning function. This may be somewhat complex so I will try to make it as clear as possible for everyone's benefit, including my own.

1.) At the Application level, I currently have the following function in my client-side controller:

doInit: function(cmp, event, helper) {

        //Dynamically Add component to application body on application load
        $A.createComponents([["c:SelfServiceTimeOffRequestAction",{attribute:true}]],
                            function(newCmp, status){ 
                                if (cmp.isValid() && status === 'SUCCESS') { 
                                    cmp.set("v.body", newCmp); 
                                } 
                            });
    },

The purpose for this function is to dynamically insert a specified component into the body of the application when the application loads. Currently, it is loading the"SelfServiceTimeOffRequestAction" component when the application loads.

2.) I have a second function in my application client-side controller that performs the same function except as an event handler for a custom Lightning event that fires based on button clicks or anchor links in other child components to provide navigation functionality between components in the application.

handleAddComponent : function(cmp, event, helper){
        var cmpId = event.getParam("cmpId");
        if(cmpId !== null && cmpId !== ''){
            var cmpFinal = "c:" + cmpId;
            $A.createComponents([[cmpFinal,{attribute:true}],],
            function(newCmp, status){ 
                if (cmp.isValid() && status === 'SUCCESS') { 
                    cmp.set("v.body", newCmp); 
                } 
            });
        }

In the application markup, I have declared all components as dependencies so that any component can be passed to either of these functions and a new call to the component server will not be required each time. See this post for more information.

<aura:application implements="force:appHostable">

    <!-- Attributes -->
    <aura:dependency resource="markup://c:*" type="COMPONENT"/>

    <!-- Registered Application Events -->
    <aura:registerEvent name="HideArticleDescription" type="c:HideArticleDescription"/>

    <!-- Event Handlers -->
    <aura:handler value="{!this}" name="init" action="{!c.doInit}"/>
    <aura:handler event="c:AddComponent" action="{!c.handleAddComponent}"/>

    <!-- Application Body -->
    <div class="container-fluid">
        <!-- New Case Modal -->
        <c:SelfServiceNewCaseModal />
        <div class="row">
            <!-- Header -->
            <c:SelfServiceHeader />
        </div>
        <div class="row">
            <div class="col-md-12">
                <div class="row">
                    <!-- Main Body -->
                    **{!v.body}**
                </div>
                <div class="row">
                    <c:Footer />
                </div>
            </div>
        </div>
    </div>    
    <!--*******************************-->
     <!-- Script Files -->
    <ltng:require scripts="/resource/CommunityResources/js/jquery.min.js, /resource/CommunityResources/js/bootstrap.min.js" afterScriptsLoaded="{!c.OnScriptsLoaded}"/>
</aura:application>

I have highlighted the area where I am dynamically adding the application body using {!v.body}.

3.) My issue lies in this next step. Within the one of the child components contained within the SelfServiceTimeOffRequestAction component that is loading when the application initializes, I am calling the $A.createComponents() function again to dynamically add a child component to the SelfServiceTimeOffRequestAction component based on some criteria.

This is the markup reference in the SelfServiceTimeOffRequestAction to the first child:

<div class="col-md-6">
                        <c:SelfServiceTimeOffActionForm />
                    </div>

Within the SelfServiceTimeOffActionForm component is there the $A.createComponent() function is used a second time.

This is the full function in the helper of the SelfServiceTimeOffActionForm component:

getSelfServiceSettingSteps : function(cmp){
        //Call the listTimeOffRequests Apex class method 
        var action = cmp.get("c.getSelfServiceSettings");
        var self = this;
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var result = response.getReturnValue();
                cmp.set("v.noOfSteps", result.No_of_Steps__c);

                var noOfSteps = cmp.get("v.noOfSteps");
                if(!$A.util.isEmpty(noOfSteps)){
                    console.log('Steps Not empty');
                    var firstStep = (noOfSteps) - (noOfSteps - 1);
                    if(firstStep === 1){
                        **$A.createComponents([["c:SelfServiceActionFormStep1",{attribute:true}]],
                                            function(newCmp, status){ 
                                                if (cmp.isValid() && status === 'SUCCESS') {
                                                    console.log('Create Cmp Success');
                                                    cmp.set("v.body", newCmp);** 
                                                } 
                                            });
                    }
                }
            }
        });
        $A.enqueueAction(action); 
    },

What I am seeing is that the SelfServiceTimeOffRequestAction component will render before the SelfServiceTimeOffActionForm component finishes rendering, which presents a blank area of the containing component. I was thinking that declaring all components as a dependency at the application level would prevent the application from rendering until all components had completed rendering an retrieving their data. Ideally, I would like for the application to not render to the page until all the components are visible.

I have provided a link to a video recording to show and clarify what I am seeing on the page: Click HERE for the video.

Does anyone have some suggestions regarding how I could resolve this?

Thanks!

1
  • Hey everyone, as I have looked at this more, I'm wondering if it is possible to prevent a component from rendering until it has successfully received data from the server during initialization? Commented Aug 3, 2015 at 17:45

1 Answer 1

3

As to your question about waiting to render a component till a server action has concluded, no you can't do it. Aura under the covers has some abilities to do this, but we're trying to remove them and they aren't exposed in the platform version anyway.

The chatter feed kind of has the same issue, the component loads, fetches it's feed items and then draws them. I would suggest you follow the same pattern they do by showing a stencil. (Stencil being a non descriptive image of what the output will eventually kind of look like)

This has the benefit of improving performance for the user who thinks the content is very close to being finished loading.

Dependencies

In regards to dependencies, adding any dependencies to your component has no effect on rendering. It has to do with downloads. When you create a ui:button, it's likely that component is already downloaded from the server. But we can't download every component definition, so if you use one that we don't have yet, we go to the server to fetch it. Using the dependency is a way to tell the system that it should also download x, y, z components so it doesn't need a round trip for each one when it's used.

7
  • Kris, thanks for that info. What would be the impact of using something like <aura:dependency resource="markup://c:*" type="COMPONENT"/> at the application level where all the custom built components would be referenced as wildcards? Commented Aug 6, 2015 at 14:29
  • We used to do something similar for One.app, and the payload of components was just very large. If you don't have many components, it's not a big deal. Though the system will get better and better at knowing which components to include and your dependencies will start fighting that.
    – Kris Gray
    Commented Aug 6, 2015 at 14:31
  • Good to know. In that case, what is the best practice for determining which components to include as dependencies? I was assuming it is mainly needed when using the $A.createComponents() method, which I am using in my .app controller and in one of the child components of the application. If I declared dependencies at the application level, I was thinking that it would not be necessary to declare them again at the child component level, but I might be wrong. Commented Aug 6, 2015 at 14:36
  • You are correct it's for $A.createComponents(). It's not where we want it to be feature wise, it's a focus of our next release. For now, putting in dependencies in your component for those components you plan to create is really best. If your namespace is small, using the wildcard isn't that bad.
    – Kris Gray
    Commented Aug 6, 2015 at 14:38
  • Also, having a dependency tag at the app level is sufficient, unless your component gets used outside your app. :)
    – Kris Gray
    Commented Aug 6, 2015 at 14:38

You must log in to answer this question.

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