Loading external and internal modules
This topic describes how to load both an external third party module as well as a locally defined custom one and use them together to generate some simple web content from Qlik Sense.
The external module we are using is handlebars, a JavaScript library which allows you to define semantic templates in your html and populate them easily with data from your JavaScript code, keeping a clean separation between data and UI.
-
We start by defining the HTML file (default.html).
<html lang="en"> <head> <title>Loading External & Internal Modules</title> <script data-main="script" src="/resources/assets/external/requirejs/require.js"></script> <style> body { line-height:1.5em; } #refresh { cursor:pointer; color:blue; margin-top:10px; } th{ font-weight:bold; text-align:left; } body, th, td { font-family:Verdana, Geneva, Tahoma, sans-serif; font-size:0.8em; } #error{color:red;} </style> </head> <body> <script id="handlebars-documents-template" type="text/x-handlebars-template"> <span>Refreshed {{count}} times</span> <div> <h1>{{title}}</h1> {{#if show}} There are {{items.length}} documents. {{/if}} <table> <tr><th>Doc Id</th><th>Doc Name</th><th>File Size</th></tr> {{#each items}} <tr><td>{{qDocId}}</td><td>{{qDocName}}</td><td>{{qFileSize}}</td></tr> {{/each}} </table> </div> </script> <div id="target"></div> <span id='refresh'>Reload Data</span> <div id="error"></div> </body> </html>
Full details on how handlebars templates are constructed can be found on their website but as shown above, we are simply binding named fields from a JavaScript object into HTML elements. We will see below how and where this data object is constructed.
-
We now define our own JavaScript module by creating my_module.js with the following content:
define(["handlebars", "jquery"], function (hb, jq) { var hb = hb.default; return { // // Attach a function to our module which returns a new function which // evaluates a particular template and applies it to a particular html // element. // getTemplateRunner: function (templateId, targetId) { var source = jq(templateId).html(); var template = hb.compile(source); var target = jq(targetId); return function (context) { var html = template(context); target.html(html); } } }; });
The line:
define(["handlebars", "jquery"], function (hb, jq) {
defines a module which ‘depends on’ both handlebars and jQuery, it will run the function passed as a second argument when both these modules are loaded passing in a parameter representing each module.
This function then returns the object which is our custom module. In our case we simply return an object with a single function getTemplateRunner which is then utilized in script.js below.
This function takes the id of a HTML element containing a handlebars template, the id of a HTML element in which to inject the results of running the template and returns a new function which will then apply this template for a given data object.
-
Finally, we create the script.js file to tie everything together:
var config = { host: 'QSE_domain', prefix: "/", port: 443, isSecure: true // webIntegrationId: 'web-integration-id-here' // only needed in SaaS editions }; const baseUrl = ( config.isSecure ? 'https://' : 'http://' ) + config.host + (config.port ? ':' + config.port : '') + config.prefix; require.config({ baseUrl: `${baseUrl}resources`, paths: { // // CDN versions listed here: http://cdnjs.com/libraries/handlebars.js/ // Don't forget to remove .js extension for requirejs path. // handlebars: "//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars.amd.min", myModule: "/extensions/example_modules/my_module" } }); require(["js/qlik"], function (qlik) { require(["jquery", "myModule"], function (jq, mod) { var templateRunner = mod.getTemplateRunner("#handlebars-documents-template", "#target"); var bindCount = 0; function bindAppList(list) { // Structure of this object matches handlebars template in html. var data = { items: list, show: (list.length > 0), count: ++bindCount }; templateRunner(data); } $('#refresh').click(function () { qlik.getAppList(bindAppList); }); qlik.getAppList(bindAppList); qlik.on("error", function (e) { jq("#error").html("Qlik Error: " + e.message); }); }); });
We first create paths for both the handlebars module (using the hosted version of their library) as well as our own locally defined module on the RequireJS config object.
We then bootstrap the functionality with a call to:
require(["js/qlik"], function (qlik) {
Once the passed function runs we are able to load jQuery and our own custom module:
require(["jquery", "myModule"], function (jq, mod) {
After which point we make use of our custom module:
var templateRunner = mod.getTemplateRunner("#handlebars-documents-template", "#target");
We now have a function which we can pass a data object to to be boud to our UI. For example we could simply do:
templateRunner({items:[{qDocId:1, qDocName:"My Doc", qFileSize: 0}], show: true, count: 1);
But of course we want this populated from Qlik Sense so we create a function to do this:
var bindCount = 0; function bindAppList(list) { // Structure of this object matches handlebars template in html. var data = { items: list, show: (list.length > 0), count: ++bindCount }; templateRunner(data); } $('#refresh').click(function () { qv.getAppList(bindAppList);
Additionally in the above we have wired up a ‘button’ which can be clicked to trigger a refresh of the data.