Clone Opportunity with Products action on Opportunities ( Lightning Component )

I am following this idea since I needed this feature in one of my projects. Since Salesforce already has this in their future roadmap, I realized that It could be an addition to my #GivingBackToCommunity list. I managed to create a lightning component which can be used with lightning action on Opportunity records. This will allow users to clone an opportunity with the products.

IdeaExchange Idea

Here is the idea on the IdeaExchange

clone with products

Lightning Component for Clone with Products

Here is the link to the Lightning Component GitHub repository
clone with products

 

Steps to Install/Configure this component in your Org

For Sandbox/Dev Environment

First, you need to identify whether you want to install a managed or unmanaged package. This can be determined based on your purpose. If you want to amend existing functionality then the unmanaged package is what you are looking for. If that’s not the case and you just want to test it before installing it to the production then managed package is your best bet.

  1. Install the package in your Org. Following are the links to the package installation URL.
    Link to unmanaged package
    Link to managed package
  2. After installing package successfully, you need to configure it and you will have to switch to Salesforce classic view as you need to update the fieldsets ( fieldsets are not available in lightning experience till Winter ‘2018 release).
  3. You have 2 fieldsets first one on Opportunity object and the second one on the OpportunityProduct object. These fieldsets let you decide which field values you want to copy to new opportunity ( cloned opportunity ). By default, it will copy those fields only which are required to save the record.
  4. To modify the fieldset fields navigate to Setup | Customize | Opportunity | FieldSets and you will find the Clone Opportunity Field Set.
  5. You can add fields which you want to be copied in the new opportunity. See the below screenshot.
  6. After adding the fields in the fieldset, you need to add action to the page layout. So navigate to the page layout section and drag the “Clone with Products” action on the pane.
  7. Save everything and switch back to lightning experience view to use this feature.
Screenshot_9
Clone Opportunity Field Set

 

Screenshot_10
Lightning action in page layout

 

For Production Environment

  1. Install the package in your Org. Following are the links to the package installation URL.
    Link to managed package

Rest steps are same as explained for the sandbox environment.

Please let me know if you have any feedback on this.

 

How to create charts using D3 Library in Lightning Components

D3.js is the most popular library for creating interactive charts in any web application. You can also use D3.js library in Lightning components for graphical representation of data. These charts can be used to give sales insights to a member ( Salesforce user) of the sales team.  This article will show how can we leverage D3.js to build charts with a simple example.

First, let’s see the component code.

D3Charts.cmp

Screenshot_2

D3ChartsController.js
({
    initD3Charts : function(component, event, helper) {

        var width = 960;
        var height = 500;

        // Create SVG element, we can't just use  tag in lightning component
        // So creating one dynamically using jquery
        var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height);	

        // Calling server-action to get the data
        var action = component.get("c.getData");

        // Create a callback that is executed after
        // the server-side action returns
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                let data = response.getReturnValue();
                console.log(data);
                // Render the returned data as a pie chart
                helper.renderChart( component, data );
            }
            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");
                }
            }
        });

        $A.enqueueAction(action);

    }
})
D3ChartsHelper.js
({
    renderChart : function( component, data ) {
        var svg = d3.select("svg"),
            width = +svg.attr("width"),
            height = +svg.attr("height"),
            radius = Math.min(width, height) / 2,
            g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

        var color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);

        var pie = d3.pie()
        .sort(null)
        .value(function(d) { return d.candidates; });

        var path = d3.arc()
        .outerRadius(radius - 10)
        .innerRadius(0);

        var label = d3.arc()
        .outerRadius(radius - 80)
        .innerRadius(radius - 80);

        var arc = g.selectAll(".arc")
        .data(pie(data))
        .enter().append("g")
        .attr("class", "arc");

        arc.append("path")
        .attr("d", path)
        .attr("fill", function(d) { return color(d.data.age); });

        arc.append("text")
        .attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
        .attr("dy", "0.35em")
        .text(function(d) { return d.data.age; });
    }
})

So, we have included d3.js library along with jquery.js and then called a server-side action when the script has loaded in order to fetch the chart data. In the defined callback function we passed that data to a helper function which rendered the chart.

D3ChartsController.apex
public class D3ChartsController {
  
    @AuraEnabled
    public static List getData(){
      
        // For this example, it's just a static data but it can be generated 
        // as per any business logic
        List lstData = new List();
        lstData.add( new PieChart( '=65', 61 ) );
        return lstData;
    }
    
    public class PieChart {
        @AuraEnabled public String age;
        @AuraEnabled public Integer candidates;
        
        public PieChart( String age, Integer candidates ) {
            this.age = age;
            this.candidates = candidates;
        }
    }
}
Screenshot_4
Preview

Reference to resources –

 

How to create table with resizable columns using Lightning Design System ( Column Resizable DataTable )

Lightning design system comes with some really nice cascading stylesheet but still, there are some components where JS support is not provided. During a project, I came across the same where I had to write Javascript code by myself. After going many rounds of testing and changes, finally, I managed to get it working. It’s Data Table component which I am talking about.

How does the column resizing work?

I have used mousedown and mousemove events to recognize the mouse cursor moment. Resizable columns have a divider which let users change the column width. You can check the following Gist for the code.

LINK TO SAMPLE CODE

Table

Please feel free to comment your question/suggestions in the comment box.

How to recreate modal component for force:lightningQuickActionWithoutHeader

If you are building a lightning component for a lightning-quick action then you must be having a hard time to add additional buttons to the footer. If you are implementing the force:lightningQuickActionWithoutHeader interface in the component then you can get rid of the default header and footer and can create your own footer with the buttons of your choice. But still, it doesn’t match with the Lightning Experienceince view. I had the same issue on a recent project and I was able to make it look like the default button’s pop-up.

In order to get the lightning experience matching modal, you need to make some tweaks in the CSS along with lightning design system CSS. You need to include some styles which will modify the standard style so you can get the same look and feel.

After Salesforce Spring ’18 release, Tag Not Allowed in Components. You can’t add a  tag in component markup or when you dynamically create a component in JavaScript code. This restriction ensures better component encapsulation and prevents component styling interfering with the styling of another component.

So,  In order to use a workaround, we will have to include a CSS in our lightning component using a static resource to modify the CSS of the container which is wrapping the lightning component.

.cuf-content{
    padding:0 !important;
}
.slds-p-around--medium{
    padding:0 !important;
}
.slds-modal__container{
    width:80% !important; // You can update the width as per your need
    max-width:80% !important;
}
.slds-modal__content{
    overflow-y:hidden !important;
    height:unset !important;
    max-height:unset !important;
}

Above provided CSS needs to be loaded as an external resource. You will need to copy and paste the CSS into a file and save it as a static resource. Make sure file extension is “.css”. For instance, assume we have uploaded it as a static resource and named it “quickActionStyle”.

Sample Component

Screenshot_1

 

Now, the quick action will look like below.

Screenshot_6

So, we are able to achieve what we were looking for with some small tweaks. If you have any question and concern then feel free to comment on the post.

Happy Easter!!

Are you looking for a way to open a lightning component in a new tab/window on detail page button click?

While working on a lightning component development, I found that there is no standard way to open a lighting component in a new tab. You can find the same problem being asked on developer forums.

So, to be honest there is no standard way to achieve this behavior. I used a workaround to get this done in a project. I thought it’s good to share it if someone is looking for a way. I will divide this into 3 simple steps.

1. Create a New Lightning Component Tab

In order to create a new Lightning Component Tab, please follow these steps.

  • Navigate to Setup and then Tabs.
  • Click on New button in Lightning Component Tab section.

pvzyfs0wteqpgztn1grmha

  • We will need the tab name for the URL which we are going to use for the button.
2. Create a New Button
  • Create a Detail Page button with “Display in new window” behaviour.
  • Button or Link URL
    /one/one.app#/n/tab_name
    
    After URL UPDATE Use the following 
    /lightning/n/tab_name
    

    tab_name is the Tab name which we created in the previous step. Additionally, you can add parameters to the URL. For example – Case record Id

    /one/one.app#/n/New_Case?id={!Case.Id}
    
    After URL UPDATE Use the following 
    /lightning/n/New_Case?id={!Case.Id}
  • Add this button to the page layout under Salesforce1 and Lightning Experience Actions from available Salesforce1 and Lightning Actions.
3. Get the passed Id in the URL

Now, here is the tricky part. Above 2 steps are enough if you don’t want to get the Id passed in the parameter. force:hasRecordId doesn’t work in this scenario. In order to get the passed Id or any other parameter, you will have to do some code in the lightning component which is being displayed in the new page. I did the following things to get the URL parameter.

  • Added an event handler which will be fired when the component gets loaded.
Component Code
<aura:attribute name="recordId"
                    type="String"
                    description="ID of the record."/>

<aura:handler name="init"
			  value="this"
			  action="{!c.doInit}"/>
Client-side controller code
doInit : function(component, event, helper) {

	var recordId = helper.getParamValue('New_Case', 'id');
	component.set('v.recordId', recordId);

}
Client-side helper JS code
({
	getParamValue : function( tabName, paramName ) {

		var url = window.location.href;
		var allParams = url.substr(url.indexOf(tabName) + tabName.length+1).split('&amp;amp;amp;');
		var paramValue = '';
		for(var i=0; i&amp;amp;lt;allParams.length; i++) {
			if(allParams[i].split('=')[0] == paramName)
				paramValue = allParams[i].split('=')[1];
		}
		console.log(paramValue);
		return paramValue;
	}
})

That’s all you need to achieve this functionality. Please comment your questions in case of any doubt/clarification.

UPDATE

If you have enabled the “NEW URL FORMAT” update in your org then you will have to update URL in the tab as per new format.

critical_update_screenshot_cptaqo

Difference

Old URL – /one/one.app#/n/tab_name

New URL –  /lightning/n/tab_name

 

Reference

Here’s What You Need To Know About The New URL Format For Lightning

Jquery Datepicker in Lightning Components for better control on behaviour of Datepicker

Having trouble with <lightning:input type=”date”> and <ui:inputDate> components? Then why don’t you use Jqyery Datepicker?

Jquery datepicker gives a lot of flexibility and freedom to developers to let them use a datepicker as per business needs. A developer can enforce validation on dates and date range which is very hard to impose using these out-of-the-box components as they rely on HTML5 input element.

Following component is using the jquery datepicker to get the date of birth from users.

JqueryDatePicker.cmp

&lt;aura:component &gt;
    &lt;ltng:require scripts=&quot;{!join(',', $Resource.jquery_2_2_4, $Resource.jquery_ui_1_12_1 + '/jquery-ui-1.12.1/jquery-ui.min.js')}&quot;
    afterScriptsLoaded=&quot;{!c.afterScriptsLoaded}&quot; 
    styles=&quot;{!$Resource.jquery_ui_1_12_1 + '/jquery-ui-1.12.1/jquery-ui.min.css'}&quot;
    /&gt;

    Birth Date: &lt;input aura:id=&quot;birthdate&quot; class=&quot;datepicker&quot; type=&quot;text&quot;/&gt;
 
&lt;/aura:component&gt;

JqueryDatePickerController.js

({
    afterScriptsLoaded : function(component, event, helper) {
       $('.datepicker').datepicker();
    }
})

 

NOTE –

In the above example, I used an old version of jquery which is jquery-2.2.4 due to LockerService bug. LockerService’s SecureWindow does not support a ‘hasOwnProperty’ method which is being used in the latest versions of the jQuery library.

Preview

20171006-021715_capture