Posted in Lightning, Lightning Component, Lightning Experience, Salesforce, Trailblazer

Hyperlink/Formula Fields In lightning:datatable

In many cases, while working with lightning:datatable we got challenge to display URL which redirect to record page and also for related lookup fields and formula fields. If it’s a Salesforce Id, a lookup to record or to some other record, and we would like to display it as a link with an appropriate value. Unfortunately, <lightning:dataTable> doesn’t have an Id column type, and the url type is not smart enough to realize it’s been handed a record Id and handle it.

Instead, we need to generate the URL ourselves and add it as a property of the line items in the source data. So for that one we have to use typeAttributes in column property.

Basically typeAttributes are the one of the major building blocks of <lightning:datatable>. This will help us to format the data types /output display and attaching some of actions to your data displayed in table at each cell level ,row level or at the columns level.

The typeAttributes are attached to each columns data type when your specifying the columns for your data table in your java script file. Each typeAttributes is having pre-defined set of attributes/properties basis on the data type of your column (your defining at the time of columns for table).

Hyperlink/Image Formula :

Here we have one formula field Demo Formula on Task where it will display task Priority name on click on that link it will redirect it to Task List view. So to use that formula in data table we have to do following.

Inside Column we have to set typeAttributes like below.

{
    label: 'Demo Formula',
    fieldName: 'DemoLink',
    type: "url",
    typeAttributes: {
        label: {
            fieldName: 'Priority'
        },
        target: '_blank'
    }
},
//For Image formula define colums like this.
{
    label: 'Status',
    fieldName: 'Status',
    cellAttributes: {
        iconName: {
            fieldName: 'statusIconName'
        },
        iconLabel: {
            fieldName: 'statusIconLabel'
        },
        iconPosition: 'right'
    }
}

Also inside response we have to handle it like below.

var records = response.getReturnValue();
records.forEach(function(record) {
    record.Priority = record.Priority;
    record.DemoLink = '/lightning/o/Task/home'; //Define here hyperlink as per formula field
    //For Image you can add icons dynamically like this.
    if (record.Status === 'Not Started') {
        record.statusIconName = 'standard:assignment';
        record.statusIconLabel = record.Status;
        record.Status = '';
    }
    if (record.Status === 'In Progress') {
        record.statusIconName = 'standard:investment_account';
        record.statusIconLabel = record.Status;
        record.Status = '';
    }

});
component.set("v.taskList", records);

Name/Lookup/Related Field :

For any related field or lookup/Name field we have to change both display name and link functionality. For example on Task we have WhatId so we have to display Related To name and we have to set hyperlink here. So to use that field in data table we have to do following.

Inside Column we have to set typeAttributes like below.

{
    label: 'Related To',
    fieldName: 'WhatId',
    type: 'url',
    typeAttributes: {
        label: {
            fieldName: 'WhatName'
        },
        target: '_blank'
    }
}

Also inside response we have to handle it like below.

var records = response.getReturnValue();
records.forEach(function(record) {
    record.WhatName = record.What.Name; //Here define your value which you want to display
    record.WhatId = '/' + record.WhatId ';// Here define where you want to redirect.

});
component.set("v.taskList", records);

Let’s try all together inside lightning component.

Here is component output.

Apex Class

public with sharing class TaskListController {
    @AuraEnabled(cacheable=true)
    public static List<Task> getTaskList() {
        return [SELECT Id,Subject, ActivityDate, What.Name, WhatId, OwnerId,Owner.Name FROM Task LIMIT 10];
    }
}

TaskList.cmp

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" controller="TaskListController">          
    <aura:attribute type="Task[]" name="taskList"/>
    <aura:attribute name="mycolumns" type="List"/>
    
    <aura:handler name="init" value="{!this}" action="{!c.fetchTasks}"/>
     <lightning:card title="Datatable Example" iconName="action:new_task">
    <lightning:datatable data="{!v.taskList}" 
                         columns="{! v.mycolumns }" 
                         keyField="id"
                         hideCheckboxColumn="true"/>
    </lightning:card>
</aura:component>


TaskListController.js

({
    fetchTasks: function(component, event, helper) {
        component.set('v.mycolumns', [{
                label: 'Subject',
                fieldName: 'subjectLink',
                type: 'url',
                typeAttributes: {
                    label: {
                        fieldName: 'Subject'
                    },
                    target: '_blank'
                }
            },
            {
                label: 'Due Date',
                fieldName: 'ActivityDate',
                type: "date",
                typeAttributes: {
                    weekday: "long",
                    year: "numeric",
                    month: "long",
                    day: "2-digit"
                }
            },
            {
                label: 'Demo Formula',
                fieldName: 'DemoLink',
                type: "url",
                typeAttributes: {
                    label: {
                        fieldName: 'Priority'
                    },
                    target: '_blank'
                }
            },
            {
                label: 'Related To',
                fieldName: 'WhatId',
                type: 'url',
                typeAttributes: {
                    label: {
                        fieldName: 'WhatName'
                    },
                    target: '_blank'
                }
            },
            {
                label: 'Status',
                fieldName: 'Status',
                cellAttributes: {
                    iconName: {
                        fieldName: 'statusIconName'
                    },
                    iconLabel: {
                        fieldName: 'statusIconLabel'
                    },
                    iconPosition: 'right'
                }
            },
            {
                label: 'Assigned To',
                fieldName: 'OwnerId',
                type: 'url',
                typeAttributes: {
                    label: {
                        fieldName: 'OwnerName'
                    },
                    target: '_blank'
                }
            }

        ]);
        var action = component.get("c.getTaskList");
        action.setParams({});
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var records = response.getReturnValue();
                records.forEach(function(record) {
                    record.WhatName = record.What.Name;
                    record.WhatId = '/' + record.WhatId;
                    record.Priority = record.Priority;
                    record.DemoLink = '/lightning/o/Task/home';
                    record.OwnerName = record.Owner.Name;
                    record.OwnerId = '/' + record.OwnerId;
                    record.subjectLink = '/' + record.Id;
                    if (record.Status === 'Not Started') {
                        record.statusIconName = 'standard:assignment';
                        record.statusIconLabel = record.Status;
                        record.Status = '';
                    }
                    if (record.Status === 'In Progress') {
                        record.statusIconName = 'standard:investment_account';
                        record.statusIconLabel = record.Status;
                        record.Status = '';
                    }
                    if (record.Status === 'Waiting on someone else') {
                        record.statusIconName = 'standard:call';
                        record.statusIconLabel = record.Status;
                        record.Status = '';
                    }
                    if (record.Status === 'Deferred') {
                        record.statusIconName = 'standard:first_non_empty';
                        record.statusIconLabel = record.Status;
                        record.Status = '';
                    }
                    if (record.Status === 'Completed') {
                        record.statusIconName = 'standard:task2';
                        record.statusIconLabel = record.Status;
                        record.Status = '';
                    }
                });
                console.log(records);

            }
            component.set("v.taskList", records);
        });
        $A.enqueueAction(action);
    }
})

Live Demo :

References:

Displaying image formula field in lightning data table

How to Hyperlink a Record in lightning:datatable?

Posted in Lightning, Lightning Web Component, LWC, Salesforce

Lightning Web Component for AURA Developers Part – III

Call 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.Before you use an Apex method, make sure that there isn’t an easier way to get the data. Before calling apex method first review a base Lightning component, like lightning-record-form, lightning-record-view-form, or lightning-record-edit-form works for your use case.

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

For more information take a look here

Here some of highlights of this component.

  1. This component is used to display list of Accounts in picklist.
  2. The component is used to display list of Contacts related to respective Account. User can select Account Name from dropdown to see list of related contacts.

Here you go step by step Instructions to create the Lightning Web Component

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.

To use @wire to call an Apex method, we must set cacheable=true. But once we have used cacheable we cannot use DML in that. If an Apex method is annotated with @AuraEnabled(cacheable=true), you can invoke it from a component via the @wirewire service.You can @wire a property if you just wanna use the response as it is.

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.

public class AccountLWCController {
     @AuraEnabled(cacheable=true)
    public static List<Account> getAccountList() {
        return [SELECT Id, Name FROM Account];
    }
    @AuraEnabled
    public static List<Contact> getContacts(String accountId){
        return [Select Id,FirstName,LastName,Email,Phone from Contact where accountId=:accountId];
        
    }  
}

Let’s build the AccountList LWC

Create a new Lightning web component using the below SFDX command
sfdx force:lightning:component:create --type lwc -n AccountList -d force-app/main/default/lwc

accountList.html

<template>
    <lightning-card >
        <div class="slds-page-header slds-page-header--object-home">
            <lightning-layout>
                <lightning-layout-item >
                    <lightning-icon icon-name="standard:contact" alternative-text="Contact" ></lightning-icon>
                </lightning-layout-item>
                <lightning-layout-item class="slds-m-left--small">
                    <p class="slds-text-title--caps slds-line-height--reset">Accounts</p>
                    <h1 class="slds-page-header__title slds-p-right--x-small">Contact
                        Viewer</h1>
                </lightning-layout-item>
            </lightning-layout>
            <lightning-layout >
                <lightning-layout-item >
                    <p class="slds-text-body--small"> Contacts • View
                        Contacts Based on Selected Account</p>
                </lightning-layout-item>
            </lightning-layout>
        </div>
        <label class="slds-form-element__label" for="select-01">Select Account</label>
        <div class="slds-form-element__control">
            <div class="slds-select_container">
                <select class="slds-select" id="select-01" onchange={onValueSelection}>
                    <!--iterate all picklist values from accounts list using for:each loop-->
                    <template for:each={accounts.data} for:item="acc">
                        <option key={acc.Id} value={acc.Id}>
                            {acc.Name}
                        </option>
                    </template>
                </select>
            </div>
        </div>
        <template if:true={contacts}>
            <lightning-card title="Contacts"  >
                <div style="height: 300px;">
                    <lightning-datatable
                                         key-field="Id"
                                         data={contacts}
                                         columns={columns}>
                    </lightning-datatable>
                </div>    
            </lightning-card>
        </template>
    </lightning-card>
</template>

accountList.js

The component’s JavaScript code imports the Apex method.On selection of picklist onValueSelection get triggered and call the getContacts() method with accountId parameter. Once getContacts() get called it gives the response as Promise and will handle that using .then() and .catch() properties.

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

import getAccountList from '@salesforce/apex/AccountLWCController.getAccountList';
import getContacts from '@salesforce/apex/AccountLWCController.getContacts';

const columns = [{
        label: 'First Name',
        fieldName: 'FirstName'
    },
    {
        label: 'Last Name',
        fieldName: 'LastName'
    },
    {
        label: 'Email',
        fieldName: 'Email',
        type: 'email'
    },
    {
        label: 'Phone',
        fieldName: 'phone',
        type: 'phone'
    }

];
export default class AccountList extends LightningElement {
    @track accountId = '';
    @track contacts;
    @track columns = columns;
    //  invoke apex method with wire property and fetch picklist options.
    // pass 'object information' and 'picklist field API name' method params which we need to fetch from apex
    @wire(getAccountList) accounts;
    onValueSelection(event) {
        // eslint-disable-next-line no-alert
        const selectedAccount = event.target.value;
        this.accountId = event.target.value;
        if (selectedAccount != null) {
            getContacts({
                    accountId: selectedAccount
                })
                .then(result => {
                    this.contacts = result;
                    // eslint-disable-next-line no-console
                    console.log('result' + JSON.stringify(result) + selectedAccount);
                })
                .catch(error => {
                    this.error = error;
                });
        }
    }
} 

accountList.js-meta.xml

To make sure component is visible in Lightning App Builder you need to add targets and change isExposed to true in LightningDatatableExample.js-meta.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>47.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Account List</masterLabel>
  <description>This is a demo component.</description>
  <targets>
      <target>lightning__RecordPage</target>
      <target>lightning__AppPage</target>
      <target>lightning__HomePage</target>
  </targets>
</LightningComponentBundle>

Below is the output of component.

References:

Calling Apex class using LWC

Posted in Lightning Component, Lightning Web Component, LWC, Salesforce

Lightning Web Components for AURA Developers Part – II

The best way to compare the Aura Component and Lightning Web Components programming models is to look at code for similar components written in the two models. The goal of this post is to help you to leverage your Aura components skills to accelerate learning about Lightning web components. You learn how the two types of components work well together.

Prerequisites

Before you dive into this post, complete the Lightning Web Components Basics module, which gives you a great introduction to the new programming model.
I am assuming that you’re familiar with the Aura Components programming model, and we won’t explain its features, except in comparison to the Lightning Web Components programming model. If you’re not familiar with Aura components, start with the Aura Components Basics module.

Aura Initializers Become Lifecycle Hooks

Replace an init event handler in an Aura component with the standard JavaScript connectedCallback() method in a Lightning web component. The connectedCallback() lifecycle hook fires when a component is inserted into the DOM. Lifecycle hooks are callback methods that let you run code at each stage of a component’s lifecycle.We use the init event in an Aura component to initialize a component after component construction but before rendering.

Here’s an init event handler in the PropertyCarousel Aura component.

<aura:handler name="init" value="{!this}" action="{!c.onInit}"/>

The onInit function in the Aura component’s controller performs any necessary initialization.In a Lightning web component, use connectedCallback() instead in the component’s JavaScript file. Here’s an example inpropertySummary.js.

export default class PropertySummary extends LightningElement {
    ...
    connectedCallback() {
        // initialize component
    }
}

Migrate Base Components

This Aura component uses the lightning:formattedNumber base component.

<aura:component>
<lightning:formattedNumber value="5000" style="currency"
currencyCode="USD"/>
</aura:component>

To migrate this markup to a Lightning web component:

  • Change the colon that separates the namespace (lightning) and component name (formattedNumber) to a dash.
  • Change the camel-case component name (formattedNumber) to a dash-separated name (formatted-number).
  • Change the camel-case attribute name (currencyCode) to a dash-separated name (currency-code).

Here’s the equivalent Lightning web component.

<template>
    <lightning-formatted-number value="5000" style="currency"
      currency-code="USD" >
    </lightning-formatted-number>
</template>

Aura CSS Becomes Standard CSS

Lightning web components use standard CSS syntax. Remove the proprietary THIS class that Aura components use.
Here’s a portion of the CSS file for the PropertyTile Aura component.

.THIS .lower-third {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    color: #FFF;
    background-color: rgba(0, 0, 0, .3);
    padding: 6px 8px;
}

Similar CSS for the propertyTile Lightning web component uses standard CSS instead. The THIS keyword is specific to Aura and isn’t used in Lightning web components.

.lower-third {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    color: #FFF;
    background-color: rgba(0, 0, 0, .3);
    padding: 6px 8px;
}

Call Apex Methods

Here’s a portion of the js file for the calling apex method “getContacts” in Aura component.

var action = component.get("c.getContacts");
action.setCallback(this, function(data) {
    component.set("v.Contacts", data.getReturnValue());
});
$A.enqueueAction(action);

The JavaScript function calls the getContacts method of the Apex controller. It then populates the attribute named Contacts with the results.

Here’s a portion of the js file for the calling apex method “getContacts” in lightning web component.

import getContacts from '@salesforce/apex/ContactController.getContacts';

export default class ApexImperativeMethod extends LightningElement {
    @track contacts;
    @track error;

    handleLoad() {
        getContacts()
            .then(result => {
                this.contacts = result;
                this.error = undefined;
            })
            .catch(error => {
                this.error = error;
                this.contacts = undefined;
            });
    }
}

Also don’t forget to review awesome Salesforce Lightning Web Component Cheat Sheet (Unofficial) prepared by @Santanu Boral

References:

Lightning Web Components for Aura Developers

Working with Aura and Lightning Web Components: Interoperability and Migration

Posted in Lightning Component, Lightning Web Component, LWC, Salesforce

Lightning Web Components for AURA Developers Part – I

The best way to compare the Aura Component and Lightning Web Components programming models is to look at code for similar components written in the two models. The goal of this post is to help you to leverage your Aura components skills to accelerate learning about Lightning web components. You learn how the two types of components work well together.

Prerequisites

Before you dive into this post, complete the Lightning Web Components Basics module, which gives you a great introduction to the new programming model.
I am assuming that you’re familiar with the Aura Components programming model, and we won’t explain its features, except in comparison to the Lightning Web Components programming model. If you’re not familiar with Aura components, start with the Aura Components Basics module.

Component Bundles

The component bundle file structure is different for an Aura component and a Lightning web component. Here’s how the files map between the two types of component.

(Source: Trailhead )

Migrate Markup

  • An Aura component contains markup in a .cmp file. The file starts with an <aura:component> tag and can contain HTML and Aura-specific tags.
  • A Lightning web component contains markup in a .html file. The file starts with a <template> tag and can contain HTML and directives for dynamic content.

Attributes Become JavaScript Properties

Migrate attributes from tags in the markup (.cmp) of an Aura component to JavaScript properties in the JavaScript file (.js) of a Lightning web component.

Aura component:

 <aura:attribute name="recordId" type="Id" />
 <aura:attribute name="account" type="Account" />

LWC :

import { LightningElement, api, track } from 'lwc';
export default class AccountSummary extends LightningElement {
    @api recordId;
    @track account;
        ...
}

The recordId and account attributes in the Aura component become the recordId and account JavaScript properties in the Lightning web component.

The two Lightning web component properties have different decorators. The @api and @track decorators both make a property reactive, which means that when the property value changes, the component’s HTML template rerenders.

The @api decorator marks recordId as a public reactive property. A public property is part of the public API for the component, which means that it can be set in Lightning App Builder, or by a parent component that uses the component in its markup.

The @track decorator marks property as a private reactive property, also known as a tracked property. These properties are used to track internal component state and aren’t part of the component’s public API.

Basic Aura Expressions Become HTML Expressions

Migrate basic expressions from markup in an Aura component to expressions in HTML in a Lightning web component. An example of a basic expression is a reference to an attribute in an Aura component.
For example, the AccountPaginator Aura component uses basic expressions to display the values of the page, pages, and total attributes.

<aura:component >
    <aura:attribute name="page" type="integer"/>
    <aura:attribute name="pages" type="integer"/>
    <aura:attribute name="total" type="integer"/>
    
    <div class="centered">{!v.total} Accounts • page {!v.page} of {!v.pages}</div>
</aura:component>

Here’s the equivalent syntax in the HTML file of the accountPaginator Lightning web component.

<template>
    {totalItemCount} items • page {currentPageNumber} of {totalPages}
</template>

The HTML references the totalItemCount property in paginator.js. The {currentPageNumber} and {totalPages} expressions reference getters that process the pageNumber and pageSize properties.

import { LightningElement, api } from 'lwc';
export default class Paginator extends LightningElement {
    /** The current page number. */
    @api pageNumber;
    /** The number of items on a page. */
    @api pageSize;
    /** The total number of items in the list. */
    @api totalItemCount;
    get currentPageNumber() {
        return this.totalItemCount === 0 ? 0 : this.pageNumber;
    }
    get totalPages() {
        return Math.ceil(this.totalItemCount / this.pageSize);
    }
}

Aura Conditionals Become HTML Conditionals

Migrate <aura:if> tags in an Aura component to if:true or if:false directives in a Lightning web component’s HTML file.
Here’s some conditional markup in the AccountDetails Aura component.

<aura:if isTrue="{!v.spinner}">
    <lightning:recordForm recordId="{!v.accountId}"
      objectApiName="Account"
      fields="{!v.accountFields}" columns="2"/>
</aura:if>

Here’s similar HTML in the accountDeatils Lightning web component.

<template if:true={spinner}>
    <lightning-record-form object-api-name="Account" 
      record-id={accountId} fields={accountFields} 
      columns="2">
    </lightning-record-form>
</template>

The HTML file of a Lightning web component starts with the standard HTML <template> tag, and it can also contain other <template> tags in its body. In this example, the content in the <template> tag conditionally renders depending on the result of the if:true directive.

Aura Iterations Become HTML Iterations

Migrate <aura:iteration> tags in an Aura component to for:each directives in a Lightning web component’s HTML file.
Here’s the Aura syntax in Sample.cmp.

<aura:iteration items="{!v.items}" itemVar="item">
    {!item}
</aura:iteration>

Here’s similar HTML in the sample Lightning web component.

<template for:each={items} for:item='item'>
    <p key={item.id}>{item}</p>
</template>

Stay tuned for next blog post for Lightning Web Components for AURA Developers Part- II.

References:

Lightning Web Components for Aura Developers

Working with Aura and Lightning Web Components: Interoperability and Migration