SObject Lookup – Lightning Component

Introduction

In salesforce lightning we don’t have any lookup field component like salesforce classic has <apex:inputfield>, which render as lookup for lookup and master detail relationships and also take care of , how to display lookup dialog or lookup result.

However , there is a force:inputfield tag in salesforce lightning, But lookup field is not yet supported by this tag.

This reusable lookup component will allow user to add lookup field in lightning component UI, as they do in visualforce.

Benefits

  • Single Lookup components can be used for any SObject lookup present in our org without changing any major code.
  • Its look & feel is exactly same as SLDS, which is device friendly

Pre-requisite:

Upload the above downloaded zip into the static resource.

Here in my code base I have used static resource Name as “SLDS_STATIC_RESOURCE”. You can replace this name with your actual static resource name.

Reusable Lightning Component Bundle

  1. svg Component:

 The Lightning Components framework doesn’t directly support SVG icons tag at this point.

So we need to build a component called “svg” which will generated SVG tag while loading in component.

This component is being used in “LightningSObjectLookup” reusable component to show the svg icon.

This component has been designed by taking help from below trailhead module.

https://developer.salesforce.com/trailhead/project/slds-lightning-components-workshop/slds-lc-4

Component Source Code

<aura:component >
<aura:attribute name="class" type="String" description="CSS classname for the SVG element" />
<aura:attribute name="xlinkHref" type="String" description="SLDS icon path. Ex: /assets/icons/utility-sprite/svg/symbols.svg#download" />
<aura:attribute name="ariaHidden" type="String" default="true" description="aria-hidden true or false. defaults to true" />
</aura:component>

Renderer Source Code


({
 render: function(component, helper) {
 //grab attributes from the component markup
 var classname = component.get("v.class");
 var xlinkhref = component.get("v.xlinkHref");
 var ariaHidden = component.get("v.ariaHidden");

 //return an svg element w/ the attributes
 var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
 svg.setAttribute('class', classname);
 svg.setAttribute('aria-hidden', ariaHidden);
 svg.innerHTML = '&amp;amp;amp;amp;lt;use xlink:href="'+xlinkhref+'"&amp;amp;amp;amp;gt;&amp;amp;amp;amp;lt;/use&amp;amp;amp;amp;gt;';
 return svg;
 }
})

2. lightningLookup Component:

This is the main reusable component. This component contains following attributes

  • sObjectAPIName : The API name of the SObject to search
  • label : The label to be displayed as lookup field Label, eg: Account
  • pluralLabel : The plural label to be displayed in Lookup Search result header, eg: <searchString> in Accounts”
  • listIconSVGPath : The static resource path to the svg icon to use.
  • listIconClass : The SLDS class to use for the icon.
  • searchString: The search string to find. It will be inputted by user
  • recordId: selected sObject record Id
  • matchingResult: The resulting matches returned by the Apex controller.

Controller Name : LookupSObjectController.

There is one event registered : “ClearLookupIdEvent” , which is fired when user clicks on cross icon in selection box.

This reusable lighnting component bundle have its own clientSide Controller and Helper js file.

ClearLookupId.evt Source Code

In Developer Console Goto File | New | Lightning Event


<aura:event type="COMPONENT" description="Clear the Lookup" />

LookupSObjectController.cls Class Source Code


/**
* Apex Controller for fetching the records as per search String inputted by user
*/
public with sharing class LookupSObjectController
{
/**
* Aura enabled method to search a specified SObject for a specific string
*/
@AuraEnabled
public static SearchResult[] lookup(String searchString, String sObjectAPIName)
{
// escaped the input String
String escapedSearchString = String.escapeSingleQuotes(searchString);
String escapedSObjectAPIName = String.escapeSingleQuotes(sObjectAPIName);

List<SearchResult> results = new List<SearchResult>();

// Build our SOQL query
String searchQuery = 'SELECT Id, Name FROM ' + escapedSObjectAPIName + ' WHERE Name LIKE ' + '\'%' + escapedSearchString + '%\'' + ' Limit 50';
List<SObject> searchList = DataBase.query(searchQuery);
// Create a list of matches to return
for (SObject so : searchList)
{
results.add(new SearchResult((String)so.get('Name'), so.Id));
}

return results;
}

/**
* Inner class to wrap up an SObject Label and its Id
*/
public class SearchResult
{
@AuraEnabled public String SObjectLabel {get; set;}
@AuraEnabled public Id SObjectId {get; set;}

public SearchResult(String sObjectLabel, Id sObjectId)
{
this.SObjectLabel = sObjectLabel;
this.SObjectId = sObjectId;
}
}
}

lightningLookup.cmp Source Code


<aura:component controller="LookupSObjectController" >
<!-- Required Scripts/Styles -->
<!-- Salesforce Lightning Design System : https://www.lightningdesignsystem.com/ -->
<ltng:require styles="/resource/SLDS_STATIC_RESOURCE/assets/styles/salesforce-lightning-design-system-ltng.css" />

<!-- Attributes -->
<aura:attribute name="sObjectAPIName" type="String" required="true"
description="The API name of the SObject to search" />
<aura:attribute name="label" type="String" required="true"
description="The label to be displayed as field Label, eg: Account" />
<aura:attribute name="pluralLabel" type="String" required="true"
description="The plural label to be displayed in Lookup Search result header, eg: xxx in Accounts" />
<aura:attribute name="listIconSVGPath" type="String" default="/resource/SLDS_STATIC_RESOURCE/assets/icons/custom-sprite/svg/symbols.svg#custom11"
description="The static resource path to the svg icon to use." />
<aura:attribute name="listIconClass" type="String" default="slds-icon-custom-11"
description="The SLDS class to use for the icon." />
<aura:attribute name="searchString" type="String"
description="The search string to find." />
<aura:attribute name="matchingResult" type="LookupSObjectController.SearchResult[]"
description="The resulting matches returned by the Apex controller." />
<aura:attribute name="recordId" type="Id"
description="selected soBject record Id" />
<!-- Events -->

<aura:registerEvent name="clearLookupIdEvent" type="c:ClearLookupId"/>

<!-- Lookup Markup : See https://www.lightningdesignsystem.com/components/lookups -->
<div class="slds">
<div aura:id="lookup-div" class="slds-lookup" data-select="single" data-scope="single" data-typeahead="true">
<!-- This is the Input form markup -->
<div class="slds-form-element">
<label class="slds-form-element__label" for="lookup">{!v.label}</label>
<div class="slds-form-element__control slds-input-has-icon slds-input-has-icon--right">
<c:svg class="slds-input__icon" xlinkHref="/resource/SLDS_STATIC_RESOURCE/assets/icons/utility-sprite/svg/symbols.svg#search" />
<!-- This markup is for when an item is currently selected -->
<div aura:id="lookup-pill" class="slds-pill-container slds-hide">
<span class="slds-pill slds-pill--bare">
<span class="slds-pill__label">
<c:svg class="{!'slds-icon ' + v.listIconClass + ' slds-icon--small'}" xlinkHref="{!v.listIconSVGPath}" />{!v.searchString}
</span>
<button class="slds-button slds-button--icon-bare" onclick="{!c.clear}">
<c:svg class="slds-button__icon" xlinkHref="/resource/SLDS_STATIC_RESOURCE/assets/icons/utility-sprite/svg/symbols.svg#close" />
<span class="slds-assistive-text">Remove</span>
</button>
</span>
</div>
<!-- This markup is for when searching for a string -->
<ui:inputText aura:id="lookup" value="{!v.searchString}" class="slds-input" updateOn="keyup" keyup="{!c.search}" />

</div>
</div>
<!-- This is the lookup list markup. Initially it's hidden -->
<div aura:id="lookuplist" class="slds-lookup__menu slds-hide" role="listbox">
<div class="slds-lookup__item">
<button class="slds-button">
<c:svg class="slds-icon slds-icon-text-default slds-icon--small" xlinkHref="/resource/SLDS_STATIC_RESOURCE/assets/icons/utility-sprite/svg/symbols.svg#search" />
&quot;{!v.searchString}&quot; in {!v.pluralLabel}
</button>
</div>
<ul aura:id="lookuplist-items" class="slds-lookup__list" role="presentation">
<aura:iteration items="{!v.matchingResult}" var="match">
	<li class="slds-lookup__item">
<a id="{!globalId + '_id_' + match.SObjectId}" href="javascript:void(0);" role="option" onclick="{!c.select}">
<c:svg class="{!'slds-icon ' + v.listIconClass + ' slds-icon--small'}" xlinkHref="{!v.listIconSVGPath}" />{!match.SObjectLabel}
</a></li>
</aura:iteration></ul>
</div>
</div>
</div>
</aura:component>

lightningLookupController.js Controller Source Code


({
 /**
 * Search an SObject for a match
 */
 search : function(cmp, event, helper) {
 console.log('--Inside Search method of Controller---' + event.getSource());
 helper.doSearch(cmp); 
 },

 /**
 * Select an SObject from a list
 */
 select: function(cmp, event, helper) {
 console.log('--Inside selection method of Controller---' + event.currentTarget.id);
 helper.doSelection(cmp, event);
 },

 /**
 * Clear the currently selected SObject
 */
 clear: function(cmp, event, helper) {
 helper.clearSelection(cmp); 
 }
})

 

lightningLookupHelper.js Helper Source Code


({
 /**
 * Search SObject
 */
 doSearch : function(cmp) {
 console.log('--Inside doSearch method---');
 // Get the search string, input element and the selection container
 var searchString = cmp.get("v.searchString");
 var inputElement = cmp.find('lookup');
 var lookupList = cmp.find("lookuplist");
 var lookupListItems = cmp.find("lookuplist-items");

 // Clear any errors 
 inputElement.set('v.errors', null);

 // min 2 characters is required for an effective search
 if (typeof searchString === 'undefined' || searchString.length &amp;lt; 2)
 {
 // Hide the lookuplist
 $A.util.addClass(lookupList, 'slds-hide');
 return;
 }

 // Show the lookuplist
 $A.util.removeClass(lookupList, 'slds-hide');

 // Get the API Name
 var sObjectAPIName = cmp.get('v.sObjectAPIName');

 // Create an Apex action
 var action = cmp.get("c.lookup");

 // Mark the action as abortable, this is to prevent multiple events from the keyup executing
 action.setAbortable();

 // Set the parameters
 action.setParams({ "searchString" : searchString, "sObjectAPIName" : sObjectAPIName});

 // Define the callback function
 action.setCallback(this, function(response) {
 var state = response.getState();

 // Callback succeeded
 if (cmp.isValid() &amp;amp;&amp;amp; state === "SUCCESS")
 {
 // Get the search matches
 var matchingResult = response.getReturnValue();

 // If we have no matches, return
 if (matchingResult.length == 0)
 {
 cmp.set('v.matchingResult', null);
 return;
 }

 // Set the results in matchingResult attribute of component
 console.log('--Result found--'+matchingResult);
 cmp.set('v.matchingResult', matchingResult);
 console.log('--Result found2--'+cmp.get('v.matchingResult'));
 }
 else if (state === "ERROR") // Handle any error 
 {
 var errors = response.getError();

 if (errors) 
 {
 if (errors[0] &amp;amp;&amp;amp; errors[0].message) 
 {
 this.displayErrorDialog('Error', errors[0].message);
 }
 }
 else
 {
 this.displayErrorDialog('Error', 'Unknown error.');
 }
 }
 });

 // Enqueue the action 
 $A.enqueueAction(action); 
 },

 /**
 * Handle the Selection of an searched Item
 */
 doSelection : function(cmp, event) {
 // Resolve the Object Id from the events Element Id (this will be the &amp;lt;a&amp;gt; tag)
 var recId = this.getSelectedRecordId(event.currentTarget.id);
 // The Object label is the 2nd child (index 1)
 var recLabel = event.currentTarget.innerText;
 // Log the Object Id and Label to the console
 console.log('recId=' + recId);
 console.log('recLabel=' + recLabel);
 //set the selected record Id
 cmp.set('v.recordId',recId); 

 // Update the Searchstring with the Label
 cmp.set("v.searchString", recLabel);

 // Hide the Lookup List
 var lookupList = cmp.find("lookuplist");
 $A.util.addClass(lookupList, 'slds-hide');

 // Hide the Input Element
 var inputElement = cmp.find('lookup');
 $A.util.addClass(inputElement, 'slds-hide');

 // Show the Lookup pill
 var lookupPill = cmp.find("lookup-pill");
 $A.util.removeClass(lookupPill, 'slds-hide');

 // Lookup Div has selection
 var inputElement = cmp.find('lookup-div');
 $A.util.addClass(inputElement, 'slds-has-selection');

 },

 /**
 * Clear the Selection
 */
 clearSelection : function(cmp) {
 // Create the ClearLookupId event
 var clearEvent = cmp.getEvent("clearLookupIdEvent");

 // Fire the event
 clearEvent.fire();

 // Clear the Searchstring
 cmp.set("v.searchString", '');
 cmp.set('v.recordId', null);
 // Hide the Lookup pill
 var lookupPill = cmp.find("lookup-pill");
 $A.util.addClass(lookupPill, 'slds-hide');

 // Show the Input Element
 var inputElement = cmp.find('lookup');
 $A.util.removeClass(inputElement, 'slds-hide');

 // Lookup Div has no selection
 var inputElement = cmp.find('lookup-div');
 $A.util.removeClass(inputElement, 'slds-has-selection');
 },

 /**
 * Resolve the Object Id from the Element Id by splitting the id at the _
 */
 getSelectedRecordId : function(recId)
 {
 var i = recId.lastIndexOf('_');
 return recId.substr(i+1);
 },

 /**
 * Display a message
 */
 displayErrorDialog : function (title, message) 
 {
 var toast = $A.get("e.force:showToast");

 // For lightning1 show the toast
 if (toast)
 {
 //fire the toast event in Salesforce1
 toast.setParams({
 "title": title,
 "message": message
 });

 toast.fire();
 }
 else // otherwise throw an alert
 {
 alert(title + ': ' + message);
 }
 }
})

 

That’s all! Now our component is ready for use.

Here I’m explaining how to use the above reusable lookup component.

Example: How to use above reusable Lightning Bundle

Here , you can find “AccountLookup” and “lightningLookupApp” bundle.

  1. AccountLookUp Component Bundle:

 In this component , a form is designed to create contact having an account lookup selection box, which uses above “lightningLookup” component

There is an attribute “recordId” which holds selected account record Id. By using below code , we can call the above “lightningLookup” component.

AccountLookup.cmp Component Source Code

<aura:component controller="AccountLookUpController" implements="force:appHostable,flexipage:availableForAllPageTypes">
<!-- Attributes -->
<aura:attribute name="recordId" type="Id" description="The current record Id to display" />
<aura:attribute name="firstName" type="String" description="First Name of Contact" />
<aura:attribute name="lastName" type="String" description="Last Name of Contact" />
<aura:attribute name="contId" type="Id" description="ID of Created Contact" />
<!-- Event handlers -->
<!--aura:handler name="updateLookupIdEvent" event="c:UpdateLookupId" action="{!c.handleAccountIdUpdate}"/-->
<aura:handler name="clearLookupIdEvent" event="c:ClearLookupId" action="{!c.handleAccountIdClear}"/>
<div class="slds">
<!-- Lookup component -->
<c:lightningLookup label="Account" pluralLabel="Accounts" sObjectAPIName="Account"
listIconSVGPath="/resource/SLDS_STATIC_RESOURCE/assets/icons/standard-sprite/svg/symbols.svg#account"
listIconClass="slds-icon-standard-account"
recordId="{!v.recordId}"
/>
<div class="slds-form-element">
<label class="slds-form-element__label" for="fName">First Name</label>
<div class="slds-form-element__control">
<ui:inputText aura:id="fName" class="slds-input" value="{!v.firstName}"/>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="lName">Last Name</label>
<div class="slds-form-element__control ">
<ui:inputText aura:id="lName" class="slds-input" value="{!v.lastName}"/>
</div>
</div>
<ui:button aura:id="submitButton" label="Submit" press="{!c.saveContact}"/>


<aura:if isTrue="{!v.contId!=null}">
<SPAN>
Created Record Id : {!v.contId}
<force:recordView recordId="{!v.contId}" />
</SPAN>
</aura:if>
</div>
</aura:component>

AccountLookupController.js Controller Source Code


({

 /**
 * receiving the clearLookupIdEvent event
 */
 handleAccountIdClear : function(cmp, event, helper) {
 // Clear the Id bound to the View
 cmp.set('v.recordId', null);
 cmp.set('v.contId', null);
 },

 saveContact : function(cmp,event,helper){

 helper.saveContactRecord(cmp, event);
 }
})

 

AccountLookupHelper.js Helper Source Code

({
 saveContactRecord : function(cmp,event) {
 var fName = cmp.get("v.firstName");
 var lName = cmp.get("v.lastName");
 var accId = cmp.get("v.recordId");
 console.log('---selected Account RecordId--'+accId);
 // Create an Apex action
 var createAction = cmp.get("c.createRecord");
 // Set the parameters
 createAction.setParams({ "fName" : fName, "lName" : lName, "accountId" : accId});
 // Define the callback
 createAction.setCallback(this, function(response) {
 var state = response.getState();

 // Callback succeeded
 if (cmp.isValid() &amp;amp;&amp;amp; state === "SUCCESS")
 {
 console.log('Contaxt Record Created-Successfully-'+response.getReturnValue());
 cmp.set('v.contId', response.getReturnValue());
 }
 else{
 console.log('Contaxt Record Created Failure--');
 cmp.set('v.contId', null);
 }
 });

 // Enqueue the action 
 $A.enqueueAction(createAction); 

 }
})

 

Component’s Apex Controller –  AccountLookupController.cls Source Code


public class AccountLookUpController {

/**
 * Aura enabled method to search a specified SObject for a specific string
 */
 @AuraEnabled
 public static Id createRecord(String fName, String lName, ID accountId )
 {
 try{
 Contact con = new Contact();
 con.FirstName = fName;
 con.LastName= lName;
 con.accountId = accountId;
 Insert con;
 return con.Id;
 }catch(Exception e){
 return null;
 }
 }
}

2. lightningLookupApp.app Component Bundle:

This app only use the above component by using below code base

<aura:application >
<c:AccountLookup />
</aura:application>

Now click on “Preview” button of this app  or access the below Url to show the output.

https://<mydomainInstance>.lightning.force.com/c/lightningLookupApp.app

 

 

Testing Future Methods in Test Class

To test methods defined with the future annotation, call the class containing the method in a startTest(), stopTest()code block. All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously.

For our example, please take a look at below test class.
@isTest
private class FutureApexTest {

private static testMethod void callFutureMethod() {

Test.startTest();

// Call your @future method here.
FutureApex.doCallFuture();

// Now that we've queued up the @future method to be executed, we
// need to call Test.stopTest(). When Test.stopTest() runs, all
// queued and scheduled Apex that would occur in the future is actually
// executed immediately at this time.

Test.stopTest();

// Validate the expected results.
System.assertEquals(expectedValue, actualValue);

}
}

Future Method Performance Best Practices

Salesforce uses a queue-based framework to handle asynchronous processes from such sources as future methods and batch Apex. This queue is used to balance request workload across organizations. Use the following best practices to ensure your organization is efficiently using the queue for your asynchronous processes.

  • Avoid adding large numbers of future methods to the asynchronous queue, if possible.If more than 2,000 unprocessed requests from a single organization are in the queue, any additional requests from the same organization will be delayed while the queue handles requests from other organizations.
  • Ensure that future methods execute as fast as possible. To ensure fast execution of batch jobs, minimize Web service callout times and tune queries used in your future methods. The longer the future method executes, the more likely other queued requests are delayed when there are a large number of requests in the queue.
  • Test your future methods at scale. Where possible, test using an environment that generates the maximum number of future methods you’d expect to handle. This will help determine if delays will occur.
  • Consider using batch Apex instead of future methods to process large numbers of records.

For Details, please visit

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_invoking_future_methods.htm

 

How to log status of a completed Batch Job Status

While we are processing bulk records in a batch Apex, we may need to know the status or want to keep log of completed batch.
For example – one batch job is scheduled at night 4.00 AM, and every day we need to track whether current job status, whether It was success or failure.

One way is to check the log at SetUp | Administration Setup | Monitoring | Apex jobs.

But we can get same thing in Apex , within Finish method of batch class. and then we can log the same in a Custom Object or we can send it via Email to Admin user or any other user.

please find the below sample code , which send status via Email


global void finish(Database.BatchableContext BC){
   // Get the ID of the AsyncApexJob representing this batch job
   // from Database.BatchableContext.
   // Query the AsyncApexJob object to retrieve the current job's information.
   AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed,
      TotalJobItems, CreatedBy.Email,ExtendedStatus,JobType,ApexClassId,MethodName,
      FROM AsyncApexJob WHERE Id =
      :BC.getJobId()];
   // Send an email to the Apex job's submitter notifying of job completion.
   Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
   String[] toAddresses = new String[] {a.CreatedBy.Email};
   mail.setToAddresses(toAddresses);
   mail.setSubject('Batch Class Status ' + a.Status);
   mail.setPlainTextBody
   ('The batch Apex job processed ' + a.TotalJobItems +
   ' batches with '+ a.NumberOfErrors + ' failures.'+
   '\n' + ' Apex Class:' + a.ApexClassId);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); }

Delete records from multiple objects via apex batch class

In order to delete record from multiple Objects in batch , we can go ahead with below possible options.

  1. Using Custom Iterator to process List of Ids from multiple Objects –
    For Details : https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_iterable.htm
  2. Re-run batch after processiong 9999 records – I will explain it in my next post
  3. Call batches in Chain –  On each chain we can query the object which we want to remove and then delete it.
    Here same thing has been explained. please see the below apex code.

public with sharing class MyBatch implements Database.Batchable<sObject>, Database.Stateful
{
private Integer currentBatchChain;

public MyBatch()
{
currentBatchChain = 1;
}

public MyBatch(Integer chain)
{
currentBatchChain = chain;
}

public Database.QueryLocator start(Database.BatchableContext BC)
{
String qry;

if(currentBatchChain == 1)
{
qry = 'Select Id From Object1__c';
}
else if(currentBatchChain == 2)
{
qry = 'Select Id From Object2__c';
}

return Database.getQueryLocator(qry);
}

public void execute(Database.BatchableContext BC, List<sObject> scope)
{
delete scope;
}

public void finish(Database.BatchableContext BC)
{
if(currentBatchChain == 1)
{
MyBatch mb = new MyBatch(currentBatchChain+1);
Database.executeBatch(mb, 200);
}
else if(currentBatchChain == 2)
{
//sendEmail();
}
}
}

Switch from one Tab to another in apex:Tab panel

Here is the sample code using which we can do navigate between tabs present inside apex:tabpanel.

VF Code:


<apex:page controller="TabNavigation">
<!-- Define Tab panel .css styles -->
<style>
.activeTab {background-color: #236FBD; color:white; background-image:none}
.inactiveTab { background-color: lightgrey; color:black; background-image:none}
</style>

<apex:form >
<apex:tabPanel id="theTabPanel" value="{!tabOpt}" tabClass="activeTab" inactiveTabClass="inactiveTab">
<apex:tab label="One" name="Tab1" id="Tab1"><apex:commandButton value="Go to Tab2" action="{!switch}"/></apex:tab>
<apex:tab label="Two" name="Tab2" id="Tab2"><apex:commandButton value="Go to Tab1" action="{!switch}"/></apex:tab>
</apex:tabPanel>
</apex:form>
</apex:page>

Apex Controller:


public class TabNavigation
{
public String tabOpt {get;set;}
public String amount {get;set;}
public Boolean curencyBool {get;set;}
Integer i = 1;
public TabNavigation()

{
tabOpt = 'Tab1';
}
public void switch()
{
i++;
i = (i > 2) ? 1 : i;
tabOpt = 'Tab'+i;
}
}

Topics for Object’s record

Today I saw a new improvement of Topics in spring’14. And I want to write a post for this i.e. how we can enable this or use in list view. May be you people already aware from this But I want to share the new things which I have learn today.

First of all Topics used for easy access of records or feeds from topic detail page or object’s list view. that’s interesting to add topics for the object’s record and filter the topic value in the list view as well.

How to enable Topics for objects;

This is same as we enable feed for objects. To do this you need to go Setup -> Customize -> Topic -> Topics for Objects. And in the left side you can see the object list and when you select any one of them you can see the object’s fields and you can select more than one field on which basis you can add topics.

Suppose you enable topics for lead object so you can see a link after show feed link and the link name is ‘Click to add topics’ And now you can add topics on the basis of fields which you have selected previousaly ( When you had enable topic for lead).

Topic

Topics use to provide a common theme for group of object’s record.You can take reference from

https://help.salesforce.com/htviewhelpdoc?id=basics_topics_records_overview.htm&siteLang=en_US

How to filter list view based on Topics:

Suppose you have enable topics for Account objects and I want to take the sam eexample as they used in the above link. For example, if a number of accounts are participating in a conference, you might add the topic User Conference 2014 to their records.And now you can create a new list view in account as set the filter criteria as Topics includes ‘User Conference 2014’

List View

And you can see the related record in list view of account And you can also access the records from topic detail page.

This is a brief intoduction of ‘Topics for object‘ It would be helpful if you people give your comment with some new thing about the same.

QueryMore in Ajax toolkit

Hello Everyone,

Today I worked on queryMore() to retreive next batch of object from query() call. The definition seems difficult to understand but in reality it’s not.We all know the query() call in AJAX toolkit. we use

sforce.connection.query(‘Select Id,lastname from contact’) 

So it gives the result up to 2000 records(max).Now if we want to process more than 2000 records of an object so we use QueryMore() to get the subsequent records in up to 2000-record chunks with the help of QueryLocator. 

So how it’s work – First we user query() to retrieve the records ( number of records depends on batchSize ) in the result set.We can set the batchSize as per need.Suppose I set the batchsize to 500.Then query() call retrieve the first 500 records in result set and create a server site cursor that represent in the QueryLocator Object ( Its just a server side cursor or pointer to the next set of data to return in the queryMore call ). Then the queryMore() process the subsquent records up to 500 record chunk ( because of batch size) and reset the server site cursor and generate a new queryLocator Object i.e. When queryMore() process the next 500 records then queryLocator points to the another next 500 records if we have more than 1000 records otherwise it’s return NULL.

Here is the example how we use queryMore() in AJAX Toolikit

&lt;apex:page &gt;
   &lt;apex:form&gt;
      &lt;script type=&quot;text/javascript&quot;&gt;
         var __sfdcSessionId = '{!GETSESSIONID()}';
      &lt;/script&gt;
      &lt;script src=&quot;/soap/ajax/28.0/connection.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
      &lt;script src=&quot;/soap/ajax/28.0/apex.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
      &lt;script type=&quot;text/javascript&quot;&gt;
        function processLead() {
           //Set the batch size according to the requirement 
            sforce.connection.batchSize = 200;
            
            var resultSet = sforce.connection.query(&quot;select id,lastName from Lead where 
                                                   LeadSource ='Web'&quot;);
            var queryMore = true;
            
            while (queryMore) {
               var leadRecords = resultSet.getArray(&quot;records&quot;);
               for (var i = 0; i &lt; leadRecords.length; i++) {
                   // process record here.
               }
               
               alert(resultSet.getQueryLocator);
               // Check if the QueryLocator object in NULL or NOT to process next batch
               if (resultSet.getQueryLocator == Null ) {
                   queryMore = false;
               } else {
                   resultSet = sforce.connection.queryMore(resultSet.queryLocator);
               } 
            }
         } 
      &lt;/script&gt;
      &lt;apex:commandButton value=&quot;ProcessLead&quot; onclick=&quot;processLead();&quot;/&gt;
   &lt;/apex:form&gt;
&lt;/apex:page&gt;

In the above code you can see that I set the batch size to 200 then the first query() call retrieve the 200 records batch.And suppose we have total 600 records then first we proceed the first 200 records after that check if the queryLocator is not NULL it means we have nect 200 records batch and we use queryMore. When you try to check the value of resultSet.getQueryLocator in alert  you get like 18 Digit Id – How many records are proceed.

alert(resultSet.getQueryLocator);

At the first query() call it display [18 Digit ID] – 200  i.e. we proceed the 200 records but we have more that 200 And in next call it display [18 Digit Id ] – 400 i.e we Proceed 400 but we still have more than 400 And in another call it dipsplay NULL it means we have proceed all the records.

Hope this helps !!!!

Adding Hashtag (#) in salesforce chatter

Hello Everyone,

Today I explaining the how to add a hashtag feature in salesforce chatter in brief. Adding Hashtag in post and comments to increase their visibility and accessibility or organise them.

Example – Suppose I post something about a topic like salesforce1 and I want the other users can easily access the information related to salesforce1. So i can use the hashtag for salesforce1 like #Salesforce1.Now anyone click on this hashtag and can access all the feeds,files etc. regarding the Salesforce1.

If you put a hashtag in your post salesforce create a link for each unique hashtag Topic. When we add a hashtag in salesforce chatter feed then salesforce insert a new record in Topic object i.e. Hashtag Topic. If you want to access the detail of the HashTag topic you also can query in apex.


List<Topic> topiclist = [select Id,name from Topic];

How to add a hashtag topic:

1. When writing an update, type # followed by any text. As you type, you can pick a topic from the list of suggestions, or press ENTER at any time to add a new topic that can be up to three words i.e. you can add a hashtag topic which contains maximum three words.If you want to add a hashtag like salesforce1 override button then you need to start with #Salesforce1 Override Button after that you need to click on enter then its automatically add square brackets like #[Salesforce1 Override Button].

Commas ( , ) and closing square brackets ( ] ) automatically end a topic. Other punctuation, symbols, and separators are supported in topic names.

2. Click Share.

I am newbie in salesforce chatter.So here i am sharing few thing which i have found when I try to add a Hashtag topic in my post – I tried to add a hashtag “Salesforce1 Override Button” in my post

1. I write my text after that I put #Salesforce1 Override Button is not working… I have not click on enter after my hashtag then what happen The Salesforce1 become hashtag Topic

Hashtag Topic

2. After that i find this is different from what i want after that I read the instruction again then i got the issue and after that I again write a post with the hashtag #Salesforce1 Override Button and after that I click on enter so its automatically looks like  #[Salesforce1 Override Button] And the my post looks like

Hashtag1

Once you post an update, the only way to remove a hashtag topic is to delete the entire post or comment. You can, however, remove the topic from the top-level post.

Delete the Object’s record with AJAX toolkit via Custom button

We can implement object’s records delete functionality with AJAX toolkit in custom button.

For example – If  you want to add functionality of mass delete records for any custom or standard object ,You need to create a custom list button for particular object via

1. Create custom List button for Custom Object.
Setup -> App Setup -> Create -> Object -> select your object Name -> go to the section “Custom Buttons and Links” -> create a new list button -> choose behaviour execute java script

2.Create custom List button for Standard Object.
Setup -> App Setup -> Customize -> object name -> click on Buttons and Links -> create a new list button -> choose behaviour execute java script

After that copy the below code and save it.

{!REQUIRESCRIPT(“/soap/ajax/9.0/connection.js”)}

var records = {!GETRECORDIDS( $ObjectType.Account )}; /** You need to  change the object Name here,And the whole code is same for any object.**/

if (records[0] == null) {
alert(“Please select at least one record.”) }
else {

var errors = [];
var result = sforce.connection.deleteIds(records);
if (result && result.length){
var numFailed = 0;
var numSucceeded = 0;
for (var i = 0; i < result.length; i++){
var res = result[i];
if (res && res.success == ‘true’){
numSucceeded++;
} else {
var es = res.getArray(“errors”);
if (es.length > 0) {
errors.push(es[0].message);
}
numFailed++;
}
}
if (numFailed > 0){
alert(“Failed: ” + numFailed + “\nSucceeded: ” + numSucceeded + ” \n Due to: ” + errors.join(“\n”));
} else {
alert(“Number of records deleted: ” + numSucceeded);
}
}
window.location.reload();
}