Wednesday 10 July 2019

Display "mini page layout" on hover in Lightning Component

Requirement:
There was a requirement, while working with one of the clients, to provide an interface which would display mini page layout on hover of the Lightning component. This layout had to be customized and shown on MouseHover of the Contact object field.

Challenge: 

In Lightning, the mini page layout is only available for the fields which have a lookup or have a master-detail relationship. There is no out-of-the-box (OOTB) functionality available to display linked record field values in the pop-up in lightning component.

Solution:

To overcome this limitation, we developed a custom solution which involved creating an apex class called ContactsController. This class contains the mechanism to retrieve the list of contacts along with related accounts. This class is used by Custom Lightning Component to display the list of contacts with a pop-up functionality.


ContactsController class

public class ContactsController {
    @AuraEnabled
    public static List <contact> getContacts() {
        return [SELECT Id, name,phone, Contact.account.Name, Contact.account.industry, Contact.account.Type,
                Contact.account.Phone  FROM contact ORDER BY createdDate ASC];
    }
}

ContactsController class is referenced by MouseHover component to display retrieved contacts as a data table and use JavaScript Controller to illustrate contacts only from the list.

MouseHover.cmp

<aura:component controller="ContactsController">
    <aura:attribute name="contacts" type="List" />
    <aura:attribute name="conAccLst" type="List" />
    <aura:attribute name="reId" type="Id" />
    <aura:attribute name="mouseHoverData" type="object" />
    <aura:attribute name="togglehover" type="boolean" default="false"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <aura:attribute name="hoverRow" type="Integer" default="-1" />
    <!-- Use a data table from the Lightning Design System: https://www.lightningdesignsystem.com/components/data-tables/ -->
    <table class="slds-table slds-table_bordered slds-table_striped slds-table_cell-buffer slds-table_fixed-layout">
        <thead>
            <tr class="slds-text-heading_label">
                <th scope="col"><div class="slds-truncate" title="Name" style="text-align: center">Contact Name</div></th>
                <th scope="col"><div class="slds-truncate" title="Phone" style="text-align: center">Phone</div></th>
            </tr>
        </thead>
        <tbody>
            <!-- Use the Apex model and controller to fetch server side data -->
            <aura:iteration items="{!v.contacts}" var="contact" indexVar="index">
                <tr data-selected-Index="{!index}">
                    <td><div class="slds-truncate" title="{!contact.Name}" style="text-align: center">
                        <a id="{!contact.Id}" onmouseenter="{!c.handleMouseHover}" onmouseout="{!c.handleMouseOut}" data-index="{!index}" tabindex="-1">{!contact.Name}</a></div>
                        <aura:if isTrue="{!v.hoverRow==index}">
                            <aura:if isTrue="{!v.togglehover==true}">
                                <div   class="slds-popover slds-nubbin_bottom"
                                     role="tooltip" id="help" style="position: absolute; right: 225px; bottom: 100%; width: 22rem; padding: inherit;">
                                    Account Name: {!v.mouseHoverData.Name}<br/>
                                    Phone:{!v.mouseHoverData.Phone}<br/>
                                    Type:{!v.mouseHoverData.Type}<br/>
                                  
                                </div>
                            </aura:if>
                        </aura:if>
                    </td>
                    <td><div class="slds-truncate" title="{!contact.Phone}" style="text-align: center">{!contact.Phone}</div></td>
                </tr>
            </aura:iteration>
        </tbody>
    </table>
</aura:component>

MouseHoverController.js retrieves all contacts on the load event of MouseHover Component and call MouseHoverHelper javascript file on hover of any contact name to display a pop-up with related account details.

MouseHoverController.js


({
    doInit: function(component, event, helper) {
        // Fetch the Contact list from the Apex controller
        helper.getAccountList(component);
    },
    handleMouseHover: function(component, event, helper) {
        var my = event.srcElement.id;
        component.set("v.reId",my);
        helper.getMiniLayout(component, event, helper)
    },
    handleMouseOut: function(component, event, helper) {
        component.set("v.hoverRow",-1);
        component.set("v.togglehover",false);
    }
})

MouseHoverHelper fetches the related account details as a pop-up on hover of a contact name.

MouseHoverHelper.js 


({
    // Fetch the Contact from the Apex controller
    getAccountList: function(component) {
        var action = component.get('c.getContacts');
        // Set up the callback
        var self = this;
        action.setCallback(this, function(actionResult) {
            var result = actionResult.getReturnValue();
            component.set('v.contacts', result);
            var conAccList = [];
            for(var i=0 ; i<result.length;i++){
                conAccList.push({"Id":result[i].Id, "value":result[i]});
            }
            component.set('v.conAccLst', conAccList);
        });
        $A.enqueueAction(action);
    },
    //Fetch the releted account on mouseHover 
    getMiniLayout:function(component, event, helper){
        
        var getAccount = component.get('v.conAccLst');
        for(var i=0;i<getAccount.length;i++){
            if(getAccount[i].Id == component.get("v.reId")){
                component.set('v.mouseHoverData', getAccount[i].value.Account);
                break;
            }
        }
        component.set("v.hoverRow", parseInt(event.target.dataset.index));
        component.set("v.togglehover",true);
    }
})

After this, we have created a Lightning App to integrate all of the files as a solution

Sample.app 

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


Output :



If you have any questions you can reach out our Salesforce Consulting team here.

Wednesday 19 June 2019

Generate a PDF file with APEX Trigger in Salesforce

Requirement:
There was a requirement to generate a PDF file while inserting a new record in Account object and add it as an attachment (On-the-fly).

Challenge:
The main challenge was - when the record is saved, the apex trigger is fired on the account object, but at that time, we are unable to use the PageReference Class methods (generate PDF) within the trigger context, making it difficult to generate PDF with this OOTB Class/Method. 

Solution:
To generate PDF, we can use out-of-the-box PageReference Class method and it works only if there is not APEX trigger associated with the object. But if Apex trigger is associated with object while inserting a new record, it won't work.

To overcome this limitation, we developed a custom solution which involved creating an apex class called PDFGenerator. This class contains the mechanism to accept the HTML content and renders the PDF accordingly to the HTML specified. It will also attach the PDF file to the record as an Attachment.

PDFGenerator Class :


public with sharing class PDFGenerator
{
    public static final String FORM_HTML_START = '<HTML><BODY>';
    public static final String FORM_HTML_END = '</BODY></HTML>';

    public static void PDFGenerator(Account account)
    {
        String pdfContent = '' + FORM_HTML_START;

        try
        {
            pdfContent = '' + FORM_HTML_START;
            pdfContent = pdfContent + '<H2>Account Information in PDF</H2>';
            
            //Dynamically grab all the fields to store in the PDF
            Map<String, Schema.SObjectType> sobjectSchemaMap = Schema.getGlobalDescribe();
            Schema.DescribeSObjectResult objDescribe = sobjectSchemaMap.get('Account').getDescribe();
            Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap();
            
            //Append each Field to the PDF
            for(Schema.SObjectField fieldDef : fieldMap.values()) 
            {
                Schema.Describefieldresult fieldDescResult = fieldDef.getDescribe();
                String name = fieldDescResult.getName();
                pdfContent = pdfContent + '<P>' + name + ': ' + account.get(name) + '</P>';
            }

            pdfContent = pdfContent + FORM_HTML_END;

        }catch(Exception e)

        {
            pdfContent = '' + FORM_HTML_START;
            pdfContent = pdfContent + '<P>THERE WAS AN ERROR GENERATING PDF: ' + e.getMessage() + '</P>';
            pdfContent = pdfContent + FORM_HTML_END;
        }
        attachPDF(account,pdfContent);
    }  

    public static void attachPDF(Account account, String pdfContent)
    {
        try
        {
            Attachment attachmentPDF = new Attachment();
            attachmentPDF.parentId = account.Id;
            attachmentPDF.Name = account.Name + '.pdf';
            attachmentPDF.body = Blob.toPDF(pdfContent); //This creates the PDF content
            insert attachmentPDF;

        }catch(Exception e)
        {     
           account.addError(e.getMessage());
        }
    }
}

Now, we would modify an Apex Trigger, on Account object which will initialize the above created PDFGenerator apex class on the record creation (after insertion).

Account Trigger:


trigger AccountTrigger on Account (after insert)
{
    if(trigger.isAfter && trigger.isInsert)
    {  
        for(Account ac : trigger.new)
          {
             PDFGeneratorcController.PDFGenerator(ac);
          }          
    }  
}

This approach would generate a PDF within the context of the trigger, and help you overcome the challenge.

If you have any questions you can reach out our Salesforce Consulting team here.


Monday 17 June 2019

Brief introduction to Nodes used in Salesforce Analytics Data flow

Any manipulation done to the data can be referred as a transformation process. In order to manipulate the data, we can add transformations to a dataflow to extract data from Salesforce objects or datasets, transform them (Salesforce data or external data), and register those datasets.

Below is the glossary used in Dataflow Analytics describing the role/term in the process.
  • sfdcDigest node - Extracts data or generates a dataset from a Salesforce object to be used for queries and further transformation.
  • sfdcRegister node - is responsible for registering dataset to make them available to use them in queries.
  • augment - As the name suggests, it augments the related datasets. The left and right keys are the fields from each node that are used to match records (similar to JOIN operations from SQL).
  • digest - is used to extract data synced from the local Salesforce org, or data synced through an external connection.
  • computeExpression  - is useful to create calculated fields or derived fields and add those to the dataset.
  • update - As the name suggests, this transformation updates the specified field values in an existing dataset based on data from another dataset.
  • filter  - The filter transformation is used to filter the records from an existing data-set based on the condition.
  • edgemart  - The edgemart transformation is actually the collection of the datasets which include an existing, registered data-set, or which can contain Salesforce data, external data, or both.
  • sliceDataset - The sliceDataset transformation performs the slicing and dicing of fields from a dataset in your dataflow.
We can find on each node of Salesforce Analytics Dataflow here.

If you have any questions you can reach out our Salesforce Consulting team here.

Tuesday 11 June 2019

Embed a free Digital Signature to Salesforce Visualforce Page Using HTML5 Canvas

Requirement: 
We had to provide an interface where the user could add a signature digitally (using mouse or touch, depending on the device) in Salesforce Visualforce Page, and embed that digital signature to the invoice generated only using out-of-the-box functionality and not using any 3rd-party vendor tools like Adobe eSign, DocuSign and other tools with premium license.

Challenge:
Using open-source and out-of-the-box functionalities of Salesforce and not using any 3rd-party vendor tool.

Solution:
To achieve this, we have used a jQuery plug-in called 'Sketch.js' along with the common jQuery CDN. 

So, we have achieved the desired results by using HTML5 Canvas and JavaScript remoting. 

Now, we would be adding jQuery and Sketch.js file content in Salesforce Static resources repository.

Then, we need to setup JavaScript event listeners in the Visualforce (VF) page to capture the events like touch-start, touch-move, and touch-end performed on the canvas. The sketch.js will handle the touch/drag events performed on the canvas.

The purpose of using JavaScript remoting is to ensure that the Canvas content is properly passed to Apex Controller and can be saved. The canvas would be converted into Base64 String with the Canvas.toDataURI() method. This is how we would be pushing the converted data bytes from the Canvas into an Attachment.

Below is the source code to add digital signature in Visualforce Page & APEX class:

Visualforce Page:


<apex:page controller="SignDemo">

 <script type="text/javascript" >

    $( window ).load(function(){
        $('#Sign').sketch(); // associating sketch function with canvas.
    });

// clear canvas on clear button.
function clearSketch(){          

        $('#Sign').replaceWith('<canvas id="Sign" style="border:2px solid; border-radius:10px 10px 10px 10px;height: 150px;width: 300px;background:white; border-color:lightgray"></canvas>');

        $('#Sign').sketch();
 }  

function save(){
       // Get Data URL from the canvas i.e. signed by the user.
      var dataUrl = document.getElementById('Sign').toDataURL();

     // use Remote Action function to generate image.
     Visualforce.remoting.Manager.invokeAction(

           '{!$RemoteAction.SignDemo.GenerateImage}',dataUrl ,

            function(result, event) {

                if (event.status) {
                // Redirect to the attachment and open attachment as image.
                window.open('/servlet/servlet.FileDownload?file='+result);
           }
       });

   }

</script>

    Draw your signature : 

    <div>
        <canvas id="Sign" style="border:2px solid; border-radius:10px 10px 10px 10px;height: 150px;

                                    width: 300px;background:white; border-color:lightgray" ></canvas>

        <input type="button" value="Clear"  onclick="clearSketch()" /> 
    </div>
    <br/>

    <input type='button' value='save' onclick='save()'/>

</apex:page>

Apex Controller:

public class SignDemo{

    //Create attachment and return id of the attachment.
@RemoteAction
    public static string GenerateImage(string dataurl){

        Attachment myAttach = new Attachment();

        myAttach.name = 'Sign.png';
        myAttach.ParentId ='';  //Add ID of the parent object for the attachment (Account,Contact,etc..)
        myAttach.body = ((Blob) EncodingUtil.base64Decode(dataurl.split(',')[1]));
        insert myAttach;
        return myAttach.Id;
    }
}

Output:

Below is the illustration of the canvas board and signature, which can be performed by the user. 



Below is the sample invoice file generated containing the digital signature. 




If you have any questions you can reach out our Salesforce Consulting team here.