Sunday, June 8, 2025

Exploring Lifecycle Flow in LWC with Getters, Setters, and DOM Events

 To demonstrate the lifecycle hooks flow in Lightning Web Components (LWC) in sequence, we can create a component that logs the execution of each lifecycle hook. I'll also use getters, setters, the constructor, connected callback, render and rendered callback for this demonstration. Here's how you can implement this:

Explanation:

• Constructor: Called first when the component is created. It’s useful for initializing values.
• Getter/Setter: The fetchName property has both a getter and a setter, which log when the property is accessed or modified.
• connectedCallback: Called when the component is inserted into the DOM. It is a good place to initialize data or make external calls.
• disconnectedCallback: Called when the component is removed from the DOM. It's typically used for cleanup operations.
• render: Called before the component is rendered to the DOM, which is useful for modifying the DOM before the component is visually updated.
• renderedCallback: Called after the component is rendered to the DOM. It is useful for performing any tasks that need to occur after the DOM is updated.

 

More about Getters and Setters:

Getters:

getter is like a way to get or read the value of a property. Whenever you try to access a property, the getter is automatically called. Think of a getter like opening a box to look at what's inside. You can’t change the contents of the box directly through a getter — it only lets you see the current value.

• Example: If you want to know what the current value of a fetchName is, you can use the getter. The getter will return the current value stored inside the property.

Setters:

setter is like a guardian for a property. It allows you to set or update the value of the property. Whenever you assign a new value to a property, the setter is triggered. Setters make sure that the value you’re trying to assign is allowed, and can even adjust it before it's saved. You can think of a setter as a person who checks the box and only lets in the values that meet certain rules.

• Example: If you want to change the fetchName value, the setter will be used to update it. It may also check if the new value is valid.

 

 

Template File:

<template>

    <h1>{fetchName}</h1>

    <button onclick={handleChangeName}>Change Name</button>

</template>

 

Javascript File:

import { LightningElement } from 'lwc';

 

export default class LifecycleHooksDemo extends LightningElement {

    // Property with getter and setter

    fullName = 'LWC Lifecycle';

 

    get fetchName() {

        console.log('Getter: name is being accessed');

        return this.fullName;

    }

 

    // Setter for the name property

    set fetchName(value) {

        console.log('Setter: name is being set to', value);

        this.fullName = value;

    }

 

    

    handleChangeName() {

        this.fetchName = 'New Name';

    }

    

    // Constructor (executed when the component is created)

    constructor() {

        super();

        console.log('Constructor: Component is created');

    }

 

    // connectedCallback (executed when the component is inserted into the DOM)

    connectedCallback() {

        console.log('connectedCallback: Component is inserted into the DOM');

    }

 

    // disconnectedCallback (executed when the component is removed from the DOM)

    disconnectedCallback() {

        console.log('disconnectedCallback: Component is removed from the DOM');

    }

 

    // render (executed before the component is rendered to the DOM)

    render() {

        console.log('render: Component is about to render');

        return super.render();

    }

 

    // renderedCallback (executed after the component is rendered to the DOM)

    renderedCallback() {

        console.log('renderedCallback: Component has been rendered');

    }

 

}

 

When component is previewed:

When we click Change Name button:

Monday, June 2, 2025

Understanding the Difference Between @wire Properties and @wire Functions in LWC

Understanding the Difference Between @wire Properties and @wire Functions in LWC

When working with Lightning Web Components (LWC), the @wire decorator is used to read data reactively. However, there's often confusion between using it with properties versus functions.

Let’s simplify and clarify the distinction.

@wire with a Property — Simplicity and Reactivity

What it means:

Using @wire with a property is like telling Salesforce:

"Fetch the data and assign it automatically. I don't want to manage the logic."

Key Features:

You get a reactive object with .data and .error keys.

Salesforce manages the invocation based on reactive parameters.

Best for simple data display with minimal logic.

Syntax:

js

@wire(fetchContacts) contacts;

Usage in Template:

html

<template if:true={contacts.data}>

  <template for:each={contacts.data} for:item="contact">

    <p key={contact.Id}>{contact.Name}</p>

  </template>

</template>

Ideal When:

You just need to render data as-is.

You don't need complex logic or transformation.

Error handling isn’t critical or is handled elsewhere.

@wire with a Function — Control and Customization

What it means:

Using @wire with a function tells Salesforce:

"Give me the data, and I’ll decide what to do with it."

Key Features:

You receive data and error as arguments.

You control how to handle and manipulate the response.

Great for custom processing, filtering, or dynamic UI logic.

Syntax:

js

@wire(fetchContacts)

handleContacts({ data, error }) {

  if (data) {

    // Example: Only include contacts with email addresses

    this.filteredContacts = data.filter(c => c.Email);

  } else if (error) {

    this.showErrorNotification(error);

  }

}

Ideal When:

You need to filter, sort, or transform data.

Error handling must vary based on context.

You need conditional assignments or advanced logic.

Real-World Example Comparison:

Scenario: You want to display a list of contacts — only those with a phone number.

Using a Wire Property (not ideal here):

js

@wire(fetchContacts) contacts;

In the template, you’d have to manually check each one not efficient.

Using a Wire Function (better control):

js

@wire(fetchContacts)

processContacts({ data, error }) {

  if (data) {

    this.contactsWithPhone = data.filter(c => c.Phone);

  } else if (error) {

    this.notifyUser(error.message);

  }

}

Pro Tip:

Use @wire property when your component just needs to show data without much logic.

Use @wire function when you need custom behavior, data transformation, or enhanced error handling.

Sunday, May 25, 2025

No More Null Crashes! 5 Simple Apex Tips to Make Your Code Safer

Tired of late-night debugging sessions because of annoying null errors in Apex? We’ve all been there. These simple checks will help you handle lists, sets, maps, and objects with confidence — and fewer crashes.


1. != null“Does it even exist?”

This is your first safety net. Before you interact with any variable — object, list, set, or map — check if it's not null.

When to use: Always. This is your go-to check to prevent null reference exceptions.

Example:


Lead leadRecord = [SELECT Id, Email FROM Lead LIMIT 1];

if (leadRecord != null) {

    System.debug('Lead record exists!');

}

Note: This confirms the variable is present, but not whether it contains useful data.


2. !isEmpty()“Is there anything inside?”

A quick and clean way to see if your list or set has at least one item.

When to use: Ideal for lists and sets where you're only interested in whether there's data, not how much.

Example:


List<Case> openCases = [SELECT Id FROM Case WHERE Status = 'Open'];

if (!openCases.isEmpty()) {

    System.debug('There are open cases!');

}

Why it's better: It's more readable than writing size() > 0.


3. size() > 0“How many items are there?”

This tells you the number of elements in a list, set, or map — and whether it's empty or not.

When to use: Useful when you’re working with maps, or need the actual count.

Example:


Map<Id, Product2> productMap = new Map<Id, Product2>(

    [SELECT Id, Name FROM Product2 WHERE IsActive = TRUE]

);

if (productMap.size() > 0) {

    System.debug('Found ' + productMap.size() + ' active products.');

}


4. contains()“Is this item in there?”

Perfect for checking if a list or set contains a specific item.

When to use: Great for filtering or conditionally running logic.

Example:


Set<String> approvedStatuses = new Set<String>{'Approved', 'Validated'};

String currentStatus = 'Approved';


if (approvedStatuses.contains(currentStatus)) {

    System.debug('Status is approved.');

}


5. containsKey()“Is this key in the map?”

When working with maps, use this to verify a key exists before accessing it.

When to use: Always check before using map.get() to avoid surprises.

Example:


Map<Id, User> userMap = new Map<Id, User>(

    [SELECT Id, Name FROM User WHERE IsActive = TRUE]

);

Id targetUserId = UserInfo.getUserId();


if (userMap.containsKey(targetUserId)) {

    System.debug('User found in map!');

}


Friday, March 28, 2025

Discover Integration Connectors in Salesforce

Integration connectors play a crucial role in helping different systems and applications communicate with each other. Let’s take a look at a few key integration connectors, focusing on MuleSoft and Salesforce connectors, which enable seamless data transfer and efficient connections between platforms.

MuleSoft Salesforce Connector: The MuleSoft Salesforce Connector allows Mule applications to integrate with Salesforce and other systems, such as Workday. This connector simplifies data transfer by acting as a bridge between different systems. For example, when Salesforce sends data to the Anypoint connector, the Mule application processes the data and sends it to the destination API (like Workday). This setup eliminates the need for direct, point-to-point integrations, ensuring that systems remain decoupled and flexible. While pre-built integrations for specific combinations (like Salesforce to Workday) may not always be available, finding connectors is easy through platforms like AppExchange or marketplace applications like Workday.

Heroku Connect: Heroku Connect is another useful connector that links Salesforce instances with Heroku Postgres databases. Using various APIs (SOAP, Bulk, and Streaming), it facilitates seamless data synchronization between Salesforce and Heroku, enhancing the ability to work with both platforms simultaneously.

Salesforce Connect: Salesforce Connect enables real-time data access from external systems without storing the data in Salesforce. This is done by creating external objects, which are temporary connections that pull in data from other platforms. Salesforce Connect supports various adapters, including:

  • OData Adapters (2.0 and 4.0): These connect to data from OData producers, a protocol for sharing data over REST.
  • Cross-Org Adapter: Allows connections to other Salesforce organizations directly using the standard REST API.
  • Custom Adapter: If the other options don't fit, you can build your own adapter using Apex (Salesforce’s programming language) for more specialized needs.

In summary, these connectors offer flexible and efficient ways to link Salesforce with other platforms, ensuring smooth data exchange and improving overall system functionality. Whether it’s through MuleSoft’s powerful integration platform or Salesforce’s native tools, integration connectors streamline business processes and data flow.

Friday, March 21, 2025

Interview Questions on Salesforce Apex Part-2

 1) Let's say a user does not have permission on a child object but has permission on the parent object to read, create, edit, and delete the parent record. If I create a trigger on the parent object to insert a child record after the parent record is created from an Apex class (which does not have any keywords declared, like "with sharing" or "without sharing," nor does it have WITH SECURITY_ENFORCED), will a child record be created after the user inserts the parent record manually?

Yes, a child record will be created. By default, triggers and Apex classes run in system mode, which means they bypass the user's permissions and can insert the child record even if the user does not have access to the child object.

2) If, in the above scenario, from the trigger, I am calling an Apex class that is in "with sharing" mode and inserting a child record after the parent is manually inserted by the user, will a child record be created?

Yes, a child record will be created. The with sharing keyword only enforces the sharing rules for the user, but it does not affect object permissions. Since the trigger runs in system mode and bypasses the user's permissions, it will still create the child record. The with sharing keyword primarily governs the visibility of records based on sharing rules, not user permissions for specific objects.

3) Let’s say an Apex class, which is not declared with any keywords such as "with sharing" or "without sharing," is fetching Opportunity records. The user running under whose context this apex class is invoked does not own any Opportunity records, nor are any Opportunity records shared with them. In this case, will the class be able to fetch Opportunity records?

Yes, the class will be able to fetch Opportunity records. Since the Apex class runs in system mode by default (when neither the with sharing nor without sharing keywords are specified), it bypasses the user's record-level access and CRUD permissions. As a result, the class can retrieve all Opportunity records, regardless of the user's permissions or whether the records are shared with them.

4) Let’s say an Apex class, which is declared with the "with sharing" keyword, is fetching Opportunity records. The user, under whose context this Apex class is invoked, does not own any Opportunity records, nor are any Opportunity records shared with them. In this case, will the class be able to fetch Opportunity records?

No, the class will not be able to fetch Opportunity records. The with sharing keyword enforces sharing rules, meaning that only records the user has access to (based on sharing rules, ownership, or explicit sharing) will be retrieved. Since the user does not own any Opportunity records, nor are any records shared with them, the class will not be able to access those records.

5) What is bulkifying code in Salesforce Apex, and why is it important?

Bulkifying code means writing code that can handle multiple records at once instead of processing just one record. It is essential because Salesforce has governor limits on the number of records that can be processed at a time. Bulkifying your code ensures it can efficiently handle large data sets while staying within those limits.

6) Why should you avoid SOQL queries and DML operations inside FOR loops?

Placing SOQL queries or DML operations inside FOR loops can quickly exhaust governor limits, leading to runtime exceptions. To avoid hitting these limits, you should perform queries and DML operations outside of loops, processing records in bulk instead.

7) Why should you avoid SOQL queries and DML operations inside FOR loops?

Placing SOQL queries or DML operations inside FOR loops can quickly exhaust governor limits, leading to runtime exceptions. To avoid hitting these limits, you should perform queries and DML operations outside of loops, processing records in bulk instead.

// Bad practice: SOQL inside FOR loop

for (Contact con : Trigger.new) {

    Contact result = [SELECT Id, Name FROM Contact WHERE Id = :con.Id LIMIT 1];

}

// Good practice: Perform SOQL outside the loop

Set<Id> contactIds = new Set<Id>();

for (Contact con : Trigger.new) {

    contactIds.add(con.Id);

}

List<Contact> contacts = [SELECT Id, Name FROM Contact WHERE Id IN :contactIds];

8) What is the purpose of using @future methods in Apex, and what should be considered when using them?

@future methods are used for asynchronous processing in Apex, allowing operations to be performed in the background. They help offload long-running tasks and improve the user experience. However, @future methods cannot accept sObjects or objects as parameters and have a limit of 50 invocations per transaction.

trigger accountAsyncTrigger on Account (after insert, after update) {

    someClass.processAccount(Trigger.newMap.keySet());

}

global class someClass {

    @future

    public static void processAccount(Set<Id> accountIds) {

        // Code to process accounts asynchronously

    }

}

9) Why should you avoid hardcoding IDs in Apex code, and how can you handle record types or other dynamic values instead?

Hardcoding IDs in Apex code makes it less flexible and prone to errors when moving between environments (like from sandbox to production). Instead, use dynamic methods such as querying for IDs based on the label or record type, ensuring the code works across different environments.

List<RecordType> rtypes = [SELECT Name, Id FROM RecordType WHERE sObjectType='Contact' AND IsActive=true];

Map<String, String> contactRecordTypes = new Map<String, String>();

for (RecordType rt : rtypes) {

    contactRecordTypes.put(rt.Name, rt.Id);

}

for (Contact a : Trigger.new) {

    if (a.RecordTypeId == contactRecordTypes.get('SomeRecordTypeName')) {

        // Your logic here

    }

}