Quick Look: Lightning Components
A Flash of Lightning
During Dreamforce '14, Lightning made a big splash. For those of you who don't know, Lightning is the recently renamed Aura component UI framework with a stronger focus on client-side controllers. Why the rename, I don't know, but I guess Lightning fits better with the whole cloud motif. (Coming next year: Salesforce Thunder, Salesforce Squall, Salesforce Cumulonimbus).
And so, on a stage surrounded by 7 or so inward facing screens (shouldn't those be facing the audience? I can't see anything from back here!) a woman whose name I forget demonstrated the ease in which she could create mobile apps with the Lightning App Builder. She dragged and dropped a few components, resized them with a few clicks and voila! You have just built your first app!
While the crowd clapped one of my non-developer managers asked me how useful this actually was. I gave him a BS answer ("It'll streamline mobile development!"), but to be honest I had no idea. Because as nice as this was, you still need to build the actual components, which the presenter conveniently glossed over. I know walking through code doesn't make for engaging presentations, but you can't just throw in some buzz words like jQuery and Javascript and call it a day!
Off to the Developer Zone I go.
One of the challenges in the Developer Zone was building a simple Lightning component. I had never been to Dreamforce before so sitting down to do some actual coding that I could do at home seemed weird, but it turned out to be a nice way to meet some other developers, plus I really wanted that Tile they were giving away for completing the challenges (which, by the way, doesn't work on Android yet so my Tile does little more than an actual tile).
A Taste of Building a Lightning Component
The app was a simple component with two inputs: one for Celsius and one for Fahrenheit. Celsius should default to 0 and Fahrenheit should default to 32. Typing in either input should update other input field with the converted value. Easy enough.
Before I could start, I needed to create a namespace for my developer org. Which seemed weird, but apparently the name space is used to include your components in applications. For example, with a namespace of scuevo:
<aura:application> //I wonder if they will stick with aura or change it to lightning
<scuevo:myAuraComponent/>
</aura:application>
This seems like it would be inconvenient especially for consultants that are working in several different orgs at once; I wonder if components and applications can shared like unmanaged packages?
After namespacing my org, I opened the Developer Console (I don't believe there is any IDE support yet), and created a new Lightning Component, which appears to have 6 parts:
- The component itself
- A Client-Side controller for your Javascript
- A Helper, which I guess non-controller logic goes?
- Style for your CSS (more on this in another post)
- Documentation
- A Renderer (I don't know what this does)
For this simple component, I am going to just use the component and a controller.
The first thing I did was create the component with some simple inputs. Lightning includes some special tags similar to visualforce :
<aura:component>
<aura:attribute name="celsius" type="Decimal" default="0"/>
<aura:attribute name="fahrenheit" type="Decimal" default="32"/>
<ui:inputNumber value="{!v.celsius}" label="Celsius"/>
<ui:inputNumber value="{!v.fahrenheit}" label="Fahrenheit"/>
</aura:component>
The aura attributes allow us to bind values to different tags. For those of you familiar with AngularJs, this would be similar to ng-model. Setting defaults was easy enough as well as a type. I'm not sure how to typing is enforced, as I can still type non-numbers in the input fields. It also looks like the "v." notation is used to represent an attribute value in merge fields.
So now I needed to add some controller logic, so I created a controller which is just a javascript file.
({
convertCelsius : function(component, event) {
var celTemp = component.get("v.celsius");
component.set("v.fahrenheit", (celTemp*1.8)+32);
},
convertFahrenheit : function(component, event) {
var fahTemp = component.get("v.fahrenheit");
component.set("v.celsius", (fahTemp-32)/1.8);
}
})
It appears that javascript functions have to be enclosed in a object. Doing the following won't compile:
function aFunction(){ doSomething; }
I also updated my component to the following:
<aura:component implements="force:appHostable">
<aura:attribute name="celsius" type="Decimal" default="0"/>
<aura:attribute name="fahrenheit" type="Decimal" default="32"/>
<ui:inputNumber value="{!v.celsius}" keyup="{!c.convertCelcius}" label="Celsius"/>
<ui:inputNumber value="{!v.fahrenheit}" keyup="{!c.convertFahrenheit}" label="Fahrenheit"/>
</aura:component>
I couldn't find the documentation for all the attributes that
Everything seemed to work ok, but the inputs were only updating AFTER I took focus off of the input field. For example, after typing 100 in the Celsius box, the Fahrenheit box wouldn't update until I left the Celsius one. While close, it's not exactly right so I pressed on.
As the documentation didn't list the supported tag attributes, I had to dig through the source code, which is fortunately available on GitHub . I finally stumbled upon the updateOn attribute, and while I didn't know what it accepted I just tried throwing keyup into it. And it worked!
<aura:component implements="force:appHostable">
<aura:attribute name="celsius" type="Decimal" default="0"/>
<aura:attribute name="fahrenheit" type="Decimal" default="32"/>
<ui:inputNumber updateOn="keyup" value="{!v.celsius}" keyup="{!c.convertCelcius}" label="Celsius"/>
<ui:inputNumber updateOn="keyup" value="{!v.fahrenheit}" keyup="{!c.convertFahrenheit}" label="Fahrenheit"/>
</aura:component>
While completely unstyled, the Lightning component was complete, I got my Tile and I was off to another session with a dumb smirk on my face for figuring it out.
I barely scratched the surface of Lightning, but it's definitely a promising framework that will might replace some of the AngularJs in my future applications, at least in Salesforce. I still need to play with server-side actions, seeing how apps handle multiple components including the same libraries, how libraries play with components themselves and figure out what the other parts of the componnt resource bundle do that I just completely ignored. Also, the documentation leaves much to be desired, but I'm sure that will get better as time passes and this leaves beta.
Have you built any Lightning components? I'm sure they do much more than just convert temperature units so I'd love to see them!
Tests and SObject Factories
Usually I would find one of two things:
- SObjects would be created inside the tests themselves:
static testMethod void myUnitTest(){
Account testAccount = new Account();
testAccount.Name = 'Test Account';
testAccount.Website = 'Website';
insert testAccount
//run tests
} - Some kind of TestingUtils.cls with EVERYTHING related to creating test fixtures
@isTest
public class TestingUtils{
public static List<Account> buildAccounts( Integer numOfAccounts ){
List<Account> testAccounts = new List<Account>();
for( Integer i = 0; i < numOfAccounts; i++ ){
testAccounts.add( new Account() );
}
return testAccounts;
}
//700 lines later...
public static List<Task> buildTasks( Integer numOfTasks ){
List<Task> testTasks = new List<Task>();
for( Integer i = 0; i < numOfTasks; i++ ){
testTasks.add( new Task() );
}
return testTasks;
}
Test fixtures coded in the test will break the second you make a new required field, which will require you to find and fix every fixture.
The TestingUtils class at least keeps everything in one place, but it is rife with it's own issues:
- It will continue to grow into a massive god class as you add more fixtures and all of the weird edge cases( e.g. buildAccountWithContacts, buildAccountWithParentAccount etc.)
- Not DRY at all: the same basic pattern is used in each of the methods: build n objects and return the list of objects.
- Inevitable merge conflicts this causes when several developers all need to modify this file in each of their feature branches
When I became the Lead Developer at Apto, I quickly fell into old habits. While working with another developer a few months later, I lamented to him about my increasingly growing TestSObjectFactory.cls and how I wish I could make it more modular. With a confused look, he asked me why I don't just do it. It then dawned on me that this was our house and we could make the rules as we saw fit (If you couldn't tell, I love xckd).
So we implemented a SObjectFactory abstract class that other factories could extend from. The common logic was handled by the abstract class and each extended class can handle all of the unique logic and hold any required fields.
For Example:
@isTest
public class AccountFactory extends SObjectFactory{
public SObject buildRecordWithData(){
return new Account( Name = 'Test Account ' + counter );
}
}
Then you can call all of the abstracted methods:
AccountFactory factory = new AccountFactory();
List<Account> accountInstances = factory.buildRecords(10);
List<Account> insertedAccounts = factory.buildAndInsertRecords(10);
Account accountInstance = (Account)factory.buildRecord();
Account insertedAccount = (Account)factory.buildAndInsertRecord();
You can also pass in a map of fields to values:
final String EXPECTED_FIELD_VALUE = 'www.example.com';
Map<Schema.SObjectField, Object> fieldtoValue
= new Map<Schema.SObjectField, Object>{ Account.Website =>
EXPECTED_FIELD_VALUE };
AccountFactory factory = new AccountFactory();
factory.setFieldToValueMapping( fieldtoValue );
Account accountInstance = factory.buildRecord();
System.assertEquals( EXPECTED_FIELD_VALUE, accountInstance.Website );
The code is much DRYer, no more repeated logic, and less chance for merge conflicts.
But aren't you worried about class explosion?
Class explosion is much better than dealing with a god class. Each SObject is very different and thus should be treated as so. The toilet factory doesn't belong in the same place as the candy factory, that would just be weird and confusing.
Feel free to check out the code and use it and your org, it's using the MIT License. But the real moral of the story is, reflect on what you are coding. If you foresee the mess you are creating, then you should probably stop and think and it over.
Programming as Art
Or at least someone that makes something. There is something romantic about the idea of the artist toiling deep into the night with brush/hammer/bow in hand, perfecting their craft. That BEHOLD moment when they first gaze upon their completed work, a piece of their soul molded into some concrete form. Unfortunately I never had the patience for crafting a skill like that and any progress was motivated more by becoming an artist as opposed to improving the skill itself.
So I started to program. It's not the sexiest art, but I think it's art nonetheless. And I happen to really enjoy doing it, you might even say that it's a bit of a passion. At least, it's enough of one that I've decided to write about it here. And if I can keep writing about it maybe I'll get a little better. And if I get good enough maybe I won't be "so-called" anymore.