Saturday, 19 May 2018

Lightning Utility Bar Click Handling in Summer 18

Lightning Utility Bar Click Handler in Summer 18

Bar

Introduction

As I’ve written about before, I’m a big fan of the lightning utility bar.It allows me to add components to an application that persist on the screen regardless of which tab/record the user is working in/on. One area that has always been a bit of a let down is that I have no way of knowing that the user has opened the utility bar item. In our BrightMEDIA appcelerator I have some utility items that can be added to applications that can change during the application’s lifecycle as components register and deregister. While I can hook in to the original rendering, the item doesn’t get fire a rerender when it is opened, so there is nowhere for me to latch on. In this case I usually put a message up that the information may be out of date and a ‘Refresh’ button - very familiar if the user has viewed Salesforce dashboards in the past.

Enter Summer 18

This problem goes away in Summer 18 (assuming this feature goes live, forward looking statement, safe harbor etc) as the lightning;utilityBar API gets a new event handler - onUtilityClick(). As per the docs, this allows you to register an event handler that is called whenever the utility is clicked.The utility bar API is a headless service component, which is the direction Salesforce are clearly heading for this kind of functionality. The only slightly tricksy aspect to this is that the headless component needs to be rendered in the utility before it can be accessed and a handler can be registered. I tried initialising the utility component at startup ,via the standard checkbox, but everything went null. For this reason I register the event handler from a custom renderer when my utility component handles the render event.

There’s a Sample, right?

As usual I’ve created a sample component to demonstrate this new functionality - this is a utility item that shows the total open opportunity value for my pre-release org user. It executes a server side method whenever the utility is clicked to make it visible. The short video below shows how the utility refreshes itself as when it is visible and displays the correct amount, even if I’ve just edited the opportunity I’m on.

The component includes the lightning utility API :

<lightning:utilityBarAPI aura:id="utilitybar" />

Which is then located by the rendered function to register the handler (a function from the helper) :

var utilityBarAPI = component.find("utilitybar");
var eventHandler = function(response){
    helper.handleUtilityClick(component, response);
};

and the helper method checks if the component is visible, via the panelVisible function of the response parameter passed to it by the platform, and makes a call to the server to recalculate the open opportunity value and display it:

handleUtilityClick : function(cmp, response) {
    if (response.panelVisible) {
        this.recalculate(cmp);
    }
},
recalculate : function(cmp) {
    cmp.set('v.message', 'Calculating ...');
    var action = cmp.get("c.GetOpenOpportunityValue"); 

    self=this;
    action.setCallback(self, function(response) {
        var result = response.getReturnValue();
        cmp.set('v.message', 'Open opps = ' + result);
    });
    $A.enqueueAction(action);
}

You can access the entire component, and it’s Apex controller, at the 

What else can this API do?

Check out Andy Fawcett’s blog for more details.

Related Posts

 

 

Friday, 11 May 2018

Lightning Component Navigation in Summer 18

Lightning Component Navigation in Summer 18

Introduction

In my last but one post (Toast Message from a Visualforce Page) I explained how I needed to solve the problem due to the force:createRecord event not respecting overrides, instead always opening the standard modal-style form and there was no GA mechanism to navigate to a component, force:navigateToComponent being still in beta.

A couple of days after, the Summer 18 release notes came out in preview and towards the end I saw that there was finally a solution for this.

lightning:isUrlAddressable Interface

The new lightning:isUrlAddressable interface allows me to expose a Lightning Component via a dedicated URL. I simply implement this on my component and my component is available at the (current) URL :

   https://<instance>.lightning.force.com/lightning//cmp/<namespace>__<component name>

However, one thing we can be certain of with Lightning Components is change, so rather than hardcoding the URL to my component, there’s a way to get the platform to dynamically generate it.

lightning:navigation Component

The new lightning:navigation component is a headless (i.e. does not have a user interface aspect) service component that exposes methods to help with navigation. The method that I’m interested in is navigate(PageReference). Using this I can create a PageReference JavaScript object that describes the location I want to navigate to and leave it up to the lightning:navigation component to figure out the appropriate URL to hit. 

The beauty of using this mechanism is that my components are decoupled from the URL format. If the Lightning Components team decide the change this, it has no impact on me - the Lightning Navigation component gets updated and everything still works as it always did.

Example

Note - as these features are part of Summer 18, at the time of writing they aren’t available outside of pre-release orgs, which was where I created my example.

I’ve created a component (WebinarOverride)that implements lightning:actionOverride, so that I can use it to override the default new action, just to show that it has no effect, and lightning:isUrlAddressable so that I can navigate to it.

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,force:hasRecordId,lightning:actionOverride,lightning:isUrlAddressable">
	<lightning:card iconName="standard:event" title="Override">
	    <lightning:formattedText class="slds-p-left_small" 
value="This is the webinar override component."/> </lightning:card> </aura:component>

Note that it doesn’t do anything in terms of functionality, it’s just intended to obviously identify that we’ve ended up at this component.

I then have a component that can navigate to the override called, creatively, NavigationDemo:

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
    <lightning:navigation aura:id="navService"/>
    <lightning:button label="Force Navigate" onclick="{!c.forceNavigate}" />
    <lightning:button label="Lightning Navigate" onclick="{!c.lightningNavigate}" />
</aura:component>

That this has two buttons - one that uses force:createRecord:

var createRecordEvent = $A.get("e.force:createRecord");
createRecordEvent.setParams({
    "entityApiName": "Webinar__c"
});
createRecordEvent.fire();

and one that uses the lightning:navigation component:

var navService = cmp.find("navService");
var pageReference = {
    "type": "standard__component",
    "attributes": {
        "componentName": "c__WebinarOverride" 
    }, 
    "state": {}
};

navService.navigate(pageReference); 

Note that as the navigation aspect is provided by a headless service component, I have to locate the components via cmp.find() and then execute the method on that. Note also my pageReference object, which has a type of standard__component (even though this is a custom component - go figure) and supplies the component name as an attribute.

I’ve overridden the Lightning Experience actions for the Webinar custom object with my WebinarOverride component:

Screen Shot 2018 05 06 at 10 14 56

So clicking the New button on the webinar home page takes me to the override. If I now open my NavigationDemo component:

Screen Shot 2018 05 06 at 10 18 27

clicking the Force Navigate button is disappointing as always:

Screen Shot 2018 05 06 at 10 18 37

however, clicking on the Lightning Navigate button takes me to my WebinarOverride component:

Screen Shot 2018 05 06 at 10 18 55

You can find the full component code at the Github repository.

Conclusion

The lightning:navigate component also retires the force:navigateToComponent() function, although it’s not clear if there was a WWE-style ladder match to determine who retired. This function has had a troubled existence, leaking out before it was ready and then being withdrawn, causing wailing and gnashing of teeth in the developer community, then entering a short beta before being deprecated. Farewell little buddy, you gave it your best shot but couldn’t cut it.

The headless component mechanism feels like the way that new functionality like this will be added, rather adding functions/events etc to the framework. I guess this keeps the framework as lightweight as it can be and doesn’t force functions on applications that don’t care about them.

One additional point to note - if you forget the lightning:isUrlAddressable interface on your target component the lightning:navigation function will still happily return the URL, but when you navigate to it you will get a not particularly helpful error message that the page isn’t supported in the Lightning Experience.

Finally, I’d still prefer force:createRecord to respect overrides. While I can make my new object component configurable via a custom setting or custom metadata type, from a low code perspective I don’t want to have to change two places to use a different one. Still, we’re in a better place that we have been for a couple of years.

Related Posts

 

 

Thursday, 3 May 2018

Lightning Application Events and Caching

Lightning Application Events and Caching

Spidey

Introduction

The lightning component framework has two types of event for intra-component communication:

  • Component
    Component events can be handled by the component that fired it, or any component above it in the containment hierarchy, so essentially any ancestor.

  • Application
    Application events can be handled by any ancestor, but also have an additional propagation phase that allows any component to receive the events, assuming they’ve already registered. 

I typically use application events in a broadcast scenario, where one of my components fires an event and a bunch of other components in my app receive it and take action. For example, when a user selects a value in a picklist and other components need to react to it. Which all worked fine until I had some cached pages.

Lightning Experience Caching

By default, the Lightning Experience will cache up to 5 last visited pages in order to speed up the browser back button. If I create a page that manages an account, and access this with the account id for Universal Contains and then AW Computing, both of these will be cached in the browser. This really speeds up performance, but has an unexpected side effect when using application events,

Even though the page managing Universal Containers is not visible, the components on it are still “live”. If my page has a custom component on it that receives application events, it will continue to receive application events while cached and not visible. This may not sound that bad, but in my case the component was receiving an event asking it to validate its contents and responding with another event to indicate the status. Both the component for AW Computing (that was visible) and the component for Universal Containers (that was not visible) received the event and responded to it. 

Once this happened I was caught in a classic race condition - whichever one responded first would be taken as the current state of my component. As always seems to happen with race conditions, it was the worst outcome that prevailed most of the time. I would be working on the AW Computing page, in which the component was in an invalid state, but the Universal Containers component, which was in a valid state, would respond first and processing would proceed when it should have been blocked. What it looked like at the time was that duplicate events were being fired - tracking down what was actually happening was rather a challenge.

What to do?

Sadly there was no way for the component to know that it had been cached, everything about the visible one was the same as the invisible one. I did try a solution where I destroyed the component once the user navigated off the page, but that meant that if I used the back button the page was cached but the component missing, which meant the user had to refresh the page - also not a great experience.

The actual fix was to move away from application events in this case and use component events. As the communication was between a custom component and it’s parent, it all worked fine. If that hadn’t been the case then I’d probably have MacGyvered something where (un)rendering the component updated a window expando to indicate which version is actually “live” - it would work but I’d feel a bit dirty for having to resort to such tricks.

Related Posts

 

Saturday, 21 April 2018

Toast Message from a Visualforce Page

Toast Message from a Visualforce Page

Introduction

Pretty much everything I do now around the user interface is via Lightning Components, typically added to a Lightning page so that I can also include standard components. There’s one aspect that continues to frustrate me - when I need to send the user to a custom new record page programatically, when they press a button in a custom component, for example.

  • I can’t use the force:createRecord standard event because, as the docs say, "This event presents a standard page to create a record. That is, it doesn’t respect overrides on the object’s create action.”.
  • I can’t use a Lightning page as these aren’t URL-addressable and there’s no way to navigate to them.
  • I can’t use force:navigateToComponent as that’s still in beta and, while I’m pretty sure it will be supported in the future, there’s no guarantee it will ever go GA.
  • I can use a Lightning app (.app bundle) as these are URL addressable, but when I do they take me outside the Lightning Experience.

So I use Lightning Out for Visualforce and force:navigateToURL to send the user there. 

Events

As my Lightning components are inside the Visualforce iframe, I have no access to the standard Lightning events, such as force:navigateToURL and, to the point of this post, force:showToast. If I try to instantiate one of these events in a Lightning component:

var evt=$A.get("e.force:navigateToURL");

I’ll get null returned.

For many events this isn’t the end of the world though, as I can add a handler in my Visualforce page when I dynamically create the component:

$A.eventService.
        addHandler({
                    event: 'force:showToast',
                    handler: function(event) { 
                                 // take some action
                    }
});

Then when I instantiate the force:showToast event I’ll get one, and when I fire the event my handler function will be invoked I’m still in Visualforce though, in an iframe and not executing in the one.app container, so it doesn’t help that much so far.

One really important aspect I always forget when adding the custom event handler in Lightning Out for Visualforce - my Lightning app that is the bridge between Visualfoce and the underlying Lightning components needs to declare a dependency for the event, otherwise nothing works. Another few hours of my life I won’t get back and even more annoying as it’s about the fifth time I’ve fallen for it:

<aura:dependency resource="markup://force:showToast" type="EVENT"/>

Posting a Message

Visualforce pages can communicate with Lightning components, via the window.postMessage() JavaScript function. It’s slightly clunky in that I need to identify the target origin that I want to send the message to, but I can pull the message from the toast event and send it on wrapped in a JavaScript object:

var lexOrigin = "https://kabtutorial-developer-edition.lightning.force.com";
var message={type: "EventFromVF",
             message: event.getParams().message};
parent.postMessage(message, lexOrigin);

So now all I need is a Lightning component that can receive this and raise it’s own toast event to actually display the message to the user. I called mine ToastReceiver and  a reduced version of the code is shown below :

window.addEventListener("message", function(event) {
    if ( (event.data.type) && (event.data.type=='EventFromVF') )
        var toastEvent = $A.get('e.force:showToast');
	toastEvent.setParams({
    	        type: 'info',
        	    message: event.data.message
	        });
    	toastEvent.fire();
    }
}, false);

My first thought was to wrap my Visualforce page in a Lightning component that iframes it in. This worked perfectly, but left me in exactly the same situation as before - I didn’t have a supported (GA) mechanism to programmatically navigate to that component. It seems that I’m stuck between a rock and a hard place - I need to navigate to a Visualforce page, but somehow that Visualforce page needs to message a Lightning component on the same browser page.

Enter the Lightning Utility Bar

The Lightning Utility Bar allows me to include the same set of Lightning components across every page of my app, so I added one to my Sales app and added my ToastReceiver to it, remembering to check the box that loads the component in the background when the app opens, otherwise my toast messages only appear after I’ve opened the utility bar element.

The diagram below shows the flow:

Screen Shot 2018 04 21 at 16 59 03

I enter a message in my Lightning component, which fires a toast event (1), this is picked up by the event handle in the Visualforce JavaScript, which posts a message (2) that is received by the Lightning component in the utility bar. This fires it’s own toast event (3) that, as it is executing in the one.app container, displays a toast message to the user.

Note that while my toast message originated from a Lighting component, this can also be used to display toast messages from regular Visualforce pages inside the Lightning experience, just post a message. All standard functionality and all generally available. Not a bad result for a Saturday afternoon’s work, even if I say so myself.

Demo

In the short video below my browser is initially on a Lightning page and clicking the button programmatically sends me to a Visualforce page via the force:navigateToURL event, containing a Lightning component with a message input and a button. Clicking the button starts off the flow described above.

Related Posts

 

Saturday, 14 April 2018

Managing a List of New Records with Lightning Components

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:

Screen Shot 2018 04 14 at 16 45 14

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

 

Saturday, 31 March 2018

Building My Own Learning System - Part 6

Building My Own Learning System - Part 6

Versions

Introduction

In previous posts in this series I covered the initial idea and development through to sharing the code with installation, configuration etc instructions. Now that I have the basics working I’ve started to iterate and add a few features. Oddly the first feature I added was the one that I most recently thought of - the ability to display a custom message to the user when they complete a path, retrieved from the path itself. I’m thinking that this will allow me to carry use the system for more interesting challenges - complete a path to get a keyword, or a location, something like that anyway,. I did say I’d just thought of it, not that I’d thought it through!  It was only a few lines of code to implement this, just a change to the PassStep method so that it returned a tuple of a completed state and message to display, A few tweaks to the unit tests and I carried out an sfdx deployment to my sample endpoint and verified with my updated client code that all was working as expected and my sample Bob Buzzard character path was displaying the custom message:

Screen Shot 2018 03 31 at 16 14 48

Then it hit me - anyone that didn’t have my new client would get errors when accessing the sample endpoint, and they’d have to dig into the debug logs to figure out why. Suddenly it was important to add another feature. (Looking at my sample endpoint it appears that there’s only me using it at the moment anyway, so I guess if you are going to break things then the earlier the better!).

Versioning

Like most things in the tech world, there are loads of different ways to handle versioning. I considered the Salesforce route of having different endpoints for each version and discounted it. I’m not convinced that I want to be supporting older versions and I don’t particularly want to get into managing a number of classes representing historic versions. Salesforce can do this because they have more than one person maintaining things in their downtime, and if I ever become a multi-billion dollar company I’ll reconsider.

The route I ended up going was defining the version for each of the client and server in code, and having the client send its version with every request to an endpoint. The endpoint then compares this to its version and decides if it can handle the request. This gives me the option of supporting older versions if I want to, without committing me to any level of service! If the endpoint can't handle the request it throws an exception indicating what needs to happen - either upgrade the client or ask the admin of the endpoint to upgrade that. The updated client displays the error message to the user who can jump on any required action.

Installing the Latest ... Version

The current version of the system is now V1.0, and I’ve created github releases for both the endpoint and client, both of which have been tagged as V1.0. 

The unmanaged package the client V1.0 release is : <Salesforce instance>/packaging/installPackage.apexp?p0=04t0O000001IqIm

Related Information

As I plan to continue with these posts as I add new features or learn that I’ve made a terrible mistake should have done things differently, I’ve moved the list of posts into a dedicated page on this blog. 

 

Saturday, 24 March 2018

Building My Own Learning System - Part 5

Building My Own Learning System - Part 5

Backend server theres something very familiar about all this

Introduction

In Part 1 of this blog series I covered the problem I was trying to solve (on-boarding/accrediting internal/external staff using common content, but without opening up everything to the entire world) and the data model I was using to manage this. Part 2 was around the fledgeling user interface and a fake service to prove confidence in the method. Part 3 covered the backend, or at least the initial implementation of this - as long as there is a local interface implementation to connect to it, the concrete backend can live anywhere. Part 4 walked through the front end setup and configuration and shared the code.

In part 5 I’ll cover installing the backend code, setup of a remote endpoint, creating a training path and configuring the new endpoint for your client to access. I’ll also share the code.

Code First

 The back end code lives at : https://github.com/keirbowden/bbtrnremote

Installation

As the back end manages the data, an unmanaged package isn’t an option as it would mean recreating all training paths etc each time there was an update. As I mentioned in Part 4, I don’t think a private managed package is the right thing for something that people might install in orgs with real data, so the back end is intended to be installed as a discrete set of components. For example, using the Salesforce CLI you could deploy from the cloned repository directory using 

    sfdx force:mdapi:deploy -d src -u <username>

where <username> is from an org that you’ve previously authorised. If you are using something other than the Saleforce CLI then best of luck - I’d switch to the CLI myself ;)

Configuration

There’s a bit more to the back end compared to the front end:

  1. Enable MyDomain (there is a Lighting action and overrides, although not as many as I’d like)
  2. Assign the Training Admin permission set to your user
  3. Create a tab for Training Paths - everything else is reachable from that
  4. Create a Training Path, including at least one step and at least one question in that step. You can also create a badge to go with it if you want -  the code will handle it either way.
  5. Create a Force.com site and note down it’s address.
  6. Add the Training Site permission set to the Guest User for the site (via Public Access Settings -> View Users)

Then switch over to your client org and configure the endpoint:

  1. Add a Training Endpoint custom metadata entry - name and label as you desire and the following fields populated:

    Hostname: https://<site address>
    Path: /services/apexrest/TrainAPI
    Rewrite Image Links: Checked

  2. Add the site address to the remote site settings

And away you go. If you get any errors, have a look at the debug logs. Typically errors will be data related and I find that the stack trace in the client logs shows me what the problem is.

Caveat Emptor

Same as with the front end, the error handling is pretty basic, I just let the errors make their way back to the client. If you are authoring a training path, make sure you have a test front end to try it out on before you make it available to your users.

Same as the front end again, nothing is labels. 

Creating a training path and most of the associated data is via the regular Salesforce object pages, so be prepared to traverse a bit. The exception to this is when creating a question. The New Question action on the Training Step page will create a new question and take you to a Lightning page that allows you to manage the question and all of it’s associated answer on a single page. Over time more of this type of assistance will be added. I haven’t really focused on it yet as this is the kind of thing that admins rather than users will be accessing as a rule.

Conclusion

If you hit problems, raise an issue in the appropriate Github repo:

I’m not sure what will be in the next instalment. I might go through some of the code in more detail, or there might be new features to talk about. Stay tuned.

Related Posts