Calling Apex Methods In Lightning web components

Let’s discuss here how to call the apex class from the Lightning web components. Lightning web components can import methods from Apex classes into the JavaScript classes using ES6 import. Once after importing the apex class method you can able call the apex methods as functions into the component by calling either via the wire service or imperatively. Before you use an Apex method, make sure that there isn’t an easier way to get the data. See whether a base Lightning component, like lightning-record-form, lightning-record-view-form, or lightning-record-edit-form works for your use case. If they don’t give you enough flexibility, use a wire adapter like getListUi or getRecordUi. 

Refer this link for how to configure the Salesforce dx for web components 

Import Syntax 

You can able use default export syntax to import an Apex method via the @salesforce/apex scoped module into JavaScript controller class. The Syntax looks like below.

import apexMethod from '@salesforce/apex/Namespace.Classname.apexMethod';

apexMethod—The imported symbol that identifies the Apex method.
Namespace—The namespace of the Salesforce organization. Specify a namespace unless the organization uses the default namespace (c), in which case don’t specify it.
Classname—The name of the Apex class.

Create Apex Class 

In this example, we will be getting account data and show it into the UI. Create an apex class using SFDX create apex class command.

sfdx force:apex:class:create -n GetAccountData -d force-app/main/default/apex

Here is the apex class. To expose an Apex method to a Lightning web component, the method must be static and either global or public. Annotate the method with @AuraEnabled

public with sharing class GetAccountData {
   @AuraEnabled(cacheable=true)
    public static List<Account> getAccountList() {
        return [SELECT Id, Name,Type,Rating,Phone FROM Account];
    }
}

Now you can able to call the apex class in  Lightning web component using these different ways.

  • Wire a property
  • Wire a function
  • Call a method imperatively

Wire an Apex Method to a Property

If an Apex method is annotated with @AuraEnabled(Cacheable=true), you can invoke it from a component via the wire service. You can @wire a property or a function. Here is the syntax

import apexMethod from '@salesforce/apex/Namespace.Classname.apexMethod';
@wire(apexMethod, { apexMethodParams })
propertyOrFunction;

Create a Lightning web component using below SFDX commands

sfdx force:lightning:component:create --type lwc -n LWCWireEx -d force-app/main/default/lwc

Here is the LWCWireEx.html markup for the lightning web components.

<template>
    <lightning-card title="Apex Class Example" icon-name="custom:custom63">
            <div class="slds-m-around_medium">
                <template if:true={accounts.data}>
                    <template for:each={accounts.data} for:item="acc">
                        <p key={acc.Id}>{acc.Name}</p>
                    </template>
                </template>
                
            </div>
        </lightning-card>
</template>

Here is the LWCWireEx.js class

import { LightningElement, wire } from 'lwc';
import getAccountList from '@salesforce/apex/GetAccountData.getAccountList';

export default class LWCWireEx extends LightningElement {
    @wire(getAccountList) accounts;
}

Here is the LWCWireEx.js-meta.xml markup.

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

Push the changes to scratch org and add the lightning web component to the record page. You can able to see the result like below.

Wire an Apex Method to a Function

Now let’s look at how to wire an apex method to function.we will be updating the same code here to operate the apex method as function.

Update the LWCWireEx.js class as shown below

import { LightningElement, wire,track } from 'lwc';
import getAccountList from '@salesforce/apex/GetAccountData.getAccountList';

export default class LWCWireEx extends LightningElement {
    @track accounts;
    @track error;
    @wire(getAccountList)
    wiredAccounts({ error, data }) {
        if (data) {
            this.accounts = data;
        } else if (error) {
            console.log(error);
            this.error = error;
        }
    }
}

update the LWCWireEx.html markup as shown below

<template>

    <lightning-card title="Apex Wire To Function Example" icon-name="custom:custom63">

        <div class="slds-m-around_medium">
            <template if:true={accounts}>
                 <ul>
                <template for:each={accounts} for:item="account">
                    <li key={account.Id}> {account.Name} </li>
                </template>
             </ul>
            </template>
            <template if:true={error}>
                {error}
            </template>                
        </div>
    </lightning-card>
</template>

Push the changes to scratch org and add the lightning web component to the record page. You can able to see the result like below.

 

Call an Apex Method Imperatively

Now let’s see here how to call apex method imperatively. Create a new Lightning web component using the below SFDX command

sfdx force:lightning:component:create --type lwc -n ImperativEx -d force-app/main/default/lwc

Use the below ImperativEx.html code

<template>
        <lightning-card title="Apex Imperative Method Example">
            <div class="slds-m-around_medium">
                <p class="slds-m-bottom_small">
                    <lightning-button label="Load Accounts" onclick={handleLoad}></lightning-button>
                </p>
                <template if:true={accounts}>
                    <template for:each={accounts} for:item="account">
                        <p key={account.Id}>{account.Name}</p>
                    </template>
                </template>
                <template if:true={error}>
                   {error}
                </template>
            </div>
        </lightning-card>
    </template>

Use the below ImperativEx.js class code

import { LightningElement, wire,track } from 'lwc';
import getAccountList from '@salesforce/apex/GetAccountData.getAccountList';


export default class ImperativEx extends LightningElement {
    @track accounts;
    @track error;
    handleLoad() {
        getAccountList()
            .then(result => {
                this.accounts = result;
            })
            .catch(error => {
                this.error = error;
            });
    }
}


Use the below ImperativEx.js-meta.xml class code

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="hello">
    <apiVersion>45.0</apiVersion>
    <isExposed>false</isExposed>
     <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

push the changes to scratch org and add the lightning web component to the record page and you can able to see the below result when you click on the button .

Lightning web components Wire Service

Let’s discuss here how to use the wire service to get the Salesforce data from the server to lightning web components. You can use the Lightning data service or apex class to get the data from server and wire service is also one of the ways. To read Salesforce data, Lightning web components use a reactive wire service, which is built on Lightning Data Service. Components use @wire in their JavaScript class to read data from one of the wire adapters in the lightning/ui*Api namespace. The wire service provisions an immutable stream of data to the component. Each value in the stream is a newer version of the value that precedes it. when we call the wire service reactive in part because it supports reactive variables, which are prefixed with $. If a reactive variable changes, the wire service provisions new data. We say “provisions” instead of “requests” or “fetches” because if the data exists in the client cache, a network request may not be involved.

The wire service delegates control flow to the Lightning Web Components engine. Delegating control is great for reading operations, but it isn’t great for creating, update, and delete operations. As a developer, you want complete control over operations that change data. That’s why you perform create, update, and delete operations with a JavaScript API instead of with the wire service.

Wire Service Syntax

Import a wire adapter using named export syntax. Decorate a property or function with @wire and specify the wire adapter. Each wire adapter defines a data type.

import { adapterId } from 'adapterModule';
@wire(adapterId, adapterConfig)
propertyOrFunction;

adapterId (Identifier)—The identifier of the wire adapter.
adapterModule (String)—The identifier of the module that contains the wire adapter function, in the format namespace/moduleName. Take another look at the format! To import a module in JavaScript, use lightning/ui*Api instead of lightning-ui-*-api.
adapterConfig (Object)A configuration object specific to the wire adapter. Configuration object property values can be either strings or references to objects and fields imported from @salesforce/schema. Properties in the adapter config object can’t be undefined. If a property is undefined, the wire service doesn’t provision data.
propertyOrFunction—A private property or function that receives the stream of data from the wire service. If a property is decorated with @wire, the results are returned to the property’s data property or error property. If a function is decorated with @wire, the results are returned in an object with a data property and an error property. Now your code is like below if you convert the wire syntax.

import wire namespace into the JavaScript controller as shown below.

import { LightningElement, api, wire } from 'lwc';

import getRecord from the lightning/uiRecordApi from lightning/ui*Api Wire Adapters and Functions.

import { getRecord } from 'lightning/uiRecordApi';

Now call the @wire function to get the record data.

 @wire(getRecord, { recordId: '$recordId', fields: [ACCOUNT_NAME_FIELD] })
    record;

Create Lightning web components

create a lightning component in scratch org using the below sfdx command.

sfdx force:lightning:component:create --type lwc -n LWCWireExample -d force-app/main/default/lwc

Here is the LWCWireExample.html code.

<template>
        <lightning-card title="Contact Information" icon-name="standard:contact">
            <template if:true={contact.data}>
                <div class="slds-m-around_medium">
                    <p>{name}</p>
                    <p>{title}</p>
                    <p><lightning-formatted-phone value={phone}></lightning-formatted-phone></p>
                    <p><lightning-formatted-email value={email}></lightning-formatted-email></p>
                </div>
            </template>
        </lightning-card>
    </template>

Here is the LWCWireExample.js code.

import { LightningElement, api, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';

const fields = [
    'Contact.Name',
    'Contact.Title',
    'Contact.Phone',
    'Contact.Email',
];

export default class LWCWireExample extends LightningElement {
    @api recordId;

    @wire(getRecord, { recordId: '$recordId', fields })
    contact;

    get name() {
        return this.contact.data.fields.Name.value;
    }

    get title() {
        return this.contact.data.fields.Title.value;
    }

    get phone() {
        return this.contact.data.fields.Phone.value;
    }

    get email() {
        return this.contact.data.fields.Email.value;
    }
}

Here is the LWCWireExample.js-meta.xml markup code.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="hello">
    <apiVersion>45.0</apiVersion>
    <isExposed>false</isExposed>
     <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

Push Source to the Scratch Org

Now push the changes to the scratch org by using this SFDX command.

sfdx force:source:push

Open Scratch Org by using this SFDX command.

Add this component to the record page and you can able to see the contact data as shown below.

Configure the Lightning Web Components for Lightning App Builder

Let us discuss here how to configure the Lightning web components for Lightning App Builder and expose the attribute properties to the app builder. Please refer this link for how to configure web components environment.  In this example, we will be passing the account rating and limit to web component from the lightning app builder and will get the data and display it.

Step 1: Create an Apex Class

Create an apex class with as shown below with the method and two arguments. use the below SFDX command to create an apex class

sfdx force:apex:class:create -n GetAccountData -d force-app/main/default/apex

Here is the apex code

public with sharing class GetAccountData {

    @AuraEnabled(cacheable=true)
    public static List<Account>  getAllAccounts(String ratingVal , Integer limitVal){
        return [Select Id , Name , Type,Rating from Account Where Rating=:ratingVal Limit :limitVal] ;
    }
     
}

Step 2: Create a Lightning Web Component

Create Lightning web components in the force-app/main/default/lwc folder. Run the following command to create a lightning web component

sfdx force:lightning:component:create --type lwc -n AccountInfo -d force-app/main/default/lwc

Here is the code for AccountInfo.html

<template>
    <lightning-card title="Account App Builder Example" icon-name="standard:account">
      <div class="slds-m-around_medium">
            <template if:true={accounts.data}>
                <template for:each={accounts.data} for:item="acc">
                    <p key={acc.Id}>{acc.Name}</p>
                </template>
            </template>
           </div>
    </lightning-card>
</template>

Here is the AccountInfo.js code

import { LightningElement,api , track ,wire  } from 'lwc';
import getAllAccounts from '@salesforce/apex/GetAccountData.getAllAccounts';
export default class AccountInfo extends LightningElement {
    @api ratingVal ;
    @api limitVal ;
    @wire(getAllAccounts, { ratingVal: 'hot' , limitVal:10 })
    accounts;
}

Now if you can see in the above AccountInfo.js  code you can see the Decorators.T he Lightning Web Components programming model has three decorators that add functionality to property or function.

@api
To expose a public property, decorate it with @api. Public properties define the API for a component. An owner component that uses the component in its markup can access the component’s public properties. Public properties are reactive. If the value of reactive property changes, the component’s template rerenders any content that references the property.
@track
To track a private property’s value and re-render a component when it changes, decorate the property with @track. Tracked properties are also called private reactive properties.
@wire
To read Salesforce data, Lightning web components use a reactive wire service. When the wire service provisions data, the component rerenders. Components use @wire in their JavaScript class to specify a wire adaptor or an Apex method.

Step 3: Configure the Component for Lightning App Builder

Configure the AccountInfo component for Lightning App Builder so that we can add it to a Lightning page. Open AccountInfo.js-meta.xml and add the code in bold. The lets us add the component to an app page. The section lets us set the component’s name property in Lightning App Builder.

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>45.0</apiVersion>
    <isExposed>false</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
    <targetConfigs>
      <targetConfig targets="lightning__RecordPage">
      <property name="ratingVal" type="String" label="Rating" placeholder="Hot" description="Enter Account Ratings."/>
        <property name="limitVal" type="Integer" label="Limit" description="Enter Record Limit."/>
      </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

The <component>.js-meta.xml file defines the metadata values for the component, including the design configuration for components intended for use in Lightning App Builder. Edit the configuration file to:

  • Define what types of Lightning pages your component can be used on.
  • Configure your component’s properties.
  • Set your component’s supported objects.
  • Make the component usable outside of your own org.

The above configuration file makes the component available for Lightning page types, but you can able to extend or restricts support on record pages. The following markup  will extend the component availability to different lightning pages

  <targets>
      <target>lightning__RecordPage</target>
      <target>lightning__AppPage</target>
      <target>lightning__HomePage</target>
  </targets>

The following markup will restrict support on record pages only for an account, opportunity, and warehouse objects on the Lightning record page and lightning app page

  <targetConfigs>
      <targetConfig targets="lightning__RecordPage">
      <property name="ratingVal" type="String" label="Rating" placeholder="Hot" description="Enter Account Ratings."/>
        <property name="limitVal" type="Integer" label="Limit" description="Enter Record Limit."/>
          <objects>
              <object>Account</object>
              <object>Opportunity</object>
              <object>Warehouse__c</object>
          </objects>
      </targetConfig>
      <targetConfig targets="lightning__AppPage, lightning_HomePage">
      <property name="ratingVal" type="String" label="Rating" placeholder="Hot" description="Enter Account Ratings."/>
        <property name="limitVal" type="Integer" label="Limit" description="Enter Record Limit."/>
      </targetConfig>
  </targetConfigs>

 

Step 4: Push Source to the Scratch Org

Now push the changes to the scratch org by using this SFDX command.

sfdx force:source:push

Open Scratch Org by using this SFDX command.

sfdx force:org:open

Step 5: Add the Component to a Lightning Page

Now add the web component to the lightning app record page by using the app builder as shown below and configure the attributes. Then the component will be displayed 10 accounts with the rating type hot.

Navigate to Component Using Lightning Navigation API

With summer 18 release, navigating with to lightning components is made easier with new navigation API. With the lightning:isUrlAddressable interface, you now control which Lightning components can be opened programmatically. You can also now easily capture URL parameters using the v.pageReference attribute and use the parameter values in your component. With the lightning:navigation component, define a pageReference object for navigating to a custom component that implements lightning:isUrlAddressable and set any attributes the component allows. In standard navigation Lightning apps, you can use the lightning:navigation component to navigate to a custom component that implements lightning:isUrlAddressable. Using lightning:navigation with pageReference provides the following benefits over the now deprecated force:navigateToComponent for standard navigation Lightning apps.

  • Gives you control over whether a component can be opened programmatically, and which attributes can be dynamically set when
    the component is opened.
  • Control and manage which URL parameters are used in your component.
  •  Future-proofs your apps from changes in URL format.
  •  Generates a user-friendly URL for these components.

Quick Notes 

  • New interface lightning:isUrlAddressable is available for components that need to be implemented to navigated directly via URL
  • This interface is used with the component lightning:navigation to navigate from one component to the URL-addressable component. This navigation feature is only supported in Salesforce Lightning and the Salesforce Mobile App.
  • The lightning:isUrlAddressable interface extends the lightning:hasPageReference interface.
  • A component that implements lightning:isUrlAddressable then gets access to the page state through the pageReference attribute.
  • The page state is a representation of the current URL query parameters.
  • lightning:isUrlAddressable enables you to generate a user-friendly URL for a Lightning component with the pattern /cmp/componentName instead of the base-64 encoded URL you get with the deprecated  force:navigateToComponent event.

Usage

For example, c:hellotarget displays a string that’s passed in from another component from the navigation url.

hellotarget.cmp

<aura:component implements="lightning:isUrlAddressable" description="c:helloTarget component">
    <aura:attribute name="firstname" type="String" />
    <aura:handler name="init" value="{!this}" action="{!c.init}"/>
    Hello {!v.firstname}.
   
</aura:component>

In c:hellotarget component’s client-side controller, retrieve the attribute values from the page state.

({
    init: function(cmp, evt, helper) {
        var myPageRef = cmp.get("v.pageReference");
        var firstname = myPageRef.state.c__firstname;
        cmp.set("v.firstname", firstname);
    }
})

In the component that you want to trigger the navigation, include an instance of the lightning:navigation component. Then include the component to perform the jump to the other component. In this example, a lightning:button component is added to c:hello to trigger the navigation to the URL addressable component.

hello.cmp

<aura:component description="c:hello component" implements="flexipage:availableForAllPageTypes">
    <aura:attribute name="pageReference" type="Object"/>
    <aura:handler name="init" value="{! this }" action="{! c.init }"/>
    <lightning:navigation aura:id="navService"/>
    <lightning:button label="Navigate" onclick="{!c.handleClick}"/>
</aura:component>

Define your page reference for the component you’re navigating to. We recommend setting the page reference using an init handler. This example stores the pageReference in an attribute in the component, and is used to navigate later in the click handler.

({
    init : function(component, event, helper) {
        var pageReference = {
            type: 'standard__component',
            attributes: {
                componentName: 'c__helloTarget',
            },
            state: {
                "c__firstname": "John"
            }
        };
        component.set("v.pageReference", pageReference);
    },
    handleClick: function(component, event, helper) {
        var navService = component.find("navService");
        var pageReference = component.get("v.pageReference");
        event.preventDefault();
        navService.navigate(pageReference);
    }
})

Clicking the button in c:hello directs you to /lightning/cmp/c__helloTarget?c__firstname=John, and Hi John is displayed on the c:helloTarget page.

lightning/cmp/c__helloTarget?c__firstname=John