subscribe our youtube channel popup

Wrapper Class in Salesforce

In this post, we will talk about the Wrapper class in Apex Salesforce. Wrapper Class is a class within a class that stores a group of similar or different data type variables into a single object.

What Is a Wrapper Class in Salesforce?

A wrapper class is a class, a data structure, or an abstract data type that contains different objects or collections of objects as its members. A wrapper class is a custom object defined by a programmer wherein he defines the wrapper class properties.

Example of Wrapper class

Here is an example of a Wrapper class in Salesforce.

public class AccountWrapperDemo {
  public Account acc {get; set;}
  public Contact cont {get; set;}
  public Boolean isSelected {get; set;}
}

What is the use of Wrapper Class in apex Salesforce

The use of Wrapper Class helps the developers in organizing the concerned data efficiently, provided the data is properly nested. Let see what is the use of wrapper class in Salesforce.

#1. Wrapper Class Use Cases in LWC

The structure of Wrapper Class is as efficient as that of a good data visualization technique for a web page, especially if the developers are dealing with the JSON structure using Lightning components.

  • Display the list of Open Cases in the Form of a data table.
  • Let the User have the ability to select multiple records a time.
  • Sending Wrapper object to Apex class.

1.1. Wrapper Class In Lightning Web Components

Take an example we need to show the Account record with its associated contact record on the LWC page.

AccountWrapperDemo Apex class

public with sharing class AccountWrapperDemo {
    @AuraEnabled(cacheable=true)
    public static List<AccountWrapper> getAccountWithContact(){
        List<AccountWrapper> wrapList = new List<AccountWrapper>();
        List<Account> accList = [ SELECT Id,Name,
                                        (SELECT Id,FirstName, LastName FROM Contacts)
                                    FROM Account LIMIT 5];
        if(!accList.isEmpty()){
            for(Account acc : accList){
                wrapList.add(new AccountWrapper(acc, acc.Contacts));
            }
        }
        return wrapList;
    } 
    public class AccountWrapper{
        @AuraEnabled public Account accRecord{get;set;}
        @AuraEnabled public List<Contact> contactList{get;set;}
        public AccountWrapper(Account accRecord, List<Contact> contactList){
            this.accRecord = accRecord;
            this.contactList = contactList;
        }
    }
}

lwcWrapperDemo

<template>
    <lightning-card title="Wrapper Class Demo in LWC" icon-name="custom:custom63">
    <template if:true={wrapperList.data}>
        <lightning-accordion class="example-accordion" >
            <template for:each={wrapperList.data} for:item="wrap">
                <lightning-accordion-section name={wrap.accRecord.Id} label={wrap.accRecord.Name} key={wrap.accRecord.Id}>
                    <template for:each={wrap.contactList} for:item="contWrap">
                        <lightning-layout vertical-align="center" key={contWrap.Id}>
                            <li>
                                {contWrap.FirstName}
                                {contWrap.LastName}
                            </li>
                        </lightning-layout>
                    </template>
                </lightning-accordion-section>

            </template>
        </lightning-accordion>
    </template>
</lightning-card>
</template>

Class Apex class in LWC.

import { LightningElement,wire } from 'lwc';
import getAccountWithContact from '@salesforce/apex/AccountWrapperDemo.getAccountWithContact';
export default class LWCWrapperDemo extends LightningElement {
    @wire(getAccountWithContact) wrapperList;
}

Result

Wrapper Class In Lightning Web Components
Wrapper Class In Lightning Web Components

#2. Salesforce Integration using Wrapper Classes: JSON to Apex

Using Wrapper classes in Integration is every normal use case. In Rest API we deal with the Request and response in JSON format. But it comes parsing the JSON response in Apex might be difficult to handle. But we can handle it easily with the help of converting the JSON into Apex.

The structure of Wrapper Class is as efficient as that of a good data visualization technique for a web page, especially if the developers are dealing with the JSON structure.

Take a example we have one JSON

{
  "invoiceList": [
    {
      "totalPrice": 5.5,
      "statementDate": "2023-10-04T16:58:54.858Z",
      "lineItems": [
        {
          "UnitPrice": 1,
          "Quantity": 5,
          "ProductName": "Apex"
        },
        {
          "UnitPrice": 0.5,
          "Quantity": 1,
          "ProductName": "Hours"
        }
      ],
      "invoiceNumber": 1
    },
    {
      "totalPrice": 11.5,
      "statementDate": "2023-10-04T16:58:54.858Z",
      "lineItems": [
        {
          "UnitPrice": 6,
          "Quantity": 1,
          "ProductName": "WrapperClass"
        }
      ],
      "invoiceNumber": 2
    }
  ]
}

We can convert the above JSON into Apex using the different tools available to convert JSON to Apex.


public class InvoiceWrapper{
	public cls_invoiceList[] invoiceList;
	class cls_invoiceList {
		public Double totalPrice;	//5.5
		public String statementDate;	//2023-10-04T16:58:54.858Z
		public cls_lineItems[] lineItems;
		public Integer invoiceNumber;	//1
	}
	class cls_lineItems {
		public Integer UnitPrice;	//1
		public Integer Quantity;	//5
		public String ProductName;	//Apex
	}
	public static InvoiceWrapper parse(String json){
		return (InvoiceWrapper) System.JSON.deserialize(json, InvoiceWrapper.class);
	}

	static testMethod void testParse() {
		String json=		'{"invoiceList":[{"totalPrice":5.5,"statementDate":"2023-10-04T16:58:54.858Z","lineItems":[{"UnitPrice":1,"Quantity":5,"ProductName":"Apex"},{"UnitPrice":0.5,"Quantity":1,"ProductName":"Hours"}],"invoiceNumber":1},{"totalPrice":11.5,"statementDate":"2023-10-04T16:58:54.858Z","lineItems":[{"UnitPrice":6,"Quantity":1,"ProductName":"WrapperClass"}],"invoiceNumber":2}]}';
		InvoiceWrapper obj = parse(json);
		System.assert(obj != null);
	}
}

2.1. Json.deserialize apex example

To parse JSON into an InvoiceWrapper instance, we can use the Serialization method in apex with JSON.serialize() and JSON.deserialize(). In our case, we can use the below code.

InvoiceWrapper ex = (InvoiceWrapper)JSON.deserialize(jsonString, InvoiceWrapper.class);

2.2. Json.serialize apex example

Alternately, to convert an InvoiceWrapper instance into JSON, execute below code

String jsonString = JSON.serialize(ex);

You can use the jsonString to pass in REST API as a body and use an ex instance to parse the response.

2.3. Parse a JSON String and Deserialize It into Objects

Here is sample code how you can use in REST API

HttpRequest request = new HttpRequest();
request.setEndpoint('API_URL');
request.setMethod('GET');

Http http = new Http();
HttpResponse response = http.send(request);
String responseBody = response.getBody();

InvoiceWrapper ex = (InvoiceWrapper)JSON.deserialize(responseBody, InvoiceWrapper.class);

System.debug('InvoiceWrapper Response ' + ex);

#3. Wrapper Class Use Cases on the Visual Force page

Display a table of records with a check box and then process only the records that are selected with the visualforce page.

public with sharing class WrapperDemoController {
   
    public List<AccountWrapper> listAccountWrapper {get; set;}
    public List<Account> selectedAccounts{get;set;}
    public WrapperDemoController (){
            listAccountWrapper = new List<AccountWrapper>();
            searchRecord();
    }
    public void searchRecord(){
        listAccountWrapper.clear();
            for(Account a: [select Id, Name,BillingState, Website, Phone ,Active__c from Account limit 10])
            {
                listAccountWrapper.add(new AccountWrapper(a));
            }
    }
    public void processSelected(){
        selectedAccounts = new List<Account>();
        selectedAccounts.clear();
        for(AccountWrapper wrapAccountObj : listAccountWrapper){
            if(wrapAccountObj.selected == true){
                selectedAccounts.add(wrapAccountObj.acc);
            }
        }
    }
    public void ActivateData(){
        for(Account acc : selectedAccounts ){
            acc.Active__c ='Yes';
        }
        update selectedAccounts ;
        searchRecord();
    }
    public void DeActivateData(){
        for(Account acc : selectedAccounts){
            acc.Active__c ='No';
        }
        update selectedAccounts ;
        searchRecord();
    }
    // This is our wrapper/container class.
    public class AccountWrapper {
        public Account acc {get; set;}
        public Boolean selected {get; set;}
        public AccountWrapper(Account a) {
            acc = a;
            selected = false;
        }
    }
}

VF Page

<apex:page controller="WrapperDemoController">
    <script type="text/javascript">
        function selectAllCheckboxes(obj, receivedInputID){
            var inputCheckBox = document.getElementsByTagName("input");
            for(var i=0; i<inputCheckBox.length; i++){
                if(inputCheckBox[i].id.indexOf(receivedInputID)!=-1){
                    inputCheckBox[i].checked = obj.checked;
                }
            }
        }
    </script>
    <apex:form >
        <apex:pageBlock id="PB1">
            <apex:pageBlockButtons >
                <apex:commandButton value="Add to Grid" action="{!processSelected}" rerender="table2,PB2"/>
            </apex:pageBlockButtons>
            <apex:pageblockSection title="All Accounts" collapsible="false" columns="1">
                <apex:pageBlockTable value="{!listAccountWrapper}" var="accWrap" id="table" title="All Accounts">
                    <apex:column >
                        <apex:facet name="header">
                            <apex:inputCheckbox onclick="selectAllCheckboxes(this,'inputId')"/>
                        </apex:facet>
                        <apex:inputCheckbox value="{!accWrap.selected}" id="inputId"/>
                    </apex:column>
                    <apex:column value="{!accWrap.acc.Name}" />
                    <apex:column value="{!accWrap.acc.BillingState}" />
                    <apex:column value="{!accWrap.acc.Phone}" />
                    <apex:column value="{!accWrap.acc.Active__c}" />
                </apex:pageBlockTable>
            </apex:pageblockSection>
        </apex:pageBlock>
        <apex:pageBlock id="PB2" >
            <apex:pageBlockButtons >
                <apex:commandButton value="Activate" action="{!ActivateData}" rerender="PB1,PB2"/>
                <apex:commandButton value="DeActivate" action="{!DeActivateData}" rerender="PB1,PB2"/>
            </apex:pageBlockButtons>
                <apex:pageBlockTable value="{!selectedAccounts}" var="c" id="table2" title="Selected Accounts">
                    <apex:column value="{!c.Name}" headerValue="Account Name"/>
                    <apex:column value="{!c.BillingState}" headerValue="Billing State"/>
                    <apex:column value="{!c.Phone}" headerValue="Phone"/>
                    <apex:column value="{!c.Active__c}" headerValue="Active"/>
                </apex:pageBlockTable>
        </apex:pageBlock>
    </apex:form>
</apex:page>
Wrapper Class Use Cases on the Visual Force page

Benefits Of Wrapper Class in Salesforce

  • We can store the data from multiple sObject using wrapper classes.
  • We can use a wrapper class in VF pagesLightning Component and we can also convert the JSON string into a wrapper class.
  • We can use Wrapper classes to parse the JSON request and response in REST API.

Summary

A wrapper or container class is a class, data structure, or abstract data type whose instances are collections of other objects. It is a custom object defined by a Salesforce developer where he defines the properties of the wrapper class. Within Apex & Visualforce this can be extremely helpful to achieve many business scenarios within the Salesforce CRM software.

Amit Chaudhary
Amit Chaudhary

Amit Chaudhary is a Salesforce Application & System Architect who has been working on the Salesforce Platform since 2010. He has been Salesforce MVP since 2017 and has 23 Salesforce Certificates.

Articles: 145

6 Comments

  1. Such a class has no clear purpose. “The use of Wrapper Class helps the developers in organizing the concerned data efficiently, provided the data is properly nested.” – It just indicates a poorly thought-off object oriented abstraction. Such classes should be strictly avoided.
    Please read about SOLID design principles for better object oriented design https://en.wikipedia.org/wiki/SOLID

    • Thanks for your feedback Vibhor. But you can let me know the issue in above code and why you are able to see clear purpose in code.

      Example 1) public class AccountWrapper
      Contain code which is following proper abstraction to bind the account and contact data. Properly is public because we need to bind it to LWC.

      Sample 2) Is also good example of JSON to Apex.

      • I agree Amit, I’m not sure Vibhor has much salesforce experience! Thanks for the post, comprehensive and easy to understand. I’ve made a website that I think improves on the original json2apex converter in a few different ways (immediate results, more error handling, explanation of how to use it). Let me know what you think.

  2. Do the wrapper classes need the arguments/parameters in them so that it can used like when creating a contact?

    E.g.

    Public class wrapper{

    Map conmap {get; set;}
    List sobs {get; set;}

    Public wrapper(){
    this.conmap = conmap;
    This.sobs = sobs;
    }

    Wrapper wrap = new Wrapper();
    wrap.conmap = someMapHere;

  3. Do the wrapper classes need the arguments/parameters in them so that it can used like when creating a contact?

    E.g.

    Public class wrapper{

    Map conmap {get; set;}
    List sobs {get; set;}

    Public wrapper(){
    this.conmap = conmap;
    This.sobs = sobs;
    }

    Wrapper wrap = new Wrapper();
    wrap.conmap = someMapHere;

  4. Credit : Chat GPT
    Not necessarily! Wrapper classes don’t require arguments/parameters, but they can have them if you want to initialize the values at the time of creating an instance.

    Let’s break down your example and correct it so it works properly:

    Issues in Your Code:
    You’re not initializing conmap and sobs properly
    In the constructor, you should initialize them with new Map(); and new List();.
    Right now, this.conmap = conmap; is incorrect because conmap itself isn’t defined in the constructor.
    You’re missing the type definition for the Map and List
    The data types should be clearly defined, e.g., Map and List.

    Corrected Code:
    apex
    Copy
    Edit
    public class Wrapper {
    public Map conmap { get; set; }
    public List sobs { get; set; }

    // Constructor initializes the variables
    public Wrapper() {
    this.conmap = new Map();
    this.sobs = new List();
    }
    }

    // Creating an instance of Wrapper class
    Wrapper wrap = new Wrapper();
    wrap.conmap.put(‘0035g00000XyZ123’, new Contact(LastName = ‘Smith’));
    wrap.sobs.add(new Account(Name = ‘Test Account’));
    Explanation:
    Wrapper Class Definition

    We define a class called Wrapper that holds a Map of Contacts and a List of sObjects.
    Constructor (public Wrapper())

    It initializes conmap as an empty Map and sobs as an empty List.
    This ensures that when you create an object of Wrapper, these collections are ready to use.
    Creating an Instance (Wrapper wrap = new Wrapper();)

    Now, wrap.conmap is an empty map, and wrap.sobs is an empty list.
    You can add data to these collections afterward.
    Alternative: Constructor with Parameters
    If you want to pass values when creating the wrapper instance, you can modify the constructor like this:

    apex
    Copy
    Edit
    public class Wrapper {
    public Map conmap { get; set; }
    public List sobs { get; set; }

    // Constructor with parameters
    public Wrapper(Map inputMap, List inputList) {
    this.conmap = inputMap;
    this.sobs = inputList;
    }
    }

    // Creating an instance with predefined data
    Map contactMap = new Map{
    ‘0035g00000XyZ123’ => new Contact(LastName = ‘Smith’)
    };
    List objectList = new List{
    new Account(Name = ‘Test Account’)
    };

    Wrapper wrap = new Wrapper(contactMap, objectList);
    When to Use:
    Use a constructor without parameters if you want to create an empty object and add data later.
    Use a constructor with parameters if you want to initialize the object with predefined values.

Leave a Reply

Your email address will not be published. Required fields are marked *