Tag Archives: Lightning

Upload Multiple Files Using Lightning File Upload Component

In Winter ’18 Salesforce released a new lightning component lightning:fileUpload, which provides an easy and integrated way for users to upload multiple files. The file uploader includes drag-and-drop functionality and filtering by file types.

File Upload Component Limits:

  • By default, you can upload up to 10 files simultaneously unless your Salesforce admin has changed that limit.
  • The org limit for the number of files simultaneously uploaded is a maximum of 25 files and a minimum of 3 files.
  • The maximum file size you can upload is 2 GB. In Communities, the file size limits and types allowed follow the settings determined by community file moderation.

Considerations:

  • This component is not supported in Lightning Out or standalone apps, and displays as a disabled input.
  • The file uploader cannot be used to upload files with the following file extensions: .htm, .html, .htt, .htx, .mhtm, .mhtml, .shtm, .shtml, .acgi, .svg.

In this article I will show you how you can upload multiple files using lightning file upload component, without writing the apex code.

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

<!--FileUpload.cmp-->
<aura:component controller="FileUploadController" implements="force:appHostable, flexipage:availableForAllPageTypes, flexipage:availableForRecordHome, force:hasRecordId, forceCommunity:availableForAllPageTypes, force:lightningQuickAction" access="global">
    
    <lightning:fileUpload label="Upload File" multiple="true" accept=".pdf, .png" recordId="{!v.recordId}" aura:id="multifileUpload" onuploadfinished="{!c.handleUploadFinished}" />
</aura:component>

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

({    
    handleUploadFinished: function (cmp, event) {
        //Get the list of uploaded files
        var uploadedFiles = event.getParam("files");
        //Show success message – with no of files uploaded
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            "title": "Success!",
            "type" : "success",
            "message": uploadedFiles.length+" files has been uploaded successfully!"
        });
        toastEvent.fire();
        
        $A.get('e.force:refreshView').fire();
        
        //Close the action panel
        var dismissActionPanel = $A.get("e.force:closeQuickAction");
        dismissActionPanel.fire();
    }
})

Steps to test the functionality:
Go to Setup || Object Manager || Select Object(For example Account) || Buttons, Links, and Actions || New Action || Create Quick Action

Now add the created Quick Action into Object Page Layout.
Go to Setup || Object Manager || Select Object(For example Account) || Page Layouts || Select Your Layouts (For example Account Layout) || Select Mobile & lightning Actions || Add the Quick Action into Salesforce Mobile & Lightning Experience Actions section.

Now, Open an account record click Upload File action from right upload files and try to upload file.

Salesforce Generic sObject Lightning Lookup Component

In Salesforce Lightning we don’t have any lookup field component like Salesforce classic. So, here is a generic lookup Lightning Component, which can be used for any sObject lookup present in our org without changing any major code.

sObjectLookupController:
Create below apex controller to use it in sObjectLookup.cmp component.

/**
* @Author	:	Biswajeet Samal
* @Date		:	9 Dec 2017
* @Desc		:	Controller for sObject Lookup Lightning Component
* */
public with sharing class sObjectLookupController {
    @AuraEnabled
    public static List<sObject> searchRecord(String objectAPIName, String fieldAPIName, 
                                             List<String> moreFields, String searchText,
                                             Integer recordLimit)
    {
        
        List<sObject> objectList =  new List<sObject>();
        
        searchText='\'%' + String.escapeSingleQuotes(searchText.trim()) + '%\'';
        
        String soqlQuery = 'SELECT Id, Name';
        if(!moreFields.isEmpty()){
            soqlQuery = soqlQuery + ',' + String.join(moreFields, ',') ;
        }
        soqlQuery = soqlQuery + ' FROM ' + objectAPIName + ' WHERE ' +
            + fieldAPIName +' LIKE '+ searchText + ' LIMIT '+ recordLimit;
        objectList = Database.query(soqlQuery);
        return objectList;
    }
}

sObjectLookupSelectEvent.evt:
Create below Lightning Event, which is used to store and fill the input field with selected record Id and Name. Fired from sObjectLookupItem.cmp component, handled at Lookup component.

<!--sObjectLookupSelectEvent.evt-->
<aura:event type="COMPONENT" description="sObjectLookupSelectEvent">
    <aura:attribute name="recordId" type="String" required="true" description="Used to send selected record Id"/>
    <aura:attribute name="recordName" type="String" required="true" description="Used to send selected record Name" />
</aura:event>

sObjectLookup.cmp:
Create below Lightning Component, which will be used for sObject lookup.

<!--sObjectLookup.cmp-->
<aura:component controller="sObjectLookupController" description="Lightning component for lookup fields">
    
    <!--Declare Attributes-->
    <aura:attribute name="objectAPIName" type="String" required="true"
                    description="Object API name used for searching records"/>
    
    <aura:attribute name="fieldAPIName" type="String" required="true"
                    description="API Name of field to be searched"/>
    
    <aura:attribute name="lookupIcon" type="String" default="standard:contact"
                    description="Icon for lookup records"/>
    
    <aura:attribute name="placeholder" type="String" default="Search..."
                    description="Placeholder text for input search filed"/>
    
    <aura:attribute name="fieldLabel" type="String" required="true"
                    description="input search field Label"/>
    
    <aura:attribute name="selectedRecordId" type="String"
                    description="Used to store the selected record id.
                                 While calling this component from other component,
                                 set this attribute to the lookup field API name"/>
    
    <aura:attribute name="selectedRecordName" type="String"
                    description="This is used to show the selected record Name in search input"/>
    
    <aura:attribute name="subHeadingFieldsAPI" type="String[]"
                    description="Field API for the fields to be shown under the record Name.
                                 Must be comma separated. Example: Email,Phone"/>
    
    <aura:attribute name="matchingRecords" type="Object[]" access="private"
                    description="List of records returned from server side call"/>
    
    <aura:attribute name="recordLimit" type="Integer" access="public" default="5" 
                    description="Total number of record to be returned"/>
    
    <!--Declare Handlers-->
    <aura:handler name="lookupSelect" event="c:sObjectLookupSelectEvent" action="{!c.handleLookupSelectEvent}"
                  description="Event handler to get the selected record Id and Name from LookupItem component"/>
    
    <!--Component-->
    <div class="slds-form-element__control">
        <div class="slds-combobox_container slds-has-inline-listbox">
            <div aura:id="divLookup"
                 class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-combobox-lookup"
                 aria-expanded="false" aria-haspopup="listbox" role="combobox">
                
                <div class="slds-combobox__form-element">
                    <lightning:input type="search"
                                     aura:id="searchinput"
                                     label="{!v.fieldLabel}"
                                     name="{!v.fieldLabel}"
                                     value="{!v.selectedRecordName}"
                                     onchange="{!c.handleSearchRecords}"
                                     isLoading="false"
                                     placeholder="{!v.placeholder}"
                                     onfocus="{!c.handleSearchRecords}"
                                     onblur="{!c.hideList}"/>
                </div>
                <div id="listbox-unique-id" role="listbox">
                    <ul class="slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid" role="presentation">
                        <aura:iteration var="rec" items="{!v.matchingRecords}">
                            <c:sObjectLookupItem record="{!rec}" subHeadingFieldsAPI="{!v.subHeadingFieldsAPI}" iconCategoryName="{!v.lookupIcon}"/>
                        </aura:iteration>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</aura:component>

sObjectLookupController.js:

({
    handleSearchRecords : function (component, event, helper) {
        var searchText = component.find("searchinput").get("v.value");
        if(searchText){
            helper.searchRecord(component,searchText);
        }else{
            helper.searchRecord(component, '');
        }
    },
    
    handleLookupSelectEvent : function (component, event, helper) {
        var selectedRecordId = event.getParam("recordId");
        var selectedrecordName = event.getParam("recordName");
        component.set("v.selectedRecordId", selectedRecordId);
        component.set("v.selectedRecordName", selectedrecordName);
        helper.toggleLookupList(component, false, 'slds-combobox-lookup', 'slds-is-open');
    },
    
    hideList :function (component,event,helper) {
        window.setTimeout(
            $A.getCallback(function() {
                if (component.isValid()) {
                    helper.toggleLookupList(component, false, 'slds-combobox-lookup','slds-is-open');
                }
            }), 200
        );
    }
})

sObjectLookupHelper.js:

({
    toggleLookupList : function (component, ariaexpanded, classadd, classremove) {
        component.find("divLookup").set("v.aria-expanded", true);
        $A.util.addClass(component.find("divLookup"), classadd);
        $A.util.removeClass(component.find("divLookup"), classremove);
    },
    
    searchRecord : function (component, searchText) {
        component.find("searchinput").set("v.isLoading", true);        
        var action = component.get("c.searchRecord");
        action.setParams({
            "objectAPIName": component.get("v.objectAPIName"),
            "fieldAPIName":component.get("v.fieldAPIName"),
            "moreFields":component.get("v.subHeadingFieldsAPI"),
            "searchText":searchText,
            "recordLimit":component.get("v.recordLimit")
        });
        
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(component.isValid() && state === "SUCCESS") {
                if(response.getReturnValue()){
                    component.set("v.matchingRecords", response.getReturnValue());
                    if(response.getReturnValue().length > 0){
                        this.toggleLookupList(component, true, 'slds-is-open', 'slds-combobox-lookup');
                    }
                    component.find("searchinput").set("v.isLoading", false);
                }
            }
        });
        $A.enqueueAction(action);
    }
})

sObjectLookupItem.cmp:
Create below lightning component, which will be used for creating list elements for records in sObjectLookup.cmp component.

<!--sObjectLookupItem.cmp-->
<aura:component description="Component used for creating list elements for records">
    
    <!--Declare Attributes-->
    <aura:attribute name="record" type="Object" required="true"
                    description="Holds the single record instance"/>
    
    <aura:attribute name="subHeadingFieldsAPI" type="String[]"
                    description="Holds the field API names to show as meta entity in list"/>
    
    <aura:attribute name="subHeadingFieldValues" type="String"
                    description="Used to construct the meta entity value. Works as subheading in record option"/>
    
    <aura:attribute name="iconCategoryName" type="String"
                    description="Lightning icon category and icon name to show with each record element"/>
    
    <!--Declare Events-->
    <aura:registerEvent name="lookupSelect" type="c:sObjectLookupSelectEvent"
                        description="Event used to send the selected record Id and Name to Lookup component"/>
    
    <!--Declare Handlers-->
    <aura:handler name="init" value="{!this}" action="{!c.getValues}"
                  description="standard init event to prepare the sub heading mete entity value"/>
    
    <!--Component-->
    <li role="presentation" class="slds-listbox__item" onclick="{!c.handleSelect}">
        <span class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta"
              role="option">
            <!--Lightning Icon-->
            <span class="slds-media__figure">
                <lightning:icon iconName="{!v.iconCategoryName}" size="small" alternativeText="{!v.record.Name}"/>
            </span>
            <!--Option-->
            <span class="slds-media__body">
                <span class="slds-listbox__option-text slds-listbox__option-text_entity">
                    {!v.record.Name}
                </span>
                <!--Option sub heading-->
                <span class="slds-listbox__option-meta slds-listbox__option-meta_entity">
                    {!v.subHeadingFieldValues}
                </span>
            </span>
        </span>
    </li>
</aura:component>

sObjectLookupItemController.js:

({
    getValues : function (component) {
        var record = component.get("v.record");
        var subheading = '';
        for(var i=0; i<component.get("v.subHeadingFieldsAPI").length; i++ ){
            if(record[component.get("v.subHeadingFieldsAPI")[i]]){
                subheading = subheading + record[component.get("v.subHeadingFieldsAPI")[i]] + ' - ';
            }
        }
        subheading = subheading.substring(0,subheading.lastIndexOf('-'));
        component.set("v.subHeadingFieldValues", subheading);
    },
    
    handleSelect : function (component,event) {
        var chooseEvent = component.getEvent("lookupSelect");
        chooseEvent.setParams({
            "recordId" : component.get("v.record").Id,
            "recordName":component.get("v.record").Name
        });
        chooseEvent.fire();
    }
})

Usage:

<!--sObjectLookupApp.app-->
<aura:application extends="force:slds">
    <c:sObjectLookup fieldLabel = "Contact" objectAPIName = "Contact"
                     fieldAPIName = "Name" subHeadingFieldsAPI = "Email,Phone"
                     lookupIcon = "standard:contact" placeholder = "Search Contact"/>
</aura:application>

Output:

Simple Calculator Using Salesforce Lightning Components

Calculator Component:

<!--Calculator-->
<aura:component >
    <aura:attribute name="num1" type="Integer"/>
    <aura:attribute name="num2" type="Integer"/>
    <aura:attribute name="num3" type="Integer"/><br/>
    <ui:inputNumber label="Number 1 " value="{!v.num1}"/><br/>
    <ui:inputNumber label="Number 2 " value="{!v.num2}"/><br/>
    <aura:attribute name="isAdd" type="Boolean" default="false"/>
    <aura:attribute name="isSub" type="Boolean" default="false"/>
    <aura:attribute name="isMul" type="Boolean" default="false"/>
    <aura:attribute name="isDiv" type="Boolean" default="false"/>
    <aura:attribute name="isRefresh" type="Boolean" default="false"/>
    
    <aura:if isTrue="{!v.isAdd}">
        Addition of Two Numbers is :  {!num3}
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    
    <aura:if isTrue="{!v.isSub}">
        Substraction of Two Numbers is :  {!num3} 
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    
    <aura:if isTrue="{!v.isMul}">
        multiplication of Two Numbers is :  {!num3}
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    
    <aura:if isTrue="{!v.isDiv}">
        Division of Two Numbers is :  {!num3}
        <ui:outputNumber value="{!v.num3}"/>
    </aura:if>
    <br/>
    <br/>
    <ui:button press="{!c.addAction}" label="Addition" />
    <aura:if isTrue="{!v.isRefresh}">
        <ui:button press="{!c.refreshAction}" label="Refresh" />
    </aura:if>
    <ui:button press="{!c.substractionAction}" label="Substraction" />    
    <ui:button press="{!c.multiplicationAction}" label="Multiplication" />    
    <ui:button press="{!c.divisionAction}" label="Division" />
    
</aura:component>

Calculator JavaScript Controller:

({
    addAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1+num2;
        component.set("v.num3", num3);
        component.set("v.isAdd", true);
        component.set("v.isRefresh", true);
        component.set("v.isMul", false);
        component.set("v.isSub", false);
        component.set("v.isDiv", false);
    },
    multiplicationAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1*num2;
        component.set("v.num3", num3);
        component.set("v.isMul", true);
        component.set("v.isRefresh", true);
        component.set("v.isDiv", false);
        component.set("v.isSub", false);
        component.set("v.isAdd", false);
    },
    substractionAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1-num2;
        component.set("v.num3", num3);
        component.set("v.isSub", true);
        component.set("v.isRefresh", true);
        component.set("v.isMul", false);
        component.set("v.isDiv", false);
        component.set("v.isAdd", false);
    },
    divisionAction : function(component, event, helper) {
        var num1=component.get("v.num1");
        var num2=component.get("v.num2");
        var num3=num1/num2;
        component.set("v.num3", num3);
        component.set("v.isDiv", true);
        component.set("v.isRefresh", true);
        component.set("v.isMul", false);
        component.set("v.isSub", false);
        component.set("v.isAdd", false);
    },
    
    refreshAction : function(component, event, helper) {
        component.set("v.num1", 0);
        component.set("v.num2", 0);
        component.set("v.isDiv", false);
        component.set("v.isMul", false);
        component.set("v.isSub", false);
        component.set("v.isAdd", false);
        component.set("v.isRefresh", false);
    }
})

Lightning App:

<!--TestApp-->
<aura:application extends="force:slds">
    <c:Calculator />
</aura:application>

Output:

Lightning Tab With Next And Back Buttons In Lightning Component

Component:

<!--TabComponent-->
<aura:component>
    <aura:attribute name="selTabId" type="String" default="tab1" />
    <div aura:id="data-entry">
        <lightning:tabset variant="default" selectedTabId="{!v.selTabId}" >
            <lightning:tab label="Mobile" aura:id="tab1" tabindex="1" id="tab1" title="Mobile">
                <p>Apple</p>
                <p>Samsung</p>
                <p>Lenovo</p>
            </lightning:tab>
            
            <lightning:tab label="Laptop" aura:id="tab2" tabindex="2" id="tab2" title="Laptop">
                <p>Apple</p>
                <p>Lenovo</p>
                <p>Dell</p>
            </lightning:tab>  
            
            <lightning:tab label="Tablet" aura:id="tab3" tabindex="3" id="tab3" title="Tablet">
                <p>Apple</p>
                <p>Lenovo</p>
                <p>Dell</p>
            </lightning:tab> 
        </lightning:tabset>
    </div>
    
    <div class="slds-clearfix">
        <div class="slds-float_left">
            <!--disabled the back button on first Tab-->    
            <lightning:button disabled="{!v.selTabId == 'tab1'}" variant="neutral" label="Back" onclick="{!c.back}" />
        </div>
        <div class="slds-float_right">
            <!--disabled the back button on last Tab--> 
            <lightning:button variant="brand" disabled="{!v.selTabId == 'tab3'}" label="Next" onclick="{!c.next}" />
        </div>
    </div>
</aura:component>

Component Controller:

({
    next : function(component, event, helper) {
        //Get the current selected tab value
        var currentTab = component.get("v.selTabId");
        
        if(currentTab == 'tab1'){
            component.set("v.selTabId" , 'tab2');   
        }else if(currentTab == 'tab2'){
            component.set("v.selTabId" , 'tab3');     
        }
    },
    
    back : function(component, event, helper) {
        //Get the current selected tab value  
        var currentTab = component.get("v.selTabId");
        
        if(currentTab == 'tab2'){
            component.set("v.selTabId" , 'tab1');     
        } else if(currentTab == 'tab3'){
            component.set("v.selTabId" , 'tab2');     
        }
    }
})

Output:

Get Selected Tab of Lightning Tabset

Component:

<!--TabComponent-->
<aura:component>
    <aura:attribute name="selTabId" type="String" default="tab1" />
    <div aura:id="data-entry">
        <lightning:tabset onselect="{!c.tabSelected}" variant="default" selectedTabId="{!v.selTabId}" >
            <lightning:tab label="Mobile" aura:id="tab1" tabindex="1" id="tab1" title="Mobile">
                <p>Apple</p>
                <p>Samsung</p>
                <p>Lenovo</p>
            </lightning:tab>
            
            <lightning:tab label="Laptop" aura:id="tab2" tabindex="2" id="tab2" title="Laptop">
                <p>Apple</p>
                <p>Lenovo</p>
                <p>Dell</p>
            </lightning:tab>      
        </lightning:tabset>
    </div>
</aura:component>

Component Controller:

({
    tabSelected: function(component,event,helper) {
        alert(component.get("v.selTabId"));
    }
})

Output: