Apply bookmarks (Platform SDK)
qlik/api: The typescript platform SDK used in this tutorial has been deprecated. Review how to transition to qlik/api.
Introduction
In this tutorial, you are going to learn how to apply a bookmark in a Qlik Sense application programmatically and list out the current selected fields and values with the Platform SDK.
Requirements
- A Qlik Cloud tenant
- An API key or machine-to-machine OAuth client to authenticate to your tenant
- Executive Dashboard Qlik Sense App imported into your tenant
- Typescript Platform SDK
- NodeJs version 18 or higher
- Basic knowledge of JavaScript
Variables you need to complete this tutorial
- host: The hostname for your Qlik Cloud tenant
(for example
hello.us.qlikcloud.com
) - clientId: The
clientId
for the machine-to-machine OAuth client - clientSecret: The
clientSecret
for the machine-to-machine OAuth client - appId The
appId
is the unique identifier (guid) for the application you want to connect to.
Authorize a connection to Qlik Cloud
Configure a Nodejs application and add the @qlik/sdk
package using NPM.
npm install @qlik/sdk
Create a file named bookmark-apply.js
file of your application you want to
use the sdk, import the Qlik
and AuthType
modules from the sdk.
const Qlik = require('@qlik/sdk').default;
const { AuthType } = require("@qlik/sdk");
Update the configuration values with the variables identified earlier in the tutorial.
const host = process.env['host'] // "<tenant.region.qlikcloud.com>";
const clientId = process.env['clientId'] // "<OAUTH_CLIENT_ID>";
const clientSecret = process.env['clientSecret'] //"<OAUTH_CLIENT_SECRET>";
const appId = process.env['appId'] // "<APPID_GUID_LIKE_THIS_b1b79fcd-e500-491c-b6e9-2ceaa109214c";
Configure authorization to Qlik. This example uses a machine-to-machine OAuth token to connect to Qlik Cloud.
const config = {
authType: AuthType.OAuth2,
host: host,
clientId: clientId,
clientSecret: clientSecret
};
Apply a bookmark
Begin with an async
function to immediately execute this snippet. The
remainder of the code goes in between the curly {}
braces.
(async () => {})();
Establish a connection to your Qlik Cloud tenant.
const qlik = new Qlik(config);
await qlik.auth.authorize();
Get a reference to the Qlik Sense application to open and open it.
const app = await qlik.apps.get(appId);
await app.open();
Add the objectId
for the bookmark you want to apply into the
app.applyBookmark
function. An objectId for the sample application referenced
in the Requirements section has been entered so you don’t have
to look it up.
const bmk = await app.applyBookmark('SmDXrz');
Once the bookmark is applied, create a session object to query the current selections from the app.
const sessObj = await app.createSessionObject({
"qInfo": {
"qType": "CurrentSelections"
},
"qSelectionObjectDef": {}
}
);
qType
: UseCurrentSelections
to identify what kind of session object to create.qSelectionObjectDef
: This property instructs the session object to use the selection object schema to format the data returned in the object.
Get the layout of the current selections session object.
const selectionLayout = await sessObj.getLayout();
The current selections layout is a JSON object with the following
structure and information. To get the fields representing the current
selection, look for the qSelections
property in the output.
GenericObjectLayout {
qExtendsId: undefined,
qHasSoftPatches: undefined,
qInfo: NxInfo {
qId: '1aa4a8ec-1d03-4cd5-8c31-eb7a372567d3',
qType: 'CurrentSelections'
},
qMeta: NxMeta {
qName: undefined,
privileges: [ 'read', 'update', 'delete' ]
},
qSelectionInfo: NxSelectionInfo {
qInSelections: undefined,
qMadeSelections: undefined
},
qStateName: undefined,
qListObject: ListObject {
qDimensionInfo: NxDimensionInfo {
qApprMaxGlyphCount: undefined,
qCalcCondMsg: undefined,
qCardinal: undefined,
qCardinalities: [NxCardinalities],
qContinuousAxes: undefined,
qDerivedField: undefined,
qDimensionType: undefined,
qFallbackTitle: undefined,
qGroupFallbackTitles: undefined,
qGroupFieldDefs: undefined,
qGroupPos: undefined,
qGrouping: undefined,
qIsAutoFormat: undefined,
qIsCalculated: undefined,
qIsCyclic: undefined,
qIsOneAndOnlyOne: undefined,
qIsSemantic: undefined,
qLibraryId: undefined,
qLocked: undefined,
qMax: undefined,
qMin: undefined,
qNumFormat: [FieldAttributes],
qReverseSort: undefined,
qSortIndicator: undefined,
qTags: undefined
},
qStateName: undefined
},
qNxLibraryMeasure: NxLibraryMeasure {
qActiveExpression: undefined,
qDef: undefined,
qExpressions: undefined,
qGrouping: undefined,
qLabel: undefined,
qLabelExpression: undefined,
qNumFormat: FieldAttributes {
qnDec: 10,
qDec: undefined,
qFmt: undefined,
qThou: undefined,
qType: 'UNKNOWN',
qUseThou: undefined
}
},
qSelectionObject: SelectionObject {
qBackCount: 1,
qForwardCount: 0,
qSelections: [ [NxCurrentSelectionItem], [NxCurrentSelectionItem] ],
qStateName: undefined
}
}
To get the list of selected field values, you are going to create a list object
based on the field names present in the qSelections
array. A helper function
named getListObject
accepts arguments including the app reference, the field
name, and the number of selected items in the field to retrieve the list of
selected field values.
First, loop through the qSelections
array to get the field names and access
to the selected field values.
for (const item of selectionLayout.qSelectionObject.qSelections)
{
console.log(item.qField);
console.log("==========");
const myListObj = await getListObject(app, item.qSelectedCount, item.qField);
const listLayout = await myListObj.getLayout();
for (const pages of listLayout.qListObject.qDataPages)
{
let selVals = await pages.qMatrix.filter(item => item[0].qState == 'S');
for (const val of selVals)
{
console.log(val[0].qText);
}
}
console.log("==========");
}
process.exit();
Observe the myListObj
constant. It is making a request to a helper function in
this snippet called getListObject
.
const myListObj = await getListObject(app, item.qSelectedCount, item.qField);
This function is needed in order for the code snippet to work properly. Add the
getListObject
function to your script file as a separate async
function
outside the main function you’ve been working in.
async function getListObject(app, valCount, fieldName)
{
const listObj = await app.createSessionObject({
"qInfo": {
"qId": "LB01",
"qType": "ListObject"
},
"qListObjectDef": {
"qStateName": "$",
"qLibraryId": "",
"qDef": {
"qFieldDefs": [
fieldName
],
"qFieldLabels": [
fieldName
],
"qSortCriterias": [
{
"qSortByLoadOrder": 1
}
]
},
"qInitialDataFetch": [
{
"qTop": 0,
"qHeight": valCount,
"qLeft": 0,
"qWidth": 1
}
]
}
});
return listObj;
}
With the list object defined and returned, get the layout of the session object containing the field values of the selected field.
const listLayout = await myListObj.getLayout();
Loop through qDataPages
array and filter the qMatrix
child array to get the
current selected field values by returning values with a qState='S'
. The 'S'
qState indicates a selected field value in the array.
for (const pages of listLayout.qListObject.qDataPages)
{
let selVals = await pages.qMatrix.filter(item => item[0].qState == 'S');
for (const val of selVals)
{
console.log(val[0].qText);
}
}
console.log("==========");
}
process.exit();
Next steps
Now you know the basics of working with bookmarks programmatically using the Qlik Platform SDK. Head up to the top of qlik.dev to learn more about how you can embed analytics into your applications and bring insight right to where your end users work.
Overview - Hypercube columns
Learn about how columns are defined in hypercubes
Apply dimension limits
Dimension limits is defined in the qOtherTotalSpec object and specifies if some values, grouped as Others, should be grouped together in the visualization.
Dimensions
Dimensions are created from fields in the data model tables and they determine how the data in a visualization is grouped.
Full code snippet
//bookmark-apply.js
//Apply a shared bookmark in an app and print out the selected values.
//Authorizes through OAuth2 M2M to connect to a Qlik Cloud application.
//To create an OAuth client for this snippet, go here:
//https://qlik.dev/authenticate/oauth/create/create/create-oauth-client
//To use a sample app with this snippet, download and import the executive
//dashboard from this github location https://github.com/qlik-oss/qlik-cloud-examples/raw/main/qlik.dev/sample-apps/qlik-dev-exec-dashboard.qvf
/*PARAMS
* host: the hostname of your tenant
* clientId: the clientId of the OAuth2 client you created
* clientSecret: the client secret for the OAuth2 client you created
* appId: The GUID for the Qlik Sense app
*/
//Uncomment below if you're using this code with https://repl.it
//global.fetch = require('@replit/node-fetch');
const Qlik = require('@qlik/sdk').default;
const { AuthType } = require("@qlik/sdk");
//config-values
const host = process.env['host'] // "<tenant.region.qlikcloud.com>";
const clientId = process.env['clientId'] // "<OAUTH_CLIENT_ID>";
const clientSecret = process.env['clientSecret'] //"<OAUTH_CLIENT_SECRET>";
const appId = process.env['appId'] // "<APPID_GUID_LIKE_THIS_b1b79fcd-e500-491c-b6e9-2ceaa109214c";
const config = {
authType: AuthType.OAuth2,
host: host,
clientId: clientId,
clientSecret: clientSecret
};
(async () => {
const qlik = new Qlik(config);
await qlik.auth.authorize();
const app = await qlik.apps.get(appId);
await app.open();
const bmk = await app.applyBookmark('SmDXrz');
if(!bmk)
{
console.log("bookmark apply failed.");
process.exit();
}
const sessObj = await app.createSessionObject({
"qInfo": {
"qType": "CurrentSelections"
},
"qSelectionObjectDef": {}
}
);
const selectionLayout = await sessObj.getLayout();
for (const item of selectionLayout.qSelectionObject.qSelections)
{
console.log(item.qField);
console.log("==========");
const myListObj = await getListObject(app, item.qSelectedCount, item.qField);
const listLayout = await myListObj.getLayout();
for (const pages of listLayout.qListObject.qDataPages)
{
let selVals = await pages.qMatrix.filter(item => item[0].qState == 'S');
for (const val of selVals)
{
console.log(val[0].qText);
}
}
console.log("==========");
}
process.exit();
})();
async function getListObject(app, valCount, fieldName)
{
const listObj = await app.createSessionObject({
"qInfo": {
"qId": "LB01",
"qType": "ListObject"
},
"qListObjectDef": {
"qStateName": "$",
"qLibraryId": "",
"qDef": {
"qFieldDefs": [
fieldName
],
"qFieldLabels": [
fieldName
],
"qSortCriterias": [
{
"qSortByLoadOrder": 1
}
]
},
"qInitialDataFetch": [
{
"qTop": 0,
"qHeight": valCount,
"qLeft": 0,
"qWidth": 1
}
]
}
});
return listObj;
}