7

I would like to find out which cases are appropriate for using inheritance in lightning components, particularly how can we use polymorphism by extending lightning componets or maybe implementing custom interfaces? As far as I know according to this article https://developer.salesforce.com/blogs/developer-relations/2015/03/salesforce-lightning-components-by-example-component-extension.html we can only inherit parent attributes and helper methods

e.g.

parent

<aura:component extensible="true" abstract="true" controller="ObjectPanelController">
   <aura:attribute name="sObjectType" type="String" required="true" />
<aura:attribute name="maxRows" type="Integer" default="20" />
<aura:attribute name="fields" type="String" />
<aura:attribute name="records" type="Object[]" />
<aura:attribute name="sObjectInfo" type="Object" />
</aura:component>

helper:

  navigateToRecord : function(component, event, helper) {    
        ///
    },
    deleteRecord : function(component, event, helper) {
       ////
    }

Child

<aura:component extends="c:objectPanel">  
    <aura:set attribute="sObjectType" value="Account" />
    <aura:set attribute="fields" value="AccountNumber,Website" /> 
            <a onclick="{!c.deleteRecord}">Del</a> | 
            <a onclick="{!c.navigateToRecord}">navigate</a>

</aura:component>

Controller

({
    navigateToRecord : function(component, event, helper) {    
        helper.navigateToRecord(component);//calls parent helper
    },
    deleteRecord : function(component, event, helper) {
        helper.deleteRecord(component); //calls parent helper
    }
})

Could we have other reasonable use cases of inheritance with using polymorphism for instance something like this

<aura:attribute name="MainComponent" type="Object[]" />
 <aura:iteration items="{!v.MainComponent}" var="main">
   {!main}
// particular instance that extends MainComponent
 </aura:iteration>
7

I have also started using inheritance + interfaces to remove boilerplate and leverage a consistent codebase.

I use many of the same techniques and also add in in Notifers and Spinners to get a nice consistent messaging framework (I note that I've seen a better implementation of spinners than mine by using the doneWaiting and waiting system events, so you might want to take that into consideration if implementing some of my example code.

My interface looks a little like this:

<aura:interface description="Interface that enforces the use of certain events and attributes">

  <aura:attribute name="opportunityId" type="String" description="Id of the Opportunity "/>
  <aura:attribute name="componentId" type="String" description="Id of this component, which should match the aura:id"/>

  <aura:registerEvent name="someCommonEvent" type="c:MySharedEvent"/>

</aura:interface>

My abstract component looks like this:

<aura:component  abstract="true" extensible="true" >
  <aura:handler name="init" value="{!this}" action="{!c.init}" />
  <aura:method name="refresh" action="{!c.reInit}"/>

  <c:Notifier aura:id="notifier" />
  <c:Spinner aura:id="spinner" />

  <!-- body of sub components -->
  {!v.body}

</aura:component>

Inside the Abstract helper, here is some of the code:

showSuccessAlert: function(message, component) {
  var notifier = component.getSuper().find('notifier');
  notifier.showSuccess(message, true, 3000);

},
showErrorAlert: function(message, component) {
  var notifier = component.getSuper().find('notifier');
  notifier.showError(message);
},

callServerMethod: function(component,apexMethodName,apexParams,successCallBack,errorCallBack) {

  var superSelf = this;

  if (!apexMethodName ){
    superSelf.showErrorAlert("Helper Error: no method name supplied");
    return;
  }
  ....
}

Finally, components extending the Abstract component can make server calls, show and hide spinners very easily as shown here:

saveData: function(component) {
  self = this;
  self.showHideSpinner(true,component);
  this.callServerMethod(
    component,
    'c.saveData',                        //apexMethodName
    {data: somedata},                    //apexParams
    function(result){                    //successCallBack
      self.postSaveActions(component);
    },
    null
  );
},

Most of this code is available on Github here

Note, in the Github version, I also have an interface and a common message format between Apex and Lightning that you may or may not want to use - it simplifies data transfer between back and front end, but does impose some restrictions that you might not like.

| improve this answer | |
  • @CasperHarmer I definitely like this approach but will there be a overhead if multiple components extending the same/different base component rendered in view?. Let's us I have an Assessment and under each Assessment has few or more Section which inturn contains Questions(min 20). Each Assessment, Section and Question component as its own set of complex functionalities built-in. In that case, I believe above pattern is not suitable , any thought how you would approach it – Praveen Apr 17 '17 at 15:51
  • Current approach I'm using a Service & DataService component combo. For eg:AssessmentServiceCmp, which would attach all the Assessment related server calls to the window and cache the assessment data as well. At the end of the day, component which needs to get hold of the Assessment data, would do window.AssessmentService.getRecord(id,successCallback,errorCallback). All I did here is mix ServiceComponent and DataService Component concepts explained here – Praveen Apr 17 '17 at 16:04
  • You may be right - I also like you technique of attaching services to the SecureWindow. I think I need to do some testing to see what actually gets rendered. I was thinking that since each component shares it's helper code with all other similar components, there may not actually be that much code behind all these components - I'll have a look, as I do have a component that has 8 other sub components, all extending the same abstract class. – Caspar Harmer Apr 17 '17 at 17:35
2

We use inheritance extensively in our work places. some of the use cases for extending a base lightning component kind are if you have some functions that you want to use in multiple components.

Lets say in your first component you have a method called callserver which makes a server call and returns the response. all your components which needs server interaction will need this method so instead of writing logic to call server in each and every helper you can write it in the abstract component and all the other methods can extend it. So in our case it takes parameters 1.Component instance 2.Server method name 3.Params 4.Callback function 5.cacheable property

So when other components extend this component we do this.callserver(); from them to re-use this function. All the logic related to calling server checking for status success,error and then incase of errors showing a error banner happens in the abstract method. This way we increase re-usablility and in future if SF change definition of server call we just change it one place and it will work fine across all the components.

For Interfaces we have lot of components which have pagination kind of functionality. they have kind of similar parameters like firstpage,lastpage,currentpage. so instead of re-creating them in each and every component we created a interface for it and re-use it across components.

All re-usable methods we need across multiple components we put it on the abstract component and re-use them across components by extending it. In the longer run these methods will get time tested and will become robust and that will result in a very stable solution.

Some of the reusable methods we have are 1.Calling server 2.Parsing URL parameters 3.Custom console.log function instead of javascript console.log to control them from one place 4.merging null value fields with sobjects attributes since SOQL doesnt return null fields back to lightning components.

Hope this helps!

If you need specific code examples i can post that as well if its going to help you.

| improve this answer | |

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.