mirror of
https://github.com/Karaka-Management/jsOMS.git
synced 2026-01-11 09:58:39 +00:00
external actions possible
This commit is contained in:
parent
aa71a436a3
commit
301f73fda5
|
|
@ -17,18 +17,6 @@ import { UriFactory } from '../../Uri/UriFactory.js';
|
|||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @since 1.0.0
|
||||
*
|
||||
* data-ui-content = what is the main parent
|
||||
* data-ui-element = what are the elements to replace
|
||||
*
|
||||
* @todo Karaka/jsOMS#60
|
||||
* On change listener
|
||||
* Allow to add a on change listener in a form. This should result in automatic submits after changing a form.
|
||||
* Consider the following cases to submit the form:
|
||||
* * on Enter (all except textarea)
|
||||
* * on Change (by using a timer)
|
||||
* * on Leave (all elements)
|
||||
* The listener should be defined in the form definition once and in js be applied to all form elements.
|
||||
*/
|
||||
export class Form
|
||||
{
|
||||
|
|
@ -145,22 +133,31 @@ export class Form
|
|||
|
||||
const remove = self.forms[id].getRemove()[elementIndex];
|
||||
const callback = function () {
|
||||
const parent = remove.closest(document.getElementById(id).getAttribute('data-ui-element'));
|
||||
/**
|
||||
* @var {Element} elementContainer Container which holds all the data for a specific element
|
||||
* (e.g. table row (tr), div, ...)
|
||||
*/
|
||||
const elementContainer = remove.closest(document.getElementById(id).getAttribute('data-ui-element'));
|
||||
console.log(document.getElementById(id).getAttribute('data-ui-element'));
|
||||
parent.parentNode.removeChild(parent);
|
||||
elementContainer.parentNode.removeChild(elementContainer);
|
||||
};
|
||||
|
||||
const container = document.getElementById(id);
|
||||
/** @var {Element} formElement Form element */
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
|
||||
// container can be the table tr, form or just a div
|
||||
if (container !== null
|
||||
&& ((container.tagName.toLowerCase() !== 'form' && container.getAttribute('data-method') !== null)
|
||||
|| (container.tagName.toLowerCase() === 'form' && container.getAttribute('method') !== 'NONE'))
|
||||
// Perform the element deletion.
|
||||
// If the form has a remote endpoint this is called in advance
|
||||
if (formElement !== null
|
||||
&& ((formElement.tagName.toLowerCase() !== 'form' && formElement.getAttribute('data-method') !== null)
|
||||
|| (formElement.tagName.toLowerCase() === 'form' && formElement.getAttribute('method') !== 'NONE'))
|
||||
) {
|
||||
const deleteRequest = new Request(
|
||||
container.tagName.toLowerCase() !== 'form'
|
||||
? container.getAttribute('data-method')
|
||||
: container.getAttribute('method'),
|
||||
formElement.hasAttribute('data-method-delete')
|
||||
? formElement.getAttribute('data-method-delete')
|
||||
: (formElement.tagName.toLowerCase() !== 'form'
|
||||
? formElement.getAttribute('data-method')
|
||||
: formElement.getAttribute('method')
|
||||
),
|
||||
RequestMethod.DELETE
|
||||
);
|
||||
deleteRequest.setSuccess(callback);
|
||||
|
|
@ -171,130 +168,122 @@ export class Form
|
|||
} else if ((elementIndex = Array.from(self.forms[id].getAdd()).indexOf(event.target)) !== -1) {
|
||||
// handle add
|
||||
|
||||
/* The form is the UI element the user can edit.
|
||||
* This will be added to the UI on click.
|
||||
* Since the add is inline no form exists which the user can use, hence it must be created
|
||||
*/
|
||||
if (!self.forms[id].isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.getElementById(id).getAttribute('data-add-form') !== null) {
|
||||
// handline inline add
|
||||
// handle inline add
|
||||
// Since the add is inline no form exists which the user can use, hence it must be created
|
||||
|
||||
/** @var {HTMLElement} formElement */
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
const subMain = formElement.getAttribute('data-add-content').charAt(0) === '#'
|
||||
? document.querySelector(formElement.getAttribute('data-add-content'))
|
||||
: formElement.querySelector(formElement.getAttribute('data-add-content'));
|
||||
|
||||
/**
|
||||
* @todo Karaka/jsOMS#76
|
||||
* In the beginning there was a fixed amount of templates required (even if some were not used) for adding new dom elements to a lest, table etc.
|
||||
* This no longer works especially for inline editing
|
||||
* ```js
|
||||
* const newEle = subMain.getElementsByTagName('template')[0].content.cloneNode(true);
|
||||
* ```
|
||||
*/
|
||||
const newEle = subMain.getElementsByTagName('template')[1].content.cloneNode(true);
|
||||
let eleId = '';
|
||||
/** @var {string} uiContainerName Container which holds all elements (e.g. div, tbody) */
|
||||
const uiContainerName = formElement.getAttribute('data-add-content');
|
||||
|
||||
do {
|
||||
eleId = 'f' + Math.random().toString(36).substring(7);
|
||||
} while (document.getElementById(eleId) !== null);
|
||||
/** @var {HTMLElement} uiContainer Container which holds all elements (e.g. div, tbody) */
|
||||
const uiContainer = uiContainerName.charAt(0) === '#'
|
||||
? document.querySelector(uiContainerName)
|
||||
: formElement.querySelector(uiContainerName);
|
||||
|
||||
newEle.firstElementChild.id = eleId;
|
||||
newEle.firstElementChild.getElementsByTagName('form')[0].id = eleId + '-form';
|
||||
/** @var {HTMLElement} newElement New element to add */
|
||||
const newElement = uiContainer.querySelector('.add-tpl').content.cloneNode(true);
|
||||
|
||||
const fields = newEle.firstElementChild.querySelectorAll('[data-form="' + id + '"]');
|
||||
const length = fields.length;
|
||||
// set random id for element
|
||||
/** @var {string} eleId New element id */
|
||||
const eleId = Form.setRandomIdForTemplateElement(newElement);
|
||||
|
||||
for (let i = 0; i < length; ++i) {
|
||||
fields[i].setAttribute('data-form', eleId + '-form');
|
||||
// If the new element has a form it should also receive a id
|
||||
if (newElement.firstElementChild.getElementsByTagName('form').length > 0) {
|
||||
newElement.firstElementChild.getElementsByTagName('form')[0].id = eleId + '-form';
|
||||
}
|
||||
|
||||
subMain.appendChild(newEle.firstElementChild);
|
||||
uiContainer.appendChild(newElement.firstElementChild);
|
||||
|
||||
/**
|
||||
* @todo Karaka/jsOMS#82
|
||||
* The container element for inline adding isn't always tbody
|
||||
*/
|
||||
self.app.uiManager.getFormManager().get(eleId + '-form').injectSubmit(function () {
|
||||
self.forms[id].getFormElement().getElementsByTagName('tbody')[0].removeChild(
|
||||
formElement.getElementsByTagName('tbody')[0].removeChild(
|
||||
document.getElementById(eleId)
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// handle external add
|
||||
if (!self.forms[id].isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
const parents = [];
|
||||
const selectors = formElement.getAttribute('data-add-element').split(',');
|
||||
const selectorLength = selectors.length;
|
||||
const addTpl = formElement.getAttribute('data-add-tpl').split(',');
|
||||
/** @var {HTMLElement} formElement External form */
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
|
||||
const subMain = formElement.getAttribute('data-add-content').charAt(0) === '#'
|
||||
? document.querySelector(formElement.getAttribute('data-add-content'))
|
||||
: formElement.querySelector(formElement.getAttribute('data-add-content'));
|
||||
/** @var {string} uiContainerName Container which holds all elements (e.g. div, tbody) */
|
||||
const uiContainerName = formElement.getAttribute('data-add-content');
|
||||
|
||||
let values = [];
|
||||
let text = [];
|
||||
const newEle = [];
|
||||
/** @var {HTMLElement} uiContainer Container which holds all elements (e.g. div, tbody) */
|
||||
const uiContainer = uiContainerName.charAt(0) === '#'
|
||||
? document.querySelector(uiContainerName)
|
||||
: formElement.querySelector(uiContainerName);
|
||||
|
||||
for (let i = 0; i < selectorLength; ++i) {
|
||||
// this handles selectors such as 'ancestor > child/or/sibling' and many more
|
||||
const selector = selectors[i].trim(' ').split(' ');
|
||||
/** @var {string[]} addTpl Templates to add to container (usually only one) */
|
||||
const addTpl = formElement.getAttribute('data-add-tpl').split(',');
|
||||
const addTplLength = addTpl.length;
|
||||
|
||||
let subSelector = '';
|
||||
if (selector.length !== 0) {
|
||||
selector.shift();
|
||||
subSelector = selector.join(' ').trim();
|
||||
}
|
||||
/** @var {string[]} values Values to add (values can be different from the displayed text) */
|
||||
let values = [];
|
||||
|
||||
newEle.push(document.querySelector(addTpl[i]).content.cloneNode(true));
|
||||
/** @var {string[]} texts Text to add (values can be different from the displayed text) */
|
||||
let texts = [];
|
||||
|
||||
const tplValue = newEle[i].querySelector('[data-tpl-value]').getAttribute('data-tpl-value');
|
||||
parents.push(
|
||||
tplValue.startsWith('http') || tplValue.startsWith('{')
|
||||
? ( // data is only added from remote response after adding
|
||||
selector.length === 0
|
||||
? newEle[i].firstElementChild
|
||||
: newEle[i].firstElementChild.querySelector(subSelector)
|
||||
)
|
||||
: formElement // data comes from the form (even if the api returns something after adding). What if remote returns a DB id? how do we add it? is this a @todo ? probably yes, maybe first use local data and then if remote data available replace local data?
|
||||
);
|
||||
/**
|
||||
* @var {Element[]} newElements Array of added elements
|
||||
* (this is actually only one element/model/object but sometimes one
|
||||
* element might be split up into multiple templates)
|
||||
*/
|
||||
const newElements = [];
|
||||
|
||||
// iterate over all add templates and find the elements
|
||||
for (let i = 0; i < addTplLength; ++i) {
|
||||
// add template to elements which should be added to the DOM
|
||||
newElements.push(document.querySelector(addTpl[i]).content.cloneNode(true));
|
||||
|
||||
/** @var {string} tplValue Where does the value come from for this template input element */
|
||||
const tplValue = newElements[i].querySelector('[data-tpl-value]').getAttribute('data-tpl-value');
|
||||
|
||||
/** @var {Element} dataOriginElement Element where the value data comes from */
|
||||
const dataOriginElement = tplValue.startsWith('http') || tplValue.startsWith('{')
|
||||
? newElements[i].firstElementChild // data comes from remote source
|
||||
: formElement; // data comes from the form (even if the api returns something after adding). What if remote returns a DB id? how do we add it? is this a @todo ? probably yes, maybe first use local data and then if remote data available replace local data?
|
||||
|
||||
values = values.concat(
|
||||
parents[i].hasAttribute('data-tpl-value')
|
||||
? parents[i]
|
||||
: Array.prototype.slice.call(parents[i].querySelectorAll('[data-tpl-value]'))
|
||||
dataOriginElement.hasAttribute('data-tpl-value')
|
||||
? dataOriginElement
|
||||
: Array.prototype.slice.call(dataOriginElement.querySelectorAll('[data-tpl-value]'))
|
||||
);
|
||||
text = text.concat(
|
||||
parents[i].hasAttribute('data-tpl-text')
|
||||
? parents[i]
|
||||
: Array.prototype.slice.call(parents[i].querySelectorAll('[data-tpl-text]'))
|
||||
texts = texts.concat(
|
||||
dataOriginElement.hasAttribute('data-tpl-text')
|
||||
? dataOriginElement
|
||||
: Array.prototype.slice.call(dataOriginElement.querySelectorAll('[data-tpl-text]'))
|
||||
);
|
||||
|
||||
Form.setRandomIdForTemplateElement(newEle[i]);
|
||||
// set random id for element
|
||||
Form.setRandomIdForTemplateElement(newElements[i]);
|
||||
}
|
||||
|
||||
// insert row values data into form
|
||||
/** @var {object} remoteUrls Texts and values which come from remote sources */
|
||||
const remoteUrls = {};
|
||||
Form.insertDataIntoNewFormElement('value', newEle, values, remoteUrls);
|
||||
|
||||
// insert row text data into form
|
||||
Form.insertDataIntoNewFormElement('text', newEle, text, remoteUrls);
|
||||
// insert values into form (populate values)
|
||||
Form.setDataInFormElement('value', newElements, values, remoteUrls);
|
||||
|
||||
for (let i = 0; i < selectorLength; ++i) {
|
||||
// The data could be added to an external element which uses external forms for updates.
|
||||
// The buttons then belong to the external element and not the update form!
|
||||
const formId = document.querySelector('[data-update-form="' + id + '"]');
|
||||
// insert text data into form (populate text)
|
||||
Form.setDataInFormElement('text', newElements, texts, remoteUrls);
|
||||
|
||||
// @todo: bind added element in general (e.g. self.app.uiManager.bind(newEle[i].firstElementChild));
|
||||
// Problem 1 is sometimes the bind functions expect an id, sometimes an element
|
||||
// Problem 2 is that sorting is handled in the Table.js which should be part of the form? because a new sorting should also get submitted to the backend!
|
||||
|
||||
subMain.appendChild(newEle[i].firstElementChild);
|
||||
// add new elements to the DOM
|
||||
for (let i = 0; i < addTplLength; ++i) {
|
||||
uiContainer.appendChild(newElements[i].firstElementChild);
|
||||
}
|
||||
|
||||
// define remote response behavior
|
||||
self.forms[id].setSuccess(function (response) {
|
||||
if (response.get('status') !== 'undefined' && response.get('status') !== NotificationLevel.HIDDEN) {
|
||||
self.app.notifyManager.send(
|
||||
|
|
@ -306,182 +295,125 @@ export class Form
|
|||
|
||||
UriFactory.setQuery('$id', response.get('response').id);
|
||||
|
||||
// fill elements with remote data after submit (if the template expects data from a remote source)
|
||||
// this is usually the case for element ids, which can only be generated by the backend
|
||||
Form.setDataFromRemoteUrls(remoteUrls);
|
||||
});
|
||||
|
||||
// reset the form after adding an element
|
||||
self.forms[id].resetValues();
|
||||
}
|
||||
} else if ((elementIndex = Array.from(self.forms[id].getSave()).indexOf(event.target)) !== -1) {
|
||||
const mainForm = document.querySelector('[data-update-form="' + id + '"');
|
||||
if (mainForm !== null) {
|
||||
id = mainForm.getAttribute('id');
|
||||
}
|
||||
|
||||
// handle save button
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
const parentsTpl = {};
|
||||
const parentsContent = {};
|
||||
const selectors = formElement.getAttribute('data-update-element').split(',');
|
||||
const selectorLength = selectors.length;
|
||||
|
||||
let values = [];
|
||||
let text = [];
|
||||
const mainForm = document.querySelector('[data-update-form="' + id + '"');
|
||||
if (mainForm === null) {
|
||||
// handle inline save
|
||||
|
||||
// find all values, texts and parents for every selector
|
||||
for (let i = 0; i < selectorLength; ++i) {
|
||||
selectors[i] = selectors[i].trim();
|
||||
// this handles selectors such as 'ancestor > child/or/sibling' and many more
|
||||
let selector = !selectors[i].startsWith('#') ? selectors[i].split(' ') : [selectors[i]];
|
||||
const selLength = selector.length;
|
||||
let closest = selector[0].trim();
|
||||
if (!self.forms[id].isValid()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// handle external save
|
||||
|
||||
// template elements (= elements which just got added due to the update/edit button, here the new data is stored)
|
||||
// @todo i don't really remember how this works and why this was needed. Try to understand it and write a comment afterwards
|
||||
let subSelector = '';
|
||||
if (selLength > 1) {
|
||||
selector.shift();
|
||||
subSelector = selector.join(' ').trim() + '[data-marker=tpl]';
|
||||
} else {
|
||||
closest += '[data-marker=tpl]';
|
||||
const externalFormId = id;
|
||||
id = mainForm.getAttribute('id');
|
||||
|
||||
if (!self.forms[id].isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parentTplName;
|
||||
if (selLength === 1 && selector[0].startsWith('#')) {
|
||||
parentTplName = selector[0] + '[data-marker=tpl]';
|
||||
} else {
|
||||
parentTplName = selLength === 1 ? closest : closest + subSelector;
|
||||
/** @var {HTMLElement} formElement */
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
|
||||
/** @var {HTMLElement} formElement External form element */
|
||||
const externalFormElement = self.forms[externalFormId].getFormElement();
|
||||
|
||||
/**
|
||||
* @var {string[]} updateElementNames Names/selectors of the containers which hold the data of a single element
|
||||
* (this is not the container which holds all elements. Most of the time this is just a single element (e.g. tr))
|
||||
*/
|
||||
const updateElementNames = formElement.getAttribute('data-update-element').split(',');
|
||||
const updateElementLength = updateElementNames.length;
|
||||
|
||||
/**
|
||||
* @var {Element[]} updateElements Array of update elements
|
||||
* (this is actually only one element/model/object but sometimes one
|
||||
* element might be split up into multiple containers)
|
||||
*/
|
||||
const updateElements = [];
|
||||
|
||||
/** @var {string[]} values New values */
|
||||
let values = [];
|
||||
|
||||
/** @var {string[]} texts New texts */
|
||||
let texts = [];
|
||||
|
||||
// iterate all element containers (very often only one element container) and find the elements
|
||||
for (let i = 0; i < updateElementLength; ++i) {
|
||||
updateElementNames[i] = updateElementNames[i].trim();
|
||||
|
||||
// get the elment to update
|
||||
updateElements.push(
|
||||
formElement.querySelector(updateElementNames[i] + '[data-id="' + externalFormElement.getAttribute('data-id') + '"]')
|
||||
);
|
||||
|
||||
/** @var {string} updateValue Where does the value come from for this template input element */
|
||||
const updateValue = updateElements[i].querySelector('[data-tpl-value]').getAttribute('data-tpl-value');
|
||||
|
||||
/** @var {Element} dataOriginElement Element where the value data comes from */
|
||||
const dataOriginElement = updateValue.startsWith('http') || updateValue.startsWith('{')
|
||||
? updateElements[i].firstElementChild // data comes from remote source
|
||||
: externalFormElement; // data comes from the form (even if the api returns something after adding). What if remote returns a DB id? how do we add it? is this a @todo ? probably yes, maybe first use local data and then if remote data available replace local data?
|
||||
|
||||
values = values.concat(
|
||||
dataOriginElement.hasAttribute('data-tpl-value')
|
||||
? dataOriginElement
|
||||
: Array.prototype.slice.call(dataOriginElement.querySelectorAll('[data-tpl-value]'))
|
||||
);
|
||||
texts = texts.concat(
|
||||
dataOriginElement.hasAttribute('data-tpl-text')
|
||||
? dataOriginElement
|
||||
: Array.prototype.slice.call(dataOriginElement.querySelectorAll('[data-tpl-text]'))
|
||||
);
|
||||
}
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(parentsTpl, parentTplName)) {
|
||||
if (selLength === 1 && selector[0].startsWith('#')) {
|
||||
parentsTpl[parentTplName] = document.querySelector(selector[0]).parentNode;
|
||||
} else {
|
||||
parentsTpl[parentTplName] = selLength === 1
|
||||
? this.closest(closest)
|
||||
: this.closest(closest).querySelector(subSelector);
|
||||
/* @todo: parentNode because of media edit. maybe I need a data-ui-parent element? */
|
||||
}
|
||||
}
|
||||
/** @var {object} remoteUrls Texts and values which come from remote sources */
|
||||
const remoteUrls = {};
|
||||
|
||||
// content elements
|
||||
selector = !selectors[i].startsWith('#') ? selectors[i].split(' ') : [selectors[i]];
|
||||
closest = selector[0].trim();
|
||||
subSelector = '';
|
||||
if (selLength > 1) {
|
||||
selector.shift();
|
||||
subSelector = selector.join(' ').trim() + ':not([data-marker=tpl])';
|
||||
} else {
|
||||
closest += ':not([data-marker=tpl])';
|
||||
}
|
||||
// update values in form (overwrite values)
|
||||
Form.setDataInFormElement('value', updateElements, values, remoteUrls);
|
||||
|
||||
let parentContentName;
|
||||
if (selLength === 1 && selector[0].startsWith('#')) {
|
||||
parentContentName = selector[0] + ':not([data-marker=tpl])';
|
||||
} else {
|
||||
parentContentName = selLength === 1 ? closest : closest + subSelector;
|
||||
}
|
||||
// update text data in form (overwrite text)
|
||||
Form.setDataInFormElement('text', updateElements, texts, remoteUrls);
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(parentsContent, parentContentName)) {
|
||||
if (selLength === 1 && selector[0].startsWith('#')) {
|
||||
parentsContent[parentContentName] = document.querySelector(selector[0]).parentNode;
|
||||
} else {
|
||||
parentsContent[parentContentName] = selLength === 1
|
||||
? this.closest(closest)
|
||||
: this.closest(closest).querySelector(subSelector).parentNode;
|
||||
/* @todo: parentNode because of media edit. maybe I need a data-ui-parent element? */
|
||||
}
|
||||
}
|
||||
// todo bind failure here, if failure do cancel, if success to remove edit template
|
||||
self.forms[externalFormId].setSuccess(function () {
|
||||
// overwrite old values from remote response
|
||||
Form.setDataFromRemoteUrls(remoteUrls);
|
||||
});
|
||||
|
||||
values = values.concat(
|
||||
parentsTpl[parentTplName].hasAttribute('data-tpl-value')
|
||||
? parentsTpl[parentTplName]
|
||||
: Array.prototype.slice.call(parentsTpl[parentTplName].querySelectorAll('[data-tpl-value]'))
|
||||
);
|
||||
text = text.concat(
|
||||
parentsContent[parentContentName].hasAttribute('data-tpl-text')
|
||||
? parentsContent[parentContentName]
|
||||
: Array.prototype.slice.call(parentsContent[parentContentName].querySelectorAll('[data-tpl-text]'))
|
||||
);
|
||||
// clear element id after saving
|
||||
externalFormElement.setAttribute('data-id', '');
|
||||
|
||||
// reset form values to default values after performing the update
|
||||
self.forms[externalFormId].resetValues();
|
||||
}
|
||||
|
||||
values = values.filter(function (value, index, self) { return self.indexOf(value) === index; });
|
||||
text = text.filter(function (value, index, self) { return self.indexOf(value) === index; });
|
||||
|
||||
// overwrite old values data in ui
|
||||
const remoteUrls = {};
|
||||
const changedValueNodes = []; // prevent same node touching
|
||||
length = values.length;
|
||||
for (const parent in parentsTpl) { // loop every selector which has elements to change
|
||||
for (let i = 0; i < length; ++i) { // loop every value
|
||||
const matches = parentsTpl[parent].querySelectorAll('[data-tpl-value="' + values[i].getAttribute('data-tpl-value') + '"');
|
||||
|
||||
const matchLength = matches.length;
|
||||
for (let c = 0; c < matchLength; ++c) { // loop every found element in the selector to change
|
||||
if (changedValueNodes.includes(matches[c])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changedValueNodes.push(matches[c]);
|
||||
const path = matches[c].hasAttribute('data-tpl-value-path') ? matches[c].getAttribute('data-tpl-value-path') : null;
|
||||
|
||||
if (values[i].getAttribute('data-tpl-value').startsWith('http')
|
||||
|| values[i].getAttribute('data-tpl-value').startsWith('{')
|
||||
) {
|
||||
Form.populateRemoteUrls('value', values[i], path, remoteUrls);
|
||||
} else {
|
||||
GeneralUI.setValueOfElement(matches[c], GeneralUI.getValueFromDataSource(values[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite old text data in ui
|
||||
const changedTextNodes = [];
|
||||
length = text.length;
|
||||
for (const parent in parentsContent) {
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const matches = parentsContent[parent].querySelectorAll('[data-tpl-text="' + text[i].getAttribute('data-tpl-text') + '"');
|
||||
|
||||
const matchLength = matches.length;
|
||||
for (let c = 0; c < matchLength; ++c) {
|
||||
if (changedTextNodes.includes(matches[c])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changedTextNodes.push(matches[c]);
|
||||
|
||||
const path = matches[c].hasAttribute('data-tpl-text-path') ? matches[c].getAttribute('data-tpl-text-path') : null;
|
||||
if (text[i].getAttribute('data-tpl-text').startsWith('http')
|
||||
|| text[i].getAttribute('data-tpl-text').startsWith('{')
|
||||
) {
|
||||
Form.populateRemoteUrls('text', text[i], path, remoteUrls);
|
||||
} else {
|
||||
GeneralUI.setTextOfElement(matches[c], GeneralUI.getTextFromDataSource(text[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo bind failure here, if failure do cancel, if success to remove edit template
|
||||
self.forms[id].setSuccess(function () {
|
||||
// overwrite old values from remote response
|
||||
Form.setDataFromRemoteUrls(remoteUrls);
|
||||
});
|
||||
|
||||
// @todo: does this submit and the previous submit in updatable mean I'm sending the data twice???? That would be bad!
|
||||
self.submit(self.forms[id]);
|
||||
self.removeEditTemplate(this, id);
|
||||
} else if ((elementIndex = Array.from(self.forms[id].getCancel()).indexOf(event.target)) !== -1) {
|
||||
// handle cancel
|
||||
const ele = document.getElementById(id);
|
||||
if (ele.getAttribute('data-update-form') === null && ele.tagName.toLowerCase() !== 'form') {
|
||||
self.removeEditTemplate(this, id);
|
||||
} else {
|
||||
// reset form values to default values
|
||||
self.forms[id].resetValues();
|
||||
|
||||
// reset element id
|
||||
self.forms[id].getFormElement().setAttribute('data-id', '');
|
||||
|
||||
let length = 0;
|
||||
|
||||
// show add button + hide update + hide cancel
|
||||
// show add button + hide update button + hide cancel button
|
||||
const addButtons = self.forms[id].getAdd();
|
||||
length = addButtons.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
|
|
@ -503,8 +435,12 @@ export class Form
|
|||
jsOMS.preventAll(event);
|
||||
}
|
||||
} else if ((elementIndex = Array.from(self.forms[id].getUpdate()).indexOf(event.target)) !== -1) {
|
||||
// handle update button which populates the update form
|
||||
// handle update
|
||||
// this doesn't handle setting new values but populating the update form
|
||||
|
||||
if (document.getElementById(id).getAttribute('data-update-form') === null) {
|
||||
// handle inline update
|
||||
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
const parents = [];
|
||||
const selectors = formElement.getAttribute('data-update-element').split(',');
|
||||
|
|
@ -515,9 +451,9 @@ export class Form
|
|||
UriFactory.setQuery('$id', formElement.getAttribute('data-id'));
|
||||
}
|
||||
|
||||
let values = [];
|
||||
let text = [];
|
||||
const newEle = [];
|
||||
let values = [];
|
||||
let texts = [];
|
||||
const newElement = [];
|
||||
|
||||
for (let i = 0; i < selectorLength; ++i) {
|
||||
selectors[i] = selectors[i].trim();
|
||||
|
|
@ -546,7 +482,7 @@ export class Form
|
|||
? parents[i]
|
||||
: Array.prototype.slice.call(parents[i].querySelectorAll('[data-tpl-value]'))
|
||||
);
|
||||
text = text.concat(
|
||||
texts = texts.concat(
|
||||
parents[i].hasAttribute('data-tpl-text')
|
||||
? parents[i]
|
||||
: Array.prototype.slice.call(parents[i].querySelectorAll('[data-tpl-text]'))
|
||||
|
|
@ -554,17 +490,17 @@ export class Form
|
|||
|
||||
jsOMS.addClass(parents[i], 'hidden');
|
||||
|
||||
newEle.push(document.querySelector(updatableTpl[i]).content.cloneNode(true));
|
||||
newElement.push(document.querySelector(updatableTpl[i]).content.cloneNode(true));
|
||||
|
||||
Form.setRandomIdForTemplateElement(newEle[i]);
|
||||
Form.setRandomIdForTemplateElement(newElement[i]);
|
||||
}
|
||||
|
||||
const fields = [];
|
||||
for (let i = 0; i < selectorLength; ++i) {
|
||||
fields.concat(
|
||||
newEle[i].firstElementChild.hasAttribute('data-form')
|
||||
? newEle[i].firstElementChild
|
||||
: newEle[i].firstElementChild.querySelectorAll('[data-form="' + id + '"]')
|
||||
newElement[i].firstElementChild.hasAttribute('data-form')
|
||||
? newElement[i].firstElementChild
|
||||
: newElement[i].firstElementChild.querySelectorAll('[data-form="' + id + '"]')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -575,16 +511,16 @@ export class Form
|
|||
|
||||
// insert row values data into form
|
||||
const remoteUrls = {};
|
||||
Form.insertDataIntoNewFormElement('value', newEle, values, remoteUrls);
|
||||
Form.setDataInFormElement('value', newElement, values, remoteUrls);
|
||||
|
||||
// insert row text data into form
|
||||
Form.insertDataIntoNewFormElement('text', newEle, text, remoteUrls);
|
||||
Form.setDataInFormElement('text', newElement, texts, remoteUrls);
|
||||
|
||||
Form.setDataFromRemoteUrls(remoteUrls);
|
||||
|
||||
for (let i = 0; i < selectorLength; ++i) {
|
||||
newEle[i].firstElementChild.setAttribute('data-marker', 'tpl');
|
||||
parents[i].parentNode.insertBefore(newEle[i].firstElementChild, parents[i]);
|
||||
newElement[i].firstElementChild.setAttribute('data-marker', 'tpl');
|
||||
parents[i].parentNode.insertBefore(newElement[i].firstElementChild, parents[i]);
|
||||
}
|
||||
|
||||
// self.bindCreateForm(eleId, id); // @todo: why this bind???
|
||||
|
|
@ -611,51 +547,61 @@ export class Form
|
|||
jsOMS.removeClass(cancelButtons[i], 'hidden');
|
||||
}
|
||||
} else {
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
const parent = event.target.closest(formElement.getAttribute('data-update-element'));
|
||||
const formId = formElement.getAttribute('data-update-form');
|
||||
const values = parent.querySelectorAll('[data-tpl-value]');
|
||||
const text = parent.querySelectorAll('[data-tpl-text]');
|
||||
// handle external update
|
||||
|
||||
const fields = self.forms[formId].getFormElement().querySelectorAll('[data-form="' + id + '"]');
|
||||
let length = 0;
|
||||
/** @var {HTMLElement} formElement */
|
||||
const formElement = self.forms[id].getFormElement();
|
||||
|
||||
/** @var {Element} elementContainer Element container that holds the data that should get updated */
|
||||
const elementContainer = event.target.closest(formElement.getAttribute('data-update-element'));
|
||||
|
||||
/** @var {string} externalFormId Id of the form where the data should get populated to (= external form) */
|
||||
const externalFormId = formElement.getAttribute('data-update-form');
|
||||
|
||||
/** @var {NodeListOf<Element>} values Value elements of the element to update */
|
||||
const values = elementContainer.querySelectorAll('[data-tpl-value]');
|
||||
|
||||
/** @var {NodeListOf<Element>} texts Text elements of the element to update */
|
||||
const texts = elementContainer.querySelectorAll('[data-tpl-text]');
|
||||
|
||||
let length = 0;
|
||||
|
||||
// clear form values to prevent old values getting mixed with update values
|
||||
self.forms[formId].resetValues();
|
||||
self.forms[externalFormId].resetValues();
|
||||
|
||||
// hide add button + show update + show cancel
|
||||
const addButtons = self.forms[formId].getAdd();
|
||||
// set the element id in the update form so we know which element is getting updated
|
||||
self.forms[externalFormId].getFormElement().setAttribute('data-id', elementContainer.getAttribute('data-id'));
|
||||
|
||||
// hide add button + show update button + show cancel button
|
||||
const addButtons = self.forms[externalFormId].getAdd();
|
||||
length = addButtons.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
jsOMS.addClass(addButtons[i], 'hidden');
|
||||
}
|
||||
|
||||
const saveButtons = self.forms[formId].getSave();
|
||||
const saveButtons = self.forms[externalFormId].getSave();
|
||||
length = saveButtons.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
jsOMS.removeClass(saveButtons[i], 'hidden');
|
||||
}
|
||||
|
||||
const cancelButtons = self.forms[formId].getCancel();
|
||||
const cancelButtons = self.forms[externalFormId].getCancel();
|
||||
length = cancelButtons.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
jsOMS.removeClass(cancelButtons[i], 'hidden');
|
||||
}
|
||||
|
||||
// set form id to fields for easier identification
|
||||
length = fields.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
fields[i].setAttribute('data-form', eleId);
|
||||
}
|
||||
|
||||
// insert row values data into form
|
||||
/** @var {object} remoteUrls Texts and values which come from remote sources */
|
||||
const remoteUrls = {};
|
||||
Form.insertDataIntoForm(self, 'value', formId, values, remoteUrls);
|
||||
|
||||
// insert row text data into form
|
||||
Form.insertDataIntoForm(self, 'text', formId, text, remoteUrls);
|
||||
// insert values into form (populate values)
|
||||
Form.insertDataIntoForm(self, 'value', externalFormId, values, remoteUrls);
|
||||
|
||||
self.forms[formId].setSuccess(function (response) {
|
||||
// insert text data into form (populate text)
|
||||
Form.insertDataIntoForm(self, 'text', externalFormId, texts, remoteUrls);
|
||||
|
||||
// define remote response behavior
|
||||
self.forms[externalFormId].setSuccess(function (response) {
|
||||
if (response.get('status') !== 'undefined' && response.get('status') !== NotificationLevel.HIDDEN) {
|
||||
self.app.notifyManager.send(
|
||||
new NotificationMessage(response.get('status'), response.get('title'), response.get('message')), NotificationType.APP_NOTIFICATION
|
||||
|
|
@ -666,6 +612,7 @@ export class Form
|
|||
|
||||
UriFactory.setQuery('$id', response.get('response').id);
|
||||
|
||||
// fill elements with remote data after submit (if the template expects data from a remote source)
|
||||
Form.setDataFromRemoteUrls(remoteUrls);
|
||||
});
|
||||
}
|
||||
|
|
@ -955,35 +902,51 @@ export class Form
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set random id of a template element
|
||||
*
|
||||
* @param {HTMLElement} templateElement Element to set the id for
|
||||
*
|
||||
* @return {string}
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
static setRandomIdForTemplateElement (templateElement)
|
||||
{
|
||||
if (templateElement.firstElementChild.id !== null) {
|
||||
if (templateElement.firstElementChild.getAttribute('data-id') !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let eleId = '';
|
||||
|
||||
do {
|
||||
eleId = 'f' + Math.random().toString(36).substring(7);
|
||||
} while (document.getElementById(eleId) !== null);
|
||||
eleId = 'r-' + Math.random().toString(36).substring(7);
|
||||
} while (document.querySelector('[data-id="' + eleId + '"]') !== null);
|
||||
|
||||
templateElement.firstElementChild.id = eleId;
|
||||
templateElement.firstElementChild.setAttribute('data-id', eleId);
|
||||
|
||||
return eleId;
|
||||
};
|
||||
|
||||
static insertDataIntoNewFormElement (type, templateElements, data, remoteUrls = {})
|
||||
static setDataInFormElement (type, elements, data, remoteUrls = {})
|
||||
{
|
||||
const changedNodes = []; // prevent same node touching
|
||||
const length = data.length;
|
||||
const templateLength = templateElements.length;
|
||||
const templateLength = elements.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const path = data[i].hasAttribute('data-tpl-' + type + '-path') ? data[i].getAttribute('data-tpl-' + type + '-path') : null;
|
||||
|
||||
for (let j = 0; j < templateLength; ++j) {
|
||||
// sometimes elements contains templates, they need to get handled differently
|
||||
const element = elements[j] instanceof DocumentFragment
|
||||
? elements[j].firstElementChild
|
||||
: elements[j];
|
||||
|
||||
// BUG: matches contains the same elment for radio/checkbox
|
||||
const matches = templateElements[j].firstElementChild.hasAttribute('data-tpl-' + type)
|
||||
&& templateElements[j].firstElementChild.getAttribute('data-tpl-' + type) === data[i].getAttribute('data-tpl-' + type)
|
||||
? [templateElements[j].firstElementChild]
|
||||
: templateElements[j].firstElementChild.querySelectorAll(
|
||||
const matches = element.hasAttribute('data-tpl-' + type)
|
||||
&& element.getAttribute('data-tpl-' + type) === data[i].getAttribute('data-tpl-' + type)
|
||||
? [element]
|
||||
: element.querySelectorAll(
|
||||
'[data-tpl-' + type + '="' + data[i].getAttribute('data-tpl-' + type) + '"'
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user