Skip to content
Snippets Groups Projects
Commit e9522649 authored by Neuber, Eugen Ramon's avatar Neuber, Eugen Ramon :speech_balloon: Committed by Reiter, Christoph
Browse files

Hange to generic DataTable

parent 4c81fd86
Branches
No related tags found
No related merge requests found
...@@ -9,22 +9,12 @@ ...@@ -9,22 +9,12 @@
``` ```
# Attributes # Attributes
- `value`: api request
- example `<vpu-data-table-view value="api/request"></vpu-data-table-view>`
- `filter`: optional filter for the request (append to api url)
- example 'Abc' will be `?search=Abc`
- `blacklisted-columns`: optional string with all column names to be excluded
. example '@id @type' (both are hydra columns)
- `lang` (optional, default: `de`): set to `de` or `en` for German or English - `lang` (optional, default: `de`): set to `de` or `en` for German or English
- example `<vpu-data-table-view lang="de"></vpu-data-table-view>` - example `<vpu-data-table-view lang="de"></vpu-data-table-view>`
- `entry-point-url` (optional, default is the TU Graz entry point url): entry point url to access the api
- example `<vpu-data-table-view entry-point-url="http://127.0.0.1:8000"></vpu-data-table-view>`
- `paging` (optional, required to let datatable do the paging of loaded rows) - `paging` (optional, required to let datatable do the paging of loaded rows)
- example `<vpu-data-table-view paging></vpu-data-table-view>` - example `<vpu-data-table-view paging></vpu-data-table-view>`
- `searching` (optional, required if a search box is desired) - `searching` (optional, required if a search box is desired)
- example `<vpu-data-table-view searching></vpu-data-table-view>` - example `<vpu-data-table-view searching></vpu-data-table-view>`
- `wait-until-all-loaded` (optional, required if all rows must load before use)
- example `<vpu-data-table-view wait-until-all-loaded></vpu-data-table-view>`
# Local development # Local development
```bash ```bash
......
...@@ -2,11 +2,9 @@ import $ from 'jquery'; ...@@ -2,11 +2,9 @@ import $ from 'jquery';
import dt from 'datatables.net'; import dt from 'datatables.net';
import resp from 'datatables.net-responsive'; import resp from 'datatables.net-responsive';
import resp2 from 'datatables.net-responsive-dt'; import resp2 from 'datatables.net-responsive-dt';
import {getAPiUrl, getAssetURL,} from './utils.js'; //import {getAssetURL,} from './utils.js';
import {i18n} from './i18n'; import {i18n} from './i18n';
import {html, LitElement} from 'lit-element'; import {html, LitElement} from 'lit-element';
import {unsafeHTML} from 'lit-html/directives/unsafe-html.js';
import JSONLD from 'vpu-common/jsonld';
import commonUtils from 'vpu-common/utils'; import commonUtils from 'vpu-common/utils';
dt(window, $); dt(window, $);
...@@ -17,136 +15,36 @@ class DataTableView extends LitElement { ...@@ -17,136 +15,36 @@ class DataTableView extends LitElement {
constructor() { constructor() {
super(); super();
this.lang = 'de'; this.lang = 'de';
this.entryPointUrl = getAPiUrl();
this.jsonld = null;
this.value = null;
this.filter = null;
this.apiUrl = null;
this.whitelist_cols = '*';
this.blacklist_cols = '';
this.table_columns = []; // all possible columns, defined by API entity
this.show_columns = []; // all columns visible in table, defined by property whhitelist/blacklist
this.display_columns = []; // all possible columns, in desired order for the table
// datatable properties // datatable properties
this.table = null; this.table = null;
this.responsive = null; this.responsive = null;
this.paging = false; this.paging = false;
this.searching = false; this.searching = false;
//
this.is_loading = false;
this.wait_until_all_loaded = false;
} }
static get properties() { static get properties() {
return { return {
lang: { type: String }, lang: { type: String },
value: { type: String },
entryPointUrl: { type: String, attribute: 'entry-point-url' },
filter: { type: String },
apiUrl: { type: String, attribute: false },
whitelist_cols: { type: String, attribute: 'whitelisted-columns' },
blacklist_cols: { type: String, attribute: 'blacklisted-columns' },
table_columns: { type: Array, attribute: false },
show_columns: { type: Array, attribute: false },
display_columns: { type: Array, attribute: false },
table: { type: Object, attribute: false }, table: { type: Object, attribute: false },
paging: { type: Boolean }, paging: { type: Boolean },
searching: { type: Boolean }, searching: { type: Boolean },
is_loading: { type: Boolean, attribute: false },
wait_until_all_loaded: { type: Boolean, attribute: 'wait-until-all-loaded'}
}; };
} }
connectedCallback() { set_datatable() {
super.connectedCallback();
const that = this;
JSONLD.initialize(this.entryPointUrl, function (jsonld) {
that.jsonld = jsonld;
try {
that.apiUrl = that.jsonld.getApiUrlForEntityName(that.value);
if (that.jsonld.entities[that.value] === undefined) {
console.dir(that.jsonld);
throw "Error: Could not get information about " + that.value;
}
that.table_columns = that.jsonld.entities[that.value]['hydra:supportedProperty'].map(obj => obj['hydra:title']);
// display empty table
that.setup_columns();
let columns = [];
for (let i = 0; i < that.display_columns.length; ++i) {
columns[i] = {
title: that.display_columns[i],
visible: that.show_columns.indexOf(that.display_columns[i]) > -1
};
}
that.set_datatable(columns);
if (that.filter) {
that.loadWebPageElement();
}
} catch (e) {
that.table_columns = ['message from server'];
that.setup_columns();
that.set_datatable([{title: 'message from server', visible: true}]);
that.table.row.add(['<span style="color:red;background:lightgray;">' + e.toString() + '</span>']);
}
});
// disabled, load first on toggle to visible
window.addEventListener("vpu-auth-init", () => that.loadWebPageElement());
}
setup_columns() {
if (this.whitelist_cols === '*') {
const blacklist_cols = this.blacklist_cols.split(' ');
this.show_columns = this.table_columns.filter(col => blacklist_cols.indexOf(col) === -1);
} else {
this.show_columns = this.whitelist_cols.split(' ');
}
this.display_columns = this.show_columns.slice();
for(let i=0; i < this.table_columns.length; ++i) {
if (this.display_columns.indexOf(this.table_columns[i]) === -1) {
this.display_columns.push(this.table_columns[i]);
}
}
}
set_datatable(columns) {
const lang_de_url = 'datatables/i18n/German.json'; const lang_de_url = 'datatables/i18n/German.json';
const lang_en_url = 'datatables/i18n/English.json'; const lang_en_url = 'datatables/i18n/English.json';
this.table = $(this.shadowRoot.querySelector('#dt')).DataTable({
destroy: true, if (this.table) {
this.table.destroy();
}
this.table = $(this.querySelector('table')).DataTable({
autoWidth: false, autoWidth: false,
language: { language: {
url: this.lang === 'de' ? lang_de_url : lang_en_url, url: this.lang === 'de' ? lang_de_url : lang_en_url,
}, },
columns: columns,
data: [],
paging: this.paging, paging: this.paging,
searching: this.searching, searching: this.searching,
columnDefs: [
{
"render": function ( data, type, row ) {
let itemText = data;
if (itemText) {
let dat = itemText.toString();
if (dat.match(/\+\d{2} \(\d+\) \d+/)) {
itemText = "<a href='tel:" + dat + "'>" + dat + '</a>';
} else if (dat.match(/\w+(?:\+\w+)?@(?:\w+\.)+\w+/)) {
itemText = "<a href='mailto:" + dat + "'>" + dat + '</a>';
} else if (dat.match(/(\d{4})-(\d{2})-(\d{2})/)) {
itemText = dat.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3.$2.$1");
// itemText = dat.replace(/(\d{4})-(\d{2})-(\d{2})/, "<input type='date' value='$1-$2-$3' min='$1-$2-$3' max='$1-$2-$3'>");
// } else if (dat.match(/CAT-\d+/)) {
// itemText = '<button>' + dat + '</button>';
}
}
// console.log(data);
return itemText;
},
"targets": [...columns.keys()]
},
]
}); });
try { try {
...@@ -158,152 +56,22 @@ class DataTableView extends LitElement { ...@@ -158,152 +56,22 @@ class DataTableView extends LitElement {
} }
} }
update_datatable(columns, rows, is_first_page) {
const that = this;
if (this.table) {
// set column visibility/titles on first page load only
if (is_first_page) {
columns.forEach(function (item) { //, index) {
let i = that.display_columns.indexOf(item.title);
that.table.columns([i]).visible(item.visible === true);
// if column order has changed, update column title also
let t = that.table.columns([i]).header();
$(t).html(item.title);
});
}
rows.forEach(row => this.table.row.add(row));
// now ready to draw
this.table.draw();
}
}
async loader(page) {
const apiUrlWithFilter = this.apiUrl + '?search=' + this.filter + '&page=' + page;
const that = this;
return await fetch(apiUrlWithFilter, {
headers: {
'Content-Type': 'application/ld+json',
'Authorization': 'Bearer ' + window.VPUAuthToken,
},
})
.then(res => res.json())
.then(function (data) {
// TODO
that.setup_columns();
const items = data['hydra:member'];
let rows = [];
let columns = [];
for (let i = 0; i < that.display_columns.length; ++i) {
columns[i] = {
title: that.display_columns[i],
visible: that.show_columns.indexOf(that.display_columns[i]) > -1
};
if (items) {
for (let j = 0; j < items.length; ++j) {
if (rows[j] === undefined) {
rows[j] = [];
}
rows[j][i] = items[j][that.display_columns[i]] || '';
}
}
}
that.update_datatable(columns, rows, page === 1);
if (!that.wait_until_all_loaded)
that.is_loading = false;
return rows.length;
});
}
async call_loader(page) {
return await this.loader(page);
}
async loadWebPageElement() {
if (window.VPUAuthToken === undefined || window.VPUAuthToken === "") {
return;
}
if (this.apiUrl === null || this.jsonld === null) {
return;
}
this.is_loading = true;
let page = 1;
while (await this.call_loader(page++) > 0) {}
this.is_loading = false;
}
update(changedProperties) { update(changedProperties) {
changedProperties.forEach((oldValue, propName) => { changedProperties.forEach((oldValue, propName) => {
// noinspection FallThroughInSwitchStatementJS if (propName === "lang") {
switch (propName) { i18n.changeLanguage(this.lang).catch(e => { console.log(e)});
case "lang":
i18n.changeLanguage(this.lang).catch(e => { console.log(e)});
break;
case "whitelist_cols":
case "blacklist_cols":
this.setup_columns();
case "filter":
case "paging":
case "searching":
if (this.table)
this.table.clear();
this.loadWebPageElement().catch(e => { console.log(e); });
break;
case "value":
case "entryPointUrl":
const that = this;
JSONLD.initialize(this.entryPointUrl, function (jsonld) {
that.jsonld = jsonld;
that.apiUrl = that.jsonld.getApiUrlForEntityName(that.value);
});
this.loadWebPageElement().catch(e => { console.log(e)});
break;
default:
// nothing to do for this properties
} }
}); });
super.update(changedProperties); super.update(changedProperties);
this.updateComplete.then(this.set_datatable()).catch(e => { console.log(e)});
} }
render() { render() {
let dt_css = getAssetURL('datatables/css/jquery.dataTables.min.css');
let rs_css = getAssetURL('datatables/css/responsive.dataTables.css');
return html` return html`
<link rel="stylesheet" href="${dt_css}">
<link rel="stylesheet" href="${rs_css}">
<style> <style>
#dt-parent {
display: block;
width: 100%;
min-height: 50px;
position: relative;
}
#cover {
display: ${this.is_loading ? 'block' : 'none'};
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 999;
background: white url("data:image/gif;base64,R0lGODlhEAAQAPQAAP///wAAAPj4+Dg4OISEhAYGBiYmJtbW1qioqBYWFnZ2dmZmZuTk5JiYmMbGxkhISFZWVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAAKAAEALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkEAAoAAgAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkEAAoAAwAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAAKAAQALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAAKAAUALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==") center center no-repeat;
opacity: .9;
}
#dt {
min-width: 100px;
width: 100%;
}
</style> </style>
<div id="dt-parent"> <slot name="table"></slot>
${unsafeHTML('<div><table id="dt" class="display"></table></div>')}
<div id="cover"></div>
</div>
`; `;
} }
} }
......
import 'vpu-auth'; import 'vpu-auth';
import './data-table-view.js'; import './data-table-view.js';
import {setting, getAPiUrl} from './utils.js'; import {setting, getAssetURL,} from './utils.js';
import {i18n} from './i18n'; import {i18n} from './i18n';
import {html, LitElement} from 'lit-element'; import {html, LitElement} from 'lit-element';
import commonUtils from 'vpu-common/utils'; import commonUtils from 'vpu-common/utils';
...@@ -27,37 +27,21 @@ class DataTableViewDemo extends LitElement { ...@@ -27,37 +27,21 @@ class DataTableViewDemo extends LitElement {
super.update(changedProperties); super.update(changedProperties);
} }
filterChange(e) {
let datatable = this.shadowRoot.querySelector('#dt1');
datatable.setAttribute('filter', e.target.value);
}
colsChange(e) {
let datatable = this.shadowRoot.querySelector('#dt1');
if (datatable === undefined) { alter('datatable not found'); return; }
datatable.setAttribute('whitelisted-columns', e.target.value);
}
render() { render() {
// datatable.net tyles must be applied here :-/
let dt_css = getAssetURL('datatables/css/jquery.dataTables.min.css');
let rs_css = getAssetURL('datatables/css/responsive.dataTables.css');
return html` return html`
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css">
<link rel="stylesheet" href="${dt_css}">
<link rel="stylesheet" href="${rs_css}">
<style> <style>
.box { .box {
margin: 10px; margin: 10px;
padding: 10px; padding: 10px;
border: 1px solid orange; border: 1px solid orange;
} }
.box2 {
margin: 10px;
padding: 10px;
border: 1px solid green;
}
.box3 {
margin: 10px;
padding: 10px;
border: 1px solid blue;
}
</style> </style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css">
<section class="section"> <section class="section">
<div class="content"> <div class="content">
...@@ -68,75 +52,175 @@ class DataTableViewDemo extends LitElement { ...@@ -68,75 +52,175 @@ class DataTableViewDemo extends LitElement {
</div> </div>
<div class="content"> <div class="content">
<h4>DataTable: paging and searching</h4> <h4>DataTable: paging and searching</h4>
<p> <div class="box">
<label for="filter">Filter für die Suche:</label> <vpu-data-table-view lang="${this.lang}" paging searching>
<input type="text" name="filter" id="filter" value="" placeholder="Geben Sie mindestens 3 Zeichen ein" @change="${this.filterChange}"> <div slot="table"><!-- slot encapsulates table -->
</p> <table class="display">
<p> <thead>
<label for="columns">Spalten im Ergbnis:</label> <tr>
<input type="text" name="columns" id="columns" value="*" placeholder="Geben Sie einen Stern * für alle ein" @change="${this.colsChange}"> <th>A</th>
</p> <th>B</th>
<div class="box"> <th>C</th>
<vpu-data-table-view </tr>
lang="${this.lang}" </thead>
value="Person" <tbody>
filter="" <tr>
whitelisted-columns="*" <td>abc</td>
blacklisted-columns="phoneExtension name" <td>123</td>
id="dt1" <td>a-2-4-g</td>
paging </tr>
searching <tr>
></vpu-data-table-view> <td>def</td>
<td>456</td>
<td>b-3-5-h</td>
</tr>
<tr>
<td>ghi</td>
<td>789</td>
<td>c-4-6-i</td>
</tr>
<tr>
<td>jkl</td>
<td>012</td>
<td>x-8-0-a</td>
</tr>
<tr>
<td>abc</td>
<td>123</td>
<td>a-2-4-g</td>
</tr>
<tr>
<td>def</td>
<td>456</td>
<td>b-3-5-h</td>
</tr>
<tr>
<td>ghi</td>
<td>789</td>
<td>c-4-6-i</td>
</tr>
<tr>
<td>jkl</td>
<td>012</td>
<td>x-8-0-a</td>
</tr>
<tr>
<td>abc</td>
<td>123</td>
<td>a-2-4-g</td>
</tr>
<tr>
<td>def</td>
<td>456</td>
<td>b-3-5-h</td>
</tr>
<tr>
<td>ghi</td>
<td>789</td>
<td>c-4-6-i</td>
</tr>
<tr>
<td>jkl</td>
<td>012</td>
<td>x-8-0-a</td>
</tr>
</tbody>
</table>
</div>
</vpu-data-table-view>
</div> </div>
</div>
</section>
<!--
<section class="section">
<div class="content">
<h4>DataTable: no paging, no searching</h4> <h4>DataTable: no paging, no searching</h4>
<div class="box2"> <div class="box">
<vpu-data-table-view <vpu-data-table-view lang="${this.lang}">
lang="${this.lang}" <div slot="table"><!-- slot encapsulates table -->
value="Person" <table class="display">
filter="Ab" <thead>
whitelisted-columns="name telephone email" <tr>
blacklisted-columns="" <th>A</th>
id="dt2" <th>B</th>
wait-until-all-loaded <th>C</th>
></vpu-data-table-view> </tr>
</thead>
<tbody>
<tr>
<td>abc</td>
<td>123</td>
<td>a-2-4-g</td>
</tr>
<tr>
<td>ghi</td>
<td>789</td>
<td>c-4-6-i</td>
</tr>
<tr>
<td>jkl</td>
<td>012</td>
<td>x-8-0-a</td>
</tr>
</tbody>
</table>
</div>
</vpu-data-table-view>
</div> </div>
</div> </div>
</section>
-->
<section class="section">
<div class="content"> <div class="content">
<h4>DataTable: DummyProduct</h4> <h4>Common Table</h4>
<div class="box2"> <div class="box">
<vpu-data-table-view <!-- <vpu-data-table-view lang="${this.lang}" paging searching> -->
lang="${this.lang}" <div slot="table">
value="DummyProduct" <table class="display">
filter="*" <thead>
whitelisted-columns="*" <tr>
blacklisted-columns="" <th>A</th>
id="dt3" <th>B</th>
wait-until-all-loaded <th>C</th>
paging </tr>
searching </thead>
></vpu-data-table-view> <tbody>
</div> <tr>
<h4>DataTable: nonExistantEntity</h4> <td>abc</td>
<div class="box3"> <td>123</td>
<vpu-data-table-view <td>a-2-4-g</td>
lang="${this.lang}" </tr>
value="nonExistantEntity" <tr>
filter="*" <td>def</td>
whitelisted-columns="*" <td>456</td>
blacklisted-columns="" <td>b-3-5-h</td>
id="dt4" </tr>
wait-until-all-loaded <tr>
paging <td>ghi</td>
searching <td>789</td>
></vpu-data-table-view> <td>c-4-6-i</td>
</tr>
<tr>
<td>jkl</td>
<td>012</td>
<td>x-8-0-a</td>
</tr>
<tr>
<td>abc</td>
<td>123</td>
<td>a-2-4-g</td>
</tr>
<tr>
<td>def</td>
<td>456</td>
<td>b-3-5-h</td>
</tr>
<tr>
<td>ghi</td>
<td>789</td>
<td>c-4-6-i</td>
</tr>
<tr>
<td>jkl</td>
<td>012</td>
<td>x-8-0-a</td>
</tr>
</tbody>
</table>
</div>
<!-- </vpu-data-table-view> -->
</div> </div>
</div> </div>
</section> </section>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment