Tweet |
Managing a List of New Records with Lightning Components
Nearly 7 years ago I wrote a blog post on Managing a list of New Records in Visualforce, and I thought it would be interesting to recreate this using Lightning Components and compare the two.
The basic concept is that I have a collection of new account records and I want to be able to enter details, add or remove rows and save once I’m happy. In Lightning I’ve created a couple of components to manage this - one that looks after the collection of records (NewAccounts) and one that captures the input for a single account (NewAccount). In the following screenshot each row equates to the NewAccount component:
One interesting aspect is that the list of records is managed outside of the rows, but each row has a button to allow it to be deleted. While I could try to juke around with the styling to line things up, this is an excellent use case for a Component Facet. A Component Facet is an attribute passed to a contained component that is itself a collection of components. As it is defined in the outer component, it can reference aspects of the outer component. In my case it defines the controller function called when the user clicks the delete button and includes the index of of the element in the collection of records in the button name, so that I can easily locate and remove the element:
<c:NewAccount account="{!account}" index="{!index}"> <aura:set attribute="buttons"> <div class="slds-form-element"> <label class="slds-form-element__label">Actions</label> <div class="slds-form-element__control"> <lightning:button label="Delete" onclick="{!c.deleteRow}" name="{!'btn' + index}" /> </div> </div> </aura:set> </c:NewAccount>
The first major difference is that in Visualforce I have to create my collection of Account records server side, in the constructor of the page controller, while in Lightning my NewAccounts component creates these in it’s init handler:
init : function(component, event, helper) { var accounts=[]; for (var idx=0; idx<5; idx++) { accounts.push({sobjectType:'Account'}); } component.set('v.accounts', accounts); }
The only field that I’m defining when I create each account record is the sobjectType - I don’t think that I actually need this, as on the server side I use a strongly typed array of Account records, but I find it’s a great habit to get into. In terms of the user experience there’s probably not a lot to choose here though - the Visualforce page will take a short while to be created and returned, and Lightning pages are hardly .. lightning fast.
However, all that changes when the user adds or deletes rows. In Visualforce I have to send the list of records back to the server and then carry out the appropriate action. In my lightning component, this is handled in the JavaScript controller. for example when deleting a row:
deleteRow : function(component, event, helper) { var name=event.getSource().get("v.name"); var index=name.substring(3); var accounts=component.get('v.accounts'); accounts.splice(index, 1); component.set('v.accounts', accounts); }
I get the index from the name which has the format ‘btn<index>’ so I just use the array substring prototype function to strip off the ‘btn’ and then use the Array.splice prototype function to remove the element at that position.
In Visualforce I’d probably show some kind of spinner to let the user know that something has happened, whereas in Lightning this happens so quickly there’s no chance for me to get in between and show something. If I really want to draw the users attention, I’d use CSS to highlight the element that was created or do some kind of slow motion hiding of the element before removing it.
When the user decides to save is the place where I have to do more work in Lightning. In Visualforce I would simply bind the button to a server side action, insert the updated accounts property, and set a page message, maybe after some checking of how many were populated etc. In Lightning I have to figure out the populated records, instantiate a controller action, add my records as s property, hand over to apex and then process the results. While it sounds like a fair bit to do, it actually isn’t that bad, especially if I create a utility function to process the response that all of my components can utilise, which I do every time for production code.
saveRows : function(component, event, helper) { var accounts=component.get('v.accounts'); var toSave=[]; for (var idx=0; idx<accounts.length; idx++) { if ( (null!=accounts[idx].Name) && (''!=accounts[idx].Name) ) { toSave.push(accounts[idx]); } } var accAction = component.get("c.SaveAccounts"); var params={"accountsStr":JSON.stringify(toSave)}; accAction.setParams(params); accAction.setCallback(this, function(response) { var state = response.getState(); if (state === "SUCCESS") { var toastEvent=$A.get("e.force:showToast"); if (toastEvent) { toastEvent.setParams({ "type":'success', "title":'Success', "message":'Accounts saved' }); toastEvent.fire(); } } else if (state === "ERROR") { var errors = response.getError(); if (errors) { if (errors[0] && errors[0].message) { reject(Error("Error message: " + errors[0].message)); } } else { reject(Error("Unknown error")); } } }); $A.enqueueAction(accAction); }
Note that I’m sending my list of records back as a JSON string, a habit I got into when the Lightning Components framework had problems with array parameters. I still use it occasionally so that my controller methods can handle multiple types of parameters. I’m always in two minds as to whether this is a good thing - it makes the code more flexible, but more difficult to understand what is going on without appropriately typed parameters.
There’s not a lot more to the code, but if you want the full picture it’s available on Github.
Related Posts
- Lightning Component Actions with Signature Capture
- Lightning Components, Visualforce and SObject Parameters
- Lightning Component Events - Change Types with Care
- Lightning Component Events
- Lightning Components and Unobtrusive JavaScript
- Lightning Components and Custom Apex Classes
- Lightning Component Wrapper Classes
A treasure trove of info and code. Thanks!!
ReplyDeleteI am an admin but I am looking for something similar to this to add to a flow that I am building. The list would be a child object of account and the account name would be a lookup. Is this easy to tweak? Thank you.
ReplyDelete