1

When I load this Lightning component a child component's init gets a null pointer as an attribute doesn't yet exist. I is populated via an Apex action during the parent components init. I know that a Child components init is called before the parents init. Nonetheless I need to make this work without dynamic components. And I'd expect this to be a very common scenario.

Can this be solved by events or change handlers or refresh view calls??

Markup

<aura:component controller="MyCtrl" implements="force:lightningQuickAction,force:hasRecordId">
    <aura:attribute name="attributes" type="List" access="private" />

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

    <c:attributeList items="{!v.attributes}" />
</aura:component>

Controller

({
    init: function(cmp, evt, helper) {
        var action = cmp.get("c.queryAttributes");
        action.setCallback(this, function(response) {
            cmp.set("v.attributes", response.getReturnValue());
        });

        $A.enqueueAction(action);   
    },
});
3
  • 1
    Without seeing the code throwing the npe it is hard to say. Typically using aura if checking the condition of the attribute can work well. Or a bit of defensive js code etc
    – Eric
    Sep 12, 2017 at 7:25
  • 2
    Maybe this is not what you're searching for but I had the same problem. Instead of using the init handler on the child component, I use an handler on the attribute value change like this: <aura:handler name="change" value="{!v.items}" action="{!c.doInit}"/> Sep 12, 2017 at 7:27
  • @Eric Using aura:if is like passing in a non-null default. That doesn't help in my case as I want the child to wait or reload when the parent has prepared its input. Sep 12, 2017 at 7:44

4 Answers 4

3

I tend to use both an init method and a change handler on the attribute that could be set after init e.g. of such a child component:

<aura:component access="global">

    <aura:attribute name="topicName" type="String" />

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:handler name="change" value="{!v.topicName}" action="{!c.doInit}"/>

</aura:component>

JS:

({
    doInit : function(component, event, helper) {
        var topicName = component.get('v.topicName');

        if(!$A.util.isEmpty(topicName)) {
            // Do whatever I need to do
        }
    }
})
1

you can use instead of init on child component Lightning method, that do the same. Call this method on child from parent init

Markup

<aura:component controller="MyCtrl" implements="force:lightningQuickAction,force:hasRecordId">
    <aura:attribute name="attributes" type="List" access="private" />
    <aura:handler name="init" value="{!this}" action="{!c.init}" />

    <c:attributeList aura:id="attributeiList" items="{!v.attributes}" />
</aura:component>

Controller

({
    init: function(cmp, evt, helper) {
        var action = cmp.get("c.queryAttributes");
        action.setCallback(this, function(response) {
            cmp.set("v.attributes", response.getReturnValue());
            var attributesList = cmp.find("attributeiList");
            attributesList.doInit();
        });

        $A.enqueueAction(action);   
    },
})

c:attributeList

<aura:component>
    <aura:attribute name="items" type="List" access="public" />
    <aura:method name="doInit" action="{!c.doInit}"/>
</aura:component>
0

Its seems i have not found any error while running above code. Child is displaying parent list properly . Here is what i tried on your code.

Apex Controller : MyCtrl

global with sharing class  MyCtrl {
   @auraEnabled
    public static List<contact> queryAttributes()   {
        List<contact> contants=new list<contact>();  
        contants=[select id,name from contact limit 10];
        return contants; 
    } 
}

component : attributeList

<aura:component  implements="force:appHostable" access="global"  >
    <aura:attribute name="items" type="list"/>   
    <aura:iteration var="cont" items="{!v.items}" >                
            <h2>{!cont.Name}</h2> <br/>     
    </aura:iteration>
</aura:component>

Here is the Application i used to call child component attributeList

APP : app191782

<aura:application controller="MyCtrl" extends="force:slds" >
    <aura:attribute name="attributes" type="List" access="private" />
    <aura:handler name="init" value="{!this}" action="{!c.init}" />
    <c:attributeList items="{!v.attributes}" />    
</aura:application>

Controller

({
    init: function(cmp, evt, helper) {
        var action = cmp.get("c.queryAttributes");
        action.setCallback(this, function(response) {
            cmp.set("v.attributes", response.getReturnValue());
        });

        $A.enqueueAction(action);   
    },
});

Here is the result i get. Working Properly

enter image description here

4
  • 1
    The original question was about the "attributes" component being null in the init method of the child component. Your example has no init method in the child - it just works as expected with re-rendering. Which is great, but I don't think that it answers the question.
    – Aidan
    Sep 12, 2017 at 10:21
  • @Aidan if i add statement " <aura:handler name="init" value="{!this}" action="{!c.doInit}"/> " to child its does not differ in displaying record. Handler init execute function for first time right.
    – NITHESH K
    Sep 12, 2017 at 10:39
  • 1
    If you were to log the value of component.get('v. items') from the doInit method, you would find the value to be null. It gets filled in later, after the Apex call is done. As far as I understand the question, the null value in the init method is the problem, not whether or not the records can be displayed.
    – Aidan
    Sep 12, 2017 at 10:53
  • @Aidan I got the statement now. thanks for more explanation.
    – NITHESH K
    Sep 12, 2017 at 10:57
0

As you know that child component init will be called first.

So, you can do one thing here. Add condition for showing your child component.

For Example.

 <aura:component controller="MyCtrl" 
   implements="force:lightningQuickAction,force:hasRecordId">
   <aura:attribute name="attributes" type="List" access="private" />
   <aura:attribute name="parentInitialized" type="Boolean"/>
   <aura:handler name="init" value="{!this}" action="{!c.init}" />

   <aura:if isTrue ="{!v.parentInitialized}">
     <c:attributeList aura:id="attributeiList" items="{!v.attributes}" />
   </aura:if>
 </aura:component>

Controller

({
    init: function(cmp, evt, helper) {
    var action = cmp.get("c.queryAttributes");
    action.setCallback(this, function(response) {
        cmp.set("v.attributes", response.getReturnValue());
        // **once init done and your resposne is here [which child component needed].**
        cmp.set('v.parentInitialized', true);
    });

    $A.enqueueAction(action);   
},
});

You must log in to answer this question.

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