There's documentation out there but as this took me a couple of days to simply install a JS widget hosted by Zoho and pass the record ID via a button to it, I'm adding it here in case I need to refer to it in future.
Why?
We have an Accounts module which holds all the companies we deal with in our Zoho CRM. I want our sales team to be able to click on a button off the CRM account record which will call an API returning all the credit information about a company. Unfortunately, if you do this simply using Zoho Deluge in a button, there are no new lines/carriage returns or styling that you can apply, which pops up as a message box to the sales person; but all on one line and not that readable. Using a JS Widget, we can popup a window over the Zoho CRM which displays a more clear HTML & CSS styled message box.
How?
There are links to how to create a widget in detail which I'll link to at the bottom of this page. What follows are the quick steps to create a widget.html file and how to get whatever the Zoho Deluge CRM function returns.
Create a JS Widget on your device (MacOS)
These steps are the minimal amount of information I need to quickly set up a JS widget:
- Open a MacOS Terminal
- sudo npm install -g zoho-extension-toolkit (this also updates existing install)
- sudo zet init
- select Zoho CRM
- enter a unique Project Name
- cd to the new project name
- To give permissions to write sudo chmod -R 777 .
- open the newly created widget.html file in your code editor
- add the code as per the below generic script
- when done, save 'widget.html'
- then return to terminal and type zet validate
- and then type zet pack
- this will create a zip file in a dist folder
- Login to ZohoCRM as a system administrator
- go to Setup > Customization > Modules and Fields > Accounts [or whichever module the button needs to be on] > Buttons > Create New Button
- Give it a name, Define the action as Open a Widget, Select Page as In Record, select position as Details then click on Configured Widget - Choose
- Click on New Widget, give it a name and set hosting to Zoho
- Click on File Upload and upload the zip file that was just created
- the Index Page is /widget.html
- Hit Save
- then find the one you just created in the list and click on Install
- Select the profile(s) that will have access to this button and click on Save
- Go to a CRM account record and test the button works.
- Go back to your Zoho CRM function and simply include HTML and CSS in the output of the function... done.
What we're here for
The following script is what I use to pass the record ID to a CRM function called fn_Accounts_CreditSafe. It was partly written by referring to documentation and using CoPilot or OpenAI ChatGPT for parsing the return value of the function (note that I have SCRIPT within the BODY tags and not in the HEAD):
copyraw
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> <script src="https://live.zwidgets.com/js-sdk/1.1/ZohoEmbededAppSDK.min.js"></script> <pre> Loading... Please Wait... </pre> <script> console.clear(); ZOHO.embeddedApp.on("PageLoad", function(r_Data) { // for debugging console.log(r_Data); // get id and module on page (if this is a button off a record details view) var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity; // get id and module on page (depending on trigger) // var v_EntityID = r_Data.EntityId[0], v_EntityModule = r_Data.Entity; // Define the function name to be called (don't include the namespace) var v_FuncName = "fn_Accounts_GetCreditSafe"; // Prepare the parameters for the function call var m_Params = { "arguments": JSON.stringify({ "p_AccountID": v_EntityID }) }; ZOHO.CRM.FUNCTIONS.execute( v_FuncName, m_Params ) .then( function( r_SubData ){ console.log("Raw response:", r_SubData); // Check if the response contains the expected structure let output = r_SubData?.details?.output; // If the output is JSON text, parse it into a real object let parsedOutput; try { // Attempt to parse the output as JSON parsedOutput = JSON.parse(output); } catch (e) { // If parse fails, just fall back to plain text parsedOutput = output; } // Log the parsed output for debugging console.log("Parsed output:", parsedOutput); // If parsedOutput is undefined or null, set a default message if(parsedOutput === undefined || parsedOutput === null) { let errorOutput = r_SubData?.message || "Response from function was unparseable"; parsedOutput = "<pre style='padding:30px;'>Error: " + errorOutput + "</pre>"; document.writeln(parsedOutput); } else { // If parsedOutput is an object, you can now use normal dot notation: if (parsedOutput && typeof parsedOutput === "object") { // If the output is an object, you can access its properties document.writeln(parsedOutput.details.output || "<pre style='padding:30px;'>Error: No parsed output found</pre>"); } else { // If the output is not an object, just write it directly document.writeln(parsedOutput); } } }) .catch( function( r_Error ){ document.write(JSON.stringify(r_Error)); }); }); ZOHO.embeddedApp.init(); </script> </body> </html>
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- </head>
- <body>
- <script src="https://live.zwidgets.com/js-sdk/1.1/ZohoEmbededAppSDK.min.js"></script>
- <pre> Loading... Please Wait... </pre>
- <script>
- console.clear();
- zoho.embeddedApp.on("PageLoad", function(r_Data) {
- // for debugging
- console.log(r_Data);
- // get id and module on page (if this is a button off a record details view)
- var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity;
- // get id and module on page (depending on trigger)
- // var v_EntityID = r_Data.EntityId[0], v_EntityModule = r_Data.Entity;
- // Define the function name to be called (don't include the namespace)
- var v_FuncName = "fn_Accounts_GetCreditSafe";
- // Prepare the parameters for the function call
- var m_Params = {
- "arguments": JSON.stringify({
- "p_AccountID": v_EntityID
- })
- };
- zoho.crm.FUNCTIONS.execute( v_FuncName, m_Params )
- .then( function( r_SubData ){
- console.log("Raw response:", r_SubData);
- // Check if the response contains the expected structure
- let output = r_SubData?.details?.output;
- // If the output is JSON text, parse it into a real object
- let parsedOutput;
- try {
- // Attempt to parse the output as JSON
- parsedOutput = JSON.parse(output);
- } catch (e) {
- // If parse fails, just fall back to plain text
- parsedOutput = output;
- }
- // Log the parsed output for debugging
- console.log("Parsed output:", parsedOutput);
- // If parsedOutput is undefined or null, set a default message
- if(parsedOutput === undefined || parsedOutput === null) {
- let errorOutput = r_SubData?.message || "Response from function was unparseable";
- parsedOutput = "<pre style='padding:30px;'>Error: " + errorOutput + "</pre>";
- document.writeln(parsedOutput);
- } else {
- // If parsedOutput is an object, you can now use normal dot notation:
- if (parsedOutput && typeof parsedOutput === "object") {
- // If the output is an object, you can access its properties
- document.writeln(parsedOutput.details.output || "<pre style='padding:30px;'>Error: No parsed output found</pre>");
- } else {
- // If the output is not an object, just write it directly
- document.writeln(parsedOutput);
- }
- }
- })
- .catch( function( r_Error ){
- document.write(JSON.stringify(r_Error));
- });
- });
- zoho.embeddedApp.init();
- </script>
- </body>
- </html>
[Note to self]
Retrieving the record itself (not needed for this task):
copyraw
ZOHO.embeddedApp.on("PageLoad", function(r_Data) { // get id and module on page var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity; // get the record details ZOHO.CRM.API.getRecord({ Entity: v_EntityModule, RecordID: v_EntityID }) .then(function(response) { console.log("Record data:"); console.log(response); }).catch(function(error) { console.log("Error:"); console.log(error); }); }); ZOHO.embeddedApp.init();
- zoho.embeddedApp.on("PageLoad", function(r_Data) {
- // get id and module on page
- var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity;
- // get the record details
- zoho.crm.API.getRecord({ Entity: v_EntityModule, RecordID: v_EntityID })
- .then(function(response) {
- console.log("Record data:");
- console.log(response);
- }).catch(function(error) {
- console.log("Error:");
- console.log(error);
- });
- });
- zoho.embeddedApp.init();
[Optional]
What follows is the code for the function fn_Accounts_CreditSafe... obviously I can't give you the real Zoho Deluge function here as it would use my credentials but what you need to know is that it is passed the crmAPIRequest (string) variable and that the function itself had to have API Key and OAuth enabled (hover over function in CRM setup and select REST API, then tick both to the "on" position). The returned output from this function should be the HTML that will be displayed in your JS Widget:
copyraw
string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest) { /* ******************************************************************************* Function: string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest) Label: Fn - Accounts - Get Credit Safe Details Trigger: On-Demand / Widget Purpose: Given an account ID returns the credit safe details from CreditSafe Connect API. This function is used by a JS Widget. Inputs: string crmAPIRequest (webhook request from Zoho CRM) Outputs: - Date Created: 2023-08-15 (Ascent Business - Joel Lipman) - Initial release - Testing auth and connection Date Modified: 2023-08-15 (Ascent Business - Joel Lipman) - Separated function from button so that is can be used by Widget ******************************************************************************* */ // // initialize v_AccountID = ""; v_Output = "Error: Could not retrieve given CRM Account ID"; // // ****************************** GENERIC CODE BELOW ****************************** // // retrieving the parameters of the request m_RequestParams = crmAPIRequest.toMap().get("params"); // // retrieving the set of values from 'arguments' passed in the javascript code m_Arguments = ifnull(m_RequestParams.get("arguments"), Map()); // if(!isNull(m_Arguments.get("p_AccountID"))) { v_AccountID = m_Arguments.get("p_AccountID").toString(); v_Output = "Info: Recognized CRM Account ID: " + v_AccountID; // r_Account = zoho.crm.getRecordById("Accounts",v_AccountID.toLong()); if(!isNull(r_Account.get("id"))) { v_Output = "Info: Retrieved Record of CRM Account ID: " + v_AccountID; } } // // format output v_OutputHtml = "<h1 style='color:green'>Hullo World!</h1><p>"+v_Output+"</p>"; return v_OutputHtml; }
- string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest)
- {
- /* *******************************************************************************
- Function: string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest)
- Label: Fn - Accounts - Get Credit Safe Details
- Trigger: On-Demand / Widget
- Purpose: Given an account ID returns the credit safe details from CreditSafe Connect API. This function is used by a JS Widget.
- Inputs: string crmAPIRequest (webhook request from Zoho CRM)
- Outputs: -
- Date Created: 2023-08-15 (Ascent Business - Joel Lipman)
- - Initial release
- - Testing auth and connection
- Date Modified: 2023-08-15 (Ascent Business - Joel Lipman)
- - Separated function from button so that is can be used by Widget
- ******************************************************************************* */
- //
- // initialize
- v_AccountID = "";
- v_Output = "Error: Could not retrieve given CRM Account ID";
- //
- // ****************************** GENERIC CODE BELOW ******************************
- //
- // retrieving the parameters of the request
- m_RequestParams = crmAPIRequest.toMap().get("params");
- //
- // retrieving the set of values from 'arguments' passed in the javascript code
- m_Arguments = ifnull(m_RequestParams.get("arguments"), Map());
- //
- if(!isNull(m_Arguments.get("p_AccountID")))
- {
- v_AccountID = m_Arguments.get("p_AccountID").toString();
- v_Output = "Info: Recognized CRM Account ID: " + v_AccountID;
- //
- r_Account = zoho.crm.getRecordById("Accounts",v_AccountID.toLong());
- if(!isNull(r_Account.get("id")))
- {
- v_Output = "Info: Retrieved Record of CRM Account ID: " + v_AccountID;
- }
- }
- //
- // format output
- v_OutputHtml = "<h1 style='color:green'>Hullo World!</h1><p>"+v_Output+"</p>";
- return v_OutputHtml;
- }
Source(s)
- Widgets in Zoho CRM
- Zoho CRM Developer > Community > Extension pointers - JS SDK Series #1: Learn how to invoke a REST API function from a widget using the execute method
Category: Zoho :: Article: 908
Add comment