Welcome to the second largest chapter of Salesforce Platform Dev II Exam Preparation: User Interface. This chapter consists of 20% of total score! Without further ado, let's get into it!
NOTE: This post is written in July 2019 and content might be changed/updated overtime. The content is inspired by focusonforce.com.
Standard Set Controller
- An object of the
StandardSetController
class can be used to create a list controller or extend the pre-built Visualforce list controller. - The
StandardSetController
constructor
accepts either aList
ofsObjects
or a query locatorDatabase.QueryLocator
. - The
StandardSetController
stores data sets on the server to reduce page state and increase performance. - Support operations including pagination through lists of records and performing bulk updates on list of selected records.
// example of Standard Set Controller using query locator
public with sharing class AccountQueryLocator{
public ApexPages.StandardSetController con{
get{
if(con == null){
con = new ApexPages.StandardSetController(
Database.getQueryLocator(
[SELECT Id, Name FROM Account WHERE Name LIKE '%A' LIMIT 10]
)
);
// work the same
con = new ApexPages.StandardSetController(
[SELECT Id, Name FROM Account WHERE Name LIKE '%A' LIMIT 10]
);
}
return con;
}
set;
}
public List<Account> getAccounts(){
return (List<Account>) con.getRecords();
}
}
<!-- example of Visualforce page that uses Standard Set Controller using query locator -->
<apex:page controller="AccountQueryLocator">
<apex:pageBlock title="Query Locator Example">
<apex:pageBlockTable value="{!accounts}" var="a">
<apex:column value="{!a.Id}" />
<apex:column value="{!a.Name}" />
</apex:pageBlockTable>
</apex:pageBlock>
</apex:page>
-
NOTE:
StandardSetController
can handle up to 10,000 records. If query result is more than 10,000 records,LimitException
will be thrown if using query locator, while results will be truncated to 10,000 records if using list ofsObjects
in the parameter. -
NOTE: Salesforce has a pagination limit of 2,000 records per page.
-
Some common methods in
StandardSetController
:getCompleteResult()
- used to determine if number of returned records is over limit. If returnedfalse
, it means the controller won't be able to process all the records.getRecord()
- used to access the prototype object that is used to store list of field updates that should be applied to the selected recordsgetRecords()
- return list ofsObjects
in current page setgetSelected()
- return list of objects that is selectedgetListViewOptions()
- specify what list view options are available to the usersave()
- upsert recordsetPageSize()
- used to set the number of records to be appeared on the pagenext()
- move to next record pageprevious()
- move to previous record page
-
Sample pagination with controller to display list of records:
// Custom Controller for visualforce page that returns up to 10,000 records
public with sharing class LargeAccountSetController{
public String searchString { get; set;} // search string
public Integer num {get; set;} // num of records returned
public Integer size {get; set;} // size of each page set
public ApexPages.StandardSetController con {
get{
if(con == null){
con = new ApexPages.StandardSetController(
Database.getQueryLocator(
[SELECT Id, Name, Type FROM Account]
)
);
if(num < size){
size = num;
} else{
size = 20;
}
con.setPageSize(size);
}
return con;
}
set;
}
// return list of account records
public List<Account> getAccounts(){
List<Account> accList = new List<Account>();
for(Account acc: (List<Account>) con.getRecords()){
accList.add(acc);
}
return accList;
}
// search by given string
public void search(){
con = new ApexPages.StandardSetController(
Database.getQueryLocator(
[SELECT Id, Name, Type FROM ACCOUNT WHERE Name LIKE :searchString + '%']
)
);
num = con.getResultSize();
if(num < size){
size = num;
} else{
size = 20;
}
con.setPageSize(size);
}
// return whether there are more records after the current page set
public Boolean hasNext{
get{
return con.getHasNext();
}
set;
}
// return whether there are more records before the current page set
public Boolean hasPrevious{
get{
return con.getHasPrevious();
}
set;
}
// return page number of current page set
public Integer pageNumber{
get{
return con.getPageNumber();
}
set;
}
// return to first page
public void first(){
con.first();
}
// return to last page
public void last(){
con.last();
}
// return to next page
public void next(){
con.next();
}
// return to previous page
public void previous(){
con.previous();
}
}
<!-- visualforce page to display a list up to 10,000 records, with search function and pagination -->
<apex:page controller="LargeAccountSetController">
<apex:form>
<apex:pageBlock title="Account Search">
<apex:pageMessages />
<apex:pageBlockButtons location="top">
<apex:inputText value="{!searchString}" label="Search..." />
<apex:commandButton value="Search" action="{!search}" />
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!accounts}" var="a">
<apex:column headerValue="Id" value="{!a.Id}" />
<apex:column headerValue="Account Name" value="{!a.Name}" />
<apex:column headerValue="Type" value="{!a.Type}" />
</apex:pageBlockTable>
</apex:pageBlock>
<apex:outputText>
{!IF(num > 0, (pageNumber * size) + 1 - size, 0)} -
{!IF((pageNumber * size) > num, num, (pageNumber * size))} of
{!num} (Page {!pageNumber})
</apex:outputText>
<apex:panelGrid columns="4">
<apex:commandButton action="{!first}" value="First" />
<apex:commandButton action="{!previous}" value="Prev" rendered="{!hasPrevious}" />
<apex:commandButton action="{!next}" value="Next" rendered="{!hasNext}" />
<apex:commandButton action="{!last}" value="Last" />
</apex:panelGrid>
</apex:form>
</apex:page>
- Example of using Wrapper Class to process records:
// Custom Controller for visualforce page to process selected records
public with sharing class AccountSelectionController{
List<AccountWrapper> acocunts {get; set;}
public ApexPages.StandardSetController con {
get{
if(con == null){
con = new ApexPages.StandardSetController(
Database.getQueryLocator(
[SELECT Id, Name, Type FROM Account]
)
);
con.setPageSize(20);
}
return con;
}
set;
}
// return list of account wrapper records
public List<AccountWrapper> getAccounts(){
accounts = new List<AccountWrapper>();
for(Account acc: (List<Account>) con.getRecords()){
accounts.add(acc);
}
return accounts;
}
public PageReference process(){
for(AccountWrapper aw : accounts){
if(aw.checked){
ApexPages.addMessage(
new ApexPages.message(
ApexPages.severity.INFO,
'Name: ' + aw.acc.Name + ', Type: ' + aw.acc.Type
)
);
}
}
return null;
}
// return whether there are more records after the current page set
public Boolean hasNext{
get{
return con.getHasNext();
}
set;
}
// return whether there are more records before the current page set
public Boolean hasPrevious{
get{
return con.getHasPrevious();
}
set;
}
// return page number of current page set
public Integer pageNumber{
get{
return con.getPageNumber();
}
set;
}
// return to first page
public void first(){
con.first();
}
// return to last page
public void last(){
con.last();
}
// return to next page
public void next(){
con.next();
}
// return to previous page
public void previous(){
con.previous();
}
// return PageReference of original page (home page)
public void cancel(){
con.cancel();
}
}
// Account Wrapper class
public class AccountWrapper{
public Boolean checked {get; set;}
public Account acc { get; set;}
public AccountWrapper(Account acc){
this.acc = acc;
checked = false;
}
}
<!-- visualforce page to display some information on selected account records after pressing Process button -->
<apex:page controller="AccountSelectionController">
<apex:form>
<apex:pageBlock title="Select and Process Accounts">
<apex:pageMessages />
<apex:pageBlockButtons location="top">
<apex:inputText value="Process" action="{!process}" />
<apex:commandButton value="Cancel" action="{!cancel}" />
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!accounts}" var="a">
<apex:column width="10px">
<apex:inputCheckbox value="{!a.checked}" />
</apex:column>
<apex:column headerValue="Account Name" value="{!a.Name}" />
<apex:column headerValue="Type" value="{!a.Type}" />
</apex:pageBlockTable>
</apex:pageBlock>
<apex:outputText>
{!IF(num > 0, (pageNumber * size) + 1 - size, 0)} -
{!IF((pageNumber * size) > num, num, (pageNumber * size))} of
{!num} (Page {!pageNumber})
</apex:outputText>
<apex:panelGrid columns="4">
<apex:commandButton action="{!first}" value="First" />
<apex:commandButton action="{!previous}" value="Prev" rendered="{!hasPrevious}" />
<apex:commandButton action="{!next}" value="Next" rendered="{!hasNext}" />
<apex:commandButton action="{!last}" value="Last" />
</apex:panelGrid>
</apex:form>
</apex:page>
- Difference between
Standard Set Controller
andStandard Controller
is thatStandard Set Controller
operates on list of records whileStandard Controller
operates on single record.
Custom Controller
- A
Custom Controller
is anApex
Class that defines and implements the logic for aVisualforce
page without usingStandard Controller
. - NOTE: custom controller uses the default, no-argument constructor.
- In
Visualforce
page, the controller attribute of<apex:page>
component is used to associate the custom controller with the page. - NOTE:
Custom Controller
runs in System Mode, meaning the current user's permission and FLS are not taken into account. - Example of
Custom Controller
:
public class CustomController{
private final Account acc;
public CustomController(){
acc = [SELECT Id, Name, Type FROM Account WHERE Id =
:ApexPages.currentPage().getParameters().get('id')];
}
public Account getAccount(){
return acc;
}
public PageReference save(){
update acc;
return null;
}
}
<apex:page controller="CustomController" tabStyle="Account">
<apex:form>
<apex:pageBlock title="Hello and welcome, {!$User.FirstName}!">
<apex:outputLabel value="Account Name: "/>{!acc.Name}<p/>
<apex:outputLabel value="Account Type: "/><apex:inputField value="{!acc.Type}" /><p/>
<apex:commandButton action="{!save}" value="Save" />
</apex:pageBlock>
</apex:form>
</apex:page>
- Miscellaneous stuff regarding to Person and Business Account:
- If creating a new account and set the Name field, the record will be Business Account; if setting LastName field, it will be Person Account
- When an input field references the Name field, you must use IsPersonAccount in your query so that the system knows whether to process the field as a Person or Business Account.
- Create custom name formula that will render the name properly for both Person and Business Account in
Visualforce
Controller Extension
Controller Extension
is anApex
class that can be used to extend the functionality of a standard or custom controller.Controller Extension
can be used to add new actions or override existing actions, such as edit, view, save or delete.- NOTE: controller extension uses logic from a standard controller and respects user permissions with single argument of
ApexPages.StandardController
orCustomerControllerName
. - NOTE: multiple
Controller Extensions
can be defined for a singleVisualforce
page through a comma-separated list (the first in the list has the highest precedence). - Example of Controller Extension that enables creation of an account and contact:
// Example of Controller Extension to create account and contact
public class ContactControllerExtension{
Contact con {get; set;}
public ContactControllerExtension (ApexPages.StandardController std){
con = (Contact) std.getRecord();
}
// the method overrides the default save action
public PageReference save(){
Account acc = new Account (
Name = con.FirstName + ' ' + con.LastName
);
insert acc;
con.AccountId = acc.Id;
insert con;
PageReference pr = New PageReference('/' + acc.Id);
return pr;
}
}
<apex:page standardController="Contact" extensions="ContactControllerExtension">
<apex:form>
<apex:pageBlock title="Create an account and contact">
<apex:outputText value="Contact First Name" /><apex:inputField value="{!con.FirstName}" /> <p/>
<apex:outputText value="Contact Last Name" /><apex:inputField value="{!con.LastName}" /> <p/>
<apex:commandButton value="Save" action="{!save}" />
</apex:pageBlock>
</apex:form>
</apex:page>
- Important notes about controllers and extensions:
- If a class includes a web service method the class must be defined as global.
- Web service is run in system mode, even though initial web service controlled by user profile.
- Access to
webservice
methods is only checked at the entry point. If access is allowed to thewebservice
, all subsequent code will execute in system mode (which means user can modify fields that they are not allowed to, call method that are not allowed as well). - Getter and setter methods cannot be used with DML or future method.
- The order of method and variables being processed is not guaranteed.
- DML statements cannot be used in getter or in constructor method.
- Like most
Apex
classes, controllers and extensions run in system mode. - The with sharing keyword can be used to ensure security policies if needed and are generally declared as public.
Performing Actions and Partial Page Refresh
-
Action attribute
can be used in certainVisualforce
page to specify which action should be taken. -
ReRender attribute
can be used in someVisualforce
component tag to rerender on certain event and it must be referencing the component id to be rerendered. -
NOTE:
reRender
attribute cannot be used to update content or data, and it is not the same asrendered
which only accept single boolean argument whether a section of the page should be rendered at all. -
The referenced name must be the same as method name wrapping in
{! }
notation. -
Visualforce
tags that can acceptaction
attribute:<apex:page action="{!loadActions}">
- action is called on page load<apex:commandButton action="{!saveRecord}">
- action is called when button is clicked<apex:commandLink action="{!openWebsite}">
- action is called when link is clicked<apex:actionPoller action="{!incrementCounter}" interval="5" reRender="counterComponent">
- action is called periodically without user input- In this case, the counter will be incremented in every 5 seconds and component and that component with id "counterComponent" will be rerendered after the event.
<apex:actionSupport action="{!incrementCounter}" event="onmouseover" reRender="counterComponent">
- action is called when an event on another component is triggered (such as "onclick", "onmouseover", and etc.)- In this case, the counter will be incremented when mouse is hovered over the component and that component iwith id "counterComponent" will be rerendered after the event.
<apex:actionFunction action="{!showAlert}">
- define a Javascript that calls an action
-
Visualforce
tags that can use withJavascript
:<apex:commandButton>
- generate a button that can use different types ofJavascript
attributes such asonclick
,onkeypress
,onmouseover
and etc.<apex:commandLink>
- generate a link that executes an action and can use different types ofJavascript
attributes as well<apex:input>
- also support different events such asonclick
andonchange
<apex:actionFunction>
- the 'name' attribute of the component can be used to specifyJavascript
function that, when invoked elsewhere in the page, causing the method specified by the 'action' attribute to execute.<apex:actionSupport>
- supports multiple attributes that supportJavascript
functionality, such asoncomplete
andonsubmit
.
-
NOTE: An action must be public, accept no argument, and return a PageReference.
Display Error Messages
ApexPages.Message
class can be used to create an error message to display onVisualforce
page, simply callingApexPages.addMessage()
and include<apex:pageMessages/>
inVisualforce
page.- Example how to use
ApexPages.addMessage()
:
public class SampleController{
public Account acc;
public SampleController(){
acc = [SELECT Id, Name FROM Account];
}
public Account getAccount(){
return acc;
}
public PageReference save(){
update account;
try{
Account a = new Account();
insert a;
} catch (Exception e){
ApexPages.Message errorMessage = new ApexPages.Message(
ApexPages.Severity.ERROR, 'An unexpected error occured: ' + e.getMessage()
);
ApexPages.addMessage(errorMessage);
}
return null;
}
}
- You can also display
toast
, but it requires "Available for Lightning Experience, Lightning Communities, and the mobile app" onVisualforce
to be checked andlightningStylesheets
must be set as true in<apex:page>
. - Example of displaying toast in
Visualforce
:
<apex:page lightningStylesheets="true">
<script>
function showToast(){
sforce.one.showToast(
{
"title" : "Success!",
"message" : "Success message goes here",
"type" : "success"
}
);
}
</script>
<apex:form>
<apex:page>
<apex:commandButton value="Show Toast" onclick="showToast();" />
</apex:page>
</apex:form>
</apex:page>
- Examples of using
<apex:pageMessages/>
inVisualforce
page:
<apex:page controller="SampleController">
<apex:form>
<apex:pageMessages />
<apex:pageBlock title="Hello {!$User.FirstName}">
Account Name: <apex:inputField value="{!acc.name}" /><p/>
<apex:commandButton action="{!save}" value="Save" /><p/>
</apex:pageBlock>
</apex:form>
</apex:page>
- You can also display error message by throwing
System.AuraHandledException
from server-side controller. - NOTE: you can use
console.error()
instead ofconsole.log()
to find message using browser's developer tools. - Example of using
AuraHandledException
:
public class SampleController2{
static final String errorInput = 'test';
@AuraEnabled
public static String throwError(String accName){
if(accName.containsIgnoreCase(errorInput){
throw new AuraHandledException('Error Input!');
}
return('Account name ' + accName + ' is valid!');
}
}
Javascript
can be used to handle exception as well:
<script type="text/javascript">
function getRemoteAccount(){
var accName = document.getElementById('accSearch').value;
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.AccountRemoteController.getAccount}',
accName,
function(result, event){
if(event.status){
document.getElementById('accId').innerHTML = result.Id;
document.getElementById('accName').innerHTML = result.Name;
} else if(event.type === 'exception'){
document.getElementById('responseErrors').innerHTML = event.message + '<br/>n<pre>' + event.where + '</pre>';
} else {
document.getElementById('responseErrors').innerHTML = event.message;
}
}
);
}
</script>
Visualforce Code Reuse
- Code can be reused in
Visualforce
in the following methods:- Custom Components (most flexible to modify code)
- Templates
- Page Includes (least flexbile since it includes an entire
Visualforce
page) - Static Resources
Visualforce Components
Visualforce Components
is referenced by API name and can be defined using<apex:component>
top-level tag.- Custom attributes can be added to a component which can be used by the component or its controller.
- NOTE: custom attributes cannot be used in component's constructor method.
<apex:attribute>
tag can have the following attributes:- name (required) - the required identifier by which calling pages reference the custom attribute (name must be unique from all other attributes in the component definition)
- description - a text description of the attribute
- type (required) - the required data type of the attribute (can be primitive, sObject, list, map, or custom
Apex
class)- Primitives - String, Integer, Boolean
- sObjects - Account, Custom_Object__c
- List (1-dimension array notation) - String[], Contact[]
- Map - specify type = "map" will do
- Apex Class
- required - indicate whether this component must set a value for the attribute or not
- id - the identifier that allows the component to be referenced by other components in the page
- rendered (default to true) - indicate whether this component should be rendered in the page
- Example of custom components:
<apex:component>
<apex:attribute name="name" description="Name" type="String" required="false" />
<apex:attribute name="age" description="Age" type="String" required="true" />
<p>Name: {!name}</p>
<p>Age: {!age}</p>
</apex:component>
- To use
Visualforce components
, you can include<c:componentName var1="test" var2="hello world">
or<customNamespace:componentName>
if the component was created in different namespace from theVisualforce
page that's using it - Custom components can affect the way markup is displayed on the final page based on the values provided for the component's attributes.
- Custom component descriptions display in the same way as standard component descriptions in the application’s component reference dialog.
Templates
Templates
are used to create a page structure where portions of it can be overwritten by the calling page.Templates
are mostly useful for overall structure to a page to be maintained but need the content of each page to be different, such as using same page layout for different articles.- A skeleton template is created which allows subsequent
Visualforce
page to implement different content within the same structure. - Following key tags are used when implementing templates:
<apex:composition>
- section of a page which contains one or more<apex:define>
tags<apex:define>
- used within<apex:composition>
to define the content that needs to be generated in a corresponding<apex:insert>
tag<apex:insert>
- indicates to pages that import a template that a section needs a definition
- Example of using
<apex:insert>
in aTemplate
:
<!-- template API name is Testing -->
<apex:page>
<h1>Header</h1>
<!-- calling form must have a <apex:define> section named "testingList" -->
<apex:insert name="testingList" />
</apex:page>
<!-- using template -->
<apex:page>
<apex:composition template="Testing">
<apex:define name="testingList">
<ul>
<li>Test 1</li>
<li>Test 2</li>
<li>Test 3</li>
</ul>
</apex:define name>
</apex:composition>
</apex:page>
Templates
cannot affect the way markup is displayed on the final page, because it doesn't have a way of passing information back to the template definition. They can only provide content to include in the<apex:insert>
areas.Templates
description can only be referenced inSetup
.
Page Includes
<apex:include>
tag can be used to duplicate the entire content of another page.- NOTE: If the page that uses
<apex:include>
tag and the page use the same controller, both page will use the same instance of the controller. - Example of using
<apex:include>
:
<apex:page controller="TestController">
<apex:include pageName="TestTemplate" />
<apex:pageBlock>
<apex:pageBlockSection>
<h1>Hello, world!</h1>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:page>
Static Resources
Javascript
andCSS
files can be uploaded to Static Resource and include the code directly.<apex:includeScript value="{!$Resource.JavascriptFileName}" />
<apex:stylesheet value="{!$Resource.CSSFileName}" />
Javascript and Visualforce
Javascript
allows implementing client-side logic and making use of asynchronous calls for processing.- 4 main ways of using
Javascript
withVisualforce
:- Client-side processing and validation (
Javascript
)- HTML elements generated by
Visualforce
can be passed toJavascript
by using$Component
global variable - The DOM (document object model) Id generated for a
Visualforce
component is used for reference. - The value of 'id' attribute must be specified to refer to a
Visualforce
component inJavascript
, ex:{!$Component.myComponentId}
- HTML elements generated by
- 3rd party
Javascript
libraries such asjQuery
<apex:includeScript>
can be used to reference the 3rd party libraries or resources (either a single file or an archive).- Reference a single file:
<apex:includeScript value="{!$Resource.singleFile}" />
- Reference a file inside an archive:
<apex:includeScript value="{!URLFOR($Resource.fileArchive, '/sub1/sub2/test.js')}" />
- Reference a single file:
<script>
can be included in a page to callJavascript
functions.
- Javascript Remoting
Javascript remoting
is used to call methods inApex Controller
asynchronously fromJavascript
.- Create UI with complex, dynamic behavior that process input faster than traditional
Visualforce
way because it is stateless. - Performs more complex logic or to traverse object relationships
- Each method call is a single transaction.
- It supports parameters and return types in
Apex
method and automatically maps betweenApex
andJavascript
types. - Setup remoting requires the following:
- A
Javascript
method to invokeApex
method viaVisualforce.remoting.Manger.invokeAction()
- A method in Apex controller annotated with
@remoteAction
which will be called byJavascript
- A callback
Javascript
function that handles the response fromApex Controller
- A
Javascript
remoting invocation inVisualforce
page looks like this:[namespace.]controller.method([parameters...,] callbackFunction, [configuration]);
- Remote Objects
Visualforce Remote Objects
are proxy objects that enable basic DML operations (CRUD
) onsObjects
directly fromJavascript
withoutApex
code while respecting FLS, sharing rules, and other data accessibility concerns and also enforcing data access restriction such as validation rules, triggers and etc.- NOTE: each
CRUD
operates as separate transaction, which might cause inconsistent data state as some transactions can fail while others succeed. - Steps to use
Remote Objects
:- Access definitions on
Visualforce
page using following components:<apex:remoteObjectField>
which defines fields within<apex:remoteObjectModel>
that can be accessed<apex:remoteObjectField>
which defines fields within<apex:remoteObjectModel>
that can be accessed
Javascipt
data access function:SObjectModel
is used to instantiate the desired object- Once instantiated,
CRUD
operations can be used:create()
retrieve()
update()
upsert()
del()
- Access definitions on
- Client-side processing and validation (
- Comparison between
Javascript Remoting
andRemote Objects
:
Javascript Remoting | Remote Objects |
---|---|
Each call to a @remoteAction is a single transaction | Each CRUD operation in a single transaction |
Requires Javascript and Apex code | Doesn't require Apex code |
Handles complex object relationships better | No auto-traversal through object relationship |
Supports complex server-side logic | Supports minimal server-side logic |
Lightning Components
Lightning Components
are self-contained, reusable units of an app which can be placed in a container app alongside other components. In fact, it is a collection of resources called a bundle which all work together to encapsulate the functionality of the component.Lightning Data Service (LDS)
can be used if needed for record processing (CRUD), remember to include<force:recordData>
tag in the component markup.
Form function | Tag |
---|---|
Display, create, or edit records | <lightning:recordForm/> |
Display records only | <lightning:recordViewForm/> (with <lightning:outputField/>) |
Create or edit records only | <lightning:recordEditForm/> (with <lightning:inputField/>) |
Display, create, or edit, or delete records with granular customization | <force:recordData/> |
- For most use cases,
<lightning:recordForm />
provides a great starting point. It combines and simplifies the functionality of<lightning:recordViewForm />
and<lightning:recordEditForm />
. - Example of
<lightning:recordForm />
with accessing record Id viaforce:hasRecordId
interface:
<!-- example of recordForm -->
<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId">
<aura:attribute name="recordId" type="String" />
<lightning:card title="Display, Create, or Edit Records">
<lightning:recordForm recordId="{!v.recordId}"
objectApiName="Account"
fields="Name" />
</lightning:card>
</aura:component>
- More advanced use cases that require custom field layouts using CSS and custom rendering of record data, use
<lightning:recordViewForm />
and<lightning:recordEditForm />
. <lightning:messages />
can be used to display any errors during record update.
<!-- example of recordViewForm and recordEditForm -->
<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId">
<lightning:card title="Display, Create, or Edit Records">
<lightning:recordEditForm recordId="{!v.recordId}"
objectApiName="Account">
<lightning:messages />
<lightning:inputField fieldName="Name" />
<lightning:button class="slds-m-top_small" type="submit" label="Create new" />
</lightning:recordEditForm>
<lightning:recordViewForm recordId="{!v.recordId}" objectApiName="Account">
<lightning:messages />
<lightning:outputField fieldName="Name" />
</lightning:recordViewForm>
</lightning:card>
</aura:component>
<force:recordData>
doesn't include any UI elements, which makes it good way to communicate to server.- To load a record on client side, you can add
<force:recordData />
tag to your component and setrecordId
,mode
,layoutType
orfields
attributes.recordId
- specific the record to loadmode
- can be set toEDIT
orVIEW
which determines the behavior of notifications and what operations are available to perform with the record.layoutType
- specific the layoutFULL
orCOMPACT
to display the record which determines what fields are included.fields
- specify which fields in the record to query.
<force:recordData />
also supports a set oftarget
attributes:targetRecord
- populated with loaded recordtargetFields
- populated with simplified view of loaded recordtargetError
- populated with any errors
<!-- aura:id is required to reference the component in your Javascript controller -->
<force:recordData aura:id="forceRecordCmp"
recordId="{!v.recordId}"
layoutType="{!v.layout}"
fields="{!v.fieldsToQuery}"
mode="VIEW"
targetRecord="{!v.record}"
targetFields="{!v.simpleRecord}"
targetError="{!v.error}"
/>
- In this example,
<lightning:formattedtext />
displays the Name field from the record loaded by<force:recordData />
.
<aura:component>
<aura:attribute name="recordId" type="String" />
<aura:attribute name="record" type="Object" />
<aura:attribute name="simpleRecord" type="Object" />
<force:recordData recordId="{!v.recordId}"
targetRecord ="{!v.record}"
targetFields ="{!v.simpleRecord}"
fields="Id, Name" />
<div class="recordName">
<p class="slds-text-heading--medium">
<lightning:formattedtext title="Record Name" value="{!v.simpleRecord.Name}" /></p>
</div>
</aura:component>
-
Several
Aura
methods to modify records are availables usingJavascript
component controller:saveRecord()
- insert or update record loaded into componentdeleteRecord()
- delete loaded recordgetNewRecord()
- load new record template that performs an insert when savedreloadRecord()
- rerun the loading code to overwrite the currenttargetRecord
with the current attribute values.
-
LDS
is available inLightning Experience
andSalesforce App
only, it can't be used inLightning Components
forVisualforce
,Lightning Out
, orCommunity
. -
All components using a record share the same cached data, hence performance is improved and any changes made to such a record from the user interface will generate a refresh in affected components.
-
NOTE:
LDS
handles sharing rules and FLS automatically, it operates on one record at a time and it is only loaded once. -
NOTE: My Domain needs to be configured and deployed in order to use
Lightning Component
. -
A controller or helper can be defined for client-side processing whereas
Apex
controller can be defined for server-side processing. -
NOTE: if to expose
Apex
controller method to client-sideJavascript
, annotation@AuraEnabled
can be specified on the controller method inApex
, it must be static and either global or public. -
Design
file from the bundle can be used to define which attributes of the component can be customized. -
Lightning component
bundle resources included:- Component file (
TestCmp.cmp
) - the only required resource in a component bundle that defines the markup for the component - Controller (
TestCmpController.js
) - client-side controller contains methods for event handling - Helper (
TestCmpHelper.js
) - containJavascript
function that are used by other scripts in the bundle - Stylesheet (
TestCmp.css
) - custom stylesheet for the component - Design (
TestCmp.design
) - used to define which attributes to be exposed, such as inLightning App Builder
- Documentation (
TestCmp.auradoc
) - formatted documentation to assist admins and devs - Renderer (
TestCmpRenderer.js
) - used to override default rendering behavior - SVG file (
TestCmp.svg
) - SVG (Scalable Vector Graphics) file is used to define a custom icon for the component that appears next to the component name inLightning App Builder
's component pane.
- Component file (
-
Lightning component
must implement one or more of the following interfaces to make it available inLightning App Builder
:flexipage:availableForAllPageTypes
- make the component available in any type of page, including record page and the app utility barflexipage:availableForRecordHome
- make the component available for record page onlyclients:availableForMailAppAppPage
- make the component available forMail App Lightning
page inLightning App Builder
and inLightning for Outlook
orLightning for Gmail
.
-
Components attributes are typed fields set on a specific instance of a component which allow making components more dynamic.
-
<aura:attribute>
tag can be used to add attribute to a component or an app, usually define at the beginning of component's markup. -
Example:
<aura:attribute name="greeting" type="String" default="Hello!" />
-
Attributes can be referenced from within component's markup using an expression syntax that uses the value provider 'v', example:
{!v.greeting}
-
Example of
Lightning Component
:
<!-- TestCmp.cmp -->
<aura:component implements="flexipage:availableForAllPageTypes" controller="SampleController">
<aura:attribute name="greeting" type="String" default="Greeting!" />
<aura:attribute name="hello" type="String" default="Hello!" />
<p>{!v.greeting}</p>
<lightning:button label="Click me..." onclick="{!c.actionHandler}" />
</aura:component>
// TestCmpController.js
({
actionHandler : function(component, event, helper){
var msg = component.get('v.hello');
component.set('v.greeting', msg);
}
})
-
Lightning component framework
is all about event-driven programming, where events are fired from client-side controller and handlers are written to respond to interface events as they occur. -
Events
are used to communicate data between components, declared by<aura:event>
tag with.evt
as extension. -
Actions can be called from
Lightning Component
, such as<lightning:button>
-
Each action function has 3 parameters:
cmp
- the component of the controllerevent
- event being handled by the actionhelper
- optional helper containing functions that can be reused
-
Example of making asynchronous calls to the server via
Javascript
and receive callback from server:
// Apex controller
public class ApexController{
@AuraEnabled
public static List<Account> getAccounts(String filter){
List<Account> accList;
// some condition on retrieving accounts
return accList;
}
}
// javascript controller
({
onGetAccounts: function(component, event, helper){
// load the method from Apex controller
var action = component.get('c.getAccounts');
// value to pass to Apex method
var filter = component.get('v.filter');
// set the Apex method parameter
action.setParams({
'filter' : filter
});
// receive callback from Apex controller
action.setCallback(this, function(response){
var state = response.getState();
if(state === 'SUCCESS'){
var accounts = response.getReturnValue();
components.set('v.accounts', accounts);
}
});
// enqueue the action
$A.enqueueAction(action);
}
})
- Two types of events:
-
Component Events - an event that is fired from an instance of component
- Events can be handled by component that fired the event or component that receives the event and it can be stopped by any registered handler.
- Event is using Capture and Bubbling mechanism (like
DOM
events) - Event handling steps:
- Event is fired up
- Event is captured down and propagates down from application root to source component
- Event is bubbled up and propagates from source component to application root.
-
Application Events - an event that is fired from an instance of component, but following a traditional publisher/subscriber model.
- All components that provide a handler for the event are notified.
event.stopPropagation()
can be used to stop event by any registered handlerevent.preventDefault()
can be used to cancel event by any registered handler (default behavior)- In default phase, event handlers are invoked in a non-deterministic way from root node through its subtree.
-
<!-- create component event -->
<aura:event type="COMPONENT">
<aura:attribute name="message" type="String" />
</aura:event>
...
<!-- register event -->
<aura:registerEvent name="testingEvent" type="c.TestEvent" />
...
<!-- fire the event -->
({
myAction : function(component, event, helper){
var compEvent = component.get('e.testingEvent');
// var compEvent = component.getEvent('testingEvent');
compEvent.setParam('message', 'hello world');
compEvent.fire();
}
})
...
<!-- handle the fired event from own or other component -->
<!-- handler name must be the same as registered event name -->
<aura:handler name="testingEvent" event="c:TestEvent" action="{!c.handleTestingEvent}" />
...
<!-- get event parameter -->
({
handleTestingEvent : function(component, event, helper){
var compEvent = event.getParam('message');
}
})
<!-- create application event -->
<aura:event type="APPLICATION">
<aura:attribute name="message" type="String" />
</aura:event>
...
<!-- register event -->
<aura:registerEvent name="testingEvent" type="c.TestEvent" />
...
<!-- fire the event -->
({
myAction : function(component, event, helper){
var appEvent = $A.get('e.c:TestEvent');
appEvent.setParams({
"message" : "An application event fired me. " +
"It all happened so fast. Now, I'm everywhere!"
});
appEvent.fire();
}
})
...
<!-- handle the fired event from own or other component -->
<!-- no need to specify handler name -->
<aura:handler event="c:TestEvent" action="{!c.handleTestingEvent}" />
...
<!-- get event parameter -->
({
handleTestingEvent : function(component, event, helper){
var compEvent = event.getParam('message');
}
})
-
NOTE: for all, double quote (") is more appreciated in
Lightning Component
, though using single or double quote should be fine. -
Component creation lifecycle:
- Instantiate app
- Create components
- Create component definition
- Create parent hierarchy
- Create component facets
- Create component dependencies (attributes, interfaces, controllers and actions)
-
Component rendering lifecycle:
- Fire Init event
- Call
Render()
- Call
AfterRender()
- Fire
<aura:doneWaiting>
event - Fire render event
- Fire
<aura:doneRendering>
event
-
Custom validation
can be added toLightning Flow
. -
The validation function runs when user clicks 'Next' or when a component runs the navigate function. It must return two parameters: 'isValid' and 'errorMessage' if 'isValid' is false, the value of 'errorMessage' will be displayed.
<!-- validate support in Lightning Component -->
<aura:component implements="lightning:availableForFlowScreens" access="global">
<!-- call init handler when rendering components -->
<aura:handler name="init" value="{!this}" action="{!c.init}" />
<!-- validation attribute to store validation logic -->
<aura:attribute name="validate" type="Aura.Action" />
</aura:component>
// validation function in component controller
({
init : function(cmp, event, helper){
cmp.set('v.validate', function(){
if(/* true condition */){
return { isValid : true};
} else {
return { isValid : false, errorMessage : 'hello world'};
}
}
}
})
$ContentAsset
global value provider can be used to reference images, stylesheets, and javascript files inLightning Component
. It allows developer to load an asset by name instead of using file paths or url.- Example:
<img src="{! $ContentAsset.logo}" />
Well, this is such a long chapter too. The content will be updated as time goes by. See you!
Post was published on , last updated on .
Like the content? Support the author by paypal.me!