Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds persistence and reloading of datatable pagination params #2112

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
34 changes: 18 additions & 16 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,26 @@
<p data-bind="html: appInitializationErrorMessage"></p>
</div>
</div>
<!-- ko if: $data.currentCohortDefinition() || $data.currentConceptSet() -->
<!-- ko if: $data.currentCohortDefinition() != null && $data.router.currentView() !='cohort-definition-manager' && $data.router.currentView() != 'loading' -->
<div class="breadcrumb-container">
<i class="fa fa-arrow-left"></i>
<a data-bind="attr: { href: '#/cohortdefinition/' + $data.currentCohortDefinition().id()}, text: $data.currentCohortDefinition().name"></a>
<!-- ko if: $data.currentConceptSet() != null && $data.currentConceptSetSource() == 'cohort' -->
<i class="fa fa-chevron-right"></i>
<a data-bind="attr: { href: '#/cohortdefinition/' + $data.currentCohortDefinition().id() + '/conceptsets/' + $data.currentConceptSet().id + '/details'}, text:$data.currentConceptSet().name()"></a>
<div>
<!-- ko if: $data.currentCohortDefinition() || $data.currentConceptSet() -->
<!-- ko if: $data.currentCohortDefinition() != null && $data.router.currentView() !='cohort-definition-manager' && $data.router.currentView() != 'loading' -->
<div class="breadcrumb-container">
<i class="fa fa-arrow-left"></i>
<a data-bind="attr: { href: '#/cohortdefinition/' + $data.currentCohortDefinition().id()}, text: $data.currentCohortDefinition().name"></a>
<!-- ko if: $data.currentConceptSet() != null && $data.currentConceptSetSource() == 'cohort' -->
<i class="fa fa-chevron-right"></i>
<a data-bind="attr: { href: '#/cohortdefinition/' + $data.currentCohortDefinition().id() + '/conceptsets/' + $data.currentConceptSet().id + '/details'}, text:$data.currentConceptSet().name()"></a>
<!-- /ko -->
</div>
<!-- /ko -->
<!-- ko if: $data.currentConceptSet() && $data.currentConceptSetSource() == 'repository' && $data.router.currentView() != 'conceptset-manager' -->
<div class="breadcrumb-container">
<i class="fa fa-arrow-left"></i>
<a data-bind="attr: { href: '#/conceptset/' + $data.currentConceptSet().id + '/details'}, text:$data.currentConceptSet().name"></a>
</div>
<!-- /ko -->
<!-- /ko -->
</div>
<!-- /ko -->
<!-- ko if: $data.currentConceptSet() && $data.currentConceptSetSource() == 'repository' && $data.router.currentView() != 'conceptset-manager' -->
<div class="breadcrumb-container">
<i class="fa fa-arrow-left"></i>
<a data-bind="attr: { href: '#/conceptset/' + $data.currentConceptSet().id + '/details'}, text:$data.currentConceptSet().name"></a>
</div>
<!-- /ko -->
<!-- /ko -->

<!-- ko ifnot: EventBus.errorMsg() -->
<div data-bind="if: $data.router.currentViewAccessible" class="flexed">
Expand Down
2 changes: 1 addition & 1 deletion js/components/faceted-datatable.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="text-center" data-bind="if: $component.componentLoading(), css: {visible:componentLoading(), hidden: !componentLoading()}">
<h3><i class="fa fa-spinner fa-spin"></i> Loading ...</h3>
</div>
<div data-bind="ifnot: $component.componentLoading(), css: {visible: !componentLoading(), hidden: componentLoading()}">
<div data-bind="css: {visible: !componentLoading(), hidden: componentLoading()}">
<div class="row">
<div class="feFilter" data-bind="visible:$component.facets().length > 0, css: {'col-xs-2': $component.facets().length > 0}">
<div data-bind="with: $component.facets">
Expand Down
65 changes: 51 additions & 14 deletions js/components/faceted-datatable.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'colvis', ], function (ko, view, crossfilter) {
define(
['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'utils/DatatablePaginationUtils', 'colvis', ],
function (ko, view, crossfilter, DatatablePaginationUtils)
{

function facetedDatatable(params) {
function getDtElement(facetedDtEl) {
return $(facetedDtEl).find('table');
}

function getSavedSearch(element) {
return DatatablePaginationUtils.getFacets(getDtElement(element).dataTable().DataTable());
}

function facetedDatatable(params, componentInfo) {
var self = this;
var subscriptions = [];

self.element = componentInfo.element;
self.selectedData = params.selectedData || null;
self.headersTemplateId = params.headersTemplateId;
self.reference = params.reference;
Expand Down Expand Up @@ -66,9 +78,16 @@ define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'colvis', ],
self.scrollY = params.scrollY || null;
self.scrollCollapse = params.scrollCollapse || false;

self.updateFilters = function (data, event) {
self.selectFacetItems = function(targetCaption, selectedFacetValues) {
const facetItems = self.facets().find(f => f.caption === targetCaption).facetItems;
if (facetItems) {
facetItems.forEach(fi => self.updateFilters(fi, null, selectedFacetValues.includes(fi.key), false));
}
};

self.updateFilters = function (data, event, selected, emitEvent = true) {
var facet = data.facet;
data.selected(!data.selected());
data.selected(typeof selected !== 'undefined' ? selected : !data.selected());
if (data.selected()) {
if (!facet.selectedItems.hasOwnProperty(data.key)) {
facet.selectedItems[data.key] = data;
Expand All @@ -88,6 +107,8 @@ define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'colvis', ],
});
}
self.data.valueHasMutated();

emitEvent && getDtElement(self.element).trigger('facet.dt', data);
}

// additional helper function to help with crossfilter-ing dimensions that contain nulls
Expand Down Expand Up @@ -126,16 +147,22 @@ define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'colvis', ],
self.facets.push(facet);
});
// Iterate over the facets and set any defaults
const savedSearch = getSavedSearch(self.element);
$.each(self.options.Facets, function (i, facetConfig) {
if (facetConfig.defaultFacets && facetConfig.defaultFacets.length > 0) {
$.each(facetConfig.defaultFacets, function (d, defaultFacet) {
var facetItem = $.grep(self.facets()[i].facetItems, function (f) {
return f.key == defaultFacet;
});
if (facetItem.length > 0) {
self.updateFilters(facetItem[0], null);
}
})
const savedFacetValues = savedSearch[facetConfig.caption];
if (savedFacetValues) {
self.selectFacetItems(facetConfig.caption, savedFacetValues);
} else {
if (facetConfig.defaultFacets && facetConfig.defaultFacets.length > 0) {
$.each(facetConfig.defaultFacets, function (d, defaultFacet) {
var facetItem = $.grep(self.facets()[i].facetItems, function (f) {
return f.key == defaultFacet;
});
if (facetItem.length > 0) {
self.updateFilters(facetItem[0], null, true, false);
}
})
}
}
});
}
Expand All @@ -144,6 +171,16 @@ define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'colvis', ],
})
);

$(self.element).on('refresh.faceted-dt', () => {
if (Array.isArray(self.options.Facets)) {
const savedSearch = getSavedSearch(self.element);
self.options.Facets.forEach(facetConfig => {
const savedFacetValues = savedSearch[facetConfig.caption] || facetConfig.defaultFacets || [];
self.selectFacetItems(facetConfig.caption, savedFacetValues);
});
}
});

// init component
if (ko.isComputed(self.reference)) {
// valueHasMutated doesn't work for computed
Expand Down Expand Up @@ -190,7 +227,7 @@ define(['knockout', 'text!./faceted-datatable.html', 'crossfilter', 'colvis', ],
};

var component = {
viewModel: facetedDatatable,
viewModel: {createViewModel: (params, info) => new facetedDatatable(params, info)},
template: view
};

Expand Down
11 changes: 6 additions & 5 deletions js/extensions/bindings/datatableBinding.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ define([
'xss',
'moment',
'services/MomentAPI',
'utils/DatatablePaginationUtils',
'datatables.net-buttons',
'colvis',
'datatables.net-buttons-html5',
Expand All @@ -16,7 +17,8 @@ define([
config,
filterXSS,
moment,
momentApi
momentApi,
paginationUtils
) {

function renderSelected(s, p, d) {
Expand Down Expand Up @@ -130,7 +132,8 @@ define([
ko.applyBindings(bindingContext, $(element).find('thead')[0]);
}

$(element).DataTable(binding.options);
const datatable = $(element).DataTable(binding.options);
paginationUtils.applyPaginationListeners(element, datatable, binding);

if (binding.api != null)
{
Expand Down Expand Up @@ -185,9 +188,7 @@ define([
if (data.length > 0)
table.rows.add(data);

// drawing may access observables, which updating we do not want to trigger a redraw to the table
// see: https://knockoutjs.com/documentation/computed-dependency-tracking.html#IgnoringDependencies
ko.ignoreDependencies(table.draw);
paginationUtils.refreshTable(table);
}


Expand Down
10 changes: 10 additions & 0 deletions js/pages/Router.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ define(
'knockout',
'const',
'services/EventBus',
'datatables.net',
'utils/DatatablePaginationUtils',
'director',
],
(
Expand All @@ -19,6 +21,8 @@ define(
ko,
constants,
EventBus,
dataTables,
DatatablePaginationUtils
) => {
class AtlasRouter {
constructor() {
Expand Down Expand Up @@ -127,6 +131,12 @@ define(
this.routerParams(routerParams);
}
this.currentView(view);
setTimeout(() => {
$.fn.dataTable.tables().forEach(dt => {
DatatablePaginationUtils.refreshTable($(dt).dataTable().DataTable());
});
$('faceted-datatable').trigger('refresh.faceted-dt');
});
}
}
return new AtlasRouter();
Expand Down
22 changes: 12 additions & 10 deletions js/pages/cohort-definitions/cohort-definition-manager.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<loading data-bind="visible: $component.isLoading()"></loading>
<div data-bind="if: currentCohortDefinition() && $root.router.currentView() == 'cohort-definition-manager' && !$component.isLoading()" class="flexed">
<heading-title params="name: cohortDefinitionCaption(), icon: 'users', theme: 'dark'"></heading-title>
<!-- ko if: currentCohortDefinition().id() != 0 -->
<!-- ko component: {name: 'authorship', params: getAuthorship()} --> <!-- /ko -->
<!-- /ko -->
<div class="authorship-container">
<!-- ko if: currentCohortDefinition().id() != 0 -->
<!-- ko component: {name: 'authorship', params: getAuthorship()} --> <!-- /ko -->
<!-- /ko -->
</div>
<access-denied params="isAuthenticated: isAuthenticated, isPermitted: hasAccess"></access-denied>

<!-- ko if: hasAccess -->
Expand Down Expand Up @@ -34,28 +36,28 @@
</div>

<ul class="nav nav-tabs">
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'definition' }, click: function() { $component.tabMode('definition'); }">
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'definition' }, click: function() { $component.selectTab('definition'); }">
<a>Definition <i data-bind="click: function () { $component.cohortDefinitionOpened(true) }" class="fa fa-question-circle-o"></i></a>
</li>

<li role="presentation" data-bind="css: { active: $component.tabMode() == 'conceptsets' }, click: function() { $component.tabMode('conceptsets'); }">
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'conceptsets' }, click: function() { $component.selectTab('conceptsets'); }">
<a>Concept Sets</a>
</li>

<li role="presentation" data-bind="css: { active: $component.tabMode() == 'generation' }, click: function() { $component.tabMode('generation'); }">
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'generation' }, click: function() { $component.selectTab('generation'); }">
<a>Generation</a>
</li>

<li role="presentation" data-bind="css: { active: $component.tabMode() == 'reporting' }, click: function() { $component.tabMode('reporting'); }">
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'reporting' }, click: function() { $component.selectTab('reporting'); }">
<a>Reporting</a>
</li>
<!--
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'explore' }, click: function() { $component.tabMode('explore'); }"><a>Explore</a></li>
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'explore' }, click: function() { $component.selectTab('explore'); }"><a>Explore</a></li>
-->
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'export' }, click: () => $component.tabMode('export')">
<li role="presentation" data-bind="css: { active: $component.tabMode() == 'export' }, click: () => $component.selectTab('export')">
<a>Export</a>
</li>
<li role="presentation" data-bind="css: { active: $component.tabMode() === 'warnings' }, click: function(){ $component.tabMode('warnings'); } ">
<li role="presentation" data-bind="css: { active: $component.tabMode() === 'warnings' }, click: function(){ $component.selectTab('warnings'); } ">
<a data-bind="attr: { class: warningClass }">Messages <span class="badge" data-bind="text: warningsTotals, visible: warningsTotals() > 0"></span></a>
</li>
</ul>
Expand Down
8 changes: 6 additions & 2 deletions js/pages/cohort-definitions/cohort-definition-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -1054,15 +1054,19 @@ define(['jquery', 'knockout', 'text!./cohort-definition-manager.html',
}
}

onRouterParamsChanged(params) {
const { cohortDefinitionId, conceptSetId, mode = 'definition', sourceKey } = params;
onRouterParamsChanged(changedParamsMap, newParams) {
const { cohortDefinitionId, conceptSetId, mode = 'definition', sourceKey } = newParams || changedParamsMap;
this.clearConceptSet();
this.tabMode(mode);
if (!this.checkifDataLoaded(cohortDefinitionId, conceptSetId, sourceKey)) {
this.prepareCohortDefinition(cohortDefinitionId, conceptSetId, sourceKey);
}
}

selectTab(key) {
commonUtils.routeTo(`/cohortdefinition/${this.currentCohortDefinition().id()}/${key}`);
}

getSourceInfo(source) {
const info = this.currentCohortDefinitionInfo();
for (var i = 0; i < info.length; i++) {
Expand Down
2 changes: 1 addition & 1 deletion js/pages/cohort-definitions/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ define(
var sourceKey = qs.sourceKey || null;
router.setCurrentView('cohort-definition-manager', {
cohortDefinitionId,
mode: 'definition',
mode: view,
sourceKey,
});
sharedState.ConceptSet.source('cohort');
Expand Down
30 changes: 30 additions & 0 deletions js/utils/CommonUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,34 @@ define([
return tooltipText.replace(/'/g, "\\'").replace(/"/g, '&quot;');
}

const getPathTo = function(element) {
if (element===document.body)
return element.tagName;

let ix = 0;
const siblings = element.parentNode.childNodes;
for (let i= 0; i<siblings.length; i++) {
const sibling = siblings[i];
if (sibling===element)
return getPathTo(element.parentNode)+'/'+element.tagName+'['+(ix+1)+']';
if (sibling.nodeType===1 && sibling.tagName===element.tagName)
ix++;
}
};

const calculateStringHash = function(string) {
let hash = 0;
if (string.length == 0) {
return hash;
}
for (var i = 0; i < string.length; i++) {
var char = string.charCodeAt(i);
hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
};

return {
build,
confirmAndDelete,
Expand All @@ -229,5 +257,7 @@ define([
normalizeUrl,
toggleConceptSetCheckbox,
escapeTooltip,
getPathTo,
calculateStringHash,
};
});
Loading