28

(I'm hoping one of the Lightning Component developers will be able to chime in on this, as I'm rather concerned that the LockerService will break code using inheritance)

Salesforce are introducing the LockerService to tighten up security around Lightning Components. One feature of this is:

In addition, custom components will only be able to access public and documented Lightning JS APIs and won’t be able to access Lightning framework’s “private” APIs

In order to maximise re-use, I have implemented inheritance in a number of my components, where the sub-component overrides and extends the helper. The functionality is typically triggered from markup in the super component which invokes a controller method. However, as there is only the super-component controller, I have to find a way to access the sub-component helper. The only way that I've found to do this is to get the concrete (sub) component in the superclass controller, then access the helper via the concrete (sub) componentDef as follows:

superHelperFunction: function(component, event) {
    var cc=component.getConcreteComponent();
    var subhelper=cc.getDef().getHelper();
    // invoke sub-component helper method
}

However, ComponentDef has been removed from the Lightning Documentation App some time around Spring 16, which presumably means it is no longer documented and supported. It was there in Winter 16, which suggests it was released by mistake and then pulled, which rather scares me:

  1. When the LockerService is in place, does this mean that making something private and removing from the documentation app will suddenly cause any production JavaScript relying on that to fail? If so, will we be notified that a breaking change of this nature is on its way?

  2. If ComponentDef (and by extension getHelper()) are no longer supported, how are we supposed to access the concrete component's helper? I can't find anything suitable in the API documentation, which suggests that I'll have to replicate the markup and controller methods in my subcomponent, and that doesn't sound anything like inheritance to me.

19

LS itself has no direct impact on inheritance or other features in the lightning framework. It does however enforce existing visibility and access rules that previously were unenforceable.

As we've gone through the process of developing the service's secure virtual proxies we have identified a number of places where things were documented but actually not marked correctly as part of our public documentation and in those cases we've actually corrected the documentation. we have not removed access to something that was marked as part of the publicly supported API (unless it represented and unsupportable security risk of course - e.g. Document.innerHTML's getter).

The specific API change you are talking about above occurred long prior to locker service and we've been working off of the current set of classes and methods marked with the @platform annotation that indicates that they are part of the publicly supported, API version and surface area. I do know that we never intended for that incantation to be a public part of Lightning.

I am researching now to get more information about the nature of the change with regard to Component.getDef() from the folks that made the change a few releases ago. The pattern your using looks like https://en.m.wikipedia.org/wiki/Template_method_pattern, correct? We do need a supported way to implement that very useful pattern. For example instead of helpers what about using an interface with aura:method's that can be implemented in the subs instead (I have not verified this works yet - just an idea)? Helpers are meant to be an internal component detail and the fact that these are "inherited" has never been treated as part of the API - was always a hack or workaround at best.

Also please note that I fully expect the summer 16 lifecycle to include many additional updates to increase what is visible in the locker service. For example the next patch schedule for deployment tomorrow has over 50 additional APIs visible primarily in the secure virtual DOM. Some of these are because we discovered even more inconsistencies between what is marked as platform versus exported. Each and every method, class, property etc. has to undergo a very range of threat analysis depending on the complexity that is being proposed for exposure.

  • Thanks for answering Doug - yes, I'm pretty much using the template method. I've got a couple of ideas that I think will work in the absence of ComponentDef, but I didn't want to proceed with them if ComponentDef or A.N.Other method for handling this was going to become available shortly. – Bob Buzzard Apr 18 '16 at 11:33
  • 1
    I went back through the history in our source control repo and verified that Component.getDef() was never marked @platform which means it was never meant to be part of our supported external API. The problem is that we had an issue with the jsdoc generator that we use to generate the documentation for the platform and it was not filtering things down to "@platform". This resulted in more surface area being exposed than expected. The issue was corrected and that is why some of these internal methods went away. We need to find a supportable way to implement the template method pattern. – Doug Chasman Apr 18 '16 at 14:45
  • I've looped in the rest of my team for their suggestions and will make sure we find a way to support the design pattern and our security/supportability requirements simultaneously. – Doug Chasman Apr 18 '16 at 14:46
  • 1
    Another alternative is to use an attribute (inherited) on the super component and then add a change handler in the sub-component. If any code (including in the super component) changes that attribute, the sub-components handler function will be invoked. Pro: does not rely on controller or helper function inheritance Con: cannot invoke functions directly, only by changing data – Steve Buikhuizen Nov 18 '16 at 5:59
  • 2
    Any updates on this? getDef() and getHelper() are still hidden from general use. How is a super element supposed to delegate to a child for specific implementation in the current design of Lightning and best practices? – Grekker Oct 31 '17 at 23:49
3

So, after a lot of struggle, I think I found a solution so Template components can work with Locker Service and call helpers of components who instanciate them , using aura:method but without having to describe 10000 times each method.

To do that, the template component must inherit from a component CmpTemplateRoot , as described in the post.

Even if it seems to works correctly, my worries are :

  • I feel it's dirty to use a component attribute as buffer for function return value

  • I use inheritance on Cmp JS controller: in the doc it says not to do it, so the day it's killed, my solution won't work anymore.

  • I really would prefer a clean solution directly in the framework to access child helper from template component code, is it really impossible for SalesForce to implement that ? Doug Chasman u said "The pattern your using looks like https://en.m.wikipedia.org/wiki/Template_method_pattern, correct? We do need a supported way to implement that very useful pattern. " ... isn't there other ways to use it, with a detailed example ?

CmpTemplateRoot: to just copy-paste in your org, then if you make your components inherit from it, you can call method callConcreteCmpHelper: (component,HelperFunctionName,HelperFunctionParams) from a Template component helper to access child component helper methods.

Cmp:

<aura:component extensible="true" abstract="true" access="global" >

    <aura:method name="callCmpHelper" action="{!c.callCmpHelper}">
        <aura:attribute name="HelperFunctionName" type="String" />
        <aura:attribute name="HelperFunctionParams" type="Object" />
    </aura:method>  

    <aura:attribute name="HelperMethodResultList" type="Object" description="Buffer parameter to store instanciated component helper methods result"/> 

    {!v.body}
</aura:component>

JS controller:

({
    // NVU: Template component calling the instanciated component helper
    // The "this" of this method is the component instanciation of the template component
    callCmpHelper : function(component, event, helper) {
        var params = event.getParam('arguments');
        var HelperFunctionName = params.HelperFunctionName ;
        var HelperFunctionParams = params.HelperFunctionParams ;
        var HelperFunctionResult = helper[HelperFunctionName].apply(helper,HelperFunctionParams);
        // As aura:method can not return value, store return value in "buffer" attribute HelperMethodResultList.
        // Will be retrieved by template component method callConcreteCmpHelper
        if (HelperFunctionResult != null)
        {
            var HelperMethodResultList = component.get('v.HelperMethodResultList');
            if (HelperMethodResultList == null)
                HelperMethodResultList = {};
            HelperMethodResultList[HelperFunctionName] = HelperFunctionResult;
            component.set('v.HelperMethodResultList',HelperMethodResultList);    
        }
    }
})

JS Helper:

({
    // NVU: This method can be called to access helper methods of inherited components
    // The "this" of this method is the template component
   callConcreteCmpHelper: function (component,HelperFunctionName,HelperFunctionParams) {
        var ConcreteComponent = component.getConcreteComponent();
        ConcreteComponent.callCmpHelper(HelperFunctionName,HelperFunctionParams);
        // Get return value in "buffer" attribute
        var HelperMethodResultList = component.get('v.HelperMethodResultList');
        if (HelperMethodResultList == null)
            HelperMethodResultList = {};
        if (HelperMethodResultList[HelperFunctionName] != null)
            return HelperMethodResultList[HelperFunctionName] ;
    }
})

Here is an example of using :

CmpDummyRoot "Template component"

Cmp

<aura:component extensible="true" abstract="true" extends="irm:CmpTemplateRoot" access="global">

    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />  

    <aura:attribute name="DummyVal" type="String" />

    My template component CmpDummyRoot displays the value <strong>"{!v.DummyVal}"</strong> returned by child component CmpDummyChild instanciating this template component
</aura:component>

JS Controller:

({
    doInit : function(component, event, helper) {
        helper.myMasterMethodHlp(component,event);
    }    
})

JS Helper:

({
    myMasterMethodHlp : function(component,event) {
        var DummyVal = this.callConcreteCmpHelper(component,'myChildMethodHlp',[component,event]);
        component.set('v.DummyVal',DummyVal);
    }
})

"Child component using the template component" CmpDummyChild

Cmp:

<aura:component extends="irm:CmpDummyRoot">

</aura:component>

Helper:

({
    myChildMethodHlp : function(component,event) {
        var ChildDummyVal = 'ValComingFromChildHelper!!';
        return ChildDummyVal;
    }
})
1

Although I may not be fully qualified to answer this but there are certainly certain info that I know from partner portal on how this is going to be rolled out that I can provide with #Safeharbour that might answer first part .

What is the impact?

LockerService will be rolled out in the Summer '16 release in the following manner:

  1. New orgs and pre-existing orgs that do not contain any custom Lightning components will have LockerService enabled with Summer '16. These orgs will not have the ability to disable LockerService.

  2. Pre-existing orgs that contain custom Lightning components will get a Critical Update by which you can enable or disable LockerService.

    Developer Edition orgs will have the CRUC enabled by default, all
    other orgs will start out with LockerService CRUC disabled. The CRUC will be permanently enabled for all orgs in Winter ’17, scheduled for release in September 2016.

Lightning components that use undocumented APIs, violate industry standard or Lightning Components best practices will most likely malfunction or stop working entirely. As this could impact your ability to sell to new customers, it is imperative that all partners test all Lightning functionality prior to the Summer '16 rollout.

The Lightning CLI scans components’ JavaScript code looking for known patterns and issues, checking for a combination of industry standard rules and Lightning-specific rules. The Lightning CLI tool will allow you to inspect their code and identify issues where that code is likely to break under LockerService.

What actions do I need to take?

Test all Lightning functionality in Summer '16 Pre-Release (GSO) orgs as well as Sandboxes in the Preview Window with LockerService enabled.

Use the new Lightning CLI tool to find and fix specific issues with your code

Update your managed package with any fixes to ensure it is secure and runs error-free with LockerService enabled.

Follow all best practices and coding guidelines. The Lightning CLI and LockerService are not static - new rules will be added as vulnerabilities are identified.

Ensure that you have sandbox org(s) that will be included in the Summer '16 preview window, and that those org(s) contain at least one Lightning component.

Lightning CLI is available as a Node Package to figure certain issues before hand .

Also look for more documentation on upcoming release notes and also there is a release webinar on this feature which i guess might be right place to bring these questions

  • 1
    Yeah, I read all this information too. I don't see how this answers my first question, as there's no mention of whether breaking changes are something we should expect, nor any mechanism to communicate changes. – Bob Buzzard Apr 16 '16 at 15:56
  • The docs mention to test in pre-release org and things will break if undocumented private API is used . – Mohith Shrivastava Apr 16 '16 at 16:32
  • 1
    Yeah, but that isn't my question. I'm asking if they will be able to change a public API to private and break our code. Hence I'm expecting only the lightning team to be able to answer this. As I say, I've read these docs and they don't answer the question that I have, hence me posting it here :) – Bob Buzzard Apr 16 '16 at 16:35
  • 1
    Ah i see :) .Hoping Doug or Someone answers this clearly :) – Mohith Shrivastava Apr 16 '16 at 16:38
0

One can invoke child aura:methods from the parent's component using regular function invocation syntax. I've used this to implement the Template pattern. This still feels like a workaround but it works.

To make this more robust, one can check to ensure that the function exists and if it doesn't, either throw an error or skip executing the function. The example below doesn't do that for brevity sake.

c:Parent Component

<aura:component extensible="true" abstract="true">
    <!-- ... -->
</aura:component>

({  // Controller code included here for brevity
    templateMethod : function(component, event, helper) {
        // doSomethingInChild is defined in the child component
        // and it can return state to its parent.
        var result = component.doSomethingInChild();
    }
})

Child Component

<aura:component extends="c:Parent">
    <aura:method name="doSomethingInChild" />
</aura:component>

({  // Controller code included here for brevity
    doSomethingInChild : function(component, event, helper) {
        // Do Stuff

        return "Success";
    }
})
  • This does not work for me unfortunately. If I don't define methods in parent component, they are not visible and can't be executed. If I do, child methods are not overriding them. – pkozuchowski Dec 28 '18 at 12:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.