Skip to content

Instantly share code, notes, and snippets.

@forcementor
Last active October 11, 2015 22:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save forcementor/a1399a77150b7a2e1304 to your computer and use it in GitHub Desktop.
Save forcementor/a1399a77150b7a2e1304 to your computer and use it in GitHub Desktop.
Developing Mobile Applications with Force.com and Sencha Touch - Part 3 - Complete Project
<!--
========================================================
Name: PocketCRM
Type: Visualforce Page
Purpose: For Sencha Touch PocketCRM App
Created by: Don Robins - www.ForceMentor.com
Created on: August 1, 2012
Copyright 2012 Outformations, Inc.
Rev # Revised on Revised by Revision Description
----- ---------- -------------------------------------
1.0 08/01/2012 Don Robins Initial Release
=========================================================
-->
<apex:page docType="html-5.0" showHeader="false" standardStylesheets="false" cache="false">
<head>
<meta charset="UTF-8" />
<title>Pocket CRM</title>
<!-- Use the Visualforce tags for scripts and stylesheets rather than their HTML counterparts -->
<apex:includeScript value="http://cdn.sencha.io/try/touch/2.0.1/sencha-touch-all.js" />
<apex:stylesheet value="http://cdn.sencha.io/try/touch/2.0.1/resources/css/sencha-touch.css" />
<!-- Custom Application Style Sheet -->
<c:PocketCRM_CSS />
<!-- Our custom Visualforce component containing our Sencha Touch application -->
<c:PocketCRM_APP />
</head>
<body>
<!-- An animated image that displays while loading -->
<div id="appLoadingIndicator">
<div></div>
<div></div>
<div></div>
</div>
</body>
</apex:page>
<!--
========================================================
Name: PocketCRM_APP
Type: Visualforce Custom Component
Purpose: For Sencha Touch PocketCRM App
Created by: Don Robins - www.ForceMentor.com
Created on: August 1, 2012
Copyright 2012 Outformations, Inc.
Rev # Revised on Revised by Revision Description
----- ---------- -------------------------------------
1.0 08/01/2012 Don Robins Initial Release
1.1 10/01/2012 Don Robins v3
=========================================================
-->
<apex:component controller="PocketCRMLeadController" >
<script type="text/javascript">
//================================================================================================================
//APPLICATION
//The Application class is the entry point into your Sencha Touch application.
//================================================================================================================
Ext.application({
name: "PocketCRM",
//Load the various MVC components into memory.
models: ["Lead"],
stores: ["Leads"],
controllers: ["Leads"],
views: ["LeadsList","LeadEditor"],
//The application's startup routine once all components are loaded.
launch: function () {
//Instantiate your main list view for Leads.
var leadsListView = {
xtype: "leadslistview"
};
var leadEditorView = {
xtype: "leadeditorview"
};
//Launch the primary fullscreen view and pass in the list view.
Ext.Viewport.add([leadsListView, leadEditorView]);
}
});
//================================================================================================================
//VIEWS
//Views display data to your users and gather input from them; they also emit events about your user interaction.
//================================================================================================================
//The Lead list view.
Ext.define("PocketCRM.view.LeadsList", {
extend: "Ext.Container",
//It uses the base list class.
requires: "Ext.dataview.List",
alias: "widget.leadslistview",
config: {
//Take up the full space available in the parent container.
layout: {
type: 'fit'
},
//Add the components to include within the list view.
items: [
{
xtype: "toolbar",
title: "PocketCRM",
docked: "top",
items: [
{
xtype: 'spacer'
},
{
xtype: "button",
text: 'New',
ui: 'action',
itemId: "newButton"
}
]
},
{
xtype: "toolbar",
docked: "bottom",
itemId: "bottomToolBar",
items: [
{xtype: 'spacer'}
,
{
xtype: 'searchfield',
itemId:'leadSearchField',
placeHolder: 'Name Contains...'
},
{
xtype: "button",
iconCls: "search",
iconMask: true,
itemId: "syncButton"
}
,
{xtype: 'spacer'}
]
},
{
//The main list and its properties.
xtype: "list",
store: "Leads",
itemId:"leadsList",
onItemDisclosure: true,
indexBar: true,
grouped: true,
disableSelection: false,
plugins: [
{
xclass: 'Ext.plugin.ListPaging',
autoPaging: true
}
],
//The template for display if the Store is empty of records.
//Note the style to control visual presentation.
loadingText: "Loading Leads...",
emptyText: '<div class="leads-list-empty-text">No leads found.</div>',
//The template for the display of each list item representing one record.
//One row will display for each record in the data Store.
//The fields referenced are from the entity's Model.
itemTpl: '<div class="list-item-line-main">{LastName}, {FirstName}</div>' +
'<div class="list-item-line-detail">{Company}</div>' +
'<div class="list-item-line-detail">{Title} - Phone: {Phone} </div>' +
'<div class="list-item-line-detail">{Email}</div>',
}],
listeners: [{
delegate: "#newButton",
event: "tap",
fn: "onNewButtonTap"
}, {
delegate: "#syncButton",
event: "tap",
fn: "onSyncButtonTap"
}, {
delegate: "#leadsList",
event: "disclose",
fn: "onLeadsListDisclose"
}, {
delegate: "#leadSearchField",
event: "clearicontap",
fn: "onLeadSearchFieldClearIconTap"
},
{
delegate: "#leadsList",
event: "refresh",
fn: "onLeadsListRefresh",
},
{
//Listener on the view's Activate event fires when redisplayed by transition.
event: "activate",
fn: "onLeadsListViewActivate",
}]
},
onSyncButtonTap: function () {
console.log("syncLeadCommand");
this.fireEvent("syncLeadCommand", this);
},
onNewButtonTap: function () {
console.log("newLeadCommand");
this.fireEvent("newLeadCommand", this);
},
onLeadsListDisclose: function (list, record, target, index, evt, options) {
console.log("editLeadCommand");
this.fireEvent('editLeadCommand', this, record);
},
onLeadSearchFieldClearIconTap: function () {
console.log("clearSearchLeadCommand");
this.fireEvent('clearSearchLeadCommand', this);
},
onLeadsListRefresh: function () {
console.log("onLeadsListRefresh");
this.updateListCounter();
},
onLeadsListViewActivate: function () {
console.log("onLeadsListViewActivate");
this.updateListCounter();
},
//Function to get count of records in the list and show on the search button's badge.
updateListCounter: function () {
var listCount = Ext.getStore("Leads").getCount();
this.getComponent("bottomToolBar").getComponent("syncButton").setBadgeText(listCount);
}
});
//The Lead form view.
Ext.define("PocketCRM.view.LeadEditor", {
extend: "Ext.form.Panel",
requires: "Ext.form.FieldSet",
alias: "widget.leadeditorview",
config: {
scrollable: 'vertical',
items: [
{
xtype: "toolbar",
docked: "top",
title: "Edit Lead",
items: [
{
xtype: "button",
ui: "back",
text: "Home",
itemId: "backButton"
},
{ xtype: "spacer" },
{
xtype: "button",
ui: "action",
text: "Save",
itemId: "saveButton"
}
]
},
{
xtype: "toolbar",
docked: "bottom",
items: [
{
xtype: "button",
iconCls: "trash",
iconMask: true,
itemId: "deleteButton"
}
]
},
{ xtype: "fieldset",
title: 'Lead Info',
items: [
{
xtype: 'textfield',
name: 'FirstName',
label: 'First Name'
},
{
xtype: 'textfield',
name: 'LastName',
label: 'Last Name',
required: true
},
{
xtype: 'textfield',
name: 'Company',
label: 'Company',
required: true
},
{
xtype: 'textfield',
name: 'Title',
label: 'Title'
},
{
xtype: 'selectfield',
name: 'Status',
label: 'Status',
required: true,
value: 'Open - Not Contacted',
options: [
{text: 'Open - Not Contacted', value: 'Open - Not Contacted'},
{text: 'Working - Contacted', value: 'Working - Contacted'},
{text: 'Closed - Converted', value: 'Closed - Converted'},
{text: 'Closed - Not Converted', value: 'Closed - Not Converted'}
],
},
]
},
{ xtype: "fieldset",
title: 'Contact Info',
items: [
{
xtype : 'textfield',
name : 'Phone',
label : 'Phone',
component: {type: 'tel'}
},
{
xtype : 'textfield',
name : 'MobilePhone',
label : 'Mobile',
component: {type: 'tel'}
},
{
xtype: 'emailfield',
name: 'Email',
label: 'Email Address'
},
]
},
],
listeners: [
{
delegate: "#backButton",
event: "tap",
fn: "onBackButtonTap"
},
{
delegate: "#saveButton",
event: "tap",
fn: "onSaveButtonTap"
},
{
delegate: "#deleteButton",
event: "tap",
fn: "onDeleteButtonTap"
}
]
},
onSaveButtonTap: function () {
console.log("saveLeadCommand");
this.fireEvent("saveLeadCommand", this);
},
onDeleteButtonTap: function () {
console.log("deleteLeadCommand");
Ext.Msg.confirm("Delete Lead", "Are you sure?", function(button){
if (button == 'yes') {
this.fireEvent("deleteLeadCommand", this);
} else {
return false;
}
}, this);
},
onBackButtonTap: function () {
console.log("backToHomeCommand");
this.fireEvent("backToHomeCommand", this);
}
});
//================================================================================================================
//CONTROLLERS
//Controllers manage the communication of your application and the coordination between the views and the model;
//they listen for the events emitted by the views and react accordingly.
//================================================================================================================
//The controller for the Leads list view
Ext.define("PocketCRM.controller.Leads", {
extend: "Ext.app.Controller",
config: {
refs: {
// We're going to lookup our views by alias.
leadsListView: "leadslistview",
leadEditorView: "leadeditorview",
leadsList: "#leadsList",
//Add a new search field.
leadsListSearchField: "#leadSearchField"
},
control: {
leadsListView: {
// The commands fired by the list container.
syncLeadCommand: "onSyncLeadCommand",
newLeadCommand: "onNewLeadCommand",
editLeadCommand: "onEditLeadCommand",
//Add an event when clearing the search text.
clearSearchLeadCommand: "onClearSearchLeadCommand"
},
leadEditorView: {
// The commands fired by the note editor.
saveLeadCommand: "onSaveLeadCommand",
deleteLeadCommand: "onDeleteLeadCommand",
backToHomeCommand: "onBackToHomeCommand"
}
}
},
//View Transitions
slideLeftTransition: { type: 'slide', direction: 'left' },
slideRightTransition: { type: 'slide', direction: 'right' },
//View Transition Helper functions
activateLeadEditor: function (record) {
var leadEditorView = this.getLeadEditorView();
leadEditorView.setRecord(record);
Ext.Viewport.animateActiveItem(leadEditorView, this.slideLeftTransition);
},
activateLeadsList: function () {
Ext.Viewport.animateActiveItem(this.getLeadsListView(), this.slideRightTransition);
},
onSyncLeadCommand: function () {
console.log("onSyncLeadCommand");
this.loadList();
},
onNewLeadCommand: function () {
console.log("onNewLeadCommand");
//Set a default value for the Status selectfield.
var newLead = Ext.create("PocketCRM.model.Lead", {
Status: "Open - Not Contacted"
});
this.activateLeadEditor(newLead);
},
onEditLeadCommand: function (list, record) {
console.log("onEditLeadCommand");
this.activateLeadEditor(record);
},
onSaveLeadCommand: function () {
console.log("onSaveLeadCommand");
//Update the field values in the record.
var leadEditorView = this.getLeadEditorView();
var currentLead = leadEditorView.getRecord();
var newValues = leadEditorView.getValues();
this.getLeadEditorView().updateRecord(currentLead);
//Check for validation errors.
var errors = currentLead.validate();
if (!errors.isValid()) {
var msg = '';
errors.each(function(error) {
msg += error.getMessage() + '<br/>';
});
console.log('Errors: ' + msg);
Ext.Msg.alert('Please correct errors!', msg, Ext.emptyFn);
currentLead.reject();
return;
}
//Get a ref to the store.
var leadsStore = Ext.getStore("Leads");
//Add new record to the store.
if (null == leadsStore.findRecord('id', currentLead.data.id)) {
leadsStore.add(currentLead);
}
//Resync the proxy and activate the list.
leadsStore.sync();
this.activateLeadsList();
},
onDeleteLeadCommand: function () {
console.log("onDeleteLeadCommand");
//Get a ref to the form and its record.
var leadEditorView = this.getLeadEditorView();
var currentLead = leadEditorView.getRecord();
//Get a ref to the store and remove it.
var leadsStore = Ext.getStore("Leads");
leadsStore.remove(currentLead);
//Resync the proxy and activate the list.
leadsStore.sync();
this.activateLeadsList();
},
onBackToHomeCommand: function () {
console.log("onBackToHomeCommand");
this.activateLeadsList();
},
//Reload the list when clearing the search value.
onClearSearchLeadCommand: function () {
console.log("onClearSearchLeadCommand");
//Get a ref to the search field.
var leadSearchField = this.getLeadsListSearchField();
leadSearchField.setValue('');
console.log("Cleared Field Value: " + leadSearchField.getValue());
this.loadList();
},
//Add separate load function to be called by multiple event handlers.
loadList: function () {
//Get a ref to the store and remove it.
var leadsStore = Ext.getStore("Leads");
//Get any search text.
var leadSearchField = this.getLeadsListSearchField();
var searchText = leadSearchField.getValue();
console.log("Search Field Value in reSync: " + searchText);
if (searchText.length > 0 && searchText.length < 2) {
var msg = 'Search requires more text.';
Ext.Msg.alert('Please correct errors!', msg, Ext.emptyFn);
return;
}
//Add wild cards.
if (searchText != '') {
//Surround the search value with a wildcard for SOQL search.
searchText = '%' + searchText + '%';
} else {
//Set wildcard for wide open filter.
searchText = '%';
}
console.log("Search Field Value: " + searchText);
//Set the value of the searchFilter param to pass with the query.
var model = Ext.ModelMgr.getModel('PocketCRM.model.Lead');
model.getProxy().setExtraParam('searchFilter', searchText);
//Clear all data in the store before loading it.
//This is necessary to make sure that the proxy doesn't get confused by
//the loss of records not reloaded with a new filter.
//Without the clear(), the proxy assumes a deletion was processed and
//calls a destroy to be executed on missing records with the next sync() operation.
leadsStore.getData().clear();
leadsStore.loadPage(1);
//Reshow the list.
this.activateLeadsList();
},
// Base Class functions.
launch: function () {
console.log("launch");
this.callParent(arguments);
//Load up the Store associated with the controller and its views.
console.log("load Leads");
this.loadList();
},
init: function() {
this.callParent(arguments);
console.log("init");
//Listen for exceptions observed by the proxy so we can report them and clean up.
Ext.getStore('Leads').getProxy().addListener('exception', function (proxy, response, operation, options) {
// only certain kinds of errors seem to have useful information returned from the server
if (response) {
if (response.errorMessage) {
Ext.Msg.alert('Error', response.errorMessage);
} else {
Ext.Msg.alert('Error', operation.config.action + ' failed: ' + response.errorMessage);
}
} else {
Ext.Msg.alert('Error', operation.config.action + ' failed for an unknown reason: proxy = ' + proxy);
}
});
},
});
//================================================================================================================
//MODELS
//Models are the objects on your application.
//================================================================================================================
//AN IMPORTANT NOTE: if your org has a registered namespace, you MUST reference the Apex controller name in the
//proxy your JavaScript with your org's namespace. If you fail to do this, you will get a a JavaScript error that
//the Apex controller can NOT be found!
//================================================================================================================
//The Lead model will include whatever fields are necssary to manage.
Ext.define("PocketCRM.model.Lead", {
extend: "Ext.data.Model",
config: {
idProperty: 'Id',
fields: [
{ name: 'Id', type: 'string', persist: false},
{ name: 'Name', type: 'string', persist: false },
{ name: 'FirstName', type: 'string' },
{ name: 'LastName', type: 'string' },
{ name: 'Company', type: 'string' },
{ name: 'Title', type: 'string' },
{ name: 'Phone', type: 'string' },
{ name: 'MobilePhone', type: 'string' },
{ name: 'Email', type: 'string' },
{ name: 'Status', type: 'string' }
],
validations: [
{ type: 'presence', field: 'LastName', message: 'Enter a last name.' },
{ type: 'presence', field: 'Company', message: 'Enter a company.' },
{ type: 'presence', field: 'Status', message: 'Select a status.' }
],
//Bind each CRUD functions to a @RemoteAction method in the Apex controller
proxy: {
type: 'direct',
api: {
read: PocketCRMLeadController.Query,
create: PocketCRMLeadController.Add,
update: PocketCRMLeadController.Edit,
destroy: PocketCRMLeadController.Destroy
},
limitParam: 'recordCount', // because "limit" is an Apex keyword
sortParam: 'sortParams', // because "sort" is a keyword too
pageParam: false, // we don't use this in the controller, so don't send it
extraParams: ( { 'searchFilter':'%' } ),
reader: {
type: 'json',
rootProperty: 'records',
messageProperty: 'errorMessage'
},
writer: {
type: 'json',
root: 'records',
writeAllFields: false, // otherwise empty fields will transmit as empty strings, instead of "null"/not present
allowSingle: false, // need to always be an array for code simplification
encode: false // docs say "set this to false when using DirectProxy"
}
}
},
});
//================================================================================================================
//PROXY RELATED EXTENSIONS
//================================================================================================================
//AN IMPORTANT NOTE: if your org has ANY installed packages, you MUST reference the Apex controller name in your
//JavaScript with your org's namespace. If you fail to do this, you will get a a JavaScript error that the Apex
//controller can NOT be found!
//================================================================================================================
//Adjust our read method to add a function that Touch expects to see to get Arguments.
PocketCRMLeadController.Query.directCfg.method.getArgs =
function (params, paramOrder, paramsAsHash) {
console.log('getArgs: ' + params.data);
return [params];
}
Ext.data.proxy.Direct.prototype.createRequestCallback =
function(request, operation, callback, scope){
var me = this;
return function(data, event){
console.log('createRequestCallback: ' + operation);
me.processResponse(event.status, operation, request, data, callback, scope);
};
};
//================================================================================================================
//STORES
//Stored serve as the client-side cache of your data; they loading data into your app's views.
//================================================================================================================
Ext.define("PocketCRM.store.Leads", {
extend: "Ext.data.Store",
requires: "Ext.data.proxy.LocalStorage",
config: {
model: "PocketCRM.model.Lead",
autoLoad: true,
pageSize: 25,
//Create a grouping; be certain to use a field with content or you'll get errors!
groupField: "Status",
groupDir: "ASC",
//Create additional sorts for within the Group.
sorters: [{ property: 'LastName', direction: 'ASC'}, { property: 'FirstName', direction: 'ASC'}],
},
listeners: {
load: function(){
console.log('store.load(): loaded!');
},
}
});
</script>
</apex:component>
<!--
========================================================
Name: PocketCRM_CSS
Type: Visualforce Custom Component
Purpose: For Sencha Touch PocketCRM App
Created by: Don Robins - www.ForceMentor.com
Created on: August 1, 2012
Copyright 2012 Outformations, Inc.
Rev # Revised on Revised by Revision Description
----- ---------- -------------------------------------
1.0 08/01/2012 Don Robins Initial Release
1.1 10/01/2012 Don Robins v3
=========================================================
-->
<apex:component>
<style type="text/css">
/**
* Example of an initial loading indicator.
* It is recommended to keep this as minimal as possible to provide instant feedback
* while other resources are still being loaded for the first time
*/
html, body {
height: 100%;
background-color: #1985D0
}
#appLoadingIndicator {
position: absolute;
top: 50%;
margin-top: -15px;
text-align: center;
width: 100%;
height: 30px;
-webkit-animation-name: appLoadingIndicator;
-webkit-animation-duration: 0.5s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: linear;
}
#appLoadingIndicator > * {
background-color: #FFFFFF;
display: inline-block;
height: 30px;
-webkit-border-radius: 15px;
margin: 0 5px;
width: 30px;
opacity: 0.8;
}
@-webkit-keyframes appLoadingIndicator {
0% {
opacity: 0.8
}
50% {
opacity: 0
}
100% {
opacity: 0.8
}
}
/**
* PocketCRM Custom styles
*/
/* Increase height of list item so title and narrative lines fit */
.x-list .x-list-item .x-list-item-label {
min-height: 4.5em!important;
}
/* Move up the disclosure button to account for the list item height increase */
.x-list .x-list-disclosure {
position: absolute;
bottom: 0.85em;
right: 0.44em;
}
.list-item-line-main {
float:left;
width:100%;
font-size:100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right:25px;
line-height:115%;
}
.list-item-line-detail {
float:left;
width:95%;
font-size:60%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right:25px;
padding-left:10px;
line-height:125%;
}
.x-item-selected .list-item-line-detail {
color:#ffffff;
font-weight: bold;
}
.leads-list-empty-text {
padding:10px;
}
</style>
</apex:component>
//========================================================
// Name: PocketCRMLeadController
// Type: Controller
// Purpose: For Sencha Touch PocketCRM App
// Created by: Don Robins - www.ForceMentor.com
// Created on: August 1, 2012
// Copyright 2012 Outformations, Inc.
//
// Rev # Revised on Revised by Revision Description
// ----- ---------- -------------------------------------
// 1.0 08/01/2012 Don Robins Initial Release
// 1.1 10/01/2012 Don Robins v3
//=========================================================
public with sharing class PocketCRMLeadController{
public PocketCRMLeadController(){}
//========================================================================
//INNER CLASSES
//These support data request/response transport for remoting.
//========================================================================
//The server response expected by the ExtJS DirectProxy API methods.
public class Response {
public Boolean success;
public String errorMessage;
public List<SObject> records;
public Integer total;
Response() {
records = new List<SObject>();
success = true;
}
}
//One of the parameters supplied by the DirectProxy read method.
public class QueryRequest {
Integer start;
Integer recordCount;
List<Map<String, String>> sortParams;
String searchFilter = '';
Public QueryRequest() {
start = 1;
recordCount = 1;
}
Public QueryRequest(Integer pStart, Integer pRecordCount) {
start = pStart;
recordCount = pRecordCount;
}
}
//========================================================================
//REMOTE ACTION METHODS
//RemoteAction CRUD methods called by the Sencha touch Proxy in the data package.
//========================================================================
@RemoteAction
public static Response Query(QueryRequest qr){
Response resp = new Response();
//Enforce a limit on the number of rows requested.
final integer QUERY_LIMIT = 500;
if( qr.start >= QUERY_LIMIT ){
resp.success = false;
resp.errorMessage = 'Maximum number of records (' + String.valueOf(QUERY_LIMIT) + ') exceeded!';
return resp;
}
try {
getAllLeads(qr, resp);
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Query failed: ' + e.getMessage();
}
return resp;
}
//=======================================================================
//PUBLIC CRUD REMOTE ACTION METHODS
//=======================================================================
@RemoteAction
public static Response Edit(List<Lead> LeadData){
return updateLeadList(LeadData);
}
@RemoteAction
public static Response Add(List<Lead> LeadData){
return insertLeadList(LeadData);
}
@RemoteAction
public static Response Destroy(List<Lead> LeadData){
return deleteLeadList(LeadData);
}
//=======================================================================
//PRIVATE HELPER METHODS
//=======================================================================
private static void getAllLeads(QueryRequest qr, Response resp){
//Page size is set in the Sencha store as recordCount.
Integer pageSize = qr.recordCount;
//Page number will be calculated.
Integer pageNumber = 0;
//Start is the record number indicating the start of the page.
if( qr.start > 0 ){
pageNumber = qr.start / pageSize;
}
//Calculate the offset for SOQL.
Integer offset = pageNumber * pageSize;
//Build the query in pieces.
String fieldList = 'Id,FirstName,LastName,Company,Title,Phone,MobilePhone,Email,Status';
String whereClause = (qr.searchFilter != '' ? 'WHERE Name LIKE \'' + qr.searchFilter + '\'' : '') ;
String orderByClause = 'LastName, FirstName';
//Construct a base query to which the page offsets will be added.
String baseQuery = 'SELECT ' + fieldList + ' FROM Lead ' + whereClause + ' ORDER BY ' + orderByClause;
//Construct a count query to pass back the total records matching a search criteria.
String baseCountQuery = 'SELECT COUNT() FROM Lead ' + whereClause ;
//Construct the fetch query with the offset.
String fetchQuery = baseQuery + ' LIMIT ' + pageSize + ' OFFSET ' + offset;
try {
//Set the count.
resp.total = Database.countQuery(baseCountQuery);
//Set the fetched recordset.
resp.records = Database.query(fetchQuery);
//Set the status flag.
resp.success = true;
} catch (Exception e) {
//Set the total count of records matching the query.
resp.total = 0;
//Set the recordset to return.
resp.records = new List<Lead>();
//Set the status flag.
resp.success = false;
}
}
private static Response insertLeadList(List<Lead> LeadData){
Response resp = new Response();
resp.success = true;
try {
INSERT LeadData;
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Insert failed: ' + e.getMessage();
}
return resp;
}
private static Response updateLeadList(List<Lead> LeadData){
Response resp = new Response();
resp.success = true;
try {
UPDATE LeadData;
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Update failed: ' + e.getMessage();
}
return resp;
}
private static Response deleteLeadList(List<Lead> LeadData){
Response resp = new Response();
resp.success = true;
try {
DELETE LeadData;
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Deletion failed: ' + e.getMessage();
}
return resp;
}
}
@forcementor
Copy link
Author

An important note: if your org has a registered namespace, you MUST reference the Apex controller name in your JavaScript with your org's namespace. If you fail to do this, you will get a JavaScript error that the Apex controller can NOT be found!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment