528
edits
No edit summary |
No edit summary |
||
Line 33: | Line 33: | ||
* You must always return a string value – this return value will be your “transformation” of the [[Merge Fields and Tags|merge tag]]. | * You must always return a string value – this return value will be your “transformation” of the [[Merge Fields and Tags|merge tag]]. | ||
* You have access to "element", "documentData" and " | * You have access to "element", "documentData", "contextObject" and the function "callout" | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript" line="1"> | ||
//Wrapper element which will be inserted in the document at requested index | //Wrapper element which will be inserted in the document at requested index | ||
console.log('element: ', element); | console.log('element: ', element); | ||
Line 41: | Line 41: | ||
//The contextual record where the Function Expression is placed in | //The contextual record where the Function Expression is placed in | ||
console.log('contextObject: ', contextObject); | console.log('contextObject: ', contextObject); | ||
//A router that lets you call an apex class | |||
callout({ | |||
endp: 'classMethodName' | |||
},'ApexClassName').then(response => { | |||
console.log('callout response from function expression: '); | |||
console.log(response); | |||
}).catch(err => { | |||
console.error('error response from function expression: '); | |||
console.error(err); | |||
}) | |||
</syntaxhighlight>''Note: The "contextObject" is the document's "Core Object" record. If the function expression is embedded in a child repeatable context, then the contextObject becomes the child record.'' | </syntaxhighlight>''Note: The "contextObject" is the document's "Core Object" record. If the function expression is embedded in a child repeatable context, then the contextObject becomes the child record.'' | ||
== Callouts == | |||
Function Expressions can perform callouts to APEX via the callout() function that is passed to a function expression. This function is a wrapper around an authenticated AJAX call. Follow the instructions below to enable your function expressions to communicate with APEX. | |||
=== Apex === | |||
Firstly, you need to set up a new class that implements both b3d.GlobalRemotingInterface and Callable (native Salesforce class). | |||
Secondly, you need to implement a global method "getRemotingData()" which will get a JSON string of a JS Object with params. The important parameter is "endp" which will contain the name of the APEX method requested by the function expression.<syntaxhighlight lang="java" line="1"> | |||
global with sharing class ApexClassName implements b3d.GlobalRemotingInterface, Callable { | |||
//Required constructor | |||
global ApexClassName() { | |||
} | |||
//Required signature by b3d.GlobalRemotingInterface | |||
global String getRemotingData(String params) { | |||
try { | |||
Map<String, Object> requestObj = (Map<String, Object>)JSON.deserializeUntyped(params); | |||
return JSON.serialize(ApexClassName.call((String)requestObj.get('endp'), (Map<String, Object>)JSON.deserializeUntyped(params))); | |||
} catch(Exception e){ | |||
String generalError = String.format( | |||
Label.CRM_GeneralApexFailureRouter, | |||
new Object[]{ | |||
e.getMessage(), | |||
e.getLineNumber(), | |||
e.getStackTraceString() | |||
} | |||
); | |||
...error handling | |||
} | |||
} | |||
private static Object call(String action, Map<String, Object> args) { | |||
String recordId = (String)args.get('recordId'); //Template ID - always present | |||
switch on action { | |||
when 'classMethodName' { | |||
return classMethodName((String)args.get('param1')); | |||
} | |||
when else { | |||
return new b3d.ErrorResponse(String.format( | |||
Label.CRM_BadRemoteRequest, | |||
new Object[]{ | |||
ApexClassName.class.getName() | |||
} | |||
)); | |||
} | |||
} | |||
} | |||
protected static Object classMethodName(String param1){ | |||
//...Do something | |||
return new b3d.SuccessResponse([param1]); | |||
} | |||
} | |||
</syntaxhighlight>Please try to always return b3d.SuccessResponse or b3d.ErrorResponse within your APEX implementation. This will make it easier for our team to support your custom implementations in the future. | |||
=== Javascript === | |||
Now that you have the APEX class created successfully, you are ready to call it from within a function expression. Here is an example of how to call our sample class<syntaxhighlight lang="javascript"> | |||
callout({ | |||
endp: 'classMethodName' | |||
}, | |||
'ApexClassName').then(response => { | |||
console.log(response); | |||
}).catch(err => { | |||
console.error(err); | |||
}) | |||
</syntaxhighlight> | |||
== Examples == | == Examples == | ||
Line 48: | Line 127: | ||
=== Return Current Date === | === Return Current Date === | ||
This example is useful for getting a dynamically generated timestamp of the current date relative to when the document is opened in dd/mm/yyyy format (don’t worry, you can still apply [[Custom Value Formatting|custom value formatters]] and transform the date to a US date!)<syntaxhighlight lang="javascript" line="1"> | This example is useful for getting a dynamically generated timestamp of the current date relative to when the document is opened in dd/mm/yyyy format (don’t worry, you can still apply [[Custom Value Formatting|custom value formatters]] and transform the date to a US date!)<syntaxhighlight lang="javascript" line="1"> | ||
//Get current date in UK format | |||
() => new Date().toLocaleDateString(‘en-GB’); | () => new Date().toLocaleDateString(‘en-GB’); | ||
</syntaxhighlight><syntaxhighlight lang="javascript"> | |||
//Get current date, formatted based on user's browser locale | |||
() =>{ | |||
const currentDate = new Date(); | |||
return currentDate.toLocaleDateString(navigator.language); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Embed Charts === | === Embed Charts === | ||
This example demonstrates how we can embed highcharts into a template. This example uses static data, but you can route with data from the template.<syntaxhighlight lang="javascript" line="1" start="1"> | This example demonstrates how we can embed highcharts into a template. This example uses static data, but you can route with data from the template.<syntaxhighlight lang="javascript" line="1" start="1"> | ||
console.log('element: ', element); | console.log('element: ', element); | ||
console.log('documentData: ', documentData); | console.log('documentData: ', documentData); | ||
console.log('contextObject: ', contextObject); | console.log('contextObject: ', contextObject); | ||
const reportData = {}; | |||
await callout({ | |||
endp: 'getReportData' | |||
},'FunctionExpressions').then(response => { | |||
console.log(response); | |||
reportData = response.responseObject; | |||
}).catch(err => { | |||
console.error(err); | |||
}); | |||
var script = document.createElement('script'); | var script = document.createElement('script'); | ||
script.src = 'https://code.highcharts.com/highcharts.js'; | script.src = 'https://code.highcharts.com/highcharts.js'; | ||
document.head.appendChild(script); | |||
let customId = Math.random().toString(36).slice(2, 7); | let customId = Math.random().toString(36).slice(2, 7); | ||
let chartWrapper = document.createElement('div'); | let chartWrapper = document.createElement('div'); | ||
Line 71: | Line 164: | ||
script.onload = function () { | script.onload = function () { | ||
window.addEventListener('documentLoaded', e => { | window.addEventListener('documentLoaded', e => { | ||
Highcharts.chart(customId, { | Highcharts.chart(customId, { | ||
title: { | title: { | ||
text: ' | text: 'Some Chart Header' | ||
}, | }, | ||
subtitle: { | subtitle: { | ||
text: ' | text: 'Example Chart Sub-Header' | ||
}, | }, | ||
yAxis: { | yAxis: { | ||
Line 86: | Line 178: | ||
xAxis: { | xAxis: { | ||
accessibility: { | accessibility: { | ||
rangeDescription: 'Range: 2010 to | rangeDescription: 'Range: 2010 to 2030' | ||
} | } | ||
}, | }, | ||
Line 102: | Line 194: | ||
} | } | ||
}, | }, | ||
series: | series: reportData.series, | ||
responsive: { | responsive: { | ||
rules: [{ | rules: [{ |