Skip to content

Commit 0c6bdc2

Browse files
megheaiuliannomego
authored andcommitted
Persist filtering in URL (#64)
* Add hash-params for groupOn, sortOn, descending * Add demos with hash-params * Cleanup and remove "changing" flags Improve serialize/deserialize and late route init Add constant, use arrow function and remove data from observer Improve conditionals and var names [polymer] Fix lint Cleanup and remove flags * Handle filters * [eslint] Add rules for es6 arrows [eslint] Fix errors eslint fixes [eslint ] Errors * Allow `-` in property name * Refactored _routeHashParamChanged and merge it with _routeHashParamsForFiltersChanged * Refact _paramForRouteChanged and _filterForRouteChanged * Revert checks * warn if column for filter not found * Refactor * WIP UNRUN UNTESTED PSEUDO-CODE DEMO PROTOTYPE To explain sequential, non-observer approach to route hash param management for sortOn/groupOn/descending Signed-off-by: Patrik Kullman <patrik.kullman@neovici.se> * Update route params early * Refactor deserialization, serialization for filters * Refactor _serializeFilter * Log colmn not found only when name is set * Add basic hash-param tests fixes #61
1 parent 91cc282 commit 0c6bdc2

13 files changed

+591
-18
lines changed

.eslintrc.json

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
"error",
2222
"never"
2323
],
24+
"arrow-parens": [
25+
"error",
26+
"as-needed"
27+
],
28+
"arrow-spacing": [
29+
"error",
30+
{ "before": true, "after": true }
31+
],
2432
"brace-style": "error",
2533
"camelcase": "error",
2634
"comma-spacing": [

bower.json

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"cosmoz-bottom-bar": "neovici/cosmoz-bottom-bar#master",
3939
"cosmoz-behaviors": "neovici/cosmoz-behaviors#master",
4040
"cosmoz-moment": "neovici/cosmoz-moment#master",
41+
"cosmoz-page-router": "Neovici/cosmoz-page-router#master",
4142
"import-filesaver": "JaySunSyn/import-filesaver",
4243
"paper-autocomplete-chips": "neovici/paper-autocomplete-chips#master",
4344
"paper-spinner": "^2.0.0",
@@ -49,6 +50,7 @@
4950
"paper-button": "PolymerElements/paper-button#^2.0.0",
5051
"iron-icons": "PolymerElements/iron-icons#^2.0.0",
5152
"web-component-tester": "^4.2.1",
53+
"iron-test-helpers": "PolymerElements/iron-test-helpers#^1.0.0",
5254
"marked-element": "^1.2.0",
5355
"prism-element": "^1.2.0",
5456
"cosmoz-viewinfo": "Neovici/cosmoz-viewinfo",

cosmoz-omnitable-column-boolean.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<paper-dropdown-menu>
1515
<paper-listbox class="dropdown-content" slot="dropdown-content"
1616
attr-for-selected="value"
17-
fallback-selection="[[ _computeSelected(item, valuePath) ]]"
17+
fallback-selection="[[ _computeSelected(item, valuePath) ]]"
1818
on-selected-item-changed="_valueChanged">
1919
<paper-item value="true">[[ trueLabel ]]</paper-item>
2020
<paper-item value="false">[[ falseLabel ]]</paper-item>
@@ -153,7 +153,7 @@
153153
var value = e.target.selected === 'true',
154154
item = e.model.item,
155155
oldValue = this.get(this.valuePath, item),
156-
formatFn = (value) => {
156+
formatFn = value => {
157157
return value ? this.trueLabel : this.falseLabel;
158158
};
159159
if (value === oldValue) {

cosmoz-omnitable-column-date-behavior.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@
211211
value = input.value,
212212
item = event.model.item,
213213
oldValue = this.get(this.valuePath, item),
214-
formatFn = (value) => {
214+
formatFn = value => {
215215
return moment(value).locale(this.locale).format(this.userFormat);
216216
},
217217
dateValue;

cosmoz-omnitable-column-time.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ <h3 style="margin: 0;">[[ title ]]</h3>
9393
item = e.model.item,
9494
oldDateMom = moment(item.date),
9595
newDateMom = moment(oldDateMom.format('YYYY-MM-DD') + ` ${timeString} ` + oldDateMom.format('Z')),
96-
formatFn = (value) => {
96+
formatFn = value => {
9797
return moment(value).locale(this.locale).format(this.userFormat);
9898
};
9999

cosmoz-omnitable.html

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<link rel="import" href="../cosmoz-web-worker/cosmoz-web-worker.html"/>
1919
<link rel="import" href="../cosmoz-i18next/cosmoz-i18next.html">
2020
<link rel="import" href="../cosmoz-bottom-bar/cosmoz-bottom-bar.html"/>
21+
<link rel="import" href="../cosmoz-page-router/cosmoz-page-location.html">
2122

2223
<link rel="import" href="../import-filesaver/import-filesaver.html"/>
2324

@@ -43,6 +44,8 @@
4344
<template>
4445
<style include="cosmoz-omnitable-styles iron-flex"></style>
4546

47+
<cosmoz-page-location id="location" route-hash-params="{{ _routeHashParams }}"></cosmoz-page-location>
48+
4649
<cosmoz-web-worker id="sortWorker" on-cosmoz-web-worker-ready="_onWebWorkerReady">
4750
<script type="text/worker">
4851
/*global onmessage, postMessage */

cosmoz-omnitable.js

+140-12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
'use strict';
55

6+
const PROPERTY_HASH_PARAMS = ['sortOn', 'groupOn', 'descending'];
7+
68
Polymer({
79

810
is: 'cosmoz-omnitable',
@@ -101,6 +103,11 @@
101103
value: ''
102104
},
103105

106+
sortOnColumn: {
107+
type: Object,
108+
computed: '_getColumn(sortOn, "name", columns)'
109+
},
110+
104111
/**
105112
* The column name to group on.
106113
*/
@@ -117,7 +124,7 @@
117124
type: Object,
118125
notify: true,
119126
observer: '_debounceGroupItems',
120-
computed: '_computeGroupOnColumn(groupOn, columns)'
127+
computed: '_getColumn(groupOn, "name", columns)'
121128
},
122129

123130
/**
@@ -182,12 +189,22 @@
182189
_filterIsTooStrict: {
183190
type: Boolean,
184191
computed: '_computeFilterIsTooStrict(_dataIsValid, sortedFilteredGroupedItems.length)'
185-
}
192+
},
193+
194+
hashParam: {
195+
type: String
196+
},
197+
198+
_routeHashParams: {
199+
type: Object,
200+
notify: true
201+
},
186202
},
187203

188204
observers: [
189205
'_dataChanged(data.*)',
190-
'_debounceSortItems(sortOn, descending, filteredGroupedItems)'
206+
'_debounceSortItems(sortOn, descending, filteredGroupedItems)',
207+
'_routeHashParamsChanged(_routeHashParams.*, hashParam, columns)'
191208
],
192209

193210
behaviors: [
@@ -200,7 +217,7 @@
200217
'update-item-size': '_onUpdateItemSize',
201218
'cosmoz-column-title-changed': '_onColumnTitleChanged',
202219
'cosmoz-column-hidden-changed': '_debounceUpdateColumns',
203-
'cosmoz-column-filter-changed': '_debounceFilterItems'
220+
'cosmoz-column-filter-changed': '_filterChanged',
204221
},
205222

206223
/** ELEMENT LIFECYCLE */
@@ -243,10 +260,6 @@
243260
return groupOn ? columns.filter(c => c.name !== this.groupOn) : columns.slice();
244261
},
245262

246-
_computeGroupOnColumn(groupOn) {
247-
return this._getColumn(groupOn);
248-
},
249-
250263
_computeDataValidity(data) {
251264
return data && Array.isArray(data) && data.length > 0;
252265
},
@@ -444,17 +457,22 @@
444457
* @param {String} attribute The attribute name of the column.
445458
* @returns {Object} The found column.
446459
*/
447-
_getColumn(attributeValue, attribute = 'name') {
448-
if (!attributeValue || !this.columns) {
460+
_getColumn(attributeValue, attribute = 'name', columns = this.columns) {
461+
if (!attributeValue || !columns) {
449462
return;
450463
}
451-
const column = this.columns.find(column => column[attribute] === attributeValue);
464+
const column = columns.find(column => column[attribute] === attributeValue);
452465
if (!column) {
453466
console.warn(`Cannot find column with ${attribute} ${attributeValue}`);
454467
}
455468
return column;
456469
},
457470

471+
_filterChanged: function (e, detail) {
472+
this._debounceFilterItems();
473+
this._filterForRouteChanged(detail.column);
474+
},
475+
458476
_debounceFilterItems: function () {
459477
this.debounce('filterItems', this._filterItems);
460478
},
@@ -493,6 +511,8 @@
493511
return;
494512
}
495513

514+
this._updateRouteParam('groupOn');
515+
496516
var groupOnColumn = this.groupOnColumn,
497517
groups;
498518

@@ -561,13 +581,16 @@
561581
return;
562582
}
563583

564-
var sortOnColumn = this._getColumn(this.sortOn),
584+
var sortOnColumn = this.sortOnColumn,
565585
items = [],
566586
numGroups = this.filteredGroupedItems.length,
567587
mappedItems,
568588
results = 0,
569589
itemMapper;
570590

591+
this._updateRouteParam('sortOn');
592+
this._updateRouteParam('descending');
593+
571594
if (!this.sortOn || !sortOnColumn) {
572595
this.sortedFilteredGroupedItems = this.filteredGroupedItems;
573596
this._debounceAdjustColumns();
@@ -1008,6 +1031,111 @@
10081031
return;
10091032
}
10101033
gl.highlightItem(i, reverse);
1034+
},
1035+
1036+
_serializeFilter: function (obj) {
1037+
const type = {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
1038+
let value = obj;
1039+
1040+
if (type === 'array' && !value.length) {
1041+
value = null;
1042+
} else if (type === 'object') {
1043+
const keys = Object.keys(obj).filter(k => obj[k] != null);
1044+
if (keys.length > 0) {
1045+
value = keys.reduce((acc, k) => {
1046+
acc[k] = obj[k];
1047+
return acc;
1048+
}, {});
1049+
} else {
1050+
value = null;
1051+
}
1052+
}
1053+
return this.serialize(value);
1054+
},
1055+
1056+
_deserializeFilter: function (obj, type = Object) {
1057+
if (type === Object && obj == null) {
1058+
return {};
1059+
}
1060+
if (type === Array && obj == null) {
1061+
return [];
1062+
}
1063+
return this.deserialize(obj, type);
1064+
},
1065+
1066+
_routeHashParamsChanged: function (changes, hashParam, columns) {
1067+
if (!changes || !hashParam || !columns || !columns.length) {
1068+
return;
1069+
}
1070+
1071+
PROPERTY_HASH_PARAMS.forEach(key => {
1072+
const hashValue = this.get(['_routeHashParams', hashParam + '-' + key]),
1073+
deserialized = this.deserialize(hashValue, this.properties[key].type);
1074+
1075+
if (hashValue === undefined || deserialized === this.get(key)) {
1076+
return;
1077+
}
1078+
1079+
this.set(key, deserialized);
1080+
});
1081+
1082+
let rule = new RegExp('^' + hashParam + '-filter\-\-([a-z0-9\-]+)$'),
1083+
routeParams = changes.base;
1084+
1085+
Object.keys(routeParams).forEach(key => {
1086+
const hashValue = routeParams[key],
1087+
matches = key.match(rule),
1088+
name = matches && matches[1],
1089+
column = name && columns.find(c => c.name === name);
1090+
1091+
if (!column) {
1092+
if (name) {
1093+
console.warn('column with name', name, 'for param', key, 'not found!');
1094+
}
1095+
return;
1096+
}
1097+
1098+
let filter = column.filter;
1099+
1100+
if (hashValue === this._serializeFilter(filter)) {
1101+
return;
1102+
}
1103+
column.set('filter', this._deserializeFilter(hashValue, filter && filter.constructor || undefined));
1104+
});
1105+
1106+
},
1107+
1108+
_updateRouteParam: function (key) {
1109+
if (!this.hashParam || !this._routeHashParams) {
1110+
return;
1111+
}
1112+
1113+
const path = ['_routeHashParams', this.hashParam + '-' + key],
1114+
hashValue = this.get(path),
1115+
value = this.get(key),
1116+
serialized = this.serialize(value, this.properties[key].type);
1117+
1118+
if (serialized === hashValue || hashValue == null && value === '') {
1119+
return;
1120+
}
1121+
1122+
this.set(path, serialized === undefined ? null : serialized);
1123+
},
1124+
1125+
_filterForRouteChanged: function (column) {
1126+
if (!this.hashParam || !this._routeHashParams || !this.data || !this.data.length) {
1127+
return;
1128+
}
1129+
1130+
const path = ['_routeHashParams', this.hashParam + '-filter--' + column.name],
1131+
hashValue = this.get(path),
1132+
serialized = this._serializeFilter(column.filter);
1133+
1134+
if (serialized === hashValue) {
1135+
return;
1136+
}
1137+
1138+
this.set(path, serialized === undefined ? null : serialized);
10111139
}
10121140
});
10131141
}());

demo/hash-param.html

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
7+
<title>cosmoz-omnitable hash-param demo</title>
8+
9+
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
10+
11+
<link rel="import" href="../../iron-flex-layout/iron-flex-layout.html">
12+
<link rel="import" href="../../iron-flex-layout/iron-flex-layout-classes.html">
13+
<link rel="import" href="../../cosmoz-page-router/cosmoz-page-router.html">
14+
<link rel="import" href="../../cosmoz-page-router/cosmoz-page-route.html">
15+
16+
<style is="custom-style" include="iron-flex iron-flex-alignment iron-positioning"></style>
17+
18+
</head>
19+
<body unresolved class="fullbleed">
20+
<cosmoz-page-router class="fit" id="appRouter" mode="hash" url-prefix="/">
21+
<cosmoz-page-route path="/" import="helpers/hash-param-template.html" template-id="home"></cosmoz-page-route>
22+
</cosmoz-page-router>
23+
</body></html>

demo/helpers/hash-param-template.html

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<link rel="import" href="x-page.html">
2+
<template id="home" is="dom-bind">
3+
<x-page class="fit" style="height: 100%; width: 100%;" hash-param="full">
4+
<template id="customNameTemplate">
5+
<span style="background: red;" on-tap="onTap">[[ item.name ]]</span>
6+
</template>
7+
</x-page>
8+
</template>
9+
<script>
10+
(function () {
11+
'use strict';
12+
13+
Cosmoz.TemplateView['home'] = {
14+
15+
};
16+
}());
17+
</script>

0 commit comments

Comments
 (0)