Содержание урока по Qlik Sense
Extension API состоит из методов и свойств, используемых для создания custom расширений визуализации (или custom visualization extensions).
Для начала рассмотрим шаблоны Visualization extension templates. В dev-hub при создании Extension вам предлагают выбрать шаблон.
Назовем наш Extension “1_BVT”. При инициализации создадутся два файла:
1_BVT.qext
{
"name": "1_BVT",
"description": "Basic empty visualization template",
"type": "visualization",
"version": "1.0.0",
"icon": "extension",
"author": "",
"homepage": "",
"keywords": "qlik-sense, visualization",
"license": "",
"repository": "",
"dependencies": {
"qlik-sense": ">=3.0.x"
}
} 1_BVT.js
define( [ "qlik"
],
function ( qlik) {
return {
support : {
snapshot: true,
export: true,
exportData : false
},
paint: function ($element) {
//add your rendering code here
$element.html( "1_BVT" );
//needed for export
return qlik.Promise.resolve();
}
};
} ); Назовем наш Extension “2_CT”. При инициализации создадутся 4 файла:
2_CT.qext
{
"name": "2_CT",
"description": "Chart visualization template",
"icon": "bar-chart-horizontal",
"type": "visualization",
"version": "1.0.0",
"author": "",
"homepage": "",
"keywords": "qlik-sense, visualization",
"license": "",
"repository": "",
"dependencies": {
"qlik-sense": ">=3.0.x"
}
} 2_CT.js
define( ["qlik", "text!./2_CT.ng.html", "css!./2_CT.css"],
function ( qlik, template ) {
"use strict";
return {
template: template,
initialProperties: {
qHyperCubeDef: {
qDimensions: [],
qMeasures: [],
qInitialDataFetch: [{
qWidth: 2,
qHeight: 50
}]
}
},
definition: {
type: "items",
component: "accordion",
items: {
dimensions: {
uses: "dimensions",
min: 1,
max: 1
},
measures: {
uses: "measures",
min: 1,
max: 1
},
sorting: {
uses: "sorting"
}
}
},
support: {
snapshot: true,
export: true,
exportData: true
},
paint: function () {
//needed for export
this.$scope.selections = [];
return qlik.Promise.resolve();
},
controller: ["$scope", "$element", function ( $scope ) {
$scope.getPercent = function ( val ) {
return Math.round( (val * 100 / $scope.layout.qHyperCube.qMeasureInfo[0].qMax) * 100 ) / 100;
};
$scope.selections = [];
$scope.sel = function ( $event ) {
if ( $event.currentTarget.hasAttribute( "data-row" ) ) {
var row = parseInt( $event.currentTarget.getAttribute( "data-row" ), 10 ), dim = 0,
cell = $scope.$parent.layout.qHyperCube.qDataPages[0].qMatrix[row][0];
if ( cell.qIsNull !== true ) {
cell.qState = (cell.qState === "S" ? "O" : "S");
if ( $scope.selections.indexOf( cell.qElemNumber ) === -1 ) {
$scope.selections.push( cell.qElemNumber );
} else {
$scope.selections.splice( $scope.selections.indexOf( cell.qElemNumber ), 1 );
}
$scope.selectValues( dim, [cell.qElemNumber], true );
}
}
};
}]
};
} ); 2_CT.css
.qv-object-2_CT .css_bars_row {
transition: all 0.3s ease;
float: left;
width: 98%;
overflow: hidden;
border: 1px solid #ccc;
background-color: #f0f0f0;
margin-bottom: 2px;
border-radius: 0 10px 10px 0;
-moz-border-radius: 0 10px 10px 0;
cursor: pointer;
}
.qv-object-2_CT .css_bars_bar {
float: left;
clear: left;
height: 30px;
background-color: #5f5f5f;
}
.qv-object-2_CT .css_bars_bar span {
font-weight: bold;
float: left;
margin-left: 5px;
margin-right: 5px;
margin-top: 7px;
color: #f0f0f0;
overflow: hidden;
text-overflow: ellipsis;
width: 90%;
white-space: nowrap;
}
.qv-object-2_CT .css_bars_per {
position: relative;
}
.qv-object-2_CT .css_bars_per span {
font-weight: bold;
float: left;
margin-right: 5px;
margin-top: 5px;
margin-left: 5px;
}
.qv-object-2_CT .css_bars_per span.over {
position: absolute;
right: 15px;
color: #f0f0f0;
}
.qv-object-2_CT .css_bars_row:hover, .qv-object-2_CT .css_bars_row.selected {
border: 1px solid #000;
} 2_CT.ng.html
<div qv-extension style="height: 100%; position: relative; overflow: auto;">
<div class="css_bars_row" ng-repeat="item in layout.qHyperCube.qDataPages[0].qMatrix" title="{{item[0].qText}}" data-row="{{ $index }}"
ng-click="sel($event)" data-value="{{ item[0].qElemNumber }}" ng-class="{selected: (item[0].qState) === 'S' || selections.indexOf(item[0].qElemNumber) !== -1 }">
<div class="css_bars_bar" ng-style="{'width':getPercent(item[1].qNum)+'%'}"><span>{{item[0].qText}}</span></div>
<div class="css_bars_per">
<span ng-class="{over: (getPercent(item[1].qNum)>95)}">{{getPercent(item[1].qNum)}} %</span></div>
</div>
</div> Назовем наш Extension “3_LBT”. При инициализации создадутся 3 файла:
3_LBT.js
define( ["qlik", "jquery", "text!./style.css"], function ( qlik, $, cssContent ) {
'use strict';
$( "<style>" ).html( cssContent ).appendTo( "head" );
return {
initialProperties: {
qListObjectDef: {
qShowAlternatives: true,
qFrequencyMode: "V",
qInitialDataFetch: [{
qWidth: 2,
qHeight: 50
}]
}
},
definition: {
type: "items",
component: "accordion",
items: {
dimension: {
type: "items",
label: "Dimensions",
ref: "qListObjectDef",
min: 1,
max: 1,
items: {
label: {
type: "string",
ref: "qListObjectDef.qDef.qFieldLabels.0",
label: "Label",
show: true
},
libraryId: {
type: "string",
component: "library-item",
libraryItemType: "dimension",
ref: "qListObjectDef.qLibraryId",
label: "Dimension",
show: function ( data ) {
return data.qListObjectDef && data.qListObjectDef.qLibraryId;
}
},
field: {
type: "string",
expression: "always",
expressionType: "dimension",
ref: "qListObjectDef.qDef.qFieldDefs.0",
label: "Field",
defaultValue: "=ValueList('A','B','C')",
show: function ( data ) {
return data.qListObjectDef && !data.qListObjectDef.qLibraryId;
}
},
frequency: {
type: "string",
component: "dropdown",
label: "Frequency mode",
ref: "qListObjectDef.qFrequencyMode",
options: [{
value: "N",
label: "No frequency"
}, {
value: "V",
label: "Absolute value"
}, {
value: "P",
label: "Percent"
}, {
value: "R",
label: "Relative"
}],
defaultValue: "V"
}
}
},
settings: {
uses: "settings"
}
}
},
support : {
snapshot: true,
export: true,
exportData : false
},
paint: function ( $element,layout ) {
var self = this, html = "<ul>";
layout.qListObject.qDataPages[0].qMatrix.forEach( function ( row ) {
html += '<li class="data state' + row[0].qState + '" data-value="' + row[0].qElemNumber + '">' + row[0].qText;
if ( row[0].qFrequency ) {
html += '<span>' + row[0].qFrequency + '</span>';
}
html += '</li>';
} );
html += "</ul>";
$element.html( html );
if ( this.selectionsEnabled ) {
$element.find( 'li' ).on( 'click', function () {
if ( this.hasAttribute( "data-value" ) ) {
var value = parseInt( this.getAttribute( "data-value" ), 10 ), dim = 0;
self.selectValues( dim, [value], true );
this.classList.toggle("selected");
}
} );
}
return qlik.Promise.resolve();
}
};
} );
3_LBT.qext
{
"name": "3_LBT",
"description": "Listbox visualization template",
"icon": "list",
"type": "visualization",
"version": "1.0.0",
"author": "",
"homepage": "",
"keywords": "qlik-sense, visualization",
"license": "",
"repository": "",
"dependencies": {
"qlik-sense": ">=3.0.x"
}
} style.css
.qv-object-3_LBT div.qv-object-content-container {
overflow: auto;
}
.qv-object-3_LBT ul {
list-style: none;
}
.qv-object-3_LBT li.data {
padding: 4px;
border-bottom: 1px solid #ddd;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* frequency */.qv-object-3_LBT li.data span
{
padding-left: 10px;
font-style: italic;
float:right;
}
/* colors when NOT in selection mode */
.qv-object-3_LBT li.stateS, .qv-object-3_LBT li.stateL {
background-image: linear-gradient(top, #52cc52, #4dc04d);
background-image: -o-linear-gradient(top, #52cc52, #4dc04d);
background-image: -moz-linear-gradient(top, #52cc52, #4dc04d);
background-image: -webkit-linear-gradient(top, #52cc52, #4dc04d);
background-image: -ms-linear-gradient(top, #52cc52, #4dc04d);
color: #fff;
}
.qv-object-3_LBT li.stateX, .qv-object-3_LBT li.stateXL, .qv-object-3_LBT li.stateXS {
background-color: #A9A9A9;
color: #fff;
}
.qv-object-3_LBT li.stateA {
background-image: none;
background-color: #ddd;
color: #000;
}
/* colors when in selection mode */.qv-object-3_LBT.qv-selections-active li {
background-image: none;
background-color: #ddd;
color: #000;
}
.qv-object-3_LBT.qv-selections-active li.selected {
background-image: linear-gradient(top, #52cc52, #4dc04d);
background-image: -o-linear-gradient(top, #52cc52, #4dc04d);
background-image: -moz-linear-gradient(top, #52cc52, #4dc04d);
background-image: -webkit-linear-gradient(top, #52cc52, #4dc04d);
background-image: -ms-linear-gradient(top, #52cc52, #4dc04d);
color: #fff;
} Назовем наш Extension “4_ABVT”. При инициализации создадутся 3 файла:
4_ABVT.qext
{
"name": "4_ABVT",
"description": "Basic empty visualization template based on angular.js",
"type": "visualization",
"version": "1.0.0",
"author": "",
"homepage": "",
"keywords": "qlik-sense, visualization",
"license": "",
"repository": "",
"dependencies": {
"qlik-sense": ">=3.0.x"
}
} 4_ABVT.js
define( ["qlik", "text!./template.html"],
function ( qlik, template ) {
return {
template: template,
support: {
snapshot: true,
export: true,
exportData: false
},
paint: function () {
return qlik.Promise.resolve();
},
controller: ['$scope', function ( $scope ) {
//add your rendering code here
$scope.html = "Hello World";
}]
};
} ); template.html
<div qv-extension style="height: 100%; position: relative; overflow: auto;" class="ng-scope">
{{ html }}
</div> Назовем наш Extension “5_ATT”. При инициализации создадутся 4 файла:
5_ATT.js
define( ["qlik","jquery", "text!./style.css", "text!./template.html"], function (qlik, $, cssContent, template ) {'use strict';
$("<style>").html(cssContent).appendTo("head");
return {
template: template,
initialProperties : {
qHyperCubeDef : {
qDimensions : [],
qMeasures : [],
qInitialDataFetch : [{
qWidth : 10,
qHeight : 50
}]
}
},
definition : {
type : "items",
component : "accordion",
items : {
dimensions : {
uses : "dimensions",
min : 1
},
measures : {
uses : "measures",
min : 0
},
sorting : {
uses : "sorting"
},
settings : {
uses : "settings",
items : {
initFetchRows : {
ref : "qHyperCubeDef.qInitialDataFetch.0.qHeight",
label : "Initial fetch rows",
type : "number",
defaultValue : 50
}
}
}
}
},
support : {
snapshot: true,
export: true,
exportData : true
},
paint: function ( ) {
//setup scope.table
if ( !this.$scope.table ) {
this.$scope.table = qlik.table( this );
}
return qlik.Promise.resolve();
},
controller: ['$scope', function (/*$scope*/) {
}]
};
} );
5_ATT.qext
{
"name": "5_ATT",
"description": "Table visualization template based on angular.js",
"icon": "table",
"type": "visualization",
"version": "1.0.0",
"author": "",
"homepage": "",
"keywords": "qlik-sense, visualization",
"license": "",
"repository": "",
"dependencies": {
"qlik-sense": ">=3.0.x"
}
} style.css
.qv-object-5_ATT div.qv-object-content-container {
overflow: auto;
}
.qv-object-5_ATT td,
.qv-object-5_ATT th {
border-top: 0px solid #fff;
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
cursor: default;
font-size: 12px;
}
.qv-object-5_ATT td.numeric {
text-align: right;
}
.qv-object-5_ATT button {
width: 100%;
} template.html
<div qv-extension style="height: 100%; position: relative; overflow: auto;">
<table>
<thead>
<tr>
<th ng-repeat="head in table.headers track by $index" ng-click="head.orderBy()">{{head.qFallbackTitle}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in table.rows track by $index">
<td ng-repeat="cell in row.cells track by $index" class="selectable" ng-class="{'selected':cell.selected,'numeric':cell.qNum}" ng-click="cell.select($event)">{{cell.qText}}</td>
</tr>
</tbody>
</table>
<button ng-if="table.rowCount>table.rows.length" ng-click="table.getMoreData()" class="lui-button more">More...</button>
</div>
Назовем наш Extension “6_TT”. При инициализации создадутся 3 файла:
6_TT.js
/*globals define*/define( ["qlik", "jquery", "text!./style.css"], function ( qlik, $, cssContent ) {
'use strict';
$( "<style>" ).html( cssContent ).appendTo( "head" );
function createRows ( rows, dimensionInfo ) {
var html = "";
rows.forEach( function ( row ) {
html += '<tr>';
row.forEach( function ( cell, key ) {
if ( cell.qIsOtherCell ) {
cell.qText = dimensionInfo[key].othersLabel;
}
html += "<td ";
if ( !isNaN( cell.qNum ) ) {
html += "class='numeric'";
}
html += '>' + cell.qText + '</td>';
} );
html += '</tr>';
} );
return html;
}
return {
initialProperties: {
qHyperCubeDef: {
qDimensions: [],
qMeasures: [],
qInitialDataFetch: [{
qWidth: 10,
qHeight: 50
}]
}
},
definition: {
type: "items",
component: "accordion",
items: {
dimensions: {
uses: "dimensions",
min: 1
},
measures: {
uses: "measures",
min: 0
},
sorting: {
uses: "sorting"
},
settings: {
uses: "settings"
}
}
},
snapshot: {
canTakeSnapshot: true
},
paint: function ( $element, layout ) {
var html = "<table><thead><tr>", self = this,
morebutton = false,
hypercube = layout.qHyperCube,
rowcount = hypercube.qDataPages[0].qMatrix.length,
colcount = hypercube.qDimensionInfo.length + hypercube.qMeasureInfo.length;
//render titles
hypercube.qDimensionInfo.forEach( function ( cell ) {
html += '<th>' + cell.qFallbackTitle + '</th>';
} );
hypercube.qMeasureInfo.forEach( function ( cell ) {
html += '<th>' + cell.qFallbackTitle + '</th>';
} );
html += "</tr></thead><tbody>";
//render data
html += createRows( hypercube.qDataPages[0].qMatrix, hypercube.qDimensionInfo );
html += "</tbody></table>";
//add 'more...' button
if ( hypercube.qSize.qcy > rowcount ) {
html += "<button class='more'>More...</button>";
morebutton = true;
}
$element.html( html );
if ( morebutton ) {
$element.find( ".more" ).on( "click", function () {
var requestPage = [{
qTop: rowcount,
qLeft: 0,
qWidth: colcount,
qHeight: Math.min( 50, hypercube.qSize.qcy - rowcount )
}];
self.backendApi.getData( requestPage ).then( function ( dataPages ) {
rowcount += dataPages[0].qMatrix.length;
if ( rowcount >= hypercube.qSize.qcy ) {
$element.find( ".more" ).hide();
}
var html = createRows( dataPages[0].qMatrix, hypercube.qDimensionInfo );
$element.find( "tbody" ).append( html );
} );
} );
}
return qlik.Promise.resolve();
}
};
} );
6_TT.qext
{
"name": "6_TT",
"description": "Table visualization template",
"icon": "table",
"type": "visualization",
"version": "1.0.0",
"author": "",
"homepage": "",
"keywords": "qlik-sense, visualization",
"license": "",
"repository": "",
"dependencies": {
"qlik-sense": ">=3.0.x"
}
} style.css
.qv-object-6_TT div.qv-object-content-container {
overflow: auto;
}
.qv-object-6_TT td,
.qv-object-6_TT th {
border-top: 0px solid #fff;
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
cursor: default;
font-size: 12px;
}
.qv-object-6_TT td.numeric {
text-align: right;
}
.qv-object-6_TT button {
width: 100%;
} Как мы видим из примеров стандартных шаблонов экстеншенов для создания расширения необходимо минимум 2 файла – js и qext. Для более богатой функциональности добавляются css и html.
Опишем зачем нужны файлы:
Рассмотрим процесс отрисовки экстеншена:
Когда пользователь открывает лист, Qlik Sense извлекает визуализации на этом листе, а затем выполняет стандартный рабочий процесс для каждой визуализации.
Пример Hello World можно посмотреть на сайте http://qliksite.io/tutorials/qliksense-visualization-extensions/part-01/03-Lets-Get-Started–Hello-World-Example/
define – это концепция, введенная RequireJS для определения зависимостей в ваших файлах JavaScript. Идея состоит в том, чтобы загрузить внешние зависимости до того, как будет выполнен ваш основной скрипт.
Зависимости – внешние модули (скрипты/библиотеки), которые необходимы для работы нашего расширения.
paint – основной метод визуализации визуализации, который будет вызываться каждый раз, когда визуализация должна быть отрисована, либо из-за новых данных с сервера, либо из-за изменения размера.
Метод paint получает два параметра $elementи layout.
| Параметр | Описание |
|---|---|
$element | Оболочка jQuery, содержащая родительский элемент HTML, в котором должна отображаться визуализация. |
layout | Данные и свойства для визуализации. |
todo
Выполняется до того, как объект будет уничтожен путем его удаления, переключения листа, открытия инструмента выделения или любых других средств удаления расширения. Используйте этот метод для того, чтобы удалить привязки и предотвратить утечку памяти.
Утечка памяти — это, в широком смысле, фрагмент памяти, выделенной приложению, который больше не нужен этому приложению, но не может быть возвращён операционной системе для дальнейшего использования. Другими словами это — блок памяти, который захвачен приложением без намерения пользоваться этой памятью в будущем.
Источник: Практическое руководство по борьбе с утечками памяти в Node.js
Выполняется один раз при инициализации объекта.
mounted($element)
Отрисовывает (или render) визуализацию расширения. Вызовите этот метод, когда необходимо отрисовать visualization extension либо из-за новых данных с сервера, либо из-за изменения размера расширения визуализации.
Оболочка jQuery, содержащая HTML-элемент, в котором должна отображаться визуализация.
Данные и свойства для визуализации.
Массив с данными об используемых dimensions. Включает qFallbackTitle, заголовок и qCardinal , количество различных значений.
Массив с данными об используемых мерах. Включает qFallbackTitle, заголовок (title), qCardinal, количество различных значений, qMin и qMax для минимального и максимального значений.
Массив с данными из Qlik Sense . Каждому объекту соответствует строка в результате. Значения измерения (dimension) и меры (measure) являются объектами JavaScript в этом массиве.
Значения Dimension для строки результата. Каждый объект содержит текст ( qText), используемый для рендеринга, и значение ( qElemNumber ), используемое для selections, и состояние значения ( qState ).
Массив со значениями measure для строки результата. Каждый объект содержит данные, фактическое значение ( qNum ) и текст ( qText ), форматированное значение.
Общее количество строк (qcy) и столбцов (qcx) в результирующем наборе.
qId – Уникальный идентификатор объекта. При необходимости полезно для создания уникального HTML-идентификатора.
Если пользователь находится в режиме выбора, это будет объект с двумя флагами: qInSelections и qMadeSelections.
Выполняется каждый раз при изменении layout или данных, например при осуществлении выборок или изменении свойств. Получает новый layout и возвращает promise.
updateData(layout)
initialProperties()
Задает свойства, которые объект должен иметь при создании. Определите либо как гиперкуб ( qHyperCubeDef ), либо как объект списка ( qListObjectDef ).
Настройте все свойства и назначьте значения по умолчанию для custom properties.
Максимальное количество ячеек ( qWidth * qHeight ), разрешенное при исходной выборке данных, составляет 10 000. Получите дополнительные данные по частям с помощью метода getData в Backend API.
Пример: использование гиперкуба
initialProperties : {
qHyperCubeDef : {
qDimensions : [],
qMeasures : [],
qInitialDataFetch : [{
qWidth : 2,
qHeight : 50
}]
}
} Пример, в котором используется объект списка, а также определяются некоторые custom properties
initialProperties : {
qListObjectDef : {
qShowAlternatives : true,
qFrequencyMode : "V",
qInitialDataFetch : [{
qWidth : 2,
qHeight : 50
}]
},
fixed : true,
width : 25,
percent : true,
selectionMode : "CONFIRM"
} Ходят упорные слухи, что при работе с HyperCube с использованием клиентских API (в расширениях визуализации или мэшапах) можно загрузить только 10 000 ячеек данных.
Этот слух не соответствует действительности !!!
Вместо этого верно, что вы можете получить только 10 000 записей на страницу данных из Engine, но вы, безусловно, можете запросить получение дополнительных страниц данных.
Таким образом, разработчик полностью решает, сколько точек данных из источника данных используется в пользовательском интерфейсе.
Включает или отключает возможность делать снимки расширения визуализации для использования в Data Storytelling.
snapshot(enable)
Включает или отключает возможность экспорта расширения визуализации как изображения или как PDF-файл. Это также позволяет включить расширение визуализации в экспорт из Storytelling (PowerPoint или PDF).
Если задано значение true , включены параметры контекстного меню «Экспорт как изображение» и «Экспорт в PDF» для расширения визуализации. Это также позволяет расширениям визуализации быть частью историй, экспортируемых в PowerPoint или PDF, в параметрах контекстного меню «Экспорт истории в PowerPoint» и «Экспорт истории в PDF».
Включает или отключает возможность экспорта данных из расширения визуализации и сохранения их в файле XLSX. Если задано значение true, параметр контекстного меню «Экспорт данных» включен для расширения визуализации.
var ext = {
support: {
snapshot: true,
export: true,
exportData: true
}
}; todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
todo
Введение Иногда при создании расширения Qlik Sense вам необходимо получить доступ к системным данным. Это…
// Команда, которая будет запускаться в Powershell на сервере LET vPowershellCommand = 'Get-ChildItem -Path ''lib://Data/QVDs''…
Как построить диаграмму Control Chart со скользящим средним в Qlik Sense? В этой статье будет…
Как сделать чередование цвета в строках прямой таблицы Qlik Sense? Если порядок строк неважен, то…
Обзор В этой серии руководств мы собираемся создать расширение Qlik Sense с использованием Nebula.js и…
Создание расширенного расширения визуализации с использованием Qlik Nebula.js и D3.js В моем последнем посте я рассказал…
This website uses cookies.