Tag Archives: Lightning Component

Custom File Upload In Salesforce Lightning Component

File Upload Component:
Create a new Lightning Component FileUpload.cmp.

<!--FileUpload.cmp-->
<aura:component controller="FileUploadController">
    <!-- 'parentId' Aura Attribute for store the Id for Parent Record where we are attach our file -->  
    <aura:attribute name="parentId" type="Id" default="00128000002KuXUAA0" />
    <!-- 'fileName' attribute for display the selected file name -->  
    <aura:attribute name="fileName" type="String" default="No File Selected.." />
    
    <!-- Lightning Input with file type and on file change call the 'handleFilesChange' controller --> 
    <lightning:input aura:id="fuploader" onchange="{!c.handleFilesChange}" type="file" name="file" label="Upload File" multiple="false"/>
    <div class="slds-text-body_small slds-text-color_error">{!v.fileName} </div>
    <br/>
    <lightning:button label="Cancel" onclick="{!c.handleCancel}" class="slds-m-top--medium" />
    <lightning:button label="Save" onclick="{!c.handleSave}"
                      variant="brand" class="slds-m-top--medium"/>
</aura:component>


File Upload JavaScript Controller:

Now create below JavaScript FileUploadController.js controller for above FileUpload.cmp component.

({    
    handleSave: function(component, event, helper) {
        if (component.find("fuploader").get("v.files").length > 0) {
            helper.uploadHelper(component, event);
        } else {
            alert('Please Select a Valid File');
        }
    },
    
    handleFilesChange: function(component, event, helper) {
        var fileName = 'No File Selected..';
        if (event.getSource().get("v.files").length > 0) {
            fileName = event.getSource().get("v.files")[0]['name'];
        }
        component.set("v.fileName", fileName);
    },
    
    handleCancel: function(component, event, helper) {
        $A.get("e.force:closeQuickAction").fire();
    }
})

File Upload JavaScript Controller Helper:
Now create below JavaScript FileUploadHelper.js helper for above FileUploadController.js component.

({
    MAX_FILE_SIZE: 4500000, //Max file size 4.5 MB 
    CHUNK_SIZE: 750000,      //Chunk Max size 750Kb 
    
    uploadHelper: function(component, event) {
        // get the selected files using aura:id [return array of files]
        var fileInput = component.find("fuploader").get("v.files");
        // get the first file using array index[0]  
        var file = fileInput[0];
        var self = this;
        // check the selected file size, if select file size greter then MAX_FILE_SIZE,
        // then show a alert msg to user,hide the loading spinner and return from function  
        if (file.size > self.MAX_FILE_SIZE) {
            component.set("v.fileName", 'Alert : File size cannot exceed ' + self.MAX_FILE_SIZE + ' bytes.\n' + ' Selected file size: ' + file.size);
            return;
        }
        
        // create a FileReader object 
        var objFileReader = new FileReader();
        // set onload function of FileReader object   
        objFileReader.onload = $A.getCallback(function() {
            var fileContents = objFileReader.result;
            var base64 = 'base64,';
            var dataStart = fileContents.indexOf(base64) + base64.length;
            
            fileContents = fileContents.substring(dataStart);
            // call the uploadProcess method 
            self.uploadProcess(component, file, fileContents);
        });
        
        objFileReader.readAsDataURL(file);
    },
    
    uploadProcess: function(component, file, fileContents) {
        // set a default size or startpostiton as 0 
        var startPosition = 0;
        // calculate the end size or endPostion using Math.min() function which is return the min. value   
        var endPosition = Math.min(fileContents.length, startPosition + this.CHUNK_SIZE);
        
        // start with the initial chunk, and set the attachId(last parameter)is null in begin
        this.uploadInChunk(component, file, fileContents, startPosition, endPosition, '');
    },
    
    
    uploadInChunk: function(component, file, fileContents, startPosition, endPosition, attachId) {
        // call the apex method 'SaveFile'
        var getchunk = fileContents.substring(startPosition, endPosition);
        var action = component.get("c.SaveFile");
        action.setParams({
            parentId: component.get("v.parentId"),
            fileName: file.name,
            base64Data: encodeURIComponent(getchunk),
            contentType: file.type,
            fileId: attachId
        });
        
        // set call back 
        action.setCallback(this, function(response) {
            // store the response / Attachment Id   
            attachId = response.getReturnValue();
            var state = response.getState();
            if (state === "SUCCESS") {
                // update the start position with end postion
                startPosition = endPosition;
                endPosition = Math.min(fileContents.length, startPosition + this.CHUNK_SIZE);
                // check if the start postion is still less then end postion 
                // then call again 'uploadInChunk' method , 
                // else, diaply alert msg and hide the loading spinner
                if (startPosition < endPosition) {
                    this.uploadInChunk(component, file, fileContents, startPosition, endPosition, attachId);
                } else {
                    alert('File has been uploaded successfully');
                }
                // handel the response errors        
            } else if (state === "INCOMPLETE") {
                alert("From server: " + response.getReturnValue());
            } else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + errors[0].message);
                    }
                } else {
                    console.log("Unknown error");
                }
            }
        });
        // enqueue the action
        $A.enqueueAction(action);
    }
})

File Upload Apex Controller:
Create below apex controller to use it in FileUpload.cmp component.

public with sharing class FileUploadController {
    
    @AuraEnabled
    public static Id SaveFile(Id parentId, String fileName, String base64Data, String contentType) {
        base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8');
        Attachment attach = new Attachment();
        attach.parentId = parentId;
        attach.Body = EncodingUtil.base64Decode(base64Data);
        attach.Name = fileName;
        attach.ContentType = contentType;
        Insert attach;
        return attach.Id;
    }
}

File Upload App:

<!--FileUploadApp.app-->
<aura:application extends="force:slds">
    <c:FileUpload />    
</aura:application>

Output:

Custom Validation For Lightning Field Value in Lightning

Component :

<aura:component>    
    <div class="slds-m-around--xx-large">
        <div class="slds-form--stacked">
            <div class="slds-form-element">  
                <div class="slds-form-element__control">
                    <ui:inputNumber aura:id="inputAge" label="Enter your age" class="slds-input"/>
                </div>
            </div>
            <div class="slds-m-around--medium">
                <button class="slds-button slds-button--brand" onclick="{!c.validateAge}">Validate Age</button>
            </div>
        </div>
    </div>  
</aura:component>

Component JS Controller :

({
    validateAge : function(component, event, helper) {
        var inputAge = component.find('inputAge');
        var ageValue = inputAge.get('v.value');
        if(parseInt(ageValue) < 18){
            inputAge.set("v.errors", [{message:"Age should be more than 18...!!"}]);
            return;
        }
    }
})

Output :

Lightning Component Client Side Pagination

Pagination Component:
Create a new Lightning Component Pagination.cmp.

<!--Pagination.cmp-->
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" 
                controller='PaginationController'
                access="global" >
    
    <!-- Attribute Declration For Pagination -->
    <aura:attribute name="ContactData" type="Object"/>
    <aura:attribute name="columns" type="List"/>
    
    <aura:attribute name="isSending" type="boolean" />
    
    <!-- Attribute Declration For Pagination -->
    <aura:attribute name="PaginationList" type="Contact"/>
    <aura:attribute name="startPage" type="Integer" />
    <aura:attribute name="endPage" type="Integer"/>
    <aura:attribute name="totalRecords" type="Integer"/>
    <aura:attribute name="pageSize" type="Integer" default="10"/>
    <!-- Attribute Declration For Pagination End-->
    
    <aura:handler name="init" value="{! this }" action="{! c.doInit }"/>
    
    <!-- Spinner Start, show the loading screen while 
   Client side controller(JS) is communicating with Server Side(APEX) controller -->
    <aura:if isTrue="{!v.isSending}">
        <div class="slds-spinner_container">
            <div class="slds-spinner--brand slds-spinner slds-spinner--large" role="alert">
                <span class="slds-assistive-text">Loading</span>
                <div class="slds-spinner__dot-a"></div>
                <div class="slds-spinner__dot-b"></div>
            </div>
        </div>
    </aura:if>
    <!-- Spinner End -->
    
    <div class="slds-page-header">
        <div class="slds-media">
            <div class="slds-media__figure">
                <span class="slds-icon_container slds-icon-standard-opportunity" 
                      title="Description of icon when needed">
                    
                </span>
            </div>
        </div>
    </div>
    
    <div class="slds-m-top_medium" >
        <lightning:datatable data="{! v.PaginationList }" class="slds-m-top_medium"
                             columns="{! v.columns }" 
                             keyField="id"
                             onrowselection="{! c.getSelectedName }"/>	
        <br/>
        <lightning:buttonGroup >
            <lightning:button label="Previous" disabled="{!v.startPage == 0}"  
                              onclick="{!c.previous}" variant="brand"
                              iconName='utility:back'/>
            &nbsp; &nbsp; &nbsp;
            <lightning:button label="Next" disabled="{!v.endPage >= v.totalRecords}" 
                              onclick="{!c.next}" variant="brand"
                              iconName='utility:forward' iconPosition='right'/>
        </lightning:buttonGroup>
    </div>
</aura:component>

Pagination JavaScript Controller:
Now create below JavaScript PaginationController.js controller for above Pagination.cmp component.

({
    doInit: function (component, event, helper) {
        // Set the columns of the Table 
        component.set('v.isSending',true);
        component.set('v.columns', [
            {label: 'Name', fieldName: 'Name', type: 'text', sortable : true},
            {label: 'Email', fieldName: 'Email', type: 'email', sortable : true},
            {label: 'Phone', fieldName: 'Phone', type: 'phone', sortable : true}]);
        helper.doFetchContact(component);
    },
    getSelectedName: function (component, event) {
        var selectedRows = event.getParam('selectedRows');
        // Display that fieldName of the selected rows
        for (var i = 0; i < selectedRows.length; i++){
            alert("You selected: " + selectedRows[i].Name);
        }
    },
    next: function (component, event, helper) {
        helper.next(component, event);
    },
    previous: function (component, event, helper) {
        helper.previous(component, event);
    }
})

Pagination JavaScript Controller Helper:
Now create below JavaScript PaginationHelper.js helper for above PaginationController.js component.

({
    /*
     * Initially this Method will be called and will fetch the records from the Salesforce Org 
     * Then we will hold all the records into the attribute of Lightning Component
     */
    doFetchContact : function(component) {
        var action = component.get('c.getContacts');
        action.setCallback(this, function(response){
            var state = response.getState();
            if(state === 'SUCCESS' && component.isValid()){
                var pageSize = component.get("v.pageSize");
                // hold all the records into an attribute named "ContactData"
                component.set('v.ContactData', response.getReturnValue());
                // get size of all the records and then hold into an attribute "totalRecords"
                component.set("v.totalRecords", component.get("v.ContactData").length);
                // set star as 0
                component.set("v.startPage",0);
                
                component.set("v.endPage",pageSize-1);
                var PaginationList = [];
                for(var i=0; i< pageSize; i++){
                    if(component.get("v.ContactData").length> i)
                        PaginationList.push(response.getReturnValue()[i]);    
                }
                component.set('v.PaginationList', PaginationList);
                component.set('v.isSending',false);
            }else{
                alert('ERROR');
            }
        });
        $A.enqueueAction(action);
    },
    /*
     * Method will be called when use clicks on next button and performs the 
     * calculation to show the next set of records
     */
    next : function(component, event){
        var sObjectList = component.get("v.ContactData");
        var end = component.get("v.endPage");
        var start = component.get("v.startPage");
        var pageSize = component.get("v.pageSize");
        var Paginationlist = [];
        var counter = 0;
        for(var i=end+1; i<end+pageSize+1; i++){
            if(sObjectList.length > i){
                Paginationlist.push(sObjectList[i]);
            }
            counter ++ ;
        }
        start = start + counter;
        end = end + counter;
        component.set("v.startPage",start);
        component.set("v.endPage",end);
        component.set('v.PaginationList', Paginationlist);
    },
    /*
     * Method will be called when use clicks on previous button and performs the 
     * calculation to show the previous set of records
     */
    previous : function(component, event){
        var sObjectList = component.get("v.ContactData");
        var end = component.get("v.endPage");
        var start = component.get("v.startPage");
        var pageSize = component.get("v.pageSize");
        var Paginationlist = [];
        var counter = 0;
        for(var i= start-pageSize; i < start ; i++){
            if(i > -1){
                Paginationlist.push(sObjectList[i]);
                counter ++;
            }else{
                start++;
            }
        }
        start = start - counter;
        end = end - counter;
        component.set("v.startPage",start);
        component.set("v.endPage",end);
        component.set('v.PaginationList', Paginationlist);
    }
})

Pagination Apex Controller:
Create below apex controller to use it in Pagination.cmp component.

public class PaginationController {
    @AuraEnabled
    public static List<Contact> getContacts(){
        List<Contact> contactList = new List<Contact>();
        contactList = [SELECT Id, Name, Email, Phone From Contact LIMIT 50];
        return contactList;
    }
}

Pagination App:

<!--PaginationApp.app-->
<aura:application extends="force:slds">
    <c:Pagination />    
</aura:application>

Output:

Nested server side method calling in Lightning Component

Apex Controller:

public class SampleAuraController {
    
    @AuraEnabled
    public static String firstAction() {
        return 'First Action';
    }
    
    @AuraEnabled
    public static String secondAction() {
        return 'Second Action';
    }
}

Lightning Component:

<!--Sample.cmp--> 
<aura:component controller="SampleAuraController" implements="flexipage:availableForAllPageTypes" access="global">
    <div class="slds-m-around_xx-large">
        <lightning:button variant="brand" label="Click" onclick="{!c.handleAction}" />
    </div>
</aura:component>

Lightning Component JS Controller:

({
    handleAction : function(component, event, helper) {
        var action1 = component.get("c.firstAction");
        action1.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                var result1  = response.getReturnValue();
                alert(result1);
                var action2 = component.get("c.secondAction");
                action2.setCallback(this, function(response){
                    var state = response.getState();
                    if (state === "SUCCESS") {
                        var result2  = response.getReturnValue();
                        alert(result2);
                    }
                });
                $A.enqueueAction(action2);
            }
        });
        $A.enqueueAction(action1);
    }
})

SVG in Lightning Component Bundle

SVG resource is to define a custom icon for the lightning component when it appears in the Lightning App Builder’s component pane. SVG icons is used in Salesforce Lightning App Builder or Community Builder.

Here is a lightning component with default SVG icon:

Sample Component:

<aura:component implements="flexipage:availableForAllPageTypes">
    <strong>Sample Component</strong>
</aura:component>

The default SVG icon looks like below:

Edit the SVG for custom SVG icon of above “Sample” lightning component: To customize the default SVG, click SVG in the component sidebar of the Developer Console.

Custom Sample Component SVG icon: Here I’ve changed the custom SVG icon to a rectangle filled with blue color. You can change it to as per your requirement.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400">
	<rect width="400" height="300" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
</svg>

The custom SVG icon looks like below: