Monday, 31 August 2020

Dynamically bind variable in Lightning Web Component (LWC)

 Use case

Once we will be working with generic component we may have to bind declared variables  dynamically in code to get expected result .

Based on below example we  can display  account name for an particular 'accountId' 

import { LightningElement, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Account.Name';

export default class Example extends LightningElement {
    @wire(getRecord, { recordId: '0013xx065790ASZ', fields: [NAME_FIELD] })
    account;

     get name() {
        return getFieldValue(this.account.data, NAME_FIELD);
    }

 
    
}

 

Instead of importing the field definition in controller we can dynamically pass  fields array  within getrecord().


Lets have a look into the code.

Controller

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
 * @File Name          : dynamicbinding.js
 * @Description        : 
 * @Author             : Swarup Satpti
 * @Group              : 
 * @Last Modified By   : Swarup Satpati
 * @Last Modified On   : 8/31/2020, 5:00:00 PM
**/
import { LightningElement, api, wire, track } from 'lwc';
import { getFieldValue } from 'lightning/uiRecordApi';
import {getRecord} from 'lightning/uiRecordApi';

export default class Chevron extends LightningElement {
    @api objectName = 'Account';
    @api fieldName = 'Name';
    
    /*component with a recordId property is used on a Lightning record page, 
    the page sets the property to the ID of the current record.*/
    @api recordId='001W000000dls6ZIAQ'; 
    @track record;
    @track error;
    @track fieldArray;
    @track fieldValue;


    connectedCallback(){
        console.log("hi..");
        this.fieldArray = [`${this.objectName}.${this.fieldName}`,`${this.objectName}.RecordTypeId`];
    }
    
    /*Use this wire adapter to get a record’s data : 
       Recordtype ID and value of status field pass through API
    */
    @wire(getRecord, { recordId: '$recordId', fields:'$fieldArray'})
    wiredAccount({ error, data }) {
        if (data) {
            this.record = data;
            console.log(JSON.stringify(data));
           // this.fieldvalue=getFieldValue(data,`${this.objectName}.${this.fieldName}`);
            this.error = undefined;
        } else if (error) {
            console.log('error..');

            this.error = error;
            this.record = undefined;
        }
    }

    get name() {
        return getFieldValue(this.record,`${this.objectName}.${this.fieldName}`);
    }
}

  HTML Template

1
2
3
4
5
<template>
    <div class="slds-m-around_medium">
        <p>Account Name: {name}</p>
    </div>
</template>


Code Explanation:

within Js controller variables are dynamically bound using '$' sign as below.

  this.fieldArray = [`${this.objectName}.${this.fieldName}`,`${this.objectName}.RecordTypeId`]

 Finally  Code will return the  value of account name for given account ID .


Hope you got it useful....! cheers ..

Wednesday, 5 August 2020

Custom Chevron component using lightning web component(LWC)

Use case :

we can use sales force standard "Path" component to build chevron on record page showed as below.


                                                                                                                                                                           
But there is an problem 😟   with this approach . As you can see once stage is completed it shows the chevron with right check mark and not displaying the status  and also i don't want user to manually change the status by using "Mark Stage Completed"  button on  right which should not be available to end user.

To achieve this we have to use custom LWC component. Let's get into the code..!

Code:

Chevron.js

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
/**
 * @File Name          : chevron.js
 * @Description        : 
 * @Author             : Swarup Satpti
 * @Group              : 
 * @Last Modified By   : Swarup satpati
 * @Last Modified On   : 8/3/2020, 5:30:00 PM
**/
import { LightningElement, api, wire, track } from 'lwc';
import { getObjectInfo, getPicklistValues} from 'lightning/uiObjectInfoApi';
import { getFieldValue } from 'lightning/uiRecordApi';
import {getRecord} from 'lightning/uiRecordApi';

export default class Chevron extends LightningElement {
    @api objectName = 'Opportunity';
    @api fieldName = 'StageName';
    /*component with a recordId property is used on a Lightning record page, 
    the page sets the property to the ID of the current record.*/
    @api recordId; 

    @track picklistvalues;
    @track record;
    @track error;
    @track chevrondata=[];
    @track itemList;
    @track fieldValue;
    @track fieldArray;

    recordtypeId;
    index=0;
    isFound=false;
    isfoundindex=0;
    fieldapi;


  connectedCallback(){
    this.fieldArray = [`${this.objectName}.${this.fieldName}`,`${this.objectName}.RecordTypeId`];
    this.fieldapi=`${this.objectName}.${this.fieldName}` ;
    }
    
    /*Use this wire adapter to get a record’s data : 
       Recordtype ID and value of status field pass through API
    */
    @wire(getRecord, { recordId: '$recordId', fields:'$fieldArray'})
    wiredAccount({ error, data }) {
        if (data) {
            this.record = data;
            console.log(JSON.stringify(data));
            this.fieldvalue=getFieldValue(data,`${this.objectName}.${this.fieldName}`);
            this.recordtypeId=this.record.fields.RecordTypeId.value;
            this.error = undefined;
            console.log('selected value..'+this.fieldvalue);
        } else if (error) {
            console.log('error..');

            this.error = error;
            this.record = undefined;
        }
    }

   /*Use this wire adapter to get the picklist values for a specified field.*/

    @wire(getPicklistValues, {
        fieldApiName: '$fieldapi',
        recordTypeId: '$recordtypeId'
    })
    fetchRecordTypeInfo({ data, error }) {
        if (data) {
            console.log('data..'+JSON.stringify(data.values));

           // this.picklistvalues = data.picklistFieldValues.Status_EI__c.values;
              this.picklistvalues = data.values ;

            this.picklistvalues.forEach(item=>{
               // console.log('rec..'+item.value);
                let classType;
                if(this.fieldvalue==item.value){
                    classType = 'slds-path__item slds-is-current slds-is-active';
                    this.isFound=true;
                    this.isfoundindex=this.index;
                }
                else{ 
                   classType='slds-path__item slds-is-incomplete';
                   this.index ++; 
                }
                this.chevrondata.push({
                 stages :item,
                 classType:classType
                });


            });
            if(this.isFound){
                for(let i=0;i<this.isfoundindex;i++){
                   this.chevrondata[i].classType='slds-path__item slds-is-complete';
                }

            }
           
             console.log('chevron data..'+JSON.stringify(this.chevrondata));
           
            //console.log(JSON.stringify(this.picklistvalues));
        }
        else if (error) {
            console.log(" Error  ---> " + JSON.stringify(error));
        }
    }
    
}

chevron.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
    <div class="slds-path">
        <div class="slds-grid slds-path__track">
          <div class="slds-grid slds-path__scroller-container">
            <div class="slds-path__scroller" role="application">
              <div class="slds-path__scroller_inner">
                <ul class="slds-path__nav" role="listbox" aria-orientation="horizontal">
                  <template for:each={chevrondata} for:item="stageIs" for:index="index">
                    <li key={stageIs} class={stageIs.classType} style="margin-left: 0em !important;" role="presentation">
                        <a aria-selected="flase" class="slds-path__link" href="javascript:void(0);"  role="option" tabindex="0">
                          <span class="slds-path__stage">
                            <!--<svg class="slds-icon slds-icon_x-small" aria-hidden="true">
                              <use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#check"></use>
                            </svg>
                          -->
                           {stageIs.stages.label}
                            <span class="slds-assistive-text">{stageIs.stages.label}</span>
                          </span>
                          <span class="slds-path__title">{stageIs.stages.label}</span>
                        </a>
                    </li>
                  </template>
                </ul>
            </div>
          </div>
        </div>
        </div>
    </div>
</template>

Metadata

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
        <target>lightningCommunity__Page</target>
        <target>lightning__Tab</target>
    </targets>
</LightningComponentBundle>


Code Explanation:

Within js controller we are using two wire adapters.

getRecord() : by using this method we are  retrieving the present stage value and record type Id for  the current record

getPicklistValues() : dynamically fetching pick list values for the given field Api 
and recordtype.

Have created custom property called 'ClassType' and 'stage' to display 
the chevron in html.

Main tricky part is to set the claastype conditionally based on the current stage.

'slds-path__item slds-is-current slds-is-active' :   for current Stage
'slds-path__item slds-is-incomplete': for all incomplete stages
'slds-path__item slds-is-complete' : for all completed stages


Hope you get it right !

Output :



Wednesday, 11 March 2020

Record Type Selection in LWC and Open New object page


In lightning web component(LWC) we don't need to call server to retrieve record type details. There is a native adapter available to retrieve object specific record types .
we have to use the wire adapter getObjectInfo to get the metadata about a specific object. As part of successful response it returns describing fields, child relationships, record type,etc.

In this example, we are using Account Object where i have overridden the new button using lightning component and in turns it's invoking LWC component  to display all record types .


let see the output..














on click Next button we are calling "handlechange" function to fetch selected record id and passing it to <lightning-record-form> to open new standard Account page for selected record type.










RecordtypePoc.html

<template>
    <div if:true={openmodel}>
        <template if:true={objectInfo.data}>
            <div role="dialog" tabindex="-1" aria-labelledby="header43" class="slds-modal slds-fade-in-open">
                <div class="slds-modal__container">
                    <div class="slds-modal__header">
                        <button class="slds-button slds-modal__close slds-button--icon-inverse" title="Close" onclick={closeModal}>
                            X<span class="slds-assistive-text">Cancel</span>
                        </button>
                        <h2 id="header43" class="slds-text-heading--medium">New Account</h2>
                    </div>
                    
                    <div class="slds-modal__content slds-p-around--medium">
                        <div class="slds-grid slds-wrap">
                            <div class="slds-size--1-of-2 slds-large-size--1-of-2">
                                <div class="slds-align--absolute-center">Select a Record Type</div>                            
                           </div>
                            <div class="slds-size--1-of-2 slds-large-size--1-of-2">
                                <lightning-combobox name="recordType" label="" placeholder="Choose Account record type"
                                    value={value} options={recordTypeId} onchange={changeHandler}>
                                </lightning-combobox>
                            </div>&nbsp; &nbsp;
                        </div>                   
                    </div>
                    <div class="slds-modal__footer">
                        <lightning:button class="slds-button slds-button--neutral" >Cancel</lightning:button>
                        <lightning:button class="slds-button slds-button--brand" onclick={handleChange}>Next</lightning:button>
                    </div>
                </div>
                </div>
                <div class="slds-backdrop slds-backdrop--open"></div>
            
        </template>
    </div>
        <template  if:true={recordTypeIdVal}> 
            
        <lightning-record-form
            object-api-name="Account"
            record-type-id={recordTypeIdVal}
            fields={fields}
            onsuccess={handleSuccess}>
    </lightning-record-form>
        </template>
</template>


recordtypePoc.js 


import { LightningElement, api, wire, track } from 'lwc';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import REVENUE_FIELD from '@salesforce/schema/Account.AnnualRevenue';
import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';

export default class RecordFormWithRecordType extends LightningElement {
// ApI name for App Builder setup
    @api recordId;
    @api objectApiName;
    @api optionVal;

    @track objectInfo;
    @track recordTypeIdVal;
    @track openmodel = true;
 
    fields = [NAME_FIELD, REVENUE_FIELD, INDUSTRY_FIELD];
    @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
    objectInfo;
    get recordTypeId() {
     
    // Returns a map of record type Ids
     
       var recordtypeinfo = this.objectInfo.data.recordTypeInfos;
       var uiCombobox = [];
   
      console.log("recordtype" + recordtypeinfo);
      for(var eachRecordtype in  recordtypeinfo)//this is to match structure of lightning combo box
      {
        if(recordtypeinfo.hasOwnProperty(eachRecordtype))
        uiCombobox.push({ label: recordtypeinfo[eachRecordtype].name, value: recordtypeinfo[eachRecordtype].name })
      }
      //console.log('uiCombobox' + JSON.stringify(uiCombobox));
      return uiCombobox;
    }
    changeHandler(event){
        this.optionVal=event.target.value;
    }
    handleChange(event) {
         // Returns a map of record type Ids
         const rtis = this.objectInfo.data.recordTypeInfos;
         this.recordTypeIdVal=(Object.keys(rtis).find(rti => rtis[rti].name === this.optionVal));
         this.closeModal();
        }

        handleSuccess(event) {
            const evt = new ShowToastEvent({
                title: "Account created",
                message: "Record ID: " + event.detail.id,
                variant: "success"
            });
            this.dispatchEvent(evt);
        }
        openModal() {
            this.openmodel = true
        }
        closeModal() {
            this.openmodel = false
        }

    }

recordtypePoc.js-meta.xml


<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>47.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
      <target>lightning__AppPage</target>
      <target>lightning__RecordPage</target>
      <target>lightning__HomePage</target>
    </targets>

</LightningComponentBundle>

Tuesday, 13 August 2019

Custom domain setup for salesforce site:

Background:
We have an existing integration with a non-SF web application where user have their own login accounts and from their dashboard, they have “manage” link to update their own info.

once they click on “manage” link the request is handover to a salesforce and open a custom page, which hosted on a salesforce site. In this case, cookies used to pass through the URL parameters has all relevant details .

Problem Statement :
In above  case, cookies has passed  through parameters, which is not a secured way to communicate ,  end user may  keep copy the site URL (static)and re-use it in future. 

To solve this problem we need custom domain setup where a non-SF web application and salesforce custom page both would be under same domain and cookies would transfer through browser and not through parameters in URL.

Therefore, it would be more secure way to communicate as  compare to previous approach.
Here cookies will be available automatically only when both application pages will open within same domain.

By default, any salesforce site pages opened in force.com domain and using custom domain setup, we can change that default behavior.

 Hope I am making sense and this is one of the cool feature, which is unknown for many folks.

 Let us setup our custom domain for salesforce site !!

 There are several ways to set up a custom domain in Salesforce, For this setup, we’ll use the following example: say we own  https://abc.com , which is hosted through Hosting Company ,now  we want salesforce site URL to be https://example.abc.com or  https://example.abc.com/customerlanding .


Step 1: Create a CNAME entry in DNS


First, make sure you own the domain and you’re logged into the correct hosting company where the domain is registered. In the account management settings of DNS provider, create a CNAME(canonical name) record. CNAME records must include the given domain name, 18–character organization ID, and the suffix live.siteforce.com.

For example, if domain name is https://abc.com  and salesforce organization ID is 00dx00000000001aaa, then the CNAME must be example.abc.com.00dx00000000001aaa.live.siteforce.com(exclude www /https)



NOTE: Depending on your DNS provider, CNAME propagation can take up to 48 hours.



Step 2: Create the Custom Domain in Salesforce


  • Log into your production Salesforce org
  • Go to Setup > Domains
  • Click New Custom URL

Add caption


·         Initially chose option (4) domain over https (without certificate) and click save.
Step 3: Map salesforce site with created custom domain

  
After completing step-2 , click on the domain URL  and we will see below details.

Now click on new custom URL and select the site via lookup icon. For the path, it should typically be “/” if no absolute path required. In this case, path would be “customerlanding” as we want that in domain URL. lets follow bellow steps: 

  • Go to Setup > Domains and select your new domain
  • Under the Custom URLs section, click the Site Label (see below)
  • Now under Custom URLs click Edit next to your custom domain
  • Then click the checkbox for Site Primary Custom URL, then click Save


Now, our site should be set up with the new custom domain!


How to test and make sure it is working?
As I mentioned above , first test the setup without certificate. If it works, then test the domain configuration with https. Prior to test make sure salesforce site configuration parameter should have “https” access as false (highlighted with yellow).


if something went wrong with the domain entry we can check with same on below link to make sure domain is properly setup or not  :-



Step 4: Setup SSL Certificate in Salesforce


After successful test ,please go through the below steps to setup certificate for our domain.
  • Log into your production Salesforce org
  • Go to Setup > Certificate and Key Management
  • Click Create CA-Signed Certificate
  • All of these fields on this page would be relevant to your company except for the Key Size , which that should remain as 2048. Also, you will be selecting this Certificate via the Label Name in the next step
  • Click Save after all fields are filled out
  • Now click Upload Signed Certificate then Choose File and navigate to the certificate we acquired

Now we should go back to step2, change the option with HTTPS and upload the signed certificate in domain configuration. please make sure site configuration parameter also set with “HTTPS”.

 Now we all set! Salesforce site should be ready with the new custom domain!

Important Consideration: ** Custom Web addresses are not supported for Sandbox or Developer Edition organizations, so all test need to be performed in production

Getting started with Heroku

I am familiar with the heroku for quite long time, have seen lots of people are interested with it but not sure from where its need to Start...