One of the most exciting features in Summer ’17 is the Lightning Data Service. Through its force:recordData
object, you’ll be able to build faster—with less code! In this post, we’ll break down everything there is to know, and how to leverage force:recordData
in your own Lightning Component development – and there’s even a Trailhead project to help you earn a badge!
A common question developers ask when moving from Visualforce to Lightning Components is, “why do I have to write code to call an Apex controller just to save or update a record”? Happily, the first step toward answering that question appeared in the Winter ’17 release with the Developer Preview of force:recordPreview
. With the Summer ’17 release, that feature has been renamed force:recordData
and is officially in Beta, so it can now be used everywhere!
A quick review
If you’ve not heard of force:recordData
, it’s part of the Lightning Data Service. It basically eliminates the need to write both an Apex Class and client-side JavaScript to create, read, update or delete a single record. Before we examine an actual use-case, let’s first do a quick force:recordData
review. force:recordData
uses the following syntax:
In addition to the aura:id
, you’ll notice that force:recordData
has a recordId attribute that it uses to retrieve the record itself. The record data is retrieved automatically by the component when the recordId
is assigned, such as when the component loads with the force:hasRecordId
interface.
How much of the record is returned—that is, which fields—is determined by the layoutType
attribute. We can either return all of the fields defined in the standard Record Detail page layout (FULL), the fields in the compact page layout (Compact), or only the fields you choose with the fields
attribute.
Once retrieved, the record result is stored in the aura:attribute
defined by the targetRecord
attribute. The Lightning component then begins to “listen” for that record to be updated by either the standard Record Detail layout or another component on the page.
Wait, did you catch that? Another component on the page?! By default, a component’s declaration of force:recordData
is in “VIEW” mode. In other words, the attribute mode="VIEW"
is the default, and is automatically applied to the force:recordData
instance. However, as developers, we can explicitly set the value to mode="EDIT"
, meaning that the force:recordData
instance is capable of modifying the record.
For example, we could have component A and component B on a page. In both components, we could declare an instance of force:recordData
with the same recordId. In component A, we set the mode to “EDIT”, while component B is set to the default mode of “VIEW”. Since both components are “watching” the same recordId, if component A updates the record, component B’s instance of force:recordData
will fire its own recordUpdated
function.
This is all pretty sweet, and I have been happily building components with force:recordPreview
for some time now. However, while building a component for the Global Lightning Now Tour’s hands-on workshop, I came up against a challenge that force:recordData
was able to elegantly solve for me.
Implementing force:recordData
The hands-on workshop uses the Dreamhouse application, which is a reference application that those of us on the Developer Evangelism team built to illustrate many of the development possibilities for Lightning Experience. This reference app is for a fictitious real estate company to help them manage houses, brokers, leads, and so on.
I built a Lightning Component that can be added to a Property Record page that shows properties which are similar to the selected property based on criteria such as price, number of bedrooms, square footage, and so on. The criteria used in the search is defined as a Design Parameter, and therefore can be selected by the Admin when they add the component to the page. This also allows multiple instances of the component to be on a page, each displaying properties based on different criteria.
The initial component is straightforward. It uses a force:recordData
instance, like the one above, to retrieve and monitor the record for the Property Record page being viewed. If, for example, the broker edits the price of the property on the Property Record page, the component’s force:recordData
catches the change and fires its recordUpdated
method, which causes the component to execute a new search for similar properties based on the new values of the Property Record. So far, so good.
Then I added the ability for the user to edit one of the similar properties being displayed, directly from the component itself, without actually navigating to its Property Record page. This was also straightforward—until I noticed a problem. If a particular property appeared in multiple instances of the component on the page, when I edited it from one of the components, its info didn’t update in the other component. Or, at least it didn’t until force:recordData
came to the rescue!
Using multiple instances of force:recordData
Using force:recordData
to monitor the current record on whose page the component is placed is fairly straightforward. But, we can also use force:recordData
to store and monitor record data that isn’t coming from the current record page. Remember, force:recordData
uses the recordId
assigned to it to retrieve the record data. So we can literally assign any recordId
to a force:recordData
instance, not just the recordId of the current record page.
So for the Similar Properties component, I made each item in the list a component itself, named SimilarProperty, with a force:recordData
instance containing the data for that particular property. This trick was the “secret sauce” to being able to update a “remote” record in the component, and having that same record update in another component without needing to reload!
force:recordData
is aware of all other instances of force:recordData
on the page with the same recordId
. Because of this, it means that it only retrieves a record once, even if it appears in multiple components. In other words, once record data is retrieved, all of the force:recordData
instances with the same recordId
and the default mode=”VIEW”
are “watching” the same copy of the record.
In the SimilarProperty component, you’ll notice that this instance is using the fields
attribute, instead of layoutType
, to only retrieve the few fields that we’re displaying or are interested in within the component.
In addition, it’s using the new targetFields
attribute instead of targetRecord
. This is an important change, if you’ve been experimenting with force:recordPreview
. During the Developer Preview, force:recordPreview
only used the targetRecord
attribute. With Lightning Data Service moving to Beta, the shape of the JSON response has changed. When retrieving data from the targetRecord, force:recordData
uses the syntax TARGET_RECORD_NAME.fields.FIELD_NAME.value
.This can seem a bit verbose, and therefore the targetFields
attribute was introduced, and uses the same (and more concise) syntax that force:recordPreview
used, for example {! v.TARGET_FIELDS_NAME.FIELD_NAME }
.
The final use of force:recordData
in the component is to pre-fill fields in a custom edit dialog, as well as handle the updating of the record when the Save button is clicked. The challenge, however, is that the edit dialog itself is also a separate component, named SimilarPropertyEdit
, added to the main Similar Properties component. Which begs the question, how do you tell the edit dialog which record is being edited?
Of course, components within the same namespace can communicate via events, but since all of the components were children of the main component, I settled on simply using attributes, allowing them to “bubble up” from the child component. You’ll notice that both child components have an attribute named remoteRecordId.
When the edit icon in the SimilarProperty component is clicked, it triggers a function named editRecord
.
The editRecord
function simply retrieves the recordId of the record that was clicked and sets the remoteRecordId
within the component. The attribute is, as mentioned, also bound to the main component’s remoteRecordId
attribute, as well as to the remoteRecordId
attribute in the SimilarPropertyEdit
component.
Dynamically setting recordId
In the SimilarPropertyEdit
component, you’ll notice that the force:recordData
instance doesn’t have a recordId
attribute:
Instead, the component has a handler that is listening for a change to remoteRecordId
and then fires the getRecord
function:
In the SimilarPropertyEditController:
When the edit button in the SimilarProperty
component sets the remoteRecordId
, it’s propagated up to the parent component and back down into the SimilarPropertyEdit
component, which in turn, triggers the change handler. The getRecord
function then sets the recordId
for the force:recordData
instance. Since there already is an instance of force:recordData
in the SimilarProperty
component with this recordId
, the component simply uses the data from that instance to populate its force:recordData
instance.
Finally, when the Save button of the custom edit dialog is clicked, the saveRecord
function is called, which updates the fields of the targetRecord
and then fires the saveRecord()
method of force:recordData
. If the save of the data is successful, all of the force:recordData
instances on the entire page—even those in different components—with that recordId
are updated without needing to go to the server! In addition, the function’s callback then fires an application event to notify other components in case they need to take action due to the update of the record.
In conclusion, Lightning Data Service and force:recordData
give Lightning Component developers a simple, straightforward way to create, read, update and delete individual records without the need to write an Apex controller. In fact, the SimilarProperties component only uses a single Apex method to execute the initial search based on the Design Parameter set by the Salesforce Admin. And yet, it reacts to changes to the main Property Record, as well as having the ability to update other records “remotely”. You can find the source code for the SimilarProperties component here, and make sure to do the whole project to earn a badge!
Be sure to check out the Release Notes for Summer ’17 for even more developer goodness, as well as the full Lightning Components Developer’s Guide.