Skip to main content

Routing

Sometimes it is useful to share a link with a colleague or business executive that opens an app in a specific state depending on the URI and points to the important information that the one would like to share. Datagrok provides routing support for package developers to achieve this.

Routing tutorial

Routing is implemented by manipulating the basePath and path properties of the View. Let's build a simple app that opens tables, based on the URL path and applies selection according to the parameters passed in the URL.

We start off by getting the path and splitting it in segments:

//name: Test
//tags: app
export function test() {
const pathSegments = window.location.pathname.split('/');
// etc.
}

Depending on the pathSegments length, we choose to either start the application with the default behavior or handle the URI parameters. Typically, when the app starts from the Apps page, the pathSegments would contain 3 elements: ['', 'apps', 'Test'] because the pathname is /apps/Test by default for the app called Test.

Let's say the default behavior would be to add a couple of tables and set default paths for them.

if (pathSegments.length <= 3) {
//Adding demog table view
const demog = grok.data.testData('demog');
const demogView = grok.shell.addTableView(demog);
demogView.scatterPlot();
demogView.basePath = '/demog';
demogView.path = '/All';
grok.shell.v = demogView;

//Adding random walk table view
const wells = grok.data.testData('wells');
const wellsView = grok.shell.addTableView(wells);
wellsView.scatterPlot();
wellsView.basePath = '/wells';
wellsView.path = '/All';
grok.shell.v = wellsView;
}

Note that by setting .basePath we add a segment to the pathname and by setting .path we add another segment after .basePath. In the case of demog table, the pathname would be /apps/Test/demog/All. Link with such URI could be opened by another user, but for this, to work we also need to handle the case, when the pathSegments contains more than 3 segments.

const tableName = pathSegments[3];
const selectionLabel = pathSegments[4] ?? 'All';
const table = grok.data.testData(tableName as testData);
const tableView = grok.shell.addTableView(table);
tableView.basePath = `/${tableName}`;
setSelection(tableView, selectionLabel);
grok.shell.v = tableView;

Path segments can be used to provide intended behavior. In this case, the table name is retrieved from the fourth path segment, which corresponds to the .basePath set previously, and the selection options can be retrieved from the fifth path segment.

That's it! Now you've learned how to use routing to enhance your apps. For additional info see the full code and useful links below.

Tutorial code

Here's the full code used in the tutorial.

/* Do not change these import lines to match external modules in webpack configuration */
import * as DG from 'datagrok-api/dg';
import * as grok from 'datagrok-api/grok';

type testData = "wells" | "demog" | "biosensor" | "random walk" | "geo" | "molecules" | "dose-response";

//name: Test
//tags: app
export function test() {
const pathSegments = window.location.pathname.split('/');

if (pathSegments.length <= 3) { //Fresh app start
//Adding demog table view
const demog = grok.data.testData('demog');
const demogView = grok.shell.addTableView(demog);
demogView.scatterPlot();
demogView.basePath = '/demog';
demogView.path = '/All';
grok.shell.v = demogView;

//Adding random walk table view
const wells = grok.data.testData('wells');
const wellsView = grok.shell.addTableView(wells);
wellsView.scatterPlot();
wellsView.basePath = '/wells';
wellsView.path = '/All';
grok.shell.v = wellsView;
} else { //Handle routing
const tableName = pathSegments[3];
const selectionLabel = pathSegments[4] ?? 'All';
const table = grok.data.testData(tableName as testData);
const tableView = grok.shell.addTableView(table);
tableView.basePath = `/${tableName}`;
setSelection(tableView, selectionLabel);
grok.shell.v = tableView;
}
}

function setSelection(tableView: DG.TableView, label: string) {
tableView.path = `/${label}`;
if (label === 'All') {
tableView.dataFrame.selection.init(_ => true);
return;
}
const [colName, category] = label.split('=');
if (!colName || !category)
throw new Error(`PathError: wrong path format '${label}'. Should be 'colName=value'.`)
tableView.dataFrame.rows.match(`${colName} = ${category}`).select();
}

See also: