Working with fields and navigation

This topic describes how to extract field values from a Qlik Sense application and make selections on them in various ways.

It also illustrates how to use some of the Qlik Sense navigation features such as Back, Forward and Clear.

In this sample we also use an external module called handlebars which is a JavaScript library allowing 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. This is also discussed in more detail in the Loading external and internal modules example where we actually remove some handlebars specific helper functionality into an external module.

  1. We start by defining the HTML file (FieldSelections.html).

    <html lang="en">
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>Qlik Sense: Mashup</title>
        <meta charset="utf-8">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <meta name="HandheldFriendly" content="True">
        <meta name="MobileOptimized" content="320">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black">
        <meta http-equiv="cleartype" content="on">
         <!--Add host to these 2 links if the mashup is on an external webserver-->
        <link rel="stylesheet" href="../../resources/autogenerated/qlik-styles.css">
        <script src="/resources/assets/external/requirejs/require.js"></script
    <style>
            body {
                line-height: 1.5em;
            }
    
            th {
                font-weight: bold;
                text-align: left;
            }
    
            body, th, td {
                font-family: Verdana, Geneva, Tahoma, sans-serif;
                font-size: 0.8em;
            }
    
            #error {
                color: red;
            }
    
            #field1, #field2, #field3
            {
                float:left;
                margin-right:20px;
                border: solid 1px #bdbdbd;
            }
    
        </style>
    </head>
    <body>
      	<!-- Handlebars template for a simple list box rendering -->
        <script id="Example-Field-Template" type="text/x-handlebars-template">
            <div>
                <div style="font-size:1.2em; font-weight:bold">{{name}}</div>
                <div><a class="Example-SelectPossible" field="{{name}}" href="#">Select Possible</a></div>
                <div><input inputfieldname="{{name}}" /><a class="Example-SelectMatch" field="{{name}}" href="#">Select Match</a></div>
    
                <table class="fieldList">
                    <tr>
                        <th>qElemNumber</th>
                        <th>qText</th>
                        <th>qState</th>
                        <th>Select</th>
                        <th>Toggle</th>
                    </tr>
                    {{#each items}}
                    <tr>
                        <td>{{qElemNumber}}</td>
                        <td>{{qText}}</td>
                        <td>{{qState}}</td>
                        <td><a class="Example-Select" href="#" field="{{../name}}" value="{{qElemNumber}}">Select</a></td>
                        <td><a class="Example-Toggle" href="#" field="{{../name}}" value="{{qText}}">Toggle</a></td>
                    </tr>
                    {{/each}}
                </table>
            </div>
        </script>
       
    
        <h1>Working With Fields and Navigation</h1>
        <div id="btnrow">
            <button id="clearAll">Clear All</button>
            <button id="back">Back</button>
            <button id="forward">Forward</button>
            <button id="lockAll">Lock</button>
            <button id="unlockAll">Unlock</button>
        </div>
        <hr/>
        <div>
            <div id="field1"></div>
            <div id="field2"></div>
            <div id="field3"></div>
        </div>
        
        <div id="error"></div>
    
    </body>
      
    </html>

    The main point of interest is the handlebars template which defines a simple layout for a HTML table based listbox which can then be populated with actual data which results in something like this:

    Note: Full details on how handlebars templates are constructed can be found on their website.
  2. The other file needed for this sample is the FieldSelections.js file.

    var config = {
        host: window.location.hostname,
        prefix: "/",
        port: window.location.port,
        isSecure: window.location.protocol === "https:"
    };
    require.config( {
    	baseUrl: ( config.isSecure ? "https://" : "http://" ) + config.host + (config.port ? ":" + config.port: "") + config.prefix + "resources",
      paths: {
       handlebars:"//cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.3/handlebars.min"
      }
    } );
    
    require( ["js/qlik","underscore","handlebars"], function (qlik,us,hb) {
      var app;
    
      
      function getTemplateRunner(templateId, targetId) {
        var source = $(templateId).html();
        var template = hb.default.compile(source);
        var target = $(targetId);
        
        return function (context) {
          var html = template(context);
          target.html(html);
        };
      } 
     
      function getAllFieldValues(listQVObjects) {
        return us.map(listQVObjects.qDataPages, function (p) {
          return us.map(p.qMatrix, function (x) {
            return x[0];
          });
        })[0];
      }
    
      function getFieldDef(fieldName, noRows) {
        return {
          qDef: {
            qFieldDefs: [fieldName]
          },
          qInitialDataFetch: [{ qTop: 0, qLeft: 0, qHeight: noRows, qWidth: 1 }]
        };
      }
      
      function wireUpClickHandler(selector, func) {
       $(selector).click(function (a) {
         var elem = $(a.target);
         
         var value = elem.attr("value");
         var fieldName = elem.attr("field");
         
         func(fieldName, value);
       });
      }
    
      qlik.getGlobal(config).getAppList(function(list){
        var appId;
        $.each(list, function(key,value){
          if(value.qDocName==="Helpdesk Management.qvf"){
            appId = value.qDocId;
          }
        });
    
        function getQlikApp(){
          return qlik.openApp(appId,config);
        }
        
    
        function createListBox(app, fieldName, elementId){
          if(!app)
          {
            app=getQlikApp();
          }
          var templateRunner = getTemplateRunner("#Example-Field-Template", elementId);
          app.createList(getFieldDef(fieldName,100),function(r){
            var elems = getAllFieldValues(r.qListObject);
            console.log(r.qListObject);
            console.log(elems);
            templateRunner({ items: elems, name: r.qListObject.qDimensionInfo.qFallbackTitle });
            
            wireUpClickHandler(elementId + " a.Example-Select", function (fieldName, value) {
              app.field(fieldName).select([parseInt(value)]);
            });
            
            wireUpClickHandler(elementId + " a.Example-Toggle", function (fieldName, value) {
              app.field(fieldName).toggleSelect(value);
            });
            
            wireUpClickHandler(elementId + " a.Example-SelectPossible", function (fieldName, value) {
              app.field(fieldName).selectPossible();
            });
            
            wireUpClickHandler(elementId + " a.Example-SelectMatch", function (fieldName, value) {
              var input = $("[inputFieldName='" + fieldName + "']");
              var match = $(input).val();
              app.field(fieldName).selectMatch(match);
            });
            
            
          });
        } 
    
        createListBox(app, "Priority","#field1");
        createListBox(app, "Case Owner Group","#field2");
        createListBox(app, "Case Owner","#field3");
    
      });  
    
      qlik.setOnError( function ( error ) {
        alert( error.message );
      });
    
    
    
    });
  3. We create paths for the handlebars module using the hosted version of their library.

    We then bootstrap the functionality with a call to:

    require(["js/qlik"], function (qlik) {
  4. Once the passed function runs we are able to load the other modules on which the mashup relies:

    require( ["js/qlik","underscore","handlebars"], function (qlik,us,hb) {
  5. Now is the time to create the following helper functions:

      function getTemplateRunner(templateId, targetId) {
        var source = $(templateId).html();
        var template = hb.default.compile(source);
        var target = $(targetId);
        
        return function (context) {
          var html = template(context);
          target.html(html);
        };
      }

    This is a function which returns a new function which, when called, will apply the data (the context parameter) passed to the handlebars template defined in the HTML file and inject the result into the HTML element specified.

  6.   function getAllFieldValues(listQVObjects) {
        return us.map(listQVObjects.qDataPages, function (p) {
          return us.map(p.qMatrix, function (x) {
            return x[0];
          });
        })[0];
      }

    This function uses the underscore js map function to convert the deeply nested data structure supplied to the callback function of the qlik.app.createList function to a straight array containing all the field values.

  7.   function getFieldDef(fieldName, noRows) {
        return {
          qDef: {
            qFieldDefs: [fieldName]
          },
          qInitialDataFetch: [{ qTop: 0, qLeft: 0, qHeight: noRows, qWidth: 1 }]
        };
      }

    The getFieldDef function is a simple helper to create the data structure expected by the qlik.app.createList function.

  8.   function wireUpClickHandler(selector, func) {
       $(selector).click(function (a) {
         var elem = $(a.target);
         
         var value = elem.attr("value");
         var fieldName = elem.attr("field");
         
         func(fieldName, value);
       });
      }

    This function takes a jQuery selector and wires up the click handler to all matching elements, running the callback passed in as the second parameter when the click event has occurred and passing in the values of the field and value attributes on the element.

  9. Next we run the main part of the application:

      qlik.getGlobal(config).getAppList(function(list){
        var appId;
        $.each(list, function(key,value){
          if(value.qDocName==="Helpdesk"){
            appId = value.qDocId;
          }
        });
    
        function getQlikApp(){
          return qlik.openApp(appId,config);
        }
        
    
        function createListBox(app, fieldName, elementId){
          if(!app)
          {
            app=getQlikApp();
          }
          var templateRunner = getTemplateRunner("#Example-Field-Template", elementId);
          app.createList(getFieldDef(fieldName,100),function(r){
            var elems = getAllFieldValues(r.qListObject);
            console.log(r.qListObject);
            console.log(elems);
            templateRunner({ items: elems, name: r.qListObject.qDimensionInfo.qFallbackTitle });
            
            wireUpClickHandler(elementId + " a.Example-Select", function (fieldName, value) {
              app.field(fieldName).select([parseInt(value)]);
            });
            
            wireUpClickHandler(elementId + " a.Example-Toggle", function (fieldName, value) {
              app.field(fieldName).toggleSelect(value);
            });
            
            wireUpClickHandler(elementId + " a.Example-SelectPossible", function (fieldName, value) {
              app.field(fieldName).selectPossible();
            });
            
            wireUpClickHandler(elementId + " a.Example-SelectMatch", function (fieldName, value) {
              var input = $("[inputFieldName='" + fieldName + "']");
              var match = $(input).val();
              app.field(fieldName).selectMatch(match);
            });
            
            
          });
        } 
    
        createListBox(app, "Priority","#field1");
        createListBox(app, "Case Owner Group","#field2");
        createListBox(app, "Case Owner","#field3");
    
      });

    This opens the Helpdesk application and then attaches three fields to three instances of the handlebar template which causes them to be injected into the HTML elements #field1, #field2 and #field3.

If you run the above sample you should see something like the following:

Did this information help you?

Thanks for letting us know. Is there anything you'd like to tell us about this topic?

Can you tell us why it did not help you and how we can improve it?