5.9 KiB
id, title
| id | title |
|---|---|
| sn-create | Quick start |
This guide will walk you through creating a simple project that renders a table.
It will include the following steps:
- Create a project
- Configure data structure
- Render data
- Select data
and requires that you have:
- Access to a Qlik Sense installation.
node.jsv10.0.0+installed on your machine.- A decent IDE, we recommend VSCode.
Create a project
The quickest way to get started is to use the nebula.js CLI:
$ npx @nebula.js/cli create hello --picasso none
The --picasso none option tells the command to not create a picasso visualization template, other options are minimal and barchart.
The command will scaffold a project into the hello folder with the following structure:
/srcindex.js- Main entry point of this visualizationobject-properties.js- Object properties stored in the appdata.js- Data configuration
/test- Integration testspackage.json
The folder contains some additional dotfiles that provides linting and formatting of code.
Start the development server
Start the development server with:
$ cd hello
$ npm run start
The command will start a local development server and open up http://localhost:8080 in your browser.
The development server needs to connect to a Qlik Associative Engine running in any Qlik deployment. Enter the WebSocket URL that corresponds to the Qlik product you are using.
Next, select an app to connect to.
You will then be redirected to the main developer UI where you should see your visualization rendered:
Any updates in /src/index.js that affects the output will automatically cause a refresh of the visualization and you will see the changes immediately.
Configure data structure
A simple Hello message is not really that useful, time to add some data.
Add a qHyperCubeDef definition in object-properties.js:
const properties = {
qHyperCubeDef: {
qInitialDataFetch: [{ qWidth: 2, qHeight: 10 }],
},
// ...
};
Then add that hypercube as a data target in data.js:
export default {
targets: [
{
path: '/qHyperCubeDef',
},
],
};
With only those changes you should now have the option to add data from the property panel on the right:
Add a dimension by clicking on Add dimension and selecting a value in the menu that appears, do the same with measure.
Render data
In order to render the data you first need to access it through the useLayout hook:
import { useLayout, useElement, useEffect } from '@nebula.js/stardust';
// ...
component() {
console.log(useLayout());
}
You can then useLayout in combination with useEffect to render the headers and rows of data in qHyperCube:
component() {
const element = useElement();
const layout = useLayout();
useEffect(() => {
if (layout.qSelectionInfo.qInSelections) {
// skip rendering when in selection mode
return;
}
const hc = layout.qHyperCube;
// headers
const columns = [...hc.qDimensionInfo, ...hc.qMeasureInfo].map((f) => f.qFallbackTitle);
const header = `<thead><tr>${columns.map((c) => `<th>${c}</th>`).join('')}</tr></thead>`;
// rows
const rows = hc.qDataPages[0].qMatrix
.map((row) => `<tr>${row.map((cell) => `<td>${cell.qText}</td>`).join('')}</tr>`)
.join('');
// table
const table = `<table>${header}<tbody>${rows}</tbody></table>`;
// output
element.innerHTML = table;
}, [element, layout])
}
Select data
Before selecting data, we need to add some meta data on each row so that we know which one to select:
// rows
const rows = hc.qDataPages[0].qMatrix
.map((row, rowIdx) => `<tr data-row="${rowIdx}">${row.map((cell) => `<td>${cell.qText}</td>`).join('')}</tr>`)
.join('');
And then add a 'click' event handler on element which does the following:
- Verifies that the clicked element is a
td - Begins selections in
/qHyperCubeDefif not already activated - Extracts the
data-rowindex fromtr - Updates
selectedRowsbased on the clickdata-row
const element = useElement();
const selections = useSelections();
const [selectedRows, setSelectedRows] = useState([]);
useEffect(() => {
const listener = (e) => {
if (e.target.tagName === 'TD') {
if (!selections.isActive()) {
selections.begin('/qHyperCubeDef');
}
const row = +e.target.parentElement.getAttribute('data-row');
setSelectedRows((prev) => {
if (prev.includes(row)) {
return prev.filter((v) => v !== row);
}
return [...prev, row];
});
}
};
element.addEventListener('click', listener);
return () => {
element.removeEventListener('click', listener);
};
}, [element]);
Next, update the styling of the selected rows in the table whenever they change:
useEffect(() => {
if (!layout.qSelectionInfo.qInSelections) {
// no need to update when not in selection mode
return;
}
element.querySelectorAll('tbody tr').forEach((tr) => {
const idx = +tr.getAttribute('data-row');
tr.style.backgroundColor = selectedRows.includes(idx) ? '#eee' : '';
});
}, [element, selectedRows, layout]);
Finally, apply the selected values through the selections API:
useEffect(() => {
if (selections.isActive()) {
if (selectedRows.length) {
selections.select({
method: 'selectHyperCubeCells',
params: ['/qHyperCubeDef', selectedRows, [0]],
});
} else {
selections.select({
method: 'resetMadeSelections',
params: [],
});
}
} else if (selectedRows.length) {
setSelectedRows([]);
}
}, [selections.isActive(), selectedRows]);




