Custom Embedded JavaScript Functions: Difference between revisions

no edit summary
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 "contextObject"
* 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">
//This is the element which will be used as a wrapper
console.log('element: ', element);
console.log('element: ', element);
//This is the object containing all the data loaded
console.log('documentData: ', documentData);
console.log('documentData: ', documentData);
//This is the current contextual object record
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);


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 => {
console.log(document.getElementById(customId));
         Highcharts.chart(customId, {
         Highcharts.chart(customId, {
             title: {
             title: {
                 text: 'U.S Solar Employment Growth by Job Category, 2010-2020'
                 text: 'Some Chart Header'
             },
             },
             subtitle: {
             subtitle: {
                 text: 'Source: <a href="https://irecusa.org/programs/solar-jobs-census/" target="_blank">IREC</a>'
                 text: 'Example Chart Sub-Header'
             },
             },
             yAxis: {
             yAxis: {
Line 86: Line 178:
             xAxis: {
             xAxis: {
                 accessibility: {
                 accessibility: {
                     rangeDescription: 'Range: 2010 to 2020'
                     rangeDescription: 'Range: 2010 to 2030'
                 }
                 }
             },
             },
Line 102: Line 194:
                 }
                 }
             },
             },
             series: [{
             series: reportData.series,
                name: 'Installation & Developers',
                data: [43934, 48656, 65165, 81827, 112143, 142383,
                    171533, 165174, 155157, 161454, 154610
                ]
            }, {
                name: 'Manufacturing',
                data: [24916, 37941, 29742, 29851, 32490, 30282,
                    38121, 36885, 33726, 34243, 31050
                ]
            }, {
                name: 'Sales & Distribution',
                data: [11744, 30000, 16005, 19771, 20185, 24377,
                    32147, 30912, 29243, 29213, 25663
                ]
            }, {
                name: 'Operations & Maintenance',
                data: [null, null, null, null, null, null, null,
                    null, 11164, 11218, 10077
                ]
            }, {
                name: 'Other',
                data: [21908, 5548, 8105, 11248, 8989, 11816, 18274,
                    17300, 13053, 11906, 10073
                ]
            }],
             responsive: {
             responsive: {
                 rules: [{
                 rules: [{