Wednesday, 30 March 2022

Why do we need getter in LWC?

 LWC template/component does not allow expression or any manipulation or array expression. To solve this problem "Getter" is solution. It atomically bind data from Java scripts to template.

Error = Invalid expression {employee[0]} - LWC1038: Template expression doesn't allow computed property accesslwc

 <div class="slds-m-around_medium">
           {employee[0]}
<p> The sum of {num1} and {num2} is {num1+num2}
 </div>

Component code
<template>
    <lightning-card title="Getters in LWC">
        <div class="slds-m-around_medium">
            {firstEmployee}
            <p> The sum of {num1} and {num2} is {sumResults}</p>
        </div>
    </lightning-card>
</template>

Java script code
When employee gets updated then automatically firstemployee getter called and updated value will be reflected in component.
import { LightningElement } from 'lwc';

export default class HelloWorld1 extends LightningElement {
   
    num1 = 10;
    num2 = 20;
    employee = ["Asish","behera","Ipsheeta"]
    get firstEmployee(){
        return this.employee[0].toUpperCase();
    }
    get sumResults(){
        return this.num1+this.num2;
    }
   
}

Why do we need track decorator in LWC?

 LWC engine can track changes or value assigned to a field or primitive properties automatically, however if field/properties is array or object type then LWC will not be able track value change automatically. 

That is when @track is helpful, this will tell LWC engine to observe changes to the properties of object or elements of array decorated with @track.

Here is my component code

We  can also achieve using spread operator, the key here is instead of mutating same object/array we can create copy and make changes. LWC framework observes value assigned to field/property and if new value !=== previous value then component re-renders.

this.address = {...this.address,"city":event.target.value}

<template>
    <lightning-card title="2 way Data Binding">
        <div class="slds-m-around_medium">
            <lightning-input type="text" label="Enter course Name"
                onkeyup={changehandler}></lightning-input>
            <div>{fullname} is course of {title}</div>            
        </div>
    </lightning-card>
    <lightning-card title="@track properties">
        <div class="slds-m-around_medium">
            <lightning-input type="text" label="Enter City Name"
                onkeyup={trackhandler}></lightning-input>
            <div>{address.city} is my city</div>            
        </div>
    </lightning-card>  
</template>

Java script code

import { LightningElement,track } from 'lwc';

export default class HelloWorld1 extends LightningElement {
    fullname = "LWC Learning"
    title ="aura"
   
     @track address = {
        city:"Louisville",
        postalCode:40291,
        country:"USA"
    }
    @track hobbies = ['Music','Reading','Netflix'];
    changehandler(event){
        this.title = event.target.value;
    }
    trackhandler(event){
        //this.address = {...this.address,"city":event.target.value}
        this.address.city=event.target.value;
    }
   
}

Tuesday, 15 March 2022

How to retrieve populated fields from a sObject ?

 As part of summer 16, the sObject method getPopulatedFieldsAsMap() was released. This method will return a map of populated field names and their corresponding values. The map contains only the fields that have been populated in memory for the SObject instance.

https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_sobject.htm#apex_System_SObject_getPopulatedFieldsAsMap

Here are certain use cases to  this method.

1.  Fields are queried in the SOQL Statement

Account acc = [SELECT id,name,phone,Gender__pc From Account
               WHERE id = '0011F00000zJIvwQAG'];
System.debug('acc->'+acc);
Map<StringObject> accMap = acc.getPopulatedFieldsAsMap();
system.debug('Fields name-->'+accMap.keySet());
for (String fieldName : accMap.keySet()){
    System.debug('field name is ' + fieldName + ', value is ' + 
        accMap.get(fieldName));

}
OutPut -
Note - Id, and Recodtype will come by default even if it is not added in Query.
We can also use dynamic query for this.

public static void getPopulatedFields(List<String> fields, String objName) {
    String soql = ' Select ' + String.join(fields, ',') + 
                  ' From ' + objName;
    
    for (sObject obj : Database.query(soql)) {
       Map<StringObject> fieldValues = obj.getPopulatedFieldsAsMap();
       for (String fieldName : fieldValues.keySet()) {
           System.debug(fieldName + '=' + fieldValues.get(fieldName));
       }
    } 
}
// Invoking method
List<String> fields = new List<String>{'Name','Phone','Gender__pc'};
getPopulatedFields(fields, 'Account');

2. Fields are explicitly set before calling the getPopulatedFieldsAsMap() method.
Account acc = new Account(Name='Test Acc',Phone='1122334565');
//sert acc;
Map<StringObject> accMap = acc.getPopulatedFieldsAsMap();
system.debug('Fields name-->'+accMap.keySet());
for (String fieldName : accMap.keySet()){
    System.debug('field name is ' + fieldName + ', value is ' + 
        accMap.get(fieldName));

}
OutPut -

3. Using Relationship Query. Child to parent.

Account acc = [SELECT id,name,phone,Gender__pc,owner.Name,Createdby.Email,
SHC_Facility__pr.Facility_Short_Name__c,(Select CaseNumber, Status From Cases)  
               FROM Account
               WHERE id = '0011F00000zJIvwQAG'];
Map<StringObject> accMap = acc.getPopulatedFieldsAsMap();
system.debug('Fields name-->'+accMap.keySet());
It will print -
13:50:17:036 USER_DEBUG [6]|DEBUG|Fields name-->
{CreatedBy, CreatedById, Gender__pc, Id, Name, Owner, OwnerId, Phone, RecordTypeId,
SHC_Facility__pc, ...}
How will we get fields of related objects such as Owner or CreatedBy or
SHC_facility__pr

User uRec = (User)accMap.get('Owner'); Map<String, Object> userMap = uRec.getPopulatedFieldsAsMap(); system.debug('Owner Fields name-->'+userMap.keySet()); User createdByRec = (User)accMap.get('CreatedBy'); Map<String, Object> CreateduserMap = createdByRec.getPopulatedFieldsAsMap(); system.debug('Created By Fields name-->'+CreateduserMap.keySet()); Rehabilitation_Center__c facility = (Rehabilitation_Center__c)accMap.get('SHC_Facility__pr'); Map<String, Object> FaciLityMap = facility.getPopulatedFieldsAsMap(); system.debug('FaciLityMap Fields name-->'+FaciLityMap.keySet());

// To get case record fields and value
List<Case> caseList = (List<Case>)accMap.get('Cases');
System.debug('caseList--'+caseList);
Map<String, Object> caseMap = caseList[0].getPopulatedFieldsAsMap();
System.debug('caseMap-fields-'+caseMap.keySet());

To know value we need to iterate over map keyset and get value.

We can also write generic code which will support all objects.
/* method to expand map of key to find out parent or child objects and get value 
and field name after
*/
public void getPopulatedFields(Map<StringObject> fieldValues) {
   for (String fieldName : fieldValues.keySet()) {
       if (fieldValues.get(fieldName) instanceof List<sObject>) {
           System.debug('------Begin Child------');
           getPopulatedFields((List<sObject>) fieldValues.get(fieldName));
           System.debug('------End Child------');
       } else if (fieldValues.get(fieldName) instanceof sObject) {
           System.debug('------Begin Parent------');
           sObject obj = (sObject) fieldValues.get(fieldName);
           getPopulatedFields(obj.getPopulatedFieldsAsMap());
           System.debug('------End Parent------');
       } else {
           System.debug(fieldName + '=' + fieldValues.get(fieldName));
       }
   }
}
/* method to iterarte over child recordList and call getPopulatedFields passing result
 of getPopulatedFieldsAsMap
*/
public void getPopulatedFields(List<sObject> objs) {
   for (sObject obj : objs) {
        getPopulatedFields(obj.getPopulatedFieldsAsMap());
    }
}

/* method will accept object name and fields name, get records by using dynamic query
 and call getPopulatedFields
*/
public void getPopulatedFields(List<String> fields, String objName) {
    String soql = ' Select ' + String.join(fields, ',') + 
                  ' From ' + objName;

    List<sObject> objs = Database.query(soql);
    getPopulatedFields(objs);
}

Invoking methods.
List<String> fields = new List<String>
{'Name,phone,Gender__pc,owner.Name,Createdby.Email,
SHC_Facility__pr.Facility_Short_Name__c, (Select CaseNumber, Status From Cases)'};
getPopulatedFields(fields,'Account')