Tuesday, June 18, 2019

Lightning:tab in lightning component

We use Lightning:tab to display tab. The tab content is displayed when we click on the tab.
Lightning:tabset is a collection of tab.

Sample syntax:

<aura:component>

    <lightning:tabset selectedTabId="one">  // selectedTabId is used to set default tab.


        <lightning:tab label="Tab 1" id="one">
            Content from first tab
        </lightning:tab>


        <lightning:tab label="Tab 2" id="two">
            Content from second tab
        </lightning:tab>


        <lightning:tab label="Tab 3" id="three">
            Content from three tab
        </lightning:tab>


    </lightning:tabset>

</aura:component>

Image:

Lightning:tab in lightning component

To display tab vertically used, variant="vertical" in lightning:tabset.

Image:

Lightning:tab in lightning component

TO handle click on tab we used onactive="{!c.handleActive}" with lightning:tab.


<lightning:tab label="Tab 2" id="two" onactive="{!c.handleActive}">

            Content from second tab

        </lightning:tab>

Controller:

({
handleActive : function(component, event, helper) {
alert("Handle active");
}
})


Tuesday, June 11, 2019

force:editRecord in lightning

This event opens the record page specified by recordId  to edit.

Syntax:

var editRecordEvent = $A.get("e.force:editRecord");
    editRecordEvent.setParams({
         "recordId": component.get("v.recordId")
     
   });
    editRecordEvent.fire();

Sample example for editing account:

COMPONENT:

<aura:component implements="flexipage:availableForAllPageTypes,force:hasRecordId">
    <lightning:button label="Edit Account" onclick="{!c.editAccount}"/>
</aura:component>

CONTROLLER:

({
editAccount : function(component, event, helper) {
    var editRecordEvent = $A.get("e.force:editRecord");
    editRecordEvent.setParams({
         "recordId": component.get("v.recordId")
     
   });
    editRecordEvent.fire();
}
})

force:editRecord in lightning

force:createRecord in lightning

force:createRecord is an event that opens a page to create a record for specific entity.

Sample syntax:

createRecord : function (component, event, helper) {
    var createRecordEvent = $A.get("e.force:createRecord");
    createRecordEvent.setParams({
        "entityApiName": "ObjectApiName"
    });
    createRecordEvent.fire();
}

defaultFieldValues attribute inside setParams let us auto populate value on fields inside record form.

Firing the force:createRecord event tells the app to use the standard create record page.
We can specify values for fields even if they’re not available in the create record form.

NOTE:
  • If we specify Field value using defaultFieldValues and If the field is hidden i.e not on the page layout, the value specified in defaultFieldValues is saved with the new record.
  • If the current user doesn’t have create access to the field, due to field-level security, attempts to save the new record result in an error. We cannot catch this error and hence it is necessary to perform access check before firing the event.
Sample Example to create account:

COMPONENT:

<aura:component implements="flexipage:availableForAllPageTypes">
    <lightning:button label="New Account" onclick="{!c.createAccount}"/>
</aura:component>

CONTROLLER:

({
createAccount : function(component, event, helper) {
        var recordEvent=$A.get("e.force:createRecord");
        recordEvent.setParams({
            "entityApiName": "Account",
            "defaultFieldValues":{
                "Industry":"Apparel"
            }
        });
        recordEvent.fire();
}
})

Saturday, June 1, 2019

Apex Code Best Practices

Here are the best Practices we should follow while writing apex code.

1) Bulkify the Code.


Bulkify here simply means writing code that handles more than one record at a time.

Here is an example of poorly written code that handles only one record at a time.

trigger contactTrigger on contact (before insert, before update) {

   //This only handles the first record in the Trigger.new collection, other records are not processed

   Contact con= Trigger.new[0];

   List<Contact> conList = [select id, firstname, lastname 

              from Contact where id= :con.Id];

}

Here is an example of code written to handle bulkification,

trigger contactTrigger on contact (before insert, before update) {

 List<Contact> conList =new List<Contact>();
   for(contact con:trigger.new)
       {
if(con.firstName="Test")
            {   con.booleanField=true;
    conList.add(con);
            }

       }

   update conList;
             

}

2) Avoid SOQL Queries or DML statements inside FOR Loops.


This is to take Governor Limit in consideration,

Governor limits are calculated at runtime. After the request is initiated (Trigger, Visualforce page, etc.), any Apex code executed in that transaction applies and shares the governor limits. 

SOQL queries per transaction.
==>100.(synchronous)
SOQL queries per transaction.
==>200(asynchronous)
dml statements per transaction
==> 150

3) Use single trigger on object.


If we have multiple trigger on one object we cannot control the order of execution.

4) Querying object having more than 50,000 records.

The total number of records that can be queried by soql queries is 50,000 record. If we query more than 50,000 record we exceeds the heap limit.

//A runtime exception is thrown if this query returns enough records to exceed your heap limit.

Account[] accts = [SELECT id FROM account];

To avoid this we should use SOQL query for loop it can process multiple batches of record using call to query and query more.

for (List<Account> acct : [SELECT id, name FROM account

                            WHERE name LIKE 'Test']) {
    // Your logic here

}


5) Avoid hardcoding id's in apex code.

Instead we should use label or some other way to avoid hardcoding id's as these will results in improper processing on records if sandbox and production environment have different id's for the match we are doing in code.

Simple example below to match record type,

  List<RecordType> rtypes = [Select Name, Id From RecordType

                  where sObjectType='Contact' and isActive=true];

     Map<String,String> contactRecordTypes= new Map<String,String>{};

     for(RecordType rt: rtypes)
{

        contactRecordTypes.put(rt.Name,rt.Id);

        }

      for(contact a: Trigger.new)
  {

          if(a.RecordTypeId==contactRecordTypes.get('someRecordTypeName')){            

             
          }

     }


6) Use of the Limits Apex Methods

Apex has a system class called Limits that let us identify the amount of resources used and amount of resource available to avoid hitting governor limit in a particular context.

System.debug('Total Number of SOQL Queries allowed in this Apex code context: ' +  Limits.getLimitQueries());

System.debug('Total Number of records that can be queried  in this Apex code context: ' +  Limits.getLimitDmlRows());

System.debug('Total Number of DML statements allowed in this Apex code context: ' +  Limits.getLimitDmlStatements() );

System.debug('Total Number of CPU usage time (in ms) allowed in this Apex code context: ' +  Limits.getLimitCpuTime());

System.debug('1. Number of Queries used in this Apex code so far: ' + Limits.getQueries());

System.debug('2. Number of rows queried in this Apex code so far: ' + Limits.getDmlRows());

System.debug('3. Number of DML statements used so far: ' +  Limits.getDmlStatements());  
System.debug('4. Amount of CPU time (in ms) used so far: ' + Limits.getCpuTime());

System.debug('Final heap size: ' +  Limits.getHeapSize());

7) Using @future

  • 50 @future invocation per Apex transaction
  • Methods with the future annotation cannot be used in Visualforce controllers in either getMethodName or setMethodName methods, nor in the constructor.
  • Methods with the future annotation cannot take sObjects or objects as arguments it always takes id's as argument.

trigger accountAsyncTrigger on Account (after insert, after update) {
  for(Account a: Trigger.new){
    // Invoke the @future method for each Account
    // This is inefficient and will easily exceed the governor limit of
    // at most 10 @future invocation per Apex transaction
    someClass.processAccount(a.id);
   }    
}



global class someClass{


  @future
  public static void processAccount(Id accountId) {
// some code here
         }
}


Best way for future method invocation,

trigger accountAsyncTrigger on Account (after insert, after update) {
    //By passing the @future method a set of Ids, it only needs to be
    //invoked once to handle all of the data.
    someClass.processAccount(Trigger.newMap.keySet());
}

Thursday, May 30, 2019

Handling Record Changes using LDS (force:recordData)

Now we know how to perform CRUD opeartion using force:recordData.

Today we will be going to see how to handle changes on record using recordUpdated.

Sample syntax:

<force:recordData aura:id="forceRecordDataCmp"
    recordId="{!v.recordId}"
    layoutType="{!v.layout}"
    targetRecord="{!v.recordObject}"
    targetFields="{!v.recordFieldstoQuery}"
    targetError="{!v.recordError}"

    recordUpdated="{!c.recordUpdated}" />

For every force:recordData component referencing the updated record, LDS does two things.


  • LDS notifies all other instances of force:recordData of the change by firing the recordUpdated event with the appropriate changeType and changedFields value.
  • It sets the targetRecord and targetFields attribute on each force:recordData to the new record value. If targetRecord or targetFields is referenced by any UI, this automatically triggers a rerender so that the UI displays the latest data.

Note: If force:recordData is in EDIT mode, targetRecord and targetFields are not automatically updated.


Sample handler:

({
    recordUpdated: function(component, event, helper) {
        var eventParams = event.getParams();
        if(eventParams.changeType === "CHANGED") {
            // getting the fields that are changed for this record
            var changedFields = eventParams.changedFields;
           Alert('Fields change detected on: ' + JSON.stringify(changedFields));
            // Refereshing the component after change
            var resultsToast = $A.get("e.force:showToast");
            resultsToast.setParams({
                "title": "Saved",
                "message": "Record updatesd."
            });
            resultsToast.fire();
        } else if(eventParams.changeType === "LOADED") {
            Alert("Record is loaded successfully.");
        } else if(eventParams.changeType === "REMOVED") {
            var resultsToast = $A.get("e.force:showToast");
            resultsToast.setParams({
                "title": "Deleted",
                "message": "Record deleted."
            });
            resultsToast.fire();
        } else if(eventParams.changeType === "ERROR") {
            alert("Error: '"+ component.get("v.recordError"));
        }
    }
})