├── .gitignore ├── LICENSE.txt ├── readme.md └── src ├── classes ├── sObjectRemote.cls ├── sObjectRemote.cls-meta.xml ├── sObjectRemoteTEST.cls └── sObjectRemoteTEST.cls-meta.xml ├── components ├── sObjectRemote.component └── sObjectRemote.component-meta.xml ├── package.xml ├── pages ├── sObjectRemoteExamples.page └── sObjectRemoteExamples.page-meta.xml └── staticresources ├── sObjectRemote.resource └── sObjectRemote.resource-meta.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /config/ 2 | /sObjectRemote.sublime-project 3 | /sObjectRemote.sublime-workspace 4 | .DS_Store 5 | .DS_Store? 6 | ._* 7 | .Spotlight-V100 8 | .Trashes 9 | Icon? 10 | ehthumbs.db 11 | Thumbs.db -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 James Sullivan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | sObject-Remote 2 | ====== 3 | 4 | 5 | Deploy to Salesforce 7 | 8 | 9 | **sObject-Remote** is a small JavaScript library for simplifying DML operations on sObjects when using the JavaScript Remoting feature of Visualforce. Here is a tasty little preview that creates and inserts a new Account record: 10 | 11 | ```js 12 | var acct = new sObject('Account',{Name: 'test', Industry: 'Aerospace'}); 13 | 14 | acct.insert(function(result,event){ 15 | console.log(result[0].Id); 16 | }); 17 | ``` 18 | This is a very new library and the current status should definitely be considered **Beta**. Options, syntax, and method names may change in the near term based on bugs and community feedback. Please report any issues you come across and I'll get them fixed as soon as possible, or feel free to fix and submit pull requests as well. 19 | 20 | ## Features 21 | 22 | * Super simple sObject management for JavaScript 23 | * Currently supports 4 basic CRUD operations (more DML operations to come) 24 | * Works with any JavaScript framework 25 | * No 3rd party JavaScript library dependencies 26 | 27 | ## Setup 28 | 29 | To use the **sObject-Remote** library you can pull the code from this repository to your target org or you can install the unmanaged package with this link: [sObject-Remote Unmanaged Package](https://login.salesforce.com/packaging/installPackage.apexp?p0=04ti000000050sf). 30 | 31 | Once installed you will need to add the sObjectRemote Visualforce Component to your page. This includes everything you need to get started. Be sure to add the component before any other JavaScript files that may use the **sObject-Remote** library. 32 | 33 | ```html 34 | 35 | 36 | 5 | 6 | 14 | -------------------------------------------------------------------------------- /src/components/sObjectRemote.component-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32.0 4 | Includes the necessary JavaScript files and variable names to use the sobject-remote.js library. 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | sObjectRemote 5 | sObjectRemoteTEST 6 | ApexClass 7 | 8 | 9 | sObjectRemote 10 | ApexComponent 11 | 12 | 13 | sObjectRemoteExamples 14 | ApexPage 15 | 16 | 17 | sObjectRemote 18 | StaticResource 19 | 20 | 32.0 21 | 22 | -------------------------------------------------------------------------------- /src/pages/sObjectRemoteExamples.page: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/pages/sObjectRemoteExamples.page-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32.0 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/staticresources/sObjectRemote.resource: -------------------------------------------------------------------------------- 1 | var sObject = function (sObjectType,fieldValues){ 2 | //Set the sObjectType 3 | this.attributes = {sObjectType: sObjectType}; 4 | 5 | //If field values were passed in to constructor populate the field values in the sObject 6 | if(fieldValues) { 7 | for(var key in fieldValues) { 8 | this[key] = fieldValues[key]; 9 | } 10 | } 11 | } 12 | 13 | //---Insert Methods--- 14 | sObject.insert = function(sObjects,callbackOrOptions,callback){ 15 | 16 | var objectsToInsert = sObject.buildArray(sObjects); //Create array of objects that will be inserted 17 | var objectStore = new Array(); //Create array that will store child objects of the sObject, these must be removed before DML 18 | var callback = sObject.setCallback(callbackOrOptions,callback); //Determine which argument is the callback function 19 | var options = sObject.setOptions(callbackOrOptions); //Set the options object 20 | 21 | //Prep the objects for DML by removing the unnecessary and adding necessary properties 22 | sObject.prepForDML(objectsToInsert,objectStore,'insert'); 23 | 24 | //Perform the insert operation 25 | Visualforce.remoting.Manager.invokeAction(sObjectInsertMethodName, objectsToInsert, options, function(result, event){ 26 | 27 | //Reattach the the child objects that were removed before the DML 28 | sObject.handleDMLResult(result,objectsToInsert,objectStore,'insert'); 29 | 30 | //Call the provided callback function with result and event objects 31 | callback(result,event); 32 | 33 | },{escape: options.escape}); 34 | } 35 | 36 | //Insert one instance of a sObject 37 | sObject.prototype.insert = function(callbackOrOptions,callback){ 38 | sObject.insert(this,callbackOrOptions,callback); 39 | } 40 | 41 | //---Query Method--- 42 | sObject.query = function(soql,callbackOrOptions,callback){ 43 | var callback = sObject.setCallback(callbackOrOptions,callback); 44 | var options = sObject.setOptions(callbackOrOptions); 45 | 46 | //Perform the query 47 | Visualforce.remoting.Manager.invokeAction(sObjectQueryMethodName, soql, function(QueryResult, event){ 48 | //Return array of queried sObjects 49 | var sObjects = []; 50 | 51 | //Populate list of sObjects if query was a success 52 | if(event){ 53 | for(var i = 0; i < QueryResult.sObjects.length; i++){ 54 | sObjects.push(new sObject(QueryResult.sObjectAttributes.sObjectType, QueryResult.sObjects[i])); 55 | } 56 | } 57 | 58 | //Return array of queried sObjects to the callback 59 | callback(sObjects,event); 60 | 61 | },{escape: options.escape}); 62 | } 63 | 64 | //---Update Methods--- 65 | sObject.update = function(sObjects,callbackOrOptions,callback){ 66 | 67 | var objectsToUpdate = sObject.buildArray(sObjects); 68 | var objectStore = new Array(); 69 | var callback = sObject.setCallback(callbackOrOptions,callback); 70 | var options = sObject.setOptions(callbackOrOptions); 71 | 72 | sObject.prepForDML(objectsToUpdate,objectStore,'update'); 73 | 74 | //Perform the update operation 75 | Visualforce.remoting.Manager.invokeAction(sObjectUpdateMethodName, objectsToUpdate, options, function(result, event){ 76 | 77 | //Convert result to object if necessary 78 | if(event.status && options.resultAsObject){ 79 | result = sObject.convertResultToObject(result,objectsToUpdate); 80 | } 81 | 82 | sObject.handleDMLResult(result,objectsToUpdate,objectStore,'update'); 83 | callback(result,event); 84 | 85 | },{escape: options.escape}); 86 | } 87 | 88 | //Update one instance of a sObject 89 | sObject.prototype.update = function(callbackOrOptions,callback){ 90 | sObject.update(this,callbackOrOptions,callback); 91 | } 92 | 93 | //---Delete Methods--- 94 | sObject.del = function(sObjects,callbackOrOptions,callback){ 95 | 96 | var objectsToDelete = sObject.buildArray(sObjects); 97 | var callback = sObject.setCallback(callbackOrOptions,callback); 98 | var options = sObject.setOptions(callbackOrOptions); 99 | var deleteObjects = new Array(); 100 | 101 | //Construct a list of super simple objects that only have an Id attribute, as this is all that is needed for delete operation 102 | for(var i = 0; i < objectsToDelete.length; i++){ 103 | deleteObjects.push({Id: objectsToDelete[i].Id}); 104 | } 105 | 106 | //Perform the delete operation 107 | Visualforce.remoting.Manager.invokeAction(sObjectDeleteMethodName, deleteObjects, options, function(result, event){ 108 | 109 | //Delete operation was success and if purge option was set to true, null object or clean out array 110 | if(event.status && options.purge){ 111 | 112 | //If sObjects has length it was an array 113 | if(sObjects.length){ 114 | 115 | var originalArrayLength = sObjects.length; 116 | 117 | //Add objects to the end of the array that were not deleted 118 | for(var i = 0; i < originalArrayLength; i++){ 119 | if(result[i].success == false){ 120 | sObjects.push(sObjects[i]); 121 | } 122 | } 123 | 124 | //Trim off original objects in the array, leaving only those objects that failed to delete 125 | sObjects.splice(0,originalArrayLength); 126 | 127 | }else{ 128 | //Single sObject 129 | if(result[0].success == true){ 130 | sObjects = null; 131 | } 132 | } 133 | } 134 | 135 | //Convert result to object if necessary 136 | if(event.status && options.resultAsObject){ 137 | result = sObject.convertResultToObject(result,deleteObjects); 138 | } 139 | 140 | callback(result,event); 141 | 142 | },{escape: options.escape}); 143 | } 144 | 145 | //Delete one instance of a sObject 146 | sObject.prototype.del = function(callbackOrOptions,callback){ 147 | sObject.del(this,callbackOrOptions,callback); 148 | } 149 | 150 | //---Utility Methods--- 151 | //If only one sObject is provided for DML it is converted to an array 152 | sObject.buildArray = function(objects){ 153 | //If objects has length its already an array 154 | if(objects.length){ 155 | return objects; 156 | }else{ 157 | //Single record, add it to array 158 | return new Array(objects); 159 | } 160 | } 161 | 162 | //Inspects the callbackOrOptions to see if it is a function (callback) or object (options), sets callback appropriately 163 | sObject.setCallback = function(callbackOrOptions,callback){ 164 | if(typeof callbackOrOptions == 'object'){ 165 | return callback; 166 | }else{ 167 | return callbackOrOptions; 168 | } 169 | } 170 | 171 | //Default options if none are set 172 | sObject.setOptions = function(callbackOrOptions){ 173 | if(typeof callbackOrOptions == 'object'){ 174 | 175 | //Default escape option to true if it was not set 176 | if(callbackOrOptions.escape == 'undefined'){ 177 | if(sObjectEscapeOverride == 'false'){ 178 | callbackOrOptions.escape = false; 179 | }else{ 180 | callbackOrOptions.escape = true; 181 | } 182 | } 183 | return callbackOrOptions; 184 | 185 | }else{ 186 | //No options define. Default escape option to true if component option was not set to false 187 | if(sObjectEscapeOverride == 'false'){ 188 | return {escape: false}; 189 | }else{ 190 | return {escape: true}; 191 | } 192 | } 193 | } 194 | 195 | //Salesforce does not like DML on sObjects if they have child objects. Remove them for the dml operation 196 | sObject.prepForDML = function(sObjects,objectStore,dmlType){ 197 | 198 | for(var i = 0; i < sObjects.length; i ++){ 199 | 200 | //Store the sObject we are currently processing in a var for easy access 201 | var sObject = sObjects[i]; 202 | 203 | //Set sObjectType for INSERT operations 204 | if(dmlType == 'insert'){ 205 | sObject.sObjectType = sObjects[i].attributes.sObjectType; 206 | } 207 | 208 | //Loop through sObjects keys and store any child objects in a childObjects object 209 | var childObjects = {}; 210 | 211 | for(var key in sObject){ 212 | if(typeof sObject[key] == 'object'){ 213 | childObjects[key] = sObject[key]; 214 | delete sObject[key]; 215 | } 216 | } 217 | 218 | //Store the child objects in the objectStore array so they can be added back to the sObject after DML, http://jsperf.com/object-clone-vs-modify 219 | objectStore.push(childObjects); 220 | } 221 | } 222 | 223 | //Updated Id form inserted records and reattaches and child objects that where removed for the DML 224 | sObject.handleDMLResult = function(result,sObjects,objectStore,dmlType){ 225 | 226 | //Loop through results, update the Id for inserts, add back child objects that were removed for DML 227 | for(var i = 0; i < sObjects.length; i++){ 228 | 229 | //Remove the sObjectType property that may be present on the main sObject 230 | delete sObjects[i]['sObjectType']; 231 | 232 | //If record was inserted successfully, update the id 233 | if(dmlType == 'insert' && result[i].success){ 234 | sObjects[i].Id = result[i].id; 235 | } 236 | 237 | //Add the child objects back to the main sObject 238 | var sObject = sObjects[i]; 239 | var childObjects = objectStore[i]; 240 | 241 | for(var key in childObjects[i]){ 242 | sObject[key] = childObjects[key]; 243 | } 244 | } 245 | } 246 | 247 | //Create save/delete result array to JavaScript object with the key as the record Id 248 | sObject.convertResultToObject = function(result,sObjects){ 249 | 250 | var resultObj = {}; 251 | 252 | for(var i = 0; i < result.length; i++){ 253 | var id = sObjects[i].Id; 254 | delete result[i]['id']; 255 | resultObj[id] = result[i]; 256 | } 257 | return resultObj; 258 | } -------------------------------------------------------------------------------- /src/staticresources/sObjectRemote.resource-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Private 4 | text/javascript 5 | Contains the sobject-remote.js library for simplifying JavaScript Remoting DML operations. 6 | 7 | --------------------------------------------------------------------------------