Lessons Learned From My First Lightning Component

I decided to write my first Lightning component and, as always, found it easier to “borrow” some code from a fellow blogger and modify it to suit my needs.

It turned out to be quite an interesting journey and I ran into various issues that I could not find answers to easily.  I am going to share some of them with you so you can avoid these speed bumps and find some of your own!

In my case, I have a Lightning component that is placed in a Visualforce page.  This component in turn contains many nested components.

Here are the weird problems I struggled with:

1. Problems Passing Non-Primitive Parameters to Apex Methods
2. Problems Returning Objects to Lightning from Apex
3. Uncaught Error: Bootstrap’s JavaScript requires jQuery
4. Cannot Read Property ‘childNodes’ of Null
5. Problems Using component.find in Object-Oriented Lightning Components
6. JSON&aura.formatAdapter=LIGHTNING_OUT Failed to load resource:

1. Problems Passing Non-Primitive Parameters to Apex Methods

 

Lesson learned: When you need to pass Lists, Maps, Objects – basically anything other than primitives – convert them to a String.

In my case, I was passing a List of Objects from my Lightning component to my Apex method. The error that I found in the debug log for my Apex method was:

FATAL_ERROR|System.UnexpectedException: null External entry point

What made it especially hard to debug this was that the error occurred deep in my Apex method and not when the Apex method first started or when the parameter was first used. Debug statements printed out the value of the parameter perfectly! It took me quite a while to figure out the cause of this error.

The Solution:

In your Lightning component, use JSON.stringify to convert your object to a String:

[code language=”javascript”]
action.setParams({ strFilters : JSON.stringify(component.get(“v.lstFilters”)) });
[/code]

In your Apex method, convert it back:

[code language=”javascript”]
MyClass[] lstFilters = (List<MyClass>)System.JSON.deserializeStrict(strFilters, List<MyClass>.Class);
[/code]

2. Problems Returning Objects to Lightning from Apex

 


Lesson learned: If you are returning an object to your Lightning component from your Apex method, make sure that you use @AuraEnabled for EVERY member of your class that you need to access from your Lightning component.

If your object includes instances of other objects, do the same for them as well.

If you do not, the Lightning component will receive empty objects!!

Example:

[code language=”javascript”]
public class TabularResponse
{
@AuraEnabled
public String error {get;set;}
@AuraEnabled
public List<List<FieldData>> fieldDataList {get; set;}
}

public class FieldData
{
@AuraEnabled
public Object fieldValue {get; set;}
@AuraEnabled
public String fieldLabel {get; set;}
@AuraEnabled
public String dataType {get; set;}
@AuraEnabled
public Boolean isHyperLink {get; set;}
}
[/code]

3. Uncaught Error: Bootstrap’s JavaScript requires jQuery

 


In my case I was using Bootstrap, so I needed to include both the jQuery and Bootstrap JavaScript libraries. I also needed to ensure that jQuery was loaded before Bootstrap.

I followed the instructions in the documentation and created Static Resources for jQuery and Bootstrap, and then included them in my component like this:

[code language=”javascript”]
<ltng:require scripts=”/resource/jQuery2_2_3_minjs, /resource/Bootstrap/js/bootstrap.min.js” />
[/code]

When I looked at the preview of my Visualforce page, I saw this error at the bottom:

Uncaught Error: Bootstrap's JavaScript requires jQuery

Unable to find the cause of the error, I showed my code to a co-worker who wondered aloud whether the resource name for the jQuery must end with “.js,” even though the mime type was correct – text/javascript.

I decided it was worth a shot. I found that the name of my Static Resource could not contain a period so I put my jQuery script file in a folder, zipped that and uploaded it as a static resource.

So now my code looked like this:

[code language=”javascript”]
<ltng:require scripts=”/resource/jQuery/jquery2_2_3_min.js, /resource/Bootstrap/js/bootstrap.min.js” />
[/code]

And voila! The error disappeared!

4. Cannot Read Property ‘childNodes’ of Null

 


My page loaded the first time without any errors. However, when I tried to refresh my component, I got the following error:

Cannot read property 'childNodes' of null.

This is what I had in my component.

[code language=”javascript”]
<!– myTable.cmp –>
<aura:iteration var=”row” items=”{!v.apexResponse}”>
<c:myRow row=”{!row}” isHeader=”false”/>
</aura:iteration>
[/code]

I googled and found various suggestions such as wrapping the child component in a span.  None of which worked for me. Here is what I did to get rid of the error, but I am not sure why this worked.  Hopefully, this is a bug that will be fixed in the future.

Both myTable.cmp and myRow.cmp included the Bootstrap library:

<ltng:require styles="/resource/Bootstrap/css/bootstrap.min.css"/>

I removed the library from myRow.cmp and my error vanished!

5. Problems Using component.find in Object-Oriented Lightning Components


Lesson learned: If you need to use component.find to get to an item, make sure it is in the inherited component (not in the abstract component).

My abstract component looked like this:

[code language=”javascript”]
<!– c.superComponent –>
<aura:component extensible=”true” abstract=”true”/>

<div><ui:spinner aura_id=”spinner” isVisible=”false”/></div>

</aura:component>
[/code]

The helper of the abstract component contained methods to hide and show the spinner:

[code language=”javascript”]
<!– superComponentHelper.js –>
({
showSpinner : function (component) {
var spinner = component.find(‘spinner’);
var evt = spinner.get(“e.toggle”);
evt.setParams({ isVisible : true });
evt.fire();
},

hideSpinner : function (component) {
var spinner = component.find(‘spinner’);
var evt = spinner.get(“e.toggle”);
evt.setParams({ isVisible : false });
evt.fire();
},
})
[/code]

The child component used the hide and show methods in its helper, while calling apex methods:

[code language=”javascript”]
<!– childComponentHelper.js –>
({
hdlDoSomething : function(component, event) {
this.showSpinner(component);
var action = component.get(“c.getSomething”);
action.setCallback(this, function(response){



this.hideSpinner(component);
});
$A.enqueueAction(action);
},
})
[/code]

I had installed Chrome’s Lightning Inspector tool and checked the actions while loading the page.  I found a failed action:

[code language=”javascript”]
aura://ComponentController/ACTION$reportFailedAction
Parameters
-{
“failedAction”: “c$childComponent$controller$doSomething”,
“failedId”: “220;a”,
“clientError”: “TypeError: Cannot read property ‘get’ of undefined”,
“clientStack”: “TypeError: Cannot read property ‘get’ of undefinedn at Object.c$superComponent.helper.showSpinner
[/code]

As you can see, the showSpinner method was failing a “get”:

[code language=”javascript”]
showSpinner : function (component) {
var spinner = component.find(‘spinner’);
var evt = spinner.get(“e.toggle”);
evt.setParams({ isVisible : true });
evt.fire();
},
[/code]

That meant that the component.find(‘spinner’) was returning undefined.

I moved the div withui:spinner into the childComponent and the showSpinner and hideSpinner methods into the childComponent’s helper to solve this problem.

6.JSON&aura.formatAdapter=LIGHTNING_OUT Failed to load resource:

 


My page was looking great.  I made a some changes to a component, saved it and refreshed my Visualforce page only to see a blank page!

I checked my Console log and saw this:

https://c.****.visual.force.com/c/****.app?aura.format=JSON&aura.formatAdapter=LIGHTNING_OUT Failed to load resource: the server responded with a status of 404 (Not Found)

I tried various things and during this process happened to open my Lightning app (the .app file) and changed something and hit Save.  An error message popped up:

Failed to save undefined: The attribute "row" was not found on the COMPONENT markup://c:TableRow: Source

I had changed the name of an input attribute in a child component and saved it.  The parent was still passing the old attribute name and this was causing the failure.

Lesson Learned: If your Visualforce page is blank, edit your Lightning app and save to find the error.

In Conclusion

Despite some quirky behavior, I thoroughly enjoyed writing Lightning components.  I love the event driven architecture.  I am very much looking forward to my next Lightning project.

If you are reading this before you write your first Lightning component, I hope I have saved you some time!

If you have run into any such peculiar problems with Lightning, or have alternate solutions or explanations for any of the ones I have talked about, please comment below.