Sunday, August 11, 2019

Custom related list Lightning Components for adding contact

In this article i am going to explain how we can create a custom related list component for specific related list. I am going to create a custom contact related list component to add contact which will be exactly similar to standard related component.

Concept's tested:

1) lightning:card
2) force:recordData
3) lightning:overlayLibrary (To create modal box)
4) Dynamically creating component using $A.createComponent
5) force:navigateToRelatedList

customContact.cmp

<aura:component controller="customContactApex" implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
    <aura:handler name="init" value="{!this}" action="{!c.doInitialization}"/>
    <aura:attribute name="existingContactList" type="contact[]"/>
    <lightning:overlayLibrary aura:id="overlayLib"/>
    <aura:attribute name="contactNumber" type="string"/>
    <lightning:card title="{!v.contactNumber}" iconName="standard:contact">
        <aura:set attribute="actions">
            <lightning:button label="New" onclick="{!c.addContact}"/>
        </aura:set>
        <p class="slds-p-horizontal_small">
            <aura:iteration items="{!v.existingContactList}" var="con">
                <a href="{!'https://myknowndomain-dev-ed.lightning.force.com/'+con.Id}" >
                {!con.Name}
                 </a> 
                <p>Title:{!con.Title}</p>
                <p>Email:{!con.Email}</p>
                <p>Phone:{!con.Phone}</p> 
              <br></br>
            </aura:iteration>
        </p>
       <aura:set attribute="footer">
        <a href="javascript:void(0)" onclick="{!c.navigateToRelatedList}">
            View All
        </a>
        </aura:set>
    </lightning:card>
</aura:component>

customContactController.js

({
    doInitialization : function (component, event, helper) {
        var parentId=component.get("v.recordId");
        var action=component.get('c.getAllContact');
        var existingContactArray=[];
        var title="Contacts";
        action.setParams({
            accId : parentId
        });
       action.setCallback(this,function(response){     
            var state=response.getState();
            var responseLength=response.getReturnValue().length;
            if(state==="SUCCESS")
            {
             
                if(responseLength <= 3)
                {
                    component.set("v.contactNumber",title + ' ' + '(' + response.getReturnValue().length + ')');
                    component.set("v.existingContactList",response.getReturnValue());
                }
                else
                {
                    component.set("v.contactNumber",title + ' ' + '(' + '3+' + ')');
                 
                    for(var i=0; i<3;i++)
                        {
                            existingContactArray.push(response.getReturnValue()[i]);
                            //alert('response.getReturnValue(i)'+response.getReturnValue()[i]);
                        }
                    component.set("v.existingContactList",existingContactArray);
                }
             
            }
       
        });
        $A.enqueueAction(action);
    },
addContact: function(component, evt, helper) {
        //alert("Before creating Modal");
        var modalBody;

        $A.createComponent("c:contactForm", {accountId:component.get("v.recordId")},
           function(instanceOfContactForm, status) {
               if (status === "SUCCESS") {
                   modalBody = instanceOfContactForm;
                   component.find('overlayLib').showCustomModal({
                       header: "Create Contact",
                       body: modalBody,
                       showCloseButton: true,
                     
                       closeCallback: function() {
                         
                       }
                   })
               }                             
           });
    },
    navigateToRelatedList: function(component,event,helper){
   var rlEvent = $A.get("e.force:navigateToRelatedList");
   rlEvent.setParams({
      "relatedListId": "Contacts",
      "parentRecordId": component.get("v.recordId")
   });
   rlEvent.fire();
    }
 
 

})


contactForm.cmp

<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
    <aura:attribute name="conFirstName" type="string" />
    <aura:attribute name="conLastName" type="string" />
    <aura:attribute name="conTitle" type="string" />
    <aura:attribute name="conDepartment" type="string" />
    <aura:attribute name="newContactRecord" type="Object"/>
    <aura:attribute name="contactFieldsToQuery" type="Object"/>
    <aura:attribute name="recordError" type="String"/>
    <aura:attribute name="accountId" type="string"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <force:recordData aura:id="creatingContactRecordOnAccount"
                   
                      layoutType="FULL"
                      mode="EDIT"
                      targetRecord="{!v.newContactRecord}"
                      targetFields="{!v.contactFieldsToQuery}"
                      targetError="{!v.recordError}"
                      />
    <lightning:input label="FirstName" value="{!v.conFirstName}"/>
    <lightning:input label="LastName" value="{!v.conLastName}"/>
    <lightning:input label="Title" value="{!v.conTitle}"/>
    <lightning:input label="Department" value="{!v.conDepartment}"/>
    <lightning:button label="Add contact" onclick="{!c.addContact}"/>
</aura:component>

contactFormController.js

({
doInit: function(component, event, helper) {
         var accId=component.get("v.accountId");
         //alert("Id of Account from main comp"+accId);
        // getNewRecord loads a new record template that performs an insert when data is saved.
        component.find("creatingContactRecordOnAccount").getNewRecord(
            "Contact", // Specify Object (entityAPIName)
            null,      // recordTypeId
            false,     // skip cache?
            $A.getCallback(function() {
                var conRec = component.get("v.newContactRecord");
                var error = component.get("v.recordError");
                if(error || (conRec === null)) {
                   // alert("Error in initializing template");
                }
                else {
                    //alert("Template initialize succesfully");
                }
            })
        );
    },
    addContact: function(component, event, helper) {
        var lastName=component.get("v.conLastName");
        var firstName=component.get("v.conFirstName");
        var conTitle=component.get("v.conTitle");
        var conDepartment=component.get("v.conDepartment");
        //alert('Name is'+lastName);
        component.set("v.contactFieldsToQuery.AccountId", component.get("v.accountId"));
        component.set("v.contactFieldsToQuery.LastName",lastName);
        component.set("v.contactFieldsToQuery.Title",conTitle);
        component.set("v.contactFieldsToQuery.Department",conDepartment);
        component.find("creatingContactRecordOnAccount").saveRecord(function(saveResult) {
       
           // Handling state of response(SUCCESS,INCOMPLETE,ERROR)
       
             if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            title : 'Success',
            message: 'Contact Inserted Successfully',
            type: 'success'
        });
        toastEvent.fire();
            }
            else if (saveResult.state === "INCOMPLETE" ) {
                alert("Error in saving record");
            }
            else if (saveResult.state === "ERROR") {
               alert("Problem saving record, error:" +
                           JSON.stringify(saveResult.error));
            }
            else
            {
                 //alert('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));
            }
       
         });
    }
})


customContactApex.apxc

public class customContactApex {
    @AuraEnabled
    public static list<contact> getAllContact(string accId)
    {
        system.debug('accId'+accId);
        List<contact> conList=new List<contact>();
        //List<contact> conListToReturn=new List<contact>();
        for(contact obj:[Select id,name,title,email,phone from contact where accountid=:accId])
        {
            conList.add(obj);
        }
     
          return conList;
     
     
    }
}




Saturday, July 20, 2019

How to pass record id from VF page to lightning component.

In the below example i will demonstrate how to pass the id of record from Visualforce page to Lightning component. 

Prerequisite: Put the Visualforce page in record detail page.


COMPONENT (Name: LightningCompForVF)


<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
<div>
    HI I AM COMPONENT AND I AM GETTING CALLED FROM VF.
    </div>
    <aura:attribute name="AccIDFromVfPage" type="string"/>
   
    <lightning:button label="Check ID from Vf" onclick="{!c.doAction}"/>
</aura:component>


COMPONENT JS CONTROLLER


({
doAction : function(component, event, helper) {

        var accIdFromVf=component.get("v.AccIDFromVfPage");
        alert('Id of record from Vf page'+accIdFromVf);
}
})


LIGHTNING APP (Name: LightningCompForVFAPP)


<aura:application access="GLOBAL" extends="ltng:outApp">

    <aura:dependency resource="c:LightningCompForVF"/>

</aura:application>



VISUALFORCE PAGE (Name: VfPageToCallLightningComp)


<apex:page standardController="account" >
    <apex:includeLightning />
    <div  id="LightningCompContainer" />
   
    <script>
        $Lightning.use("c:LightningCompForVFAPP", function() {
            $Lightning.createComponent("c:LightningCompForVF", {
            },
            "LightningCompContainer",
            function(component) {
               component.set("v.AccIDFromVfPage",'{!$CurrentPage.parameters.id}');
            });
        });
   
    </script>
</apex:page>

How to Call VF Page Javascript Method From Lightning Component

In the below example I have demonstrated how to call vf page javascripty method from lightning component.

COMPONENT (Name: LightningCompForVF)

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
<div>
    HI I AM COMPONENT AND I AM GETTING CALLED FROM VF.
    </div>
    <aura:attribute name="VfPageMethod" type="object"/>  <!--Attribute to take Vf page method-->
    <lightning:button label="Click me to pass message to VF page" onclick="{!c.doAction}"/>
</aura:component>


COMPONENT JS CONTROLLER
 
({
doAction : function(component, event, helper) {
var msgForVf="Hi This message was pass from component to me and I am VF page displaying this message";
        var vfMethod=component.get("v.VfPageMethod");
        // Calling Vf page method
        vfMethod(msgForVf,function()
                           {
                             
                             
                           });
}
})



VISUALFORCE PAGE (Name: VfPageToCallLightningComp)


<apex:page >
    <apex:includeLightning />
    <div  id="LightningCompContainer" />
   
    <script>
        $Lightning.use("c:LightningCompForVFAPP", function() {
            $Lightning.createComponent("c:LightningCompForVF", {
            VfPageMethod:getMessageFromComp,
            },
            "LightningCompContainer",
            function(cmp) {
             
            });
        });
    function getMessageFromComp(compMsg)
    {
        alert(compMsg);
    }
    </script>
</apex:page>


LIGHTNING APP (Name: LightningCompForVFAPP)

<aura:application access="GLOBAL" extends="ltng:outApp">

    <aura:dependency resource="c:LightningCompForVF"/>

</aura:application>

Lightning out in Salesforce

Lightning out is a feature by which we can use our lightning component inside of an external site. One of the best advantage is we can use our lightning component inside visualforce page.


The three simple way we need to follow to have our lightning component works on visulaforce page is:

1)Include the Lightning Out JavaScript libraries using <apex:includeLightning /> on visualforce page.

2)Specify the lightning app by extending it with ltng:outApp which will specify component dependency.

3)create the component on the page.

Now the questions arise What is lightning out dependency?

When a Lightning components app is initialized using Lightning Out, Lightning Out loads the definitions for the components in the app. To do this efficiently, Lightning Out requires you to specify the component dependencies in advance, so that the definitions can be loaded once, at startup time.The mechanism for specifying dependencies is a Lightning dependency app. A dependency app is simply an <aura:application> with a few attributes, and the dependent 
components described using the <aura:dependency> tag.

Basic syntax:

<aura:application access="GLOBAL" extends="ltng:outApp">

    <aura:dependency resource="c:myAppComponent"/>

</aura:application>


Key points to remember:

1) Lightning app access should be set to GLOBAL.
2) It can be extends either using ltng:outApp or ltng:outAppUnstyled.
3) List as a dependency every component that is referenced in a call to $Lightning.createComponent().

$Lightning.use() refer to Lightning application and is used to load and initialize lightning component. It has the callback function $Lightning.createComponent(). $Lightning.createComponent() refer the Lightning component and is use to pass the attribute from Vf page to Lightning component. The domLocator with $Lightning.createComponent() 
is used to display component.

Let us see a simple example:

COMPONENT (Name: LightningCompForVF)

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
<div>
    FROM LIGHTNING COMPONENT    
    </div>
</aura:component>


LIGHTNING APP (Name: LightningCompForVFAPP)

<aura:application access="GLOBAL" extends="ltng:outApp">

    <aura:dependency resource="c:LightningCompForVF"/>

</aura:application>

VISUALFORCE PAGE (Name: VfPageToCallLightningComp)

<apex:page >
    <apex:includeLightning />
    <div  id="LightningCompContainer" />  <!--Used to display lightning component-->
     
    <script>
        $Lightning.use("c:LightningCompForVFAPP", function() {
            $Lightning.createComponent("c:LightningCompForVF", { },
            "LightningCompContainer",
            function(cmp) {
                alert('Component created');
            });
        });
    </script>
</apex:page>


Lightning out in Salesforce



Monday, July 8, 2019

lightning:datatable in Salesforce

Lightning datatable is a table that displays columns of data, formatted according to type.
Important attributes includes data, columns, and keyField attributes.The keyField attribute is required for correct table behavior. It associates each row with a unique identifier.

Note: lightning:datatable is not supported on mobile devices.

Let us see a simple example:

COMPONENT:

<aura:component controller="ContactController">
    <aura:attribute name="mydata" type="Contact[]"/>
    <aura:attribute name="mycolumns" type="List"/>
    <aura:handler name="init" value="{! this }" action="{! c.doinit }"/>
    <lightning:datatable data="{! v.mydata }"
        columns="{! v.mycolumns }"
        keyField="id"
        />
</aura:component>

CLIENT SIDE CONTROLLER:

({
    doinit: function (component, event, helper) {
        component.set("v.mycolumns", [
         
            {label: 'Contact Name', fieldName: 'Name', type: 'text'},
            {label: 'Contact title', fieldName: 'Title', type: 'text'},
            {label: 'Contact department', fieldName: 'Department', type: 'text'}
         
         
        ]);
        var action=component.get('c.fetchContact');
     
        action.setCallback(this,function(response){       
            var state=response.getState();
            if(state==="SUCCESS")
            {alert('Inside success');
                component.set("v.mydata",response.getReturnValue());
            }
         
        });
        $A.enqueueAction(action);


    }
});

SERVER SIDE CONTROLLER:

public class ContactController {
    @AuraEnabled
    public static List<Contact> fetchContact() {
        List<Contact> contacts =
                [SELECT Id, name, title,Department FROM Contact limit 10];
     
        return contacts;
    }
}

APPLICATION:

<aura:application extends="force:slds">
<c:Datatable/>
</aura:application>

OUTPUT:

lightning:datatable in Salesforce


By default we have Checkbox column available and if we want to hide it we can use
hideCheckboxColumn="true".

Example:


<aura:component controller="ContactController">
    <aura:attribute name="mydata" type="Contact[]"/>
    <aura:attribute name="mycolumns" type="List"/>
    <aura:handler name="init" value="{! this }" action="{! c.doinit }"/>
    <lightning:datatable data="{! v.mydata }"
        columns="{! v.mycolumns }"
        keyField="id"
        hideCheckboxColumn="true"
        />
</aura:component>


If we want to handle selected records we can use onrowselection="{!c.someAction}" with lightning datatable.

Let us see a simple example to display selected record using lightning card.

COMPONENT:

<aura:component controller="ContactController">
    <aura:attribute name="mydata" type="Contact[]"/>
    <aura:attribute name="mycolumns" type="List"/>
    <aura:attribute name="conSelect" type="boolean"/>
    <aura:attribute name="selectedContacts" type="List"/>
    <aura:handler name="init" value="{! this }" action="{! c.doinit }"/>
    <lightning:datatable data="{! v.mydata }"
        columns="{! v.mycolumns }"
        keyField="id"
        onrowselection="{!c.findSelected}"           
        />
    <aura:if isTrue="{!v.conSelect}">
        <b>Selected Contact</b>
    <aura:iteration items="{!v.selectedContacts}" var="con">
        <lightning:card title="Selected Contact"  >
        {!con.Name}
        </lightning:card>
     
    </aura:iteration>
    </aura:if>
</aura:component>

CLIENT SIDE CONTROLLER:


({
    doinit: function (component, event, helper) {
        component.set("v.mycolumns", [
         
            {label: 'Contact Name', fieldName: 'Name', type: 'text'},
            {label: 'Contact title', fieldName: 'Title', type: 'text'},
            {label: 'Contact department', fieldName: 'Department', type: 'text'}
         
         
        ]);
        var action=component.get('c.fetchContact');
     
        action.setCallback(this,function(response){       
            var state=response.getState();
            if(state==="SUCCESS")
            {alert('Inside success');
                component.set("v.mydata",response.getReturnValue());
            }
         
        });
        $A.enqueueAction(action);


    },
 
    findSelected:  function (component, event, helper) {
       var selectedrows=event.getParam('selectedRows');
       var selectedArrayOfRecord=[];
        for(var i=0; i<selectedrows.length; i++)
            {
             
             selectedArrayOfRecord.push(selectedrows[i]);
            }
        component.set("v.selectedContacts",selectedArrayOfRecord);
        component.set("v.conSelect",true);
     
    }
});

SERVER SIDE CONTROLLER:

public class ContactController {
    @AuraEnabled
    public static List<Contact> fetchContact() {
        List<Contact> contacts =
                [SELECT Id, name, title,Department FROM Contact limit 10];
     
        return contacts;
    }
}

OUTPUT: