1 2 Previous Next

Qlik Design Blog

27 Posts authored by: Yianni Ververis

After reading Michael's wonderful post on the 3.2 features, https://community.qlik.com/blogs/qlikviewdesignblog/2017/04/04/introducing-qlik-sense-32?et=blogs.comment.created#commen…, I admit, I wanted to get into more details on each of the topics he mentioned. Even though this is impossible since there are so many goodies hidden in 3.2, I decided to focus more on the properties panel while creating custom extensions.

 

Some of these will make us retouch some of our extensions since, personally, I used workarounds like custom color in an input field as hex, inject dropdowns as html etc

 

Below I show the latest properties and at the end I attach an extension with everything working... Please note that, as the help pages suggest, some of these are "considered EXPERIMENTAL and may be subject to change or be removed in future releases."

For more details, please bookmark Qlik Sense Developer's help page

http://help.qlik.com/en-US/sense-developer/3.2/Subsystems/APIs/Content/extensions-api-reference.htm

 

SIMPLE TEXT DESCRIPTIONInteger
2017-04-28 23_07_23-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png2017-04-28 23_08_10-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png

text: {

    label:"This is a description for the properties panel (Text Component)",

    component: "text"

},

fontSize: {

    type: "integer",

    expression: "none",

    label: "Font Size (Integer)",

    defaultValue: "10",

    ref: "vars.fontSize"

},


 

INPUT TEXTBUTTON
2017-04-28 23_09_25-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png2017-04-28 23_09_40-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png

inputText: {

    type: "string",

    expression: "none",

    label: "String (Input Text)",

    defaultValue: "This is a test app to checkout custom properties",

    ref: "vars.inputText"

},

button: {

    label:"My Button (Button Component)",

    component: "button",

    action: function(data){

        alert("My visualization extension name is '"+data.visualization+"' and have id '"+data.qInfo.qId+"'.");

    }

},

 

BUTTON GROUPHeader 2
2017-04-28 23_09_54-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png2017-04-28 23_10_06-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png

weight: {

    type: "string",

    component: "buttongroup",

    label: "Font Weight (Button Group)",

    ref: "vars.weight",

    options: [{

        value: "bold",

        label: "Bold",

        tooltip: "Select for Bold text"

    }, {

        value: "normal",

        label: "Normal",

        tooltip: "Select for normal text"

    }],

    defaultValue: "normal"

},

show: {

    type: "boolean",

    label: "Show extra div? (boolean)",

    ref: "vars.show",

    defaultValue: false

},

 

COLOR PICKERDROP DOWN
2017-04-28 23_10_14-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png2017-04-28 23_10_24-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png

colorPicker: {

    label:"Background (Color-picker)",

    component: "color-picker",

    ref: "vars.color",

    type: "integer",

    defaultValue: 0

},

dropDown: {

    type: "string",

    component: "dropdown",

    label: "Select Options (dropdown)",

    ref: "vars.dropDown",

    options: [{

        value: "option1",

        label: "Option 1",

        tooltip: "Select for Option 1"

    }, {

        value: "option2",

        label: "Option 2",

        tooltip: "Select for Option 2"

    }, {

        value: "option3",

        label: "Option 3",

        tooltip: "Select for Option 3"

    }],

    defaultValue: "option2"

},

 

LINKSLIDER
2017-04-28 23_10_40-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png2017-04-28 23_10_59-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png

link: {

    label:"http://help.qlik.com/en-US/sense-developer/3.2/Subsystems/APIs/Content/ExtensionAPI/property-definition-link.htm",

    component: "link",

    url:"http://help.qlik.com/en-US/sense-developer/3.2/Subsystems/APIs/Content/ExtensionAPI/property-definition-link.htm"

},

slider: {

    type: "number",

    component: "slider",

    label: "Letter Spacing (Slider)",

    ref: "vars.slider",

    min: 1,

    max: 10,

    step: 1,

    defaultValue: 1

},

 

RANGE SLIDERSWITCH
2017-04-28 23_11_07-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png2017-04-28 23_11_14-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png

rangeSlider: {

    type: "array",

    component: "slider",

    label: "Range slider",

    ref: "vars.rangeSlider",

    min: 1,

    max: 20,

    step: 1,

    defaultValue: [8, 17]

},

switch: {

    type: "boolean",

    component: "switch",

    label: "Show Border (Switch)",

    ref: "vars.switch",

    options: [{

        value: true,

        label: "Show"

    }, {

        value: false,

        label: "Hide"

    }],

    defaultValue: false

},

 

TEXT AREAARRAYS
2017-04-28 23_11_23-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png2017-04-28 23_11_40-Helpdesk Management - Google Charts _ Sheets - Qlik Sense.png

textarea: {

    label:"Textarea",

    component: "textarea",

    rows: 7,//the amount of rows in the textarea component (default is 3)

    maxlength: 100,//will not allow more than 100 characters

    ref: "vars.textarea",

    defaultValue: "This can be your fottnote/legend to your visualizations"

},

MyList: {

    type: "array",

    ref: "listItems",

    label: "List Items",

    itemTitleRef: "label",

    allowAdd: true,

    allowRemove: true,

    allowMove: true,

    addTranslation: "Add Item",

    items: {

        label: {

            type: "string",

            ref: "label",

            label: "Label",

            expression: "none"

        },

        textarea: {

            label:"My textarea",

            component: "textarea",

            maxlength: 100,//you shouldn't write too much

            ref: "myTextarea"

        }

    }

}

 

Attached find the extension.

 

Yianni

Yianni Ververis

BiPartite Extension

Posted by Yianni Ververis Mar 31, 2017

One interesting Qlik Sense extension that we have successfully used, is the BiPartite one. We have used it in couple of mashups like the UK Migration http://webapps.qlik.com/telegraph/uk-migration/index.html.

 

I like this one since you have a visual representation of all the values, selected and not and there is a nice animation moving from one dimension value to the next.

2017-03-30 14_00_01-_UKMigration _ Sheets - Qlik Sense.png

 

This is a responsive extension with minimum view of 320px. You add One dimension for the left column and one for the right and then one measure for the values in each column.


You can write your own labels for each column and add your custom coloring palette.


That's it! Give it a try


Branch : http://branch.qlik.com/#!/project/58b820c55efd3b8f0b6743ce

Git: https://github.com/yianni-ververis/SenseUI-BiPartite


Another great mashup from the Demo Team!

 

After my favorite CIO where we introduced KPIs into the navigation, salesforce is beautiful, elegant and made mostly of custom extensions!

 

I started with the custom Selection toolbar (Angular Directive) that I introduced in the CIO dashboard. This changes based on the number of selections, if it's only one item of that dimension selected, or more than 6, it displays "Dimension: item selected". If there are more than one, but less than 6 items selected, it changes to a dropdown menu with the x button to deselect it. This is listening to selections, which means that whatever selections are made in the webpage, they will be displayed here as well.

2017-03-03 08_51_07-Salesforce.png

Then, we needed Sense like filters but as drop down menus (Angular Directive). I have done this with the UN Gender Statistics site but this one is more advanced and stays open until you finish making all selections and sorts them by state. It scrolls if many items.

2017-03-03 08_56_03-Salesforce.png

 

Another issue we were faced with, was that we created a combo chart in Sense but we needed to customize the colors to match our webpage. This lead me to create the Combo Chart extension. This not only gives you the option of changing colors but, hover states, borders, set bar widths, dot widths, has a better display of the labels etc.

2017-03-03 11_23_51-Salesforce_Mashup_Demo_Team _ Sheets - Qlik Sense.png

 

In the same page, we had to display and compare two vertical bar charts. The problem is that when we placed the out-of-the-box ones, the y-axis max were different thus visually misleading.  So I used the combo chart with only one measure and set the max to achieve the desired results

2017-03-03 09_32_24-_Salesforce_Mashup_Demo_Team - Dashboard 2 _ App overview - Qlik Sense.png

2017-03-03 11_26_06-Salesforce.png

 

Moving on to the next page, we had two more issues to solve.

We needed to have a Stacked bar Chart based on the value of the measure and not the number of measures. For this, I created the SenseUI-ValueStackedbarChart. I used the same tooltips, coloring, hover states etc as I used in my other extension like my most popular one, SenseUI-BarChart.

2017-03-03 10_27_46-_Salesforce_Mashup_Demo_Team - Opportunities 1 _ Sheets - Qlik Sense.png

The other issue was that we needed a horizontal menu for quarters and years. For this I used an older extension, the SenseUI-Filter.

2017-03-03 10_47_36-Salesforce.png

Lastly, I used my SenseUI-BarChart extension for all of the horizontal bars. I used the same bar color, border and hover color to match the rest of the website's objects.

2017-03-03 14_04_16-Salesforce.png

The mashup is found at http://webapps.qlik.com/salesforce/index.html

Please note that this is a work in progress. I still have to work on the mobile version and some other UI issues

 

Extensions mentioned here:

Senseui-ComboChart

SenseUI-ValueStackedbarChart

SenseUI-BarChart

SenseUI-Filter

 

For more of our work, you can go to webapps.qlik.com

 

Yianni

As we were working on one of our projects, we had the need of creating multiple combo charts. The problem was that we could not change the colors to match our mashup/webpage. So, I decided to make an extension, that gives full control of the chart appearance and it's labels. The same as my Barchart extension this is wrapping text labels underneath the bars. If you still have problems with the text, then just increase the width of the bars and everything should be back in place.

 

Installation

- [Download zip file](https://github.com/yianni-ververis/SenseUI-ComboChart/archive/master.zip)

- Desktop - Unzip at the extension folder (C:\Users\<user>\Documents\Qlik\Sense\Extensions\SenseUIComboChart)

- Server - Upload the zip file

 

 

Usage

 

Dimensions and Measures

- Set 1 dimension for the x Axis

- Set 1 Measure for an additional Line Chart

 

 

General Settings

- Font Size

- Font Color

- Display the Legend

- Enable/Disable Selections

 

 

Bar Chart Settings

- Bar Color

- Bar Hover Color

- Bar Width (0 for auto scaling)

- Bar Border Color

- Bar Border Width

 

 

Line Chart Settings

- Line Color

- Line Width

- Dot Color

- Dot Stroke Color

- Dot Stroke Width

- Dot Radius

 

preview2.png

 

preview2.png

 

Barnch: http://branch.qlik.com/#!/project/5894e07a75db504d76ac3123

Git: GitHub - yianni-ververis/SenseUI-ComboChart: Qlik Sense Combo Chart

 

Yianni

UN was one of my most challenging projects in many aspects. This was not just a simple mashup or a simple webpage that uses the Capabilities Api and app.getObject() to display Qlik Sense objects. It was an entire solution that required many teams to be involved and share their expertise.

 

One of the most interesting problems I faced, was the structure of the qvf. In order for most of the charts to work, there had to be a group of selections made and in some cases, in a specific order.

 

For this example, I will talk about the chart on the Indicators page.

 

First we have to select the field "domain" and then get all of the available "Tier" in order to populate the drop down.

2017_01_06_11_22_06_UN_Gender_Statistics1.png

 

app.obj.app.field('DomainName').select([value], false, false)
.then(function(){
     me.getTier();
});



 

Then to get the HyperCube for the Field "Tier"

  me.getTier = function () {
       api.getFieldDataQ('Tier').then(function (data) {
          $scope.Tier = _.filter(data, function(obj){
          return (obj[0].qState!=='X' && obj[0].qState!=='L')?true:false;
     });
       $scope.selectTier($scope.Tier[0][0].qNum);
  });
  }

 

and populate the drop down with Angular's ng-repeat

<div class="dropdown" id="dropdownTier">
  <label>Select Tier:</label>
  <button class="btn btn-default dropdown-toggle btn-block text-left" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
  {{selection.Tier}}
  <span class="caret pull-right"></span>
  </button>
  <ul class="dropdown-menu scrollable-menu" aria-labelledby="dropdownTier">
  <li ng-repeat="item in Tier" ng-class="(selection.Tier==item[0].qNum) ? 'active' : ''"><a ng-click="selectTier(item[0].qNum)">{{ item[0].qText }}</a></li>
  </ul>
  </div>

 

Like this

2017_01_06_11_45_52_UN_Gender_Statistics.png

Then we select the first one so get the the data from the field "seriesName" as indicators for only that tier

$scope.selectTier = function (value) {
  $scope.selection['Tier'] = value;
  app.obj.app.field('Tier').selectValues([value], false, false)
  .then(function(){
  me.getIndicator();
  })
  }

 

me.getIndicator = function () {
  api.getHyperCubeQ(['IndicatorID','SeriesName'],[]).then(function(data3){
  $scope.SeriesName = _.filter(data3, function(obj3){
  return (obj3[1].qState!=='X' && obj3[1].qState!=='L')?true:false;
  });
  if (!$scope.SeriesName.length) {
  $scope.selectionDisplay.SeriesName = "No Data";
  $scope.selection.SeriesName = null;
  } else {
                        $scope.selectIndicator($scope.SeriesName[0][0].qNum, $scope.SeriesName[0][1].qText);
  }
  })
  }

 

and populate the indicator drop down

  <div class="dropdown" id="dropdownSeriesName">
  <label>Select Indicator:</label>
  <button class="btn btn-default dropdown-toggle btn-block text-left" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
  {{ selectionDisplay.SeriesName }}
  <span class="caret pull-right"></span>
  </button>
  <ul class="dropdown-menu scrollable-menu" aria-labelledby="dropdownSeriesName">
  <li ng-repeat="item in SeriesName" ng-class="(selection.SeriesName===item[1].qText) ? 'active' : ''"><a ng-click="selectIndicator(item[0].qNum, item[1].qText)"> {{ item[0].qText }}-{{ item[1].qText }}</a></li>
  </ul>
  </div>

 

Like this

2017_01_06_17_37_41_UN_Gender_Statistics.png

 

Last, select the indicator to get the final chart

2017_01_06_18_34_35_UN_Gender_Statistics.png

 

The entire thing takes about 3 seconds to load but there is no delay to the common user because while the selections are made the object is loading and the canvas is drawn.

 

The Angular Service Api that I use for HyperCubes is here

capabilities-api-angular-template/api.js at master · yianni-ververis/capabilities-api-angular-template · GitHub

 

URL: https://genderstats.un.org/#/indicators

 

Yianni

Qlik Sense is headed to a more flexible and customizable environment regarding the interface. With Qlik Sense 3 and above, we can manually change the default Theme or create a custom one and use it on our apps by just adding /theme/<themename> at the end of the url.

 

First of all, for those that do not know, once Qlik Sense desktop is running, we can access the hub from a regular browser like http://localhost:4848/hub/my/work. Now if we add our theme at the end of the url, every app that we open, will use that theme. So, if we go to http://localhost:4848/hub/my/work/theme/yianni, this will open the hub. Every app like the "helpdesk Management" that is opened, will use that theme.


I have created a simple theme and I only changed the background color of the sheet container and it looks like this

test.png

 

In Qlik Sense 3.1.2 and above, the theme templates have been moved to C:\Users\<username>\AppData\Local\Programs\Qlik\Sense\Client\themes\old. So we can place our files there.

 

Some of us, including myself, have used the Theme.palette in our extensions like

define([
     "qlik",
     "jquery",
     "core.utils/theme"
], function (qlik, $, Theme) {


 

The above would have to change to

define([
     "qlik",
     "jquery",
     "text!themes/old/sense/theme.json"
], function (qlik, $, Theme) {
     Theme = JSON.parse(Theme);


While I was working on one of my projects that has lots of kpis and charts, I thought what if, at least for the page change, I add a simple transition. While this can accomplished easily with Angularjs, I went a little bit further and I added transitions on all of the page objects. The best part of this, is not that its only interesting but also very very easy to put together. It's all CSS3 based, so no Javascript will be needed anywhere.

 

I used the css library Animate.css. First, let's start by adding it into our html page

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css">



 

Then comes the very hard part . Add the css class "animated" and the type of animation that you want in your object's div container. In this project I used zoom in from the right for each of the page objects:

<div class="col-md-2 no-margin-padding-right animated zoomInRight ms500">



 

To make it more interesting, since I have a side navigation for all my Qlik Sense filters, I decided to add a little drop/bounce effect.

 

Now, here comes the important stuff. Since there are a lot of animations, it is not good to get our audience confused or tired from all of the things that are happening. There has to be a sequence of things. Since most of the people are reading from left to right, it makes sense to give to the user the same experience, with animations happening one after the other, from left to right so the eye can follow them. Not too slow so its not boring and not too fast so its not confusing. You can achieve that by delaying each object few milliseconds with the "animation-delay" property (CSS3 animation-delay Property).

 

I found out that 250ms between each animation, was very close to what I wanted to achieve. So, I created a lot of css classes with each being 250 milliseconds apart, like "ms250", "ms500" etc. and I added them into each of my objects.

 

This is not perfect since there is a difference from loading stating text and giving it a few milliseconds delay, from Qlik Sense objects that need some time to get pulled and drawn from our websocket connection.


Here is the work in progress. The filters bounce only once when the entire app/mashup loads since they are the same on every page, but the objects inside the actual page are hidden and then zooming in on every page transition.

CIO Dashboard

 

Yianni

As I was working on one of my latest mashups, I had a task, to get all cells with numerous sets of dimensions and measures, with a HyperCube out of Qlik Sense. The worst part is that one of my sets, had a total of 3+ million cells!

 

We know that the initial HyperCube returns only up to ~10k cells, So the question is how do we paginate to get the rest or even get them all in one call!

 

We also know that, most of the Capability API methods are using promises, something very helpful, especially if we make a lot of calls to Qlik Sense and expect to return the results only when all of the promises have been resolved. This is where promises “.all()” come into play.

 

Even though ES6 has promises build in, not all browsers support it yet, so I will use Angular's $q to handle those promises.

 

First we have to make the initial call and get the maximum rows and columns. We start by creating the cube with the dimensions

 

app.createCube({
     qDimensions : qDimensions,
     qMeasures : qMeasures
}).then(function(model) {


 

Once we have the model ready, we make the initial call to get the first set of results and get the HyperCube info like total columns and total rows

 

model.getHyperCubeData('/qHyperCubeDef', [{qTop: 0, qWidth: 20, qLeft: 0, qHeight: 500}]).then(function(data) {


 

Once we have that we can determine how many total pages do we have

 

var columns = model.layout.qHyperCube.qSize.qcx;
var totalheight = model.layout.qHyperCube.qSize.qcy;
var pageheight = Math.floor(10000 / columns);
var numberOfPages = Math.ceil(totalheight / pageheight);


 

Now, if we got all of the results, we should end the method and return those results

 

if (numberOfPages==1) {
     deferred.resolve(data.qDataPages[0].qMatrix);
}


 

Otherwise, we should get the data from all pages and then return the results! Promises in full glory!!!

 

var Promise = $q;
var promises = Array.apply(null, Array(numberOfPages)).map(function(data, index) {
var page = {
          qTop: (pageheight * index) + index,
          qLeft: 0,
          qWidth: columns,
          qHeight: pageheight,
          index: index
     };                                                                                           
     return model.getHyperCubeData('/qHyperCubeDef', [page]);
}, this);                                                                
Promise.all(promises).then(function(data) {
     for (var j=0; j<data.length; j++) {
          for (var k=0; k<data[j].qDataPages[0].qMatrix.length; k++) {                                                       
               qTotalData.push(data[j].qDataPages[0].qMatrix[k])
          }
     }
     deferred.resolve(qTotalData);
});




 

This is how we get all of the results for a given set of Dimensions and Measures.

 

If you want to paginate just create a function that passes the page+1*500 to the qInitialDataFetch like

 

app.createCube({
     qDimensions : qDimensions,
     qMeasures : qMeasures,
     qInitialDataFetch : [{
          qTop : (pageIndex)*500,
          qLeft : 0,
          qHeight : 500,
          qWidth : 20
     }]
}, function(reply) {


 

Or

 

model.getHyperCubeData('/qHyperCubeDef', [{qTop: (pageIndex)*500, qWidth: 20, qLeft: 0, qHeight: 500 }]).then(function(data) {


In our Qlik Demo Team we are trying to create mashups that are suitable for all devices. This is why we created another version of a Horizontal Bar Chart that we use almost exclusively. This has various options like setting the bar height, color, text color, text hover color etc. It can also change from regular Horizontal Bar Chart to Horizontal Stacked Bar Chart, Grouped Bar Chart or a Lollipop.

 

All of the UI/UX guidance is coming from my colleague Michael Anthony | Qlik

 

Installation

 

Settings

Dimension and Measures

  • Set 1 dimension for the Labels and
  • up to 5 Measures for Grouped, Stacked Bar or Lollipop

 

General Settings

  • Text Color
  • Font Size

 

Bar

  • Height
  • Comma separated colors for the stacked bar fill color
  • Fill color when you mouse over
  • Text Color for each bar
  • Text Color when you mouse over
  • Border Weight
  • Border Spacing
  • Enable Selections
  • Toggle between Stacked and Grouped Bar Chart

 

Tooltip

  • Enable Tooltip
  • Show Dimension in the Tooltip

 

X-Axis

  • Show X-Axis
  • Show Legend (check Stacked Bar image below)

 

Y-Axis

  • Show Y-Axis
  • Label Width (Usually 150px, even for the smallest device )
  • Number of visible characters, if you want to truncate your labels.

thumb.png

 

stackedBar.png

 

grouped.png

 

lollipop.png

 

Even though we still have a long way to go to make this perfect, features are always welcome!

 

YIANNI

 

Files

Qlik Branch

Since my last blog where I created a Google Annotation Chart (Google Annotation Chart - Qlik Sense Extension and Angular Directive), I thought it would be interesting to add more extensions based on Google's visualization library.

 

Here I created a simple gauge based on Google's Gauge found at Visualization: Gauge  |       Charts  |       Google Developers.

 

  • Download the zip file as found at the end of this blog,
  • unzip it in your extensions folder, C:\Users<user>\Documents\Qlik\Sense\Extensions\GoogleGaugeChart
  • Open your Helpdesk app and drag and drop the extension into the canvas
  • As for the first measure put
    Count( {$<Priority={'High'}, Status -={'Closed'} >} Distinct %CaseId )
    with a label "High Priority Cases"
  • For the second measure, put
    Count( {$<Status -={'Closed'} >} Distinct %CaseId )
  • with a label "Total Closed Cases"
  • Make selections and see the Gauge animate

preview.png

 

Coming up: Extension settings...

 

Branch: http://branch.qlik.com/#!/project/57b76c10b3122e2cd1e7305d

 

Yianni

In a project that I am currently working on, we thought that it would be a great idea, if we displayed data much like Google Finance, with line chart and annotations. Of course this does not currently exist in Qlik Sense so I decided first to create it in my Angular Template (Qlik Branch). Once that was done, I moved it into a directive for easier distribution and then created an extension for it, so we can use it in other projects straight from Sense without the need of my template.

 

For the Directive, we need to define the options in our controller like:

$scope.googleAnnotation = {
  id: 'cases',
  title: 'New Cases by Department over time',
  height: 600,
  dimensions: [
    'Case Created Date',
    'Case Owner Group', // Title
    'Case Owner', // Description
  ],
  measures: [
    "=Sum([Number of New Cases])",
  ],
  headers: ['Date', 'Number of New Cases','Case Owner Group','Case Owner'],
  options: {
    displayAnnotations: true,
    colors: ['#cc3c3c','#395878','#c88d8d','#6f92b5'],
    displayAnnotationsFilter: true,
    displayDateBarSeparator: true,
    displayZoomButtons: true,
    displayRangeSelector: true,
    thickness: 2,
  },
  }
  $scope.dt = {
    from: new Date(2015, 0, 1),
    to: new Date(2015, 3, 1)
  };










 

And in our html view

<google-annotation-chart data-data="googleAnnotation" data-dt="dt"></google-annotation-chart>








For this example I am connected to helpdesk that is bundled in Qlik Sense Desktop.

We have to get the data in this specific order, Date, Annotation Title, Annotation Description, X-Axis data for the first line as a measure

dimensions: [
    'Case Created Date',
    'Case Owner Group', // Title
    'Case Owner', // Description
  ],
  measures: [
    "=Sum([Number of New Cases])",
  ],








 

There are some headers that appear on top of the line chart as we move our mouse. We define those in the headers array

headers: ['Date', 'Number of New Cases','Case Owner Group','Case Owner'],








 

The options are described here and you can pass any of those if you want:

Annotation Chart  |      Charts  |      Google Developers

 

The final results should look like

google-annotation-chart2.png

 

Here is another version for the Presidential Election where we pass selections and zooming, found at

Presidential Election 2016

 

google-annotation-chart.png

 

For the Extension, you can get it from

Qlik Branch

preview.png

 

The various settings that I have so far:

  • Display Annotations
  • Annotations Filter
  • Bar Separator
  • Zoom Buttons
  • Range Selector
  • Annoation Panel Width
  • Line Headers (In single quotes separated by comma)
  • Line Colors (HEX value separated by comma)
  • Line Thickness

 

For installation instructions and usage, please follow the guidelines at the project's homepage.

 

Angular Template:

Qlik Branch

Directive Example :

Presidential Election 2016

http://webapps.qlik.com/yianni/playground/index.html#/google-annotation-chart

Google Referance:

Annotation Chart  |  Charts  |  Google Developers

Download Extension from:

Qlik Branch

GitHub - yianni-ververis/google-annotation-chart: Google Annotation Chart


Yianni

In a lot of my projects I had to create a drop down menu for selections instead of the build in filter object from Qlik Sense. This is for several reasons like ui experience, feel and look but most importantly, for giving the user the option of making only one selection. This is important if we want to guide the users on what and how to display our data in the mashups.

 

For all of my projects I use the Angular Template that I have blogged about in the past, GitHub - yianni-ververis/capabilities-api-angular-template: A simple mvc template for building webpages with Qlik Sense …

I created a simple directive that uses template's api service to get the data for the menu and boostrap to display these results into the form below

dropDown.png

 

All we have to do is add this code into our html

<drop-down data-dimension="'Case Owner Group'" data-title="'Department'" data-id="'department'"></drop-down>


  • data-dimension: is the dimension to populate the drop down list from
  • data-title: is for the text that will be displayed in the button
  • data-id: a unique id/name that the app will use to change the contents like text, colors etc
  • data-showselected="true" : if you want the button text to change to the seleted text

 

This is it. Lets try and re-make the filter toolbar that we had in our template.

So, in views/dashboard.html after L7 add the following:

<div class="col-md-12">
  <drop-down data-dimension="'Cases Open/Closed'" data-title="'Cases Open/Closed'" data-id="'cases'" data-showselected="true"></drop-down>
    <drop-down data-dimension="'Priority'" data-title="'Priority'" data-id="'priority'"></drop-down>
    <drop-down data-dimension="'Case Owner'" data-title="'IT Resources'" data-id="'resources'" data-showselected="true"></drop-down>
    <drop-down data-dimension="'Case Record Type'" data-title="'Request Type'" data-id="'type'"></drop-down>
    <drop-down data-dimension="'Case Owner Group'" data-title="'Department'" data-id="'department'"></drop-down>
</div>


 

Here is how the new filter bar should look like

newFilters.png

 

Branch: http://branch.qlik.com/#!/project/56b4a40140a985c431a64b08

Git: GitHub - yianni-ververis/capabilities-api-angular-template: A simple mvc template for building webpages with Qlik Sense …

 

Yianni

I have written several posts on Angular.js and the Capabilites API which are based on the template GitHub - yianni-ververis/capabilities-api-angular-template: A simple mvc template for building webpages with Qlik Sense …

 

In this one I will use the Capabilities API app.getObject and use it the Angular way as a directive.

 

The only code that you will need is in the html to add the object id and the height of the object:

<get-object qvid="'298bbd6d-f23d-4469-94a2-df243d680e0c'" id="'298bbd6d-f23d-4469-94a2-df243d680e0c'" height="400"></get-object>

 

If you want to disable interactions you can just add the attribute interaction="false" like below

<get-object qvid="'298bbd6d-f23d-4469-94a2-df243d680e0c'" id="'298bbd6d-f23d-4469-94a2-df243d680e0c'" height="400" interaction="false"></get-object>

 

The rest are handled by the app!

 

This directive also covers the memory management issue that I blogged about, Angularjs and Capabilities API - Memory management and "qv-collapsed-listbox-delegated-open" error, by closing all of the objects on navigation.

Just make sure you add the location change after the api.destroyObjects() method is resolved like

api.destroyObjects().then(function(){
  $location.url('/' + page);
});

 

Branch: http://branch.qlik.com/#!/project/56b4a40140a985c431a64b08

Git: GitHub - yianni-ververis/capabilities-api-angular-template: A simple mvc template for building webpages with Qlik Sense …

 

Yianni

There were times in my Qlik Demo Team projects that the objects coming from the Capabilites Api were not enough and we wanted a custom chart in our website like the one we did for PGA Championship Data Visualizer

In this tutorial I will just add the D3 library (https://d3js.org/) and create a simple bar chart having loaded Angularjs and the Capabilities API. At this point, I assume you have read the first article on setting the template up with Angularjs and the Capabilities API Creating a website with Angular and the Capabilities API

 

We will use the same template and the same qvf.

  • First, we will add d3 into bower.json
    "d3": "3.5.9"
  • from the command line, we need to run bower install
  • then, load it from js/lin/main.js L7
    'd3': scriptsUrl + 'js/vendor/d3/d3.min',
    and L83 add 'd3',
  • copy a controller and name it d3.js (controllers/d3.j)s
  • make you sure you name it as 'controller.d3 in L12'
  • add it to main.js
    'controller.d3': scriptsUrl + 'js/controllers/d3',
    and load it in L87 'controller.d3',
  • copy a view and name it as d3 (views/d3.html)
  • Add the html holder
<div class="row">
  <div class="col-md-12">
  <div id="chart"></div>
  </div>
</div>




  • in order to get the data from the qvf as a hypercube, we just add in d3.js the function, passing the dimension and measure as the default chart:
me.getData = function() {
  api.getHyperCube(['Case Owner Group'], ['Avg([Case Duration Time])'], function(data){
    me.createBarChart(data);
  });
}



  • Below is the code that will generate the Bar chart with the labels. I am borrowing the code from the barchart extension that I created and can be found on Branch Qlik Branch
me.createBarChart = function (data) {
  var vars = {
  id: 'chart',
  data: data,
  height: 300,
  width: 500,
  bar: {
  height: 35,
  padding: 3,
  border: 1,
  color: '#4477AA',
  colorHover: '#77b62a',
  borderColor: '#404040'
  },
  label: {
  visible: true,
  width: 200,
  padding: 15
  },
  footer: {
  visible: true,
  height: 20
  },
  canvasHeight: null,
  template: '',
  };
  var element = $('#'+vars.id);
  vars.data = vars.data.map(function(d) {
  return {
  "dimension":d[0].qText,
  "measure":d[1].qText,
  "measureNum":d[1].qNum,
  "qElemNumber":d[0].qElemNumber,
  }
  });


  var dMax = d3.max(vars.data, function(d) { return d.measureNum; });
  vars.canvasHeight = (vars.data.length * (vars.bar.height+(vars.bar.padding*2)+3));


  vars.template = '\
  <div class="barchart" id="barchart">\
  <div class="content"></div>\
  ';
  if (vars.footer.visible) {
  vars.template += '<div class="footer"></div>';
  };
  vars.template += '</div>';


  element.html($(vars.template).width(vars.width).height(vars.height));
  if (vars.footer.visible) {
  $('#' + vars.id + ' .content').height(vars.height-vars.footer.height);
  $('#' + vars.id + ' .footer').height(vars.footer.height);
  } else {
  $('#' + vars.id + ' .content').height(vars.height);
  }


  var x = d3.scale.linear()
  .domain([0,dMax])
  .range([0, (vars.label.visible)?vars.width-vars.label.width-(vars.label.padding*2):vars.width]);


  var y = d3.scale.linear()
  .domain([0,vars.data.length])
  .range([10,vars.canvasHeight]);


  var xAxis = d3.svg.axis()
  .scale(x)
  .orient('bottom');


  var yAxis = d3.svg.axis()
  .scale(y)
  .orient('left')
  .tickSize(1)
  .tickFormat(function(d,i){
  return vars.data[i].dimension;
  })
  .tickValues(d3.range(vars.data.length)); //1167


  var svg = d3.select('#barchart .content')
  .append('svg')
  .attr({'width':vars.width,'height':vars.canvasHeight});

  var svgFooter = d3.select('#barchart .footer')
  .append('svg')
  .attr({'width':vars.width,'height':vars.footer.height});


  // Y Axis labels
  var y_xis = svg.append('g')
  .attr("transform", "translate("+vars.label.width+",10)")
  .attr('id','yaxis')
  .call(yAxis)
  .selectAll("text")
  .style("text-anchor", "start")
  .attr("x", "-"+vars.label.width);


  // X Axis labels
  var x_xis = svgFooter.append('g')
  .attr("transform", "translate("+((vars.label.visible)?vars.label.width:0)+",0)")
  .attr('id','xaxis')
  .call(xAxis
  .tickSize(1)
      .ticks(vars.verticalGridLines)
     );


  // Draw bars
  svg.append('g')
  .attr("transform", "translate("+((vars.label.visible)?vars.label.width:0)+",-20)") //-20
  .attr('id','bars')
  .selectAll('#barchart rect')
  .data(vars.data)
  .enter()
  .append('rect')
  .attr('height', function(d,i){ return vars.bar.height; })
  .attr({'x':0,'y':function(d,i){ return y(i)+19; }})
  .attr('style', '\
  fill: ' + vars.bar.color + '; \
  stroke-width:' + vars.bar.border + '; \
  stroke: ' + vars.bar.borderColor + ';\
  cursor: pointer;\
  ')
  .attr('width',function(d){
  return x(d.measureNum);
  })
  .on('mouseover', function(d, i){
  d3.select(this).style("fill", vars.bar.colorHover);
  })
  .on('mouseout', function(d, i){
  d3.select(this).style("fill", vars.bar.color);
  })
  .on('click', function(d, i) {
  console.log(d);
  app.obj.app.field('Case Owner Group').select([d.qElemNumber], false, false)
  });


  // Draw text
  svg.append('g')
  .attr("transform", "translate("+((vars.label.visible)?vars.label.width:0)+",-20)") //-20
  .attr('id','text')
  .selectAll('#barchart text')
  .data(vars.data)
  .enter()
  .append('text')
  .attr({'x':function(d) {
  return x(d.measure)+10;
  },'y':function(d,i){
  return y(i)+40;
  }})
  .text(function(d){ return parseInt(d.measureNum); })
  .attr("class", function(d) {
  return 'barTextOut';
  });
  }



 

The template with the new addition can be found at branch and on git

Branch: Qlik Branch

Qlik Sense is getting better and better on every release!


I was working on a large website using Qlik Sense 2.1 and the Capability APIs where I used the template that I had created and blogged about Creating a website with Angular and the Capabilities API. I had almost finished the project, when we decided to upgrade to 2.2. Everything was fine, I was cleaning up my code and I was ready to deliver the project when we realized that most of the charts, had no interactivity!

 

So, lets fix our template to work properly in 2.2.

 

One of the two issues was the css. In 2.1 we had to use qlikui.css and client.css. In 2.2 these were replaced by qlik-styles.css. So in index.html replace L17-L18

<link rel="stylesheet" href="http://localhost:4848/resources/autogenerated/qlikui.css">
<link rel="stylesheet" href="http://localhost:4848/resources/assets/client/client.css" media="all">

with

<link rel="stylesheet" href="http://localhost:4848/resources/autogenerated/qlik-styles.css">

 

Also, remove the icon fix we had in index.less and remove L134-L136. This has been fixed in the new stylesheet.

.sel-toolbar-span-icon {
    top: -10px;
}

 

Another issue we found is that, we have to make sure that overflow-y: auto; is in both the html and body. We had that from beginning in this template but it seems to break things in 2.2 if it is only on body and not the html.

 

In the js/lib/main.js I used in the grid-service in order to make angular work. This has also been fixed and you can remove L30

define( "client.services/grid-service", {} );

 

Now, going to my last and more important issue, on why the interactivity was broken in 2.2. I started the blog by stating that "Qlik Sense is getting better and better on every release!" and this is so true. I spend several days trying to figure out why the interactivity was broken when I was moving from pages to pages and on random times. The issue was that simply the engine got so much faster on delivering the objects, that Angular could not keep up with it! Really, when the Capability App API app.getObject() is looking for the id to replace the html with, is looking for the parent element width and height. In my case, all of the objects, most of the time, had the canvas drawn with 0 height and width due to that! So, in order to fix this, unfortunately, I had to add a 1/2 second delay on getting my objects. So in the js/services/api.js, wrap L18-L24 in a timeout function

  setTimeout(function(){
     angular.forEach(obj, function(value, key) {
        app.obj.app.getObject(value, value).then(function(model){
           app.obj.model.push(model);
           deferred.resolve(value);
        });
        promises.push(deferred.promise);
     });
  }, 500);

 

I would add this only if you have the interactivity issues like I have described above. In cases that you have small projects like this one and you do not have a lot of dependencies, this may not be needed at all!

 

Updated Git: GitHub - yianni-ververis/capabilities-api-angular-template: A simple mvc template for building webpages with Qlik Sense …

Capability APIs documentation: https://help.qlik.com/en-US/sense-developer/2.2/Subsystems/APIs/Content/mashup-api-reference.htm

 

Happy coding!!

 

Yianni

Filter Blog

By date:
By tag: