A follow on from my article Zoho Creator: Create a Widget which uses JavaScript back from 2020 with a few adjustments now in 2024.
This widget will only work within Zoho Creator. If you want a solution which sits on an external website and which talks to Zoho, then setup an externally hosted web app with the usual JavaScript frameworks and have it send AJAX commands to a server which reads/writes information to your Zoho instance via API. No need for the Zoho Widget SDK.Why?
At time of print, I felt the documentation was a little sparse on how to connect a JS widget to the data held in Zoho Creator. Having this example for future use might save me some time. The following is the early version of a JS widget used for a customer and before I added all the layers of complexity, I want to have here an example that demonstrates a basic search on a table of data and returns one record as a result.
The use-case here is that we are enhancing a quote builder form in Zoho Creator. The JS widget (aka the Quote Builder) will need to post a new quote to the system as well as show previous orders.
How?
We're going to use the Zoho Extension SDK. You should refer to my previous linked article but the revised instructions here should also help. Then lastly, I'll post the snippet of code using in the widget to connect to Zoho based on a parameter passed in the URL that a Zoho Creator Page can receive and pass to the JS widget.
the HTML
You may note that I have additional CSS and JS files. I like to keep the copies local (especially if the hosting is included) so the app doesn't suddenly stop working when a third-party CDN / API gets upgraded. Add these to the widget as you see fit but I'm including jQuery and Bootstrap:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Company Name - Quote Builder</title> <link rel="stylesheet" href="/./css/main.css" /> <link rel="stylesheet" href="/./css/bootstrap.min.css"> </head> <body> <div class="container py-3 my-4"> <h1>Hi <span id="greeting-name"></span></h1> </div> <div class="b-example-divider"></div> <div class="col-lg-8 px-0 mx-auto"> <div class="container px-4"> <div class="row gx-5"> <div class="col"> <div class="p-3 border bg-light">You have <span id="ab-pending-quote-count">0</span> Pending Quote<span id="ab-pending-quote-count-grammar">s</span></div> </div> <div class="col"> <div class="p-3 border bg-light">You have <span id="ab-previous-quote-count">0</span> Previous Quote<span id="ab-pending-quote-count-grammar">s</span></div> </div> </div> </div> </div> <div class="b-example-divider"></div> <div class="d-grid gap-2 col-6 mx-auto"> <button class="btn btn-primary" type="button">Get a New Quote!</button> </div> <div class="b-example-divider"></div> <div class="invisible" id="debug"></div> <div class="container"> <footer class="py-3 my-4"> <p class="text-center text-muted">© <span class="year">1999</span> Joel Lipman Ltd. All Rights Reserved</p> </footer> </div> <script src="/./js/jquery-3.3.1.slim.min.js" crossorigin="anonymous"></script> <script src="/./js/bootstrap.bundle.min.js" crossorigin="anonymous"></script> <script src="/./js/widgetsdk-min.js" crossorigin="anonymous"></script> <script src="/./js/main.js" crossorigin="anonymous"></script> </body> </html>
- <!doctype html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Company Name - Quote Builder</title>
- <link rel="stylesheet" href="./css/main.css" />
- <link rel="stylesheet" href="./css/bootstrap.min.css">
- </head>
- <body>
- <div class="container py-3 my-4">
- <h1>Hi <span id="greeting-name"></span></h1>
- </div>
- <div class="b-example-divider"></div>
- <div class="col-lg-8 px-0 mx-auto">
- <div class="container px-4">
- <div class="row gx-5">
- <div class="col">
- <div class="p-3 border bg-light">You have <span id="ab-pending-quote-count">0</span> Pending Quote<span id="ab-pending-quote-count-grammar">s</span></div>
- </div>
- <div class="col">
- <div class="p-3 border bg-light">You have <span id="ab-previous-quote-count">0</span> Previous Quote<span id="ab-pending-quote-count-grammar">s</span></div>
- </div>
- </div>
- </div>
- </div>
- <div class="b-example-divider"></div>
- <div class="d-grid gap-2 col-6 mx-auto">
- <button class="btn btn-primary" type="button">Get a New Quote!</button>
- </div>
- <div class="b-example-divider"></div>
- <div class="invisible" id="debug"></div>
- <div class="container">
- <footer class="py-3 my-4">
- <p class="text-center text-muted">© <span class="year">1999</span> Joel Lipman Ltd. All Rights Reserved</p>
- </footer>
- </div>
- <script src="./js/jquery-3.3.1.slim.min.js" crossorigin="anonymous"></script>
- <script src="./js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
- <script src="./js/widgetsdk-min.js" crossorigin="anonymous"></script>
- <script src="./js/main.js" crossorigin="anonymous"></script>
- </body>
- </html>
the JavaScript
These are the contents of main.js and is working at time of print:
// Start with Zoho SDK stuff ZOHO.CREATOR.init() .then(function(data) { // Initialize var v_ShopifyID = ""; var v_ShopifyEmail = ""; var v_GreetingName = ""; // Try and catch try{ // Get Creator Parameters Passed in URL - Cannot use window.location URL var queryParams = ZOHO.CREATOR.UTIL.getQueryParams(); // Check parameters were even specified if (queryParams.p_ShopifyEmail !== undefined) { v_ShopifyEmail = queryParams.p_ShopifyEmail; } if (queryParams.p_ShopifyID !== undefined) { v_ShopifyID = queryParams.p_ShopifyID; } // Setup a config to find this record (use a custom report that only has the 2 searchable fields) var config = { appName : "joes_quote_builder", reportName : "Contacts_Report_SearchableByWidget", criteria : "(Primary_Email == \"" + v_ShopifyEmail + "\" && Shopify_ID == \"" + v_ShopifyID + "\")", page : 1, pageSize : 10 } // find the logged-in user record ZOHO.CREATOR.API.getAllRecords(config) .then(function(response) { // store the search results into an array var recordArr = response.data; // quick loop to find the preferred name for(var index in recordArr){ v_GreetingName = recordArr[index].Nick_Name; break; } // if not blank then let's display it if(v_GreetingName != "") { // can't seem to do jQuery references within this SDK document.getElementById('greeting-name').innerHTML = v_GreetingName; } }); } catch(e) { document.getElementById('debug').innerHTML = "Sorry! We don't have any matching records."; } }); /* SAMPLE TO GET ONE RECORD - Working as of March 2024 ZOHO.CREATOR.init() .then(function(data) { var config = { appName : "joes_quote_builder", reportName : "All_Customers", id : "123456789012345678" } ZOHO.CREATOR.API.getRecordById(config) .then(function(response) { document.getElementById('greeting-name').innerHTML = response.data.Name; console.log(response); }); }); */ /* ************************************ */ /* Some non-Zoho JavaScript */ /* Set Copyright Year */ var today = new Date(); var currentYear = today.getFullYear(); $('footer span.year').text(currentYear);
- // Start with Zoho SDK stuff
- zoho.creator.init()
- .then(function(data) {
- // Initialize
- var v_ShopifyID = "";
- var v_ShopifyEmail = "";
- var v_GreetingName = "";
- // Try and catch
- try{
- // Get Creator Parameters Passed in URL - Cannot use window.location URL
- var queryParams = zoho.creator.UTIL.getQueryParams();
- // Check parameters were even specified
- if (queryParams.p_ShopifyEmail !== undefined) {
- v_ShopifyEmail = queryParams.p_ShopifyEmail;
- }
- if (queryParams.p_ShopifyID !== undefined) {
- v_ShopifyID = queryParams.p_ShopifyID;
- }
- // Setup a config to find this record (use a custom report that only has the 2 searchable fields)
- var config = {
- appName : "joes_quote_builder",
- reportName : "Contacts_Report_SearchableByWidget",
- criteria : "(Primary_Email == \"" + v_ShopifyEmail + "\" && Shopify_ID == \"" + v_ShopifyID + "\")",
- page : 1,
- pageSize : 10
- }
- // find the logged-in user record
- zoho.creator.API.getAllRecords(config)
- .then(function(response) {
- // store the search results into an array
- var recordArr = response.data;
- // quick loop to find the preferred name
- for(var index in recordArr){
- v_GreetingName = recordArr[index].Nick_Name;
- break;
- }
- // if not blank then let's display it
- if(v_GreetingName != "")
- {
- // can't seem to do jQuery references within this SDK
- document.getElementById('greeting-name').innerHTML = v_GreetingName;
- }
- });
- } catch(e) {
- document.getElementById('debug').innerHTML = "Sorry! We don't have any matching records.";
- }
- });
- /* SAMPLE TO GET ONE RECORD - Working as of March 2024
- zoho.creator.init()
- .then(function(data) {
- var config = {
- appName : "joes_quote_builder",
- reportName : "All_Customers",
- id : "123456789012345678"
- }
- zoho.creator.API.getRecordById(config)
- .then(function(response) {
- document.getElementById('greeting-name').innerHTML = response.data.Name;
- console.log(response);
- });
- });
- */
- /* ************************************ */
- /* Some non-Zoho JavaScript */
- /* Set Copyright Year */
- var today = new Date();
- var currentYear = today.getFullYear();
- $('footer span.year').text(currentYear);
Error(s) Encountered:
- Error Status 401 Code 1030: Authorization Failure. The access token is either invalid or has expired. Please check your Zoho Account for more information. If your widget sits on an external server, it won't connect. I have tried publishing the Creator Widget page (doesn't work); then I have tried embedding the Widget page into another published Creator page (idem).
- CORS Policy error(s): Check your design as you can have a JS widget talking to Zoho. You won't be able to pull/download files, even published ones, but you can display published image URLs (see Zoho Creator API 2.1: Download File).
- Refused to connect to because it violates the following Content Security Policy directive: "connect-src don't bother. You're probably on an external server. Create your own web app on another server and setup a server to server API connection to your Zoho Creator instance.
Source(s):
- Zoho Creator: Create your first widget
- Zoho Community: Zoho Creator: Get a URL parameter inside a widget
- Zoho Creator: JS API documentation
- Joel Lipman: Zoho CRM: APIv2 using PHP & cURL