diff --git a/Message/Request/Request.js b/Message/Request/Request.js index 30844ad..f05fd8d 100644 --- a/Message/Request/Request.js +++ b/Message/Request/Request.js @@ -34,9 +34,9 @@ export class Request { this.requestHeader['Content-Type'] = this.setContentTypeBasedOnType(this.type); - this.result[0] = function() + this.result[0] = function(xhr) { - Logger.instance.info('Unhandled response'); + Logger.getInstance().info('Unhandled response from "' + xhr.responseURL + '" with response data "' + xhr.response + '"'); }; /** global: XMLHttpRequest */ diff --git a/UI/Component/Form.js b/UI/Component/Form.js index 4845469..9e16f22 100644 --- a/UI/Component/Form.js +++ b/UI/Component/Form.js @@ -91,7 +91,7 @@ export class Form { if (typeof id !== 'undefined' && typeof this.ignore[id] === 'undefined') { this.bindForm(id); } else { - const forms = document.getElementsByTagName('form'), + const forms = document.querySelectorAll('form, [data-tag=form]'), length = !forms ? 0 : forms.length; for (let i = 0; i < length; ++i) { @@ -119,13 +119,18 @@ export class Form { return; } + // don't overwrite bind + if (this.forms.hasOwnProperty(id)) { + return; + } + const self = this; this.forms[id] = new FormView(id); this.unbind(id); - const submits = this.forms[id].getSubmit(), - length = submits.length; + const submits = this.forms[id].getSubmit() + let length = submits.length; for (let i = 0; i < length; ++i) { submits[i].addEventListener('click', function (event) @@ -134,6 +139,28 @@ export class Form { self.submit(self.forms[id]); }); } + + const removable = this.forms[id].getRemove(); + length = removable === null ? 0 : removable.length; + for (let i = 0; i < length; ++i) { + this.bindRemovable(removable[i], id); + } + + const addable = this.forms[id].getAdd(); + length = addable === null ? 0 : addable.length; + for (let i = 0; i < length; ++i) { + this.bindAddInline(addable[i], id); + } + + if (document.getElementById(id).getAttribute('data-ui-form') !== null) { + this.bindAddExternal(id); + } + + const update = this.forms[id].getUpdate(); + length = update === null ? 0 : update.length; + for (let i = 0; i < length; ++i) { + this.bindUpdatable(update[i], id); + } }; /** @@ -220,7 +247,11 @@ export class Form { { if (!form.isValid()) { this.app.notifyManager.send( - new NotificationMessage(NotificationLevel.INFO, jsOMS.lang.Info, jsOMS.lang.invalid_form), NotificationType.APP_NOTIFICATION + new NotificationMessage( + NotificationLevel.INFO, + jsOMS.lang.Info, + jsOMS.lang.invalid_form + ), NotificationType.APP_NOTIFICATION ); Logger.instance.debug('Form "' + form.getId() + '" has invalid values.'); @@ -305,4 +336,259 @@ export class Form { { return this.forms.length; }; -}; + + /** + * Create the ui element + * + * @param {string} createForm Create form + * @param {Object} id Form id + * + * @return {void} + * + * @since 1.0.0 + */ + bindAddExternal(id) + { + const createForm = document.getElementById(id).getAttribute('data-ui-form'); + // todo: maybe allow multiple add buttons!!!! In order to do that do createForm.getAttribute('data-ui-form') and add this attribute to the add button instead of the pseudo form + + this.app.uiManager.getFormManager().get(createForm).injectSubmit(function () { + const subMain = document.getElementById(id).querySelector(document.getElementById(id).getAttribute('data-ui-content')); + const newEle = subMain.getElementsByTagName('template')[0].content.cloneNode(true); + + // set internal value + let fields = newEle.querySelectorAll('[data-tpl-value]'); + let fieldLength = fields.length; + let uuid = ''; + let value = ''; + + for (let j = 0; j < fieldLength; ++j) { + value = document.querySelectorAll( + '#' + createForm + ' [data-tpl-value="' + fields[j].getAttribute('data-tpl-value') + '"], [data-form="' + createForm + '"][data-tpl-value="' + fields[j].getAttribute('data-tpl-value') + '"]')[0] + .getAttribute('data-value'); + + // todo: we need to check what kind of tag the selector above returns in order to get the correct value. currently this only makes sense for input elements but for selection, checkboxes etc. this doesn't make sense there we need .innerHtml or [data-text=] + + fields[j].setAttribute('data-value', value); + + uuid += value; + } + + // don't allow duplicate + if (subMain.querySelectorAll('[data-tpl-uuid="' + uuid + '"').length !== 0) { + return; + } + + newEle.firstElementChild.setAttribute('data-tpl-uuid', uuid); + + // set readable text + fields = newEle.querySelectorAll('[data-tpl-text]'); + fieldLength = fields.length; + + for (let j = 0; j < fieldLength; ++j) { + fields[j].appendChild( + document.createTextNode( + document.querySelectorAll('#' + createForm + ' [data-tpl-text="' + fields[j].getAttribute('data-tpl-text') + '"], [data-form="' + createForm + '"][data-tpl-text="' + fields[j].getAttribute('data-tpl-text') + '"]')[0].value + ) + ); + + // todo: we need to check what kind of tag the selector above returns in order to get the correct value. currently this only makes sense for input elements but for selection, checkboxes etc. this doesn't make sense there we need .innerHtml or [data-text=] + } + + subMain.appendChild(newEle); + // todo: consider to do ui action as success inject to the backend request... maybe optional because sometimes there will be no backend call? + // todo: if a column has a form in the template the id of the form needs to be set unique somehow (e.g. remove button in form) + + // todo: bind removable + // todo: bind edit + + return true; + }); + }; + + /** + * Create the table row + * + * @param {string} createForm Create form + * @param {Object} id Table id + * + * @return {void} + * + * @since 1.0.0 + */ + bindAddInline(createForm, id) + { + const self = this; + + createForm.addEventListener('click', function () { + const subMain = document.getElementById(id).querySelector(document.getElementById(id).getAttribute('data-ui-content')); + const newEle = subMain.getElementsByTagName('template')[1].content.cloneNode(true); + const eleId = 'f' + Math.random().toString(36).substring(7); + // todo: check if random id doesn't already exist + + newEle.firstElementChild.id = eleId; + newEle.firstElementChild.getElementsByTagName('form')[0].id = eleId + '-form'; + + const fields = newEle.firstElementChild.querySelectorAll('[data-form="' + id + '"]'); + const length = fields.length; + + for (let i = 0; i < length; ++i) { + fields[i].setAttribute('data-form', eleId + '-form'); + } + + subMain.appendChild(newEle.firstElementChild); + + self.app.uiManager.getFormManager().get(eleId + '-form').injectSubmit(function () { + document.getElementById(id).getElementsByTagName('tbody')[0].removeChild( + document.getElementById(eleId) + ); + }); + + // todo: bind removable + // todo: bind edit + }); + + // todo: this is polluting the form manager because it should be emptied afterwards (form is deleted but not from form manager) + // todo: consider to do ui action as success inject to the backend request... maybe optional because sometimes there will be no backend call? + // todo: if a column has a form in the template the id of the form needs to be set unique somehow (e.g. remove button in form) + }; + + bindUpdatable(update, id) + { + if (document.getElementById(id).getAttribute('data-ui-form') === null) { + this.bindUpdatableInline(update, id); + } else { + this.bindUpdatableExternal(update, id); + } + }; + + bindUpdatableInline(update, id) { + const self = this; + + update.addEventListener('click', function () { + const parent = this.closest(document.getElementById(id).getAttribute('data-ui-element')); + const values = parent.querySelectorAll('[data-tpl-value]'); + const text = parent.querySelectorAll('[data-tpl-text]'); + const subMain = parent.parentNode; + + parent.style = "display: none"; // todo: replace with class instead of inline style + + const newEle = subMain.getElementsByTagName('template')[1].content.cloneNode(true); + const eleId = 'f' + Math.random().toString(36).substring(7); + // todo: don't use random id use actual row id for data which needs to be updated + + // root element is form even if it has a different tag than