Andy in the Cloud

From BBC Basic to Force.com and beyond…


6 Comments

Overriding Standard Actions with Lightning Components

In this blog we take a look at the new Summer’17 ability to override record actions with Lightning Components. We will also see how the architecture of Lightning, is starting to emerge, by performing record read and updates via Lightning Data Service. I have shared here the full example used in this blog so you can use it as a starting point in future.

LightningActionsOverview.png

Overriding the standard actions in Lightning (also Salesforce1 Mobile) is something you might want to consider if you want to provide a more fine grained or guided user experience when users are creating or editing records.

While you can override record view this to me feels less useful given the extensive ability to customise the standard record page.  The tab view can also be overridden. However unlike Classic, there is no distinction between list view and tab view, hence overriding the list view action with a Lightning Component is not applicable.

Overriding Actions with Lightning Components

Under your objects actions, where we still see Visualforce Page action overrides, we now see a new option to pick a Lightning Component Bundle. The list shows all components that implement the new interface lightning:actionOverride.

Screen Shot 2017-05-29 at 14.41.09.png

<aura:component
    implements="lightning:actionOverride,
                force:hasRecordId,
                force:hasSObjectName">

Component Design, Context and Navigation

You may have noticed from the first screenshot, the component is not showing in the traditional edit modal popup. Components need to provide the full user experience, thus the WidgetRecordEdit component provides a header and buttons, as well as editing ability.

WidgetEdit.png

The following component markup renders the header and buttons shown in the screenshot above. The componentRecord attribute is used with the Lightning Data Service component described later in this blog.

    <div class="slds-page-header">
        <div class="slds-media">
            <div class="slds-media__figure">
                <lightning:icon iconName="custom:custom67" />
            </div>
            <div class="slds-media__body">
                <p class="slds-text-body_small slds-line-height_reset">WIDGET</p>
                <h1 class="slds-page-header__title slds-truncate slds-align-middle" 
                   title="{!v.componentRecord.Name}">{!v.componentRecord.Name}</h1>
            </div>
            <lightning:buttonGroup>            
                <lightning:button label="Save" onclick="{!c.onSave}" />
                <lightning:button label="Cancel" onclick="{!c.onCancel}" />
            </lightning:buttonGroup>
        </div>
    </div>

To get record Id you must implement at least the force:hasRecordId interface to obtain the recordId of the record when overriding the view and edit actions. When overriding other actions you will want to use the force:hasSObjectName interface.

Implementation Note: Unlike Visualforce page overrides that are explicitly linked with their object at development time and only useable with the stated object, your component can actually be assigned to actions on any object. The docs remind you of this fact and offer some best practices. However i do think a trick was missed not to support the sfdc:object qualifier in the .design file, please support this idea

The component is responsible for navigating the user back to the record view. This is done in the component client controller by sending the force:navigateToSObject event. The following code shows the code behind the Cancel button.

    onCancel : function(component, event, helper) {

        // Navigate back to the record view
        var navigateEvent = $A.get("e.force:navigateToSObject");
        navigateEvent.setParams({ "recordId": component.get('v.recordId') });
        navigateEvent.fire();
    }

Reading and Updated Records with Lightning Data Services

So now we know how to get the Id of the record to be edited and perform navigation back to the record view. How do we retrieve the record data and update it?

While we could use a service side Apex Controller for this, using Lightning Data Services integrates with cached data in Lightning and optimises network traffic. It is however limited to single records at present, which is not an issue here.

The following code uses the lightning:recordData component to query the record and fields (based on the layout definition) and later update the record. You can think of this as essentially a client side version of the Visualforce StandaradController.

    <aura:attribute name="record" type="Object" />
    <aura:attribute name="componentRecord" type="Object" />
    <aura:attribute name="recordError" type="String" />

    <force:recordData 
        aura:id="recordLoader" 
        recordId="{!v.recordId}" 
        layoutType="FULL" 
        mode="EDIT"
        targetRecord="{!v.record}"
        targetFields="{!v.componentRecord}"
        targetError="{!v.recordError}" />

When Save is pressed the controller code uses the saveRecord component method.

	onSave : function(component, event, helper) {

	    // Ask Lightning Data Service to save the record
        component.find("recordLoader").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS") {

                // Display popup confirmation to the user
                var resultsToast = $A.get("e.force:showToast");
                resultsToast.setParams({
                    "title": "Saved",
                    "message": "The record was updated."});
                resultsToast.fire();

                // Navigate back to the record view
                var navigateEvent = $A.get("e.force:navigateToSObject");
                navigateEvent.setParams({ "recordId": component.get('v.recordId') });
                navigateEvent.fire();
            }
            else {
                // Basic error handling
                component.set('v.recordError',
                    'Error: ' + saveResult.state + ', message: ' + JSON.stringify(saveResult.error));
            }
        }));
	}

Once the record is saved the force:showToast event is used to display a confirmation message and the force:navigateToSObject event to return to the record view. Note that the record view is also internally using Lightning Data Service and is aware of the edit that has just been made by your code without having to round trip to the server!

SaveToast.png

This component makes use of Lightning Base Components for minimum markup and maximum alignment with Lightning Design System.

    <lightning:layout>
        <lightning:layoutitem padding="around-small">
            <lightning:input 
                name="name"
                label="Name"
                type="text" 
                value="{!v.componentRecord.Name}"/>
        </lightning:layoutitem>
        <lightning:layoutitem padding="around-small">
            <lightning:input
                name="description" 
                label="Description"
                type="text"
                value="{!v.componentRecord.Description__c}"/>
        </lightning:layoutitem>
        <lightning:layoutitem padding="around-small">
            <lightning:input
                name="color" 
                label="Color"
                type="color"
                value="{!v.componentRecord.Color__c}"/>
        </lightning:layoutitem>
    </lightning:layout>

Note the use of the HTML5 color picker via the lighnting:input component to enhance this components user experience. Since there is no Color custom field type, this would normally be rendered as a simple text field.

LightningColorPicker.png

General Guidance

Finally before you get to carried away with overriding, consider the following guidelines.

  • Administrators can override your action override! And reinstate the standard native UI. So avoid encoding any validation behaviour in your component that should be enforced via a Validation Rule and/or Apex Trigger logic.
  • Consider custom Lightning Actions? Consider if its better to simply provide an alternative or advanced user experience for editing your record? If so, retain the current standard record view UI and simply add a new Lightning Action.
  • Consider customising Record View? You can add your own components to the record page UI to add alternative views or calculated information. Its also possible to have components that edit details of the record. This blends the record view and inline edit a bit, so consider this carefully from a design perspective.


2 Comments

Understanding how your App Experience fits into Lightning Experience

Lightning Experience is not just a shiny new looking version of Salesforce Classic. Nor is it just some new cool technology for building device agnostic responsive rich clients. Its a single place where users access your application and of course others from Salesforce and those from AppExchange. Its essentially an application container a home for apps!

Like any home, its important to know how it works and how to maximise your experience in it. How do you make the occupants (your users) feel like its one place and not just bolted together. I decided to create the following graphics to summarise Lightning Experience container features. I have removed all the default actions, components you get from Salesforce, so we can easily see what it offers in a raw state.

Imagine the Widget App has several UIs to it…

  • Home page, customisable by the user using your components and others
  • Widgets tab that allows user to manage the widget records
  • Widget Manager organise your widgets, easy to access any time
  • Widget Utilities common information, contextual, easy to access any time
  • Widget Builder is a completly custom UI for constructing bigger widgets

Home Page, Utility Bar and Global Actions

The Home page is actually shared  between all applications in Lightning Experience. You can choose to include it in your tabs or not. If you do, users can customise it with Lightning App Builder by dragging Lightning Components on it that you or others provide. New to Global Actions for Spring’17 is the ability to add Lightning Actions.

lexcontainer1

widget.pngWhen you see the cog image with the Lightning logo in it, it means that that space can be anything you imagine! Because that space is driven by a Lightning Component!

Global Action Popup Management

When the user selects a Global Action, Lightning Experience automatically provides some useful features. The popup allows the user to close it, minimise or maximise it.

lexcontainer3Record Page and Record Actions

Record page content is determined by a number of things, object Actions, object Record Layout and Lightning pages (created by Lighting App Builder) associated with the object. Lightning pages are scoped based on the active application, profile or record type.

lexcontainer2

Lightning Tabs

Provide the biggest real-estate for your entirely custom UI needs. The utility bar and global actions are however still available for your users to call on at any time!

lexcontainer4

Increasingly in each Salesforce release we are seeing more and more extensibility emerging. The above graphics are designed to get you thinking about how best to leverage Lightning Experience when designing your application. Read my other blogs relating to Utility Bar and Lightning Actions.


Leave a comment

Flow in Winter’17 Lightning Experience

A short blog charting an evening with Flow, Lightning Experience in Winter’17

Flow on Record Detail Pages

Screen Shot 2016-08-30 at 22.11.47Flow makes its presence known in Lightning App Builder this release (in Beta) and with it some new possibilities for customising the Lightning Experience user experience, as well as Salesforce1 Mobile. I decided to focus on the Record Detail Pages as you want to see how it passes the recordId. As you can also see Flow gets an automatic face lift in this context, making them look properly at home!

Screen Shot 2016-08-30 at 22.05.48

This is the Screen element showing the passed information from Lighting App Builder…

Screen Shot 2016-08-30 at 22.08.48.png

By creating an Input variable in your Flow called recordId of type Text (see docs). Lightning App Builder will automatically pass in the record Id. You can also expose other input parameters, e.g. CustomMessage so long as they are Input or Input/Output.

Screen Shot 2016-08-30 at 22.00.19.png Screen Shot 2016-08-30 at 22.09.58.png

These will display in the properties pane in Lightning App Builder. Sadly you cannot bind other field values, but this does give some nice options for making the same Flow configurable for different uses on different pages!

Screen Shot 2016-08-30 at 22.04.27.png

Flow Custom Buttons with Selection List Views

Winter’17 brings with it the ability to select records in List Views. As with Salesforce Classic UI it will show checkboxes next to records in the List View, IF a Custom Button has been added to the List View layout that required multi-selection.

In my past blog Visual Flow with List View and Related List Buttons, prior to Winter’17. I was not able to replicate the very useful ability to pass user selected records to a Flow in Lightning Experience. I am now pleased to report that this works!

FlowListViewSelection.png
FlowOverSelectedRecords.pngThis results in the flow from my previous blog showing the selected records. As you can see, sadly because we are using a Visualforce page the lovely new Flow styling we see when using Flow (Beta) support in Lightning App Builder does not apply. But hey being able to select the records is a good step forward for now!  The setup of the Visualforce page and Custom Button is identical to that in my previous blog.

Screen Shot 2016-08-30 at 21.43.43.png

Summary

Flow continues to get a good level of love and investment in Salesforce releases, which pleases me a lot. Its a great tool, the only downside is with more features comes more complexity and thus a great need to stay on top of its capabilities, a nice problem to have!

 

 

 


28 Comments

Winter’17: Using a Lightning Component from an Action

Screen Shot 2016-08-21 at 17.19.18Back in 2013 i wrote a blog post with a very similar name, How To: Call Apex code from a Custom Button. It continues to gather a significant number of hits. Its a common task as its good to first consider extending the Salesforce UI’s before building your own. The Custom Button approach actually still works very well in Lightning Experience and still for now has some benefits. However Lightning Experience is increasingly offering more and more ways to be customised, Home Page, Record Detail and now Actions!

Visualforce and Standard Controllers have long since been the main stay for implementing Custom Buttons. However for any of those that have tried it, you’ll know that Visualforce pages need some work to adopt the new Lightning Design System style. So what if we could link a natively built and styled custom Lightning UI with a button?

Well in Winter’17 we can! Custom Buttons are out in the Lightning world, what are hip and trendy these days are Actions, as i mentioned in my Platform Action post, Actions are fast becoming the future! Or in this case Lightning Component Actions.

Force.com IDE and Lightning Components

Screen Shot 2016-08-21 at 15.10.07.pngI have also used this as a chance to get familiar with the recently announce Force.com IDE Beta, which supports editing Lightning Components. It worked quite well, the wizard creates the basic files of a component include template controller and helper files.

The auto complete also worked quite well in the component editor. There is also quite a neat outline view. To create a design file (not actually needed here) i had to create this as a simple text file in Eclipse and be sure to name it after my component with .design on the end. After this the IDE seemed to pick it up just fine, though it found it does not save with the other component files as i would have expected.

Screen Shot 2016-08-21 at 18.33.54

Creating an Lightning Component Action

As with the Record, Tab and Home pages, a new interface, force:lightningQuickAction, has been added to the platform to indicate that your component supports Actions. I used the sample in the Salesforce documentation to get me started and it works quite well. The following is the component markup, i’ll cover the controller code later in this post.

<aura:component implements="force:lightningQuickAction">
<!-- Stupidly simple addition -->
<ui:inputNumber aura:id="num1"/> +
<ui:inputNumber aura:id="num2"/>
<ui:button label="Add" press="{!c.clickAdd}"/>
</aura:component>

What was not immediately apparent to me once i had uploaded the code, was that i still needed to create an Action under Setup for object i wanted my action to be associated with. I chose Account for this, the following shows the New Action page i completed. It automatically detected my Lightning Component, nice!

Screen Shot 2016-08-21 at 17.21.49

I then found My Action under the Layout Editor, which was also a little odd since i have become so used to finding my components in Lightning App Builder. I guess though the distinction is record level vs page level and hence the Layout Editor was chosen, plus existing actions are managed through layouts.

Screen Shot 2016-08-21 at 18.21.10.png

Once i updated the Layout,  My Action then appeared under the actions drop down (as shown at the top of this blog). As you can see below the component is wrapped in a popup with a system provided Cancel button. I chose to use the force:lightningQuickAction interface as per the docs. The force:lightningQuickActionWithoutHeader hides the Header and Cancel button shown, though popup close X button is still shown.

Screen Shot 2016-08-22 at 00.55.06

The Component Controller code for the sample component shows how you can programatically close the popup and deliver a user message via the toast component. I enjoyed learning about this while I looked at this sample. Extra credit to the documentation author here!

({
clickAdd: function(component, event, helper) {

// Get the values from the form
var n1 = component.find("num1").get("v.value");
var n2 = component.find("num2").get("v.value");

// Display the total in a "toast" status message
var resultsToast = $A.get("e.force:showToast");
resultsToast.setParams({
"title": "Quick Add: " + n1 + " + " + n2,
"message": "The total is: " + (n1 + n2) + "."
});
resultsToast.fire();

// Close the action panel
var dismissActionPanel = $A.get("e.force:closeQuickAction");
dismissActionPanel.fire();
}
})

Firing the toast event created in the above sample looks like this…

Screen Shot 2016-08-21 at 18.23.20

Context is everything…

The force:hasRecordId interface can be used to determine which record the user is looking at. Simply add it to your component like so…

<aura:component
implements="force:lightningQuickAction,force:hasRecordId">
Record Id is {!v.recordId}
</aura:component>

Note: I have it on good authority, that contrary to some samples and articles the you do NOT need to define the recordId property via aura:attribute.

Summary

In short i am really getting quite excited by the amount of places Lightning Components are starting to popup in, not just more places within Lightning Experience, but Salesforce1 Mobile, Communities and even Lightning Outlook. Join me at my Dreamforce 2016 session where we will also be looking at Lightning Out: Components on any Platform, featuring Google App Addins.


3 Comments

Lightning Out: Components on Any Platform

This blog is my first video blog! Since Salesforce does not record the Developer Theatre sessions at the Salesforce World Tour events i thought i would do a re-run at home of my session last week and publish it here. As you know i have a love for all things API’s, and while I typically focus in this blog on backend API’s, there is one i’ve been keen to explore for a while…

The Lightning Out API, as any good API should, brings great promise and reality i’m pleased to say, to further integrating and extending the power of the platform and generally simplifying our users lives. In this case boldly going where no Lightning Component has gone before….

You can access the slides and thus the links within via this Slideshare upload.