Wednesday, 9 June 2021

The Four Streaming APIs in Salesforce

 1.Generic events

2.Push Topic events

3.Platform Events

4.Change Data Capture events


These four Streaming APIs are part of what we call the Enterprise Messaging Platform (EMP).


Generic and PushTopic came as the "first generation", then Platform Events and Change Data Capture came second generation. 

While the first generation of APIs is quite mature now, it is not evolving as much as the second one.


A Shared Model :



All Streaming APIs are built on the same core technology:CometD.

This technology relies on a messaging bus that offers a publish/subscribe mechanism for events.


The publish/subscribe model follows the following scenario:


1.Event consumers connect to the messaging bus and subscribe to specific channels (event types)

2.Event producers connect to the bus and publish an event on a channel

3.The bus broadcasts the event to all registered subscribers


This means that the message producer is fully decoupled from the consumers. This model allows to build event-driven architectures at scale.

Typical use cases for this are system integrations and real-time apps.


1.Generic 


Data Structure    : Unstructured (String)

Publication       : REST API

Subscription      : code 


2.PushTopic  


Data Structure    : Defined by target sObject retrieved via a SOQL query

Publication       : Automated

Subscription      : code


3.Platform Events 


Data Structure    : Custom typed fields

Publication       : Delarative or Code

Subscription      : Delarative or Code


4.Change Data Capture 


Data Structure    : Defined by target sObject

Publication       : Automated

Subscription      : Delarative or Code


Streaming API provides two types of events that you can publish and subscribe to : PushTopic and generic.


PushTopic events track field changes in Salesforce records and are tied to Salesforce records.

Generic events contains arbitrary payloads.


Streaming API :


Streaming APIs enables transmitting of real data from Salesforce Platform.

Transmission of data done using push technology.

Push technology transfers information that is initiated from a server to the client.

Provides a subscription mechanism for receiving events in near real time.

Push technology also called the publish/subscribe model.


Pull Technology :


1.Client request data from the server periodically.

2.Clients poll the server for data updates, and freshness of data depends on the poll frequency.

3.Clients can make excessive calls and cause server slowdown.


Push Technology :


1.Notifications are sent to the subscriber in real time.

2.No pooling required.


Mechanism of Streaming API :


To enable Publisher/Subscriber model, active connection is required between Salesforce and each client.


Short Polling :


Client keeps sending to make active connection.


Long Polling :


1.Clients send requests for information but with the expectation the server may not respond immediately.

2.Server respond when data is changed for object.

3.Long polling supported using Bayeux Protocol or CometD.


Push Topic :


PushTopic events provide a secure and scalable way to receive notifications for 

changes to Salesforce data that match a SOQL query you define.


1.Receive notifications of Salesforce record changes ,including create,update delete and undelete.

2.Capture changes for the fields and records that match a SOQL query.

3.Recieve change notifications for only the records a user has access to based on sharing rules.


ex :


PushTopic pushTopic = new PushTopic();  

pushTopic.Name = 'AccountPush';  

pushTopic.Query = 'SELECT Id, Name, Type, Phone, Website  FROM Account WHERE Active=\'Yes\'';  

pushTopic.ApiVersion = 48.0; 

pushTopic.NotifyForOperationCreate=true; 

pushTopic.NotifyForOperationUpdate=true;

pushTopic.NotifyForOperationUndelete=true;

pushTopic.NotifyForOperationDelete=true;

pushTopic.NotifyForFields='Referenced';

insert pushTopic; 


Note :

NotifyForFields is used to determine when ontification should be sent.

NotifyForFields in PushTopic values.


All,Referenced(default),Select,Where


1.All - considers changes in all fields.

2.Referenced - consider changes in fields in both SELECT and WHERE clauses.

3.SELECT - considers changes in fields in SELECT clause.

4.WHERE - considers chnages in fields in WHERE clause.


PushTopic Security :


1.To receive notification user must have access to object.

2.Field Level Access required.

3.Only record where user has access will be returned.


Considerations - Multiple Notifications in Same Transaction :


1.Streaming API notifications sent in Reverse Order Within a Trasaction:


In general, event notifications are delivered in the order of record changes.


When a record triggers multiple notifications within the same transaction, the last 

notifications are delivered first.


2.Multiple Streaming API Notifications for the Same Record and Untracked Fields :


If field change triggers other changes on the same record, more notifications are sent for those fields.


3.Only Last PushTopic Notification Sent for the Same Record


When multiple PushTopic notifications are generated for the same record within 

about one millisecond and in the same transaction,only the last notification is sent.


LIMITS :


Maximum number of topics (PushTopic records) per org

UE: 100 EE: 50 All other editions: 40


Maximum number of concurrent clients (subscribers) across all channels and for all event types

UE: 2000 EE: 1000 All other editions: 20


Maximum number of delivered event notifications within a 24-hour period, shared by all CometD clients

UE: 1000000 EE: 200000 All other editions: 50000 (10000 for free orgs)


Socket timeout during connection (CometD session)

UE : 110 seconds EE: 110 seconds All other editions: 110 seconds


Timeout to reconnect after successful connection (keepalive)

UE : 40 seconds EE : 40 seconds All other editions: 40 seconds


Maximum length of the SOQL query in the Query field of a PushTopic record

UE : 1300 characters EE : 1300 characters All other editions: 1300 characters


Maximum length for a PushTopic Name

UE: 25 characters EE: 25 characters All other editions: 25 characters



You can use the following queries to review the usage

SELECT count() FROM PushTopic

SELECT count() FROM PushTopic WHERE IsActive = true



Generic Events :


Use generic events to send custom notifications that are not tied to Salesforce data changes.


1.Publish and receive arbitrary payloads in JSON without a predefined event schema.

2.Broadcast notifications to a target set of users,specific teams,or your entire org.

3.Send notifications for events that are external to salesforce.


How to use Generic Events ?


1.Streaming Channel


A StreamingChannel that defines the channel, with a name that is case-sensitive.

It is object and can be accessed from app menus


2.Clients


one or more clients subscribed to the channel.


3.Streaming Channel Push REST API


Streaming Channel Push REST API to monitor and invoke push events on the channel.

Monday, 7 June 2021

Heroku Connect

 Heroku Connect provides a way to synchronize Salesforce data with a Heroku Postgres database making it available to 

customer-facing apps on Heroku.


Data Integration as a service.

Data replication and data virtualization across Salesforce and Heroku Postgres.


Heroku Connect has a great way to archive your Salesforce data in a way that still maintains

in a way that's accessible still within Salesforce so you have archived it but you can still 

see it in Salesforce very powerful it can be a big efficiency and cost saver for you and 

business intelligence so you have all of this customer information in salesforce 

but you're interested in harnessing it and integrating to external data 

warehouse or analytic systems that you may be managing who can connect is a great tool for 

this as well.


Note :

Force.com is for enterprise applications and Heroku id for customer facing apps built with open source stacks.


Heroku Connect features include :


1.Ability to access Salesforce data via SQL

2.Optimized Force.com API intercations

3.Performance and scale

4.Set it and forget it simplicity 


Sunday, 6 June 2021

Remote Action in Salesforce

 Remote Action is a way to access Apex method using Javascript in your Visualforce page.

It allows you to create pages with complex, dynamic behavior that isn’t possible with the standard Visualforce AJAX components.


Remote action method should always have the @RemoteAction annotation and it should be Static.


public with sharing class CasePriority {

    @RemoteAction

    public static list<Case> getCases(string priority) {

        list<Case> cases = [Select Id, CaseNumber, Subject, Description, Priority, Status 

                              From Case 

                              Where Priority = :priority

                              And Status = 'New'

                              Limit 5];

        if (cases.isEmpty()){

            return new list<Case>();

        }

        return cases;

    }

}


ex :



<apex:page controller="CasePriority" lightningStylesheets="true">

    <script type="text/javascript">

    function getCases(priority) {

        Visualforce.remoting.Manager.invokeAction(

            '{!$RemoteAction.CasePriority.getCases}', priority,

            function(result, event){

                if(event.status){

                    var html = '<table>';

                    html += '<tr><th>Case Number</th><th>Subject</th><th>Priority</th></tr>';

                    result.forEach(function(item){

                        html += '<tr>';

                        html += '<td>'+ item.CaseNumber +'</td>';

                        html += '<td>'+ item.Subject +'</td>';

                        html += '<td>'+ item.Priority +'</td>';

                        html += '</tr>';

                    })

                    html += '</table>';

                    document.getElementById('results').innerHTML = html;

                }           

            },

            {escape: true}

        );

    }

    </script>

    

    <apex:pageblock id="block">

        <apex:pageblockbuttons location="top" >

            <apex:form>

            <apex:commandButton value="High" onclick="getCases('High'); return false;" />

                <apex:commandButton value="Medium" onclick="getCases('Medium'); return false;"/>

                <apex:commandButton value="Low" onclick="getCases('Low'); return false;"/>

            </apex:form>

            

        </apex:pageblockbuttons>

        

        <apex:pageblocksection columns="1" id="blockSection">

            <div id="results">

                

            </div>

        </apex:pageblocksection>

    </apex:pageblock>

</apex:page>


Notes :


1.@RemoteAction annotation is mandatory

2.Remote Action methods should always be static

3.If you are using the Visualforce Page inline in a Page Layout, then the method should be declared as global instead of public.



Key Features are: 


1.Remote Action is a static method

2.Bandwidth Usage is less

3.View state is not maintained

Invalid Cross Reference Id Error

 Getting the “Invalid Cross Reference Id” Salesforce error and unsure what it means? 

If so, that error means that the Salesforce Id used in a lookup or master-detail relationship field is not valid.


Causes


1) Wrong Object Type – The relationship field is being set to the Salesforce Id of the wrong object. 

For example, if field A is a lookup to the Account object, an opportunity Id can’t be used in this field.


2) Record Deleted – The record no longer exists. This can happen if someone deleted the record while a user 

or the system is inserting or updating records. It can also happen during a request when the record is initially inserted 

but then rolled back so the code still has a reference to the record with an id in memory but the record does not exist in the database.


3) Relationship Field Pointing To Wrong Object – This happens less frequently but is still a cause of this error. 

Sometimes, there are multiple objects in the system with the same name and a user chose the wrong object to have the lookup or master-detail point to. 

This happens because Salesforce will show the same object name without any further detail in the object list when defining the field 

so it’s not immediately clear which to choose.


To choose the correct object when there are multiple with the same name, inspect the drop down list in the browser 

and view the values for the select list items. Identify the correct one to use and then choose it from the drop down list.

Wednesday, 2 June 2021

Salesforce Lightning Inspector

 The Salesforce Lightning Inspector opens up and improves your Lightning Component developement,

giving you access to a wealth of data about your running applications and components.


With the Lightning Inspector you can :


1.Easily inspect and navigate the structure of your components.

2.Identify performance bootlenecks by looking at a graph of comonent creation time.

3.Debug server interactions faster by monitoring and modifying responses.

4.Navigate the component tree, inspect components and their associated DOM elements.

5.Track event firing and handling sequences. 


Use Chrome Developer Tools to debug Lightning Components.

Install the 'Salesforce Lightning Inspect' which is available in Google Chrome extension.


The Lightning Tab includes several sub tabs, which each reveal more detailed information about your component.


1.Component Tree Tab

2.Performance Tab

3.Transactions Tab

4.Event Log Tab

5.Actions Tab

6.Storage Tab


Component Tree Tab :


The Component Tree shows you a detailed tree view of the structure of your components.

The Component tree shows you the actual components themselves and gives you information about the attributes they use.


Performance Tab :


Using the Performance tab in the Salesforce Lightning Inspector, we can isolate inspection to just the component code,

allowing us to identify which components are slower and locate where in the component hierarchy the slowness occurred.


This can be especially useful when you have a slow performing Lightning Page with several components.

You can use the chart to visualize the performance of each component and then spot pretty easily any outliers.


Transaction Tab :


Using the Transactions tab,we can see the recorded state(s) during portions of the application's execution.

The transaction data includes the amount of XHR's(XMLHttpRequest) that were sent,

the actions that were called,the time those actions took.


Event Log Tab :


1.CMP events

2.APP events

3.unhandled events


With the Event Log Tab , we can follow along and inspect the events being triggered either by code or user interaction.


You can easily spot whether the event was a component or an app event by looking for CMP or APP below the event.

And there are buttons that let you filter for either one , as well as unhandled events.


Expanding out the event will reveal very useful information such as the parameters,

caller,source,duration and call stack.


You can also click the Toggle Grid button to toggle on or off a diagram of the event.

This can be very useful when you have a page with multiple components and you are not 

sure which component fired which event.


Action Tab :


This is my favorite tab and one I use the most. Toggling on the recording will show you actions sorted 

according to whether they are pending, running, or completed. The view defaults to show all actions, 

but you can narrow down the list by using the filter buttons at the top. This can be useful for quickly zeroing 

in on an error since most pages will produce a lot of actions.


Storage Tab :


The Storage tab reveals the client-side storage for Lightning applications. Actions marked as storable are cached in the actions store. 

When a storable action is run it will use the value from the actions store if available. This can avoid a server trip altogether 

making your component run much faster. Using the storage tab you can see which actions are cached, the size of each cached action, 

and even where the cache lives.


(or)


The storage tab shows you the client-side storage size of any storable actions your page may use.

It will also show the specific items contained in storage.

Communication Patterns for Lightning Web Components

 1.Passing data down the component hierarchy.

2.Passing data up the component hierarchy.

3.Passing data to components that share no common ancestry.


1.Passing data down the component hierarchy :


There are three techniques for passing data from a parent to a child component.


a.Public Property

b.Public getters and setters

c.Public methods



Public Property :


A public property is exposed by applying the @api decorator on a class property of a child component.

This lets the parent component assign a value to it in two different ways.

Either via a custom HTML attribute or dynamically with javascript.


Note : public properties are reactive so the component re-rnders whenever one of its public properties is updated.

On the downside, you cannot run custom logic when the value changes and the child component should not assign values 

to its public properties.


ex :


// parentComponent.html 


<template>

 <div class="slds-var-m-around_medium">

     <c-child-component

      child-attribute={valueToSet}>

     </c-child-component>

     

      <lightning-button variant="brand" label="Set Value" title="Set Value" onclick={handleClick} class="slds-m-left_x-small"></lightning-button>

 </div>

</template>


// ParentComponent.js


import { LightningElement,track } from 'lwc';


export default class ParentComponent extends LightningElement {

   @track valueToSet;  

   

     connectedCallback(){

       //setting property value

        this.valueToSet = "someValue";

     } 


     handleClick(event) {     

        this.template.querySelector('c-child-component').childAttribute="someValue";

     }     

}


// childComponent.html 


<template>

 <lightning-card title="Public Property" icon-name="standard:product_consumed">

    {childAttribute}

 </lightning-card>

</template>


// ChildComponent.js


import { LightningElement,api } from 'lwc';


export default class ChildComponent extends LightningElement {

   @api childAttribute;   

}



Public getters and setters :


Public getters and setters behave exactly like public properties when used in a parent component. 

However, they bring additional flexibility to the child component by allowing you to execute custom logic when retrieving or updating a value. 

Additionally, a child component is able to set values using its own setters.


Another important difference with public properties is that getters and setters don’t provide persistence out of the box. 

A common pattern to handle that is to use a “private” property prefixed with an underscore to store the value internally in the child component.


Here’s an example of how a parent component uses a childAttribute public setter from a child component and saves 

its value in a _childAttribute property:


ex :


// parentComponent.html 


<template>

 <div class="slds-var-m-around_medium">

     <c-child-component

      child-attribute={valueToSet}>

     </c-child-component>

     

      <lightning-button variant="brand" label="Set Value" title="Set Value" onclick={handleClick} class="slds-m-left_x-small"></lightning-button>

 </div>

</template>


// ParentComponent.js


import { LightningElement,track } from 'lwc';


export default class ParentComponent extends LightningElement {

   @track valueToSet;  

   

     connectedCallback(){

       //setting property value

        this.valueToSet = "someValue";

     } 


     handleClick(event) {     

        this.template.querySelector('c-child-component').childAttribute="someValue";

     }     

}


// childComponent.html 


<template>

 <lightning-card title="Public Getters and Setters" icon-name="standard:product_consumed">

    {childAttribute}

 </lightning-card>

</template>


// ChildComponent.js


import { LightningElement,api } from 'lwc';


export default class ChildComponent extends LightningElement {


   _childAttribute;   

   

   @api

   get childAttribute(){

        return this._childAttribute;

         

    } 

    set childAttribute(value){

      // Extra Processing goes here 

       this._childAttribute = value;

    }

}


Public Methods :


You may call public methods to pass several values to a child component in order to perform an action like a calculation. 

As opposed to setters, methods allow you to enforce consistency by passing multiple parameters at once.


ex :


// parentComponent.html 


<template>

 <div class="slds-var-m-around_medium">

     <c-child-component>

     </c-child-component>     

      <lightning-button variant="brand" label="Set Value" title="Set Value" onclick={handleClick} class="slds-m-left_x-small"></lightning-button>

 </div>

</template>


// ParentComponent.js


import { LightningElement,track } from 'lwc';


export default class ParentComponent extends LightningElement {

    

     handleClick(event) {     

        this.template.querySelector('c-child-component').getDataFromParent("someValue1","someValue2");

     }     

}


// childComponent.html 


<template>

 <lightning-card title="Public Method" icon-name="standard:product_consumed">

    {childAttribute1}

    {childAttribute2}

 </lightning-card>

</template>


// ChildComponent.js


import { LightningElement,api } from 'lwc';


export default class ChildComponent extends LightningElement {


    childAttribute1;  

    childAttribute2;   

   

   @api

   get getDataFromParent(value1,value2){

         this.childAttribute1=value1;

         this.childAttribute2=value2;         

    } 

   

}


Note :


Unlike public properties or setters, methods are not exposed in the component’s HTML tag. 

They can only be called with JavaScript. This can be a limitation if you need to call a method in a reactive way (whenever some values change). 

Likewise, calling a method won’t automatically trigger a re-render on the target component. 

The component only re-renders if the method modifies properties used in the component’s template.


Methods support asynchronous processing as they can return any type of value including promises


2.Passing data up the component hierarchy:


You can pass data from a child component to a parent or ancestor with events. 

Lightning Web Components relies on DOM events (as defined in the standard DOM specification) to propagate data up the component hierarchy.


ex :


// parentComponent.html 


<template>   

 <c-child onselect={handleSelect}></c-child>

</template>



// ParentComponent.js


import { LightningElement,track } from 'lwc';


export default class ParentComponent extends LightningElement {

    @track contactId

    

     handleSelect(event) {   

        const conId=event.detail;     

        this.contactId=conId;

     }     

}


// childComponent.html 


<template>

    <lightning-button onclick={handleClick} label="fireEvent"></lightning-button>

</template>


// ChildComponent.js


import { LightningElement} from 'lwc';


export default class ChildComponent extends LightningElement {


    handleClick(event){

      // Creates the event

        const selectedEvent = new CustomEvent('select', {detail : contactId});

        //dispatching the custom event

        this.dispatchEvent(selectedEvent);        

    } 

   

}


Note :


You can configure an event’s propagation so that it propagates to ancestor components that are not direct parents of a child component. 

To do so, you can set the bubbles attribute when creating the event:


const myEvent = new CustomEvent('myevent', { bubbles: true };


You can also stop the event propagation at any time with Event.stopPropagation().


3.Passing data to components that share no common ancestry


Sometimes you need to send data to components that share no common ancestry. 

For example, you may need to send data to or from a component hosted in a different region of a Lightning page or in a utility bar. 

This type of communication can be achieved with the Lightning Message Service or with the pubsub module.


1.Lightning message service

2.pubsub module


Lightning message service :


The Lightning Message Service (LMS) is the preferred solution for communicating between components that aren’t in the same DOM tree.

LMS also allows communication between the three Salesforce UI technologies: Lightning Web Components, Aura and Visualforce.


LMS provides a publish/subscribe mechanism that allows for the exchange of messages between components. 

For the sake of brevity and in order not to duplicate documentation, we won’t dive into the technical details of 

how to publish and subscribe to Lightning messages – but it requires three key steps:


1.Declare a message channel using the LightningMessageChannel metadata type.

2.Publish a message using the publish() function from the @salesforce/messageChannel module.

3.Subscribe to a message using the subscribe() function from the @salesforce/messageChannel module.



pubsub module :


The pubsub module should be used as a last resort alternative in containers that do not support the Lightning Message Service. 

The module provides a publish/subscribe mechanism for exchanging data across a web page. 

It’s a functional solution but is not officially supported or actively maintained.



Summary :


1.Passing data down the component hierarchy with public properties, setters or methods.

2.Passing data up the component hierarchy with events.

3.Passing data to components that share no common ancestry with the Lightning Message Service or the pubsub module.







Tuesday, 1 June 2021

Error handling Custom Lightning web component

 On the backend, the component javascript detects the type of error object it receives. Depending on which process fails, 

the error object will look slightly different. We are involving some logic here to ensure we drill down to the error message 

and details that are relevant.


//lwcErrorHandling.html 


<template>

  <!--banner-->

  <div class="slds-notify slds-notify_alert slds-theme_alert-texture slds-theme_error" role="alert">

    <!--error graphic-->

    <span class="slds-icon_container slds-icon-utility-error slds-m-right_x-small" title="Description of icon when needed">

      <lightning-icon icon-name="utility:error" alternative-text="Error" title="Error" variant="inverse"></lightning-icon>

    </span>

    <!--error message-->

    <h2>{errorMessageToDisplay}</h2>

    <!--close button-->

    <div class="slds-notify__close">

      <button class="slds-button slds-button_icon slds-button_icon-small slds-button_icon-inverse" title="Close" onclick={closeError}>

        <lightning-icon icon-name="utility:close" alternative-text="Close" title="Close" variant="inverse"></lightning-icon>

      </button>

    </div>

  </div>

</template>


//lwcErrorHandling.js


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


export default class LwcErrorHandling extends LightningElement {

    @api error;

    errorMessageToDisplay = '';


    // this runs every time this component is loaded

    connectedCallback(){

        this.errorMessageToDisplay = 'An error occurred \n';

        console.log(this.error);

        

        // getting meaningful error message depending on error type

        // handles errors from wire & regular apex methods

        if(this.error.body){

            if (Array.isArray(this.error.body)) {

                this.errorMessageToDisplay += this.error.body.map(e => e.message).join(', ');

            }

            else if(typeof this.error.body === 'object'){

                let fieldErrors = this.error.body.fieldErrors;

                let pageErrors = this.error.body.pageErrors;

                let duplicateResults = this.error.body.duplicateResults;

                let exceptionError = this.error.body.message;


                if(exceptionError && typeof exceptionError === 'string') {

                    this.errorMessageToDisplay += exceptionError;

                }

                

                if(fieldErrors){

                    for(var fieldName in fieldErrors){

                        let errorList = fieldErrors[fieldName];

                        for(var i=0; i < errorList.length; i++){

                            this.errorMessageToDisplay += errorList[i].statusCode + ' ' + fieldName + ' ' + errorList[i].message + ' ';

                        }

                    }

                }

        

                if(pageErrors && pageErrors.length > 0){

                    for(let i=0; i < pageErrors.length; i++){

                        this.errorMessageToDisplay += pageErrors[i].statusCode + ' '+ pageErrors[i].message;

                    }

                }

        

                if(duplicateResults && duplicateResults.length > 0){

                    this.errorMessageToDisplay += 'duplicate result error';

                }

            }  

        }

        // handles errors from the lightning record edit form

        if(this.error.message){

            this.errorMessageToDisplay += this.error.message;

        }

        if(this.error.detail){

            this.errorMessageToDisplay += this.error.detail;

        }


    }


    // runs when the close button on the error banner is clicked

    closeError() {

        // reset variables

        this.errorMessageToDisplay = '';

        this.error = null;

        // create a custom event for resetting the error state

        const closeErrorEvent = new CustomEvent("errorclosed", {

          detail: this.error

        });

    

        // dispatch the custom event to the parent component, this resets the error and hides this component

        // if the parent component triggers another error, this entire component loads again and runs through the connectedCallback logic

        this.dispatchEvent(closeErrorEvent);

      }

}



//accountinfo.html :



<template>


    <!-- error handling -->

    <template if:true={error}>

        <c-errorhandling error={error} onerrorclosed={handleError}></c-errorhandling>

    </template>


    <lightning-card  title="Account Info" icon-name="standard:account">

        <div class="slds-p-around_small">

            <!-- record edit form-->

            <lightning-record-edit-form record-id={recordId} object-api-name="Account" onerror={handleFormError}>

                    <lightning-input-field field-name='Name'></lightning-input-field>

                <!-- submit button -->

                <lightning-button class="slds-m-top_small button-fixed" variant="brand" type="submit" name="save" label="Save"></lightning-button>

            </lightning-record-edit-form>


            <!-- child component -->

            <c-accountinfochild></c-accountinfochild>

        </div>       

    </lightning-card>

</template>



// accountinfo.js


import { LightningElement, api } from 'lwc';


export default class Accountinfo extends LightningElement {

    @api recordId;

    error;

    

    // handle error from child components

    errorCallback(error, stack){

        this.error = error;

    }

    

    // handle errors from self (submit button press)

    handleFormError(event){

        this.error = event.detail;

    }

    

    // handle errors from self

    handleError(error){

        this.error =  error;   

    }

}



//accountinfochild.html


<template>

    <lightning-button variant="brand" label="Click me" title="Click me" onclick={doSomething} class="slds-m-left_x-small"></lightning-button>

</template>



// accountinfochild.js


import { LightningElement } from 'lwc';


export default class Accountinfochild extends LightningElement {

    doSomething() {

        throw new Error('Whoops!');

   }

}