Sunday 11 March 2018

Turning on the Lightning Locker Service

Turning on the Lightning Locker Service

Nuke

Introduction

This week I turned on the Locker service for an application that I wrote several years ago. It’s a few “pages” built from a fairly large number of custom Lightning components with a lot of JavaScript business logic. The application itself works fine without the Locker service, but there’s more and more standard components and features that I’d like to use, but that are only available in API 40+. I also have a JavaScript library that some of the components interact with, so I needed to upgrade all of my components at the same time, or risk some of them using a different window object.

I’ve made various attempts at this in the past, but always been defeated by weird errors that I was unable to isolate or reproduce. aura:if was often in the vicinity though, so it’s always my prime suspect. The last attempt was about 6 months ago and I’d created quite a few applications running on the latest API in that time, and I was hopeful that nth time is the charm, so I ran my script to update all of the meta-xml files to API 41 (there are 300 of them, so not really something that can be done manually) and deployed the app to my dev org. Here’s what I found.

Issues

Deployment Time

The first attempt failed with 19 errors, including the following:

  • Invalid Aura API - $A.util.json.encode. 
    This shows how long this application has been around - some of the very early examples in the Lightning Components Developers Guide etc used this method, but it’s been advised against for a while. I thought I’d cleared them all out, but had obviously missed one or two. This is a simple fix, just use JSON.stringify instead.

  • Invalid Aura API - $A.util.format.
    This is around replacing tokens such as {0} in strings/labels etc and can be replaced with the String.format standard JavaScript function, so rather than:

        $A.util.format(<string>, val);

    you would have

        <string>.replace(‘{0]’, val);

  • Invalid Aura API - this was thrown from a configuration item being passed to a JavaScript library function containing the text ‘onSelect’. I’m pretty sure that this was a false positive, but as this was something that I’d created an alternative pure Lightning version of, I don’t think I’ll be needing it going forward so sacked it off.

The biggest issue was : Invalid SecureWindow API, top was blacklisted. Even though this is a Lightning Experience application and uses LEX navigation, there’s still one place where I surface part of the application through Lightning Out, when creating a child object. Even though I’ve overridden the new action, this isn’t respected so I end up in the regular modal style new component.Thus I navigate to a Visualforce page instead and use Lightning Components for Visualforce to surface the contents. This is all fine until I want to get back to the man page for the app. I can’t use force:navigateTo methods by default, as these are only available in LEX, and if I set the window.location to a new value, that just changes the Visualforce iFrame embedded in the page. Thus I used window.top.location, as this changes the URL for the outermost window of the app.

There is a solution to this though - I can create my own handler for force:navigateToSObject event in the Visualforce page, and as there is no locer service in Visualforce I’m free to tinker with the outer window location to my heart’s content. Make sure that you add the dependency reference to the event to your Lightning app though, e.g.

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

I didn’t to begin with and spent a lot of time trying to figure out why it wasn’t working!

Run Time

I only hit two issues at runtime, and the biggest hurdle was actually getting the errors surfaced - I ended up taking a binary chop approach to find the problem component - commenting out half of the functionality at a time until i was able to narrow things down, then surrounding lots of code with try/catch exception handlers.

  1. Missing ‘var’ when using a variable, e.g.

        for (i=0; i<len; i++)

    Without the locker service, this will try to find a variable named ‘i’ against through the scope chain and, if it doesn’t find one, create it in the global scope. Almost certainly not what is required and in my cases definitely not. With the locker service, ES5 strict mode is enabled and this generates a reference error

  2. Getting non-existant attributes, e.g.

        var prop=cmp.get(‘prop’)

    Spot the problem? Missing the ‘v.’ namespace for the attribute, although in one case this was there but the attribute hadn’t been declared in the markup.

  3. Breaking encapsulation
    In this case, I was programmatically finding a component in the ‘ui’ namespace and changing an attribute. This is exactly what the locker service was created to stop, so no surprises there was an error. I’d completely forgotten it was there - it was a workaround to a bug with the standard select component where I couldn’t dynamically set the multi attribute based on one of my component attributes. It’s fixed now, and probably was years ago, so I just removed the offending code.

In my app, these are genuine bugs and it’s good to get them out of the way. Things obviously still work at the moment, but are fragile - in the first case, if there is an existing variable in the scope chain I’ll overwrite it’s value, which never ends well.

Conclusion

While I haven’t exhaustively tested every aspect of my app, the weird errors that I’ve seen in the past aren’t appear this time around. I’m sure that they weren’t all down to the locker service - I’ve fixed plenty of issues in my app over the years, but the locker service definitely made things more difficult to track down and proved to have plenty of gaps when used in anger. But for my purposes, it’s ready for prime time.

Related Posts

No comments:

Post a Comment