mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-07-27 07:44:04 +02:00
Do not refresh a container when form input was changed or a form element is focused
Listen for changes in form elements and abort all reloads that contain a form with at least one changed form element. Do not refresh containers that contain a focused form element, except of elements with autofocus, to preserve form elements with a dropdown. Only focus autofocus elements when there is currently no other selection. refs #7146 refs #5537 fixes #7162
This commit is contained in:
parent
f1d3b72f05
commit
ef2f332869
@ -26,7 +26,8 @@ class JavaScript
|
|||||||
'js/icinga/behavior/tooltip.js',
|
'js/icinga/behavior/tooltip.js',
|
||||||
'js/icinga/behavior/sparkline.js',
|
'js/icinga/behavior/sparkline.js',
|
||||||
'js/icinga/behavior/tristate.js',
|
'js/icinga/behavior/tristate.js',
|
||||||
'js/icinga/behavior/navigation.js'
|
'js/icinga/behavior/navigation.js',
|
||||||
|
'js/icinga/behavior/form.js'
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static $vendorFiles = array(
|
protected static $vendorFiles = array(
|
||||||
|
104
public/js/icinga/behavior/form.js
Normal file
104
public/js/icinga/behavior/form.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls behavior of form elements, depending reload and
|
||||||
|
*/
|
||||||
|
(function(Icinga, $) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
Icinga.Behaviors = Icinga.Behaviors || {};
|
||||||
|
|
||||||
|
var Form = function (icinga) {
|
||||||
|
Icinga.EventListener.call(this, icinga);
|
||||||
|
this.on('keyup change', 'form input', this.onChange, this);
|
||||||
|
|
||||||
|
// store the modification state of all input fields
|
||||||
|
this.inputs = {};
|
||||||
|
};
|
||||||
|
Form.prototype = new Icinga.EventListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param evt
|
||||||
|
*/
|
||||||
|
Form.prototype.onChange = function (evt) {
|
||||||
|
var el = evt.target;
|
||||||
|
var form = evt.data.self.uniqueFormName($(el).closest('form')[0] || {});
|
||||||
|
evt.data.self.inputs[form] = evt.data.self.inputs[form] || {};
|
||||||
|
if (el.value !== '') {
|
||||||
|
evt.data.self.inputs[form][el.name] = true;
|
||||||
|
} else {
|
||||||
|
evt.data.self.inputs[form][el.name] = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to generate an unique form name using the action
|
||||||
|
* and the name of the given form element
|
||||||
|
*
|
||||||
|
* @param form {HTMLFormElement} The
|
||||||
|
* @returns {String} The unique name
|
||||||
|
*/
|
||||||
|
Form.prototype.uniqueFormName = function(form)
|
||||||
|
{
|
||||||
|
return (form.name || 'undefined') + '.' + (form.action || 'undefined');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutates the HTML before it is placed in the DOM after a reload
|
||||||
|
*
|
||||||
|
* @param content {String} The content to be rendered
|
||||||
|
* @param $container {jQuery} The target container where the html will be rendered in
|
||||||
|
* @param action {String} The action-url that caused the reload
|
||||||
|
* @param autorefresh {Boolean} Whether the rendering is due to an autoRefresh
|
||||||
|
*
|
||||||
|
* @returns {string|NULL} The content to be rendered, or NULL, when nothing should be changed
|
||||||
|
*/
|
||||||
|
Form.prototype.renderHook = function(content, $container, action, autorefresh) {
|
||||||
|
var origFocus = document.activeElement;
|
||||||
|
var containerId = $container.attr('id');
|
||||||
|
var icinga = this.icinga;
|
||||||
|
var self = this.icinga.behaviors.form;
|
||||||
|
var changed = false;
|
||||||
|
$container.find('form').each(function () {
|
||||||
|
var form = self.uniqueFormName(this);
|
||||||
|
if (autorefresh) {
|
||||||
|
// check if an element in this container was changed
|
||||||
|
$(this).find('input').each(function () {
|
||||||
|
var name = this.name;
|
||||||
|
if (self.inputs[form] && self.inputs[form][name]) {
|
||||||
|
icinga.logger.debug(
|
||||||
|
'form input: ' + form + '.' + name + ' was changed and aborts reload...'
|
||||||
|
);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// user-triggered reload, forget all changes to forms in this container
|
||||||
|
self.inputs[form] = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (changed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
// is the focus among the elements to be replaced?
|
||||||
|
$container.has(origFocus).length &&
|
||||||
|
// is an autorefresh
|
||||||
|
autorefresh &&
|
||||||
|
|
||||||
|
// and has focus
|
||||||
|
$(origFocus).length &&
|
||||||
|
!$(origFocus).hasClass('autofocus') &&
|
||||||
|
$(origFocus).closest('form').length
|
||||||
|
) {
|
||||||
|
icinga.logger.debug('Not changing content for ' + containerId + ' form has focus');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
Icinga.Behaviors.Form = Form;
|
||||||
|
|
||||||
|
}) (Icinga, jQuery);
|
@ -75,8 +75,9 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('input.autofocus', el).focus();
|
if (document.activeElement === document.body) {
|
||||||
|
$('input.autofocus', el).focus();
|
||||||
|
}
|
||||||
var searchField = $('#menu input.search', el);
|
var searchField = $('#menu input.search', el);
|
||||||
// Remember initial search field value if any
|
// Remember initial search field value if any
|
||||||
if (searchField.length && searchField.val().length) {
|
if (searchField.length && searchField.val().length) {
|
||||||
|
@ -661,6 +661,7 @@
|
|||||||
// Container update happens here
|
// Container update happens here
|
||||||
var scrollPos = false;
|
var scrollPos = false;
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var origFocus = document.activeElement;
|
||||||
var containerId = $container.attr('id');
|
var containerId = $container.attr('id');
|
||||||
if (typeof containerId !== 'undefined') {
|
if (typeof containerId !== 'undefined') {
|
||||||
if (autorefresh) {
|
if (autorefresh) {
|
||||||
@ -670,13 +671,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var origFocus = document.activeElement;
|
var discard = false;
|
||||||
if (
|
$.each(self.icinga.behaviors, function(name, behavior) {
|
||||||
// Do not reload menu when search field has content
|
if (behavior.renderHook) {
|
||||||
(containerId === 'menu' && $(origFocus).length && $(origFocus).val().length)
|
var changed = behavior.renderHook(content, $container, action, autorefresh);
|
||||||
// TODO: remove once #7146 is solved
|
if (!changed) {
|
||||||
|| (containerId !== 'menu' && typeof containerId !== 'undefined' && autorefresh && origFocus && $(origFocus).closest('form').length && $container.has($(origFocus)) && $(origFocus).closest('#' + containerId).length && ! $(origFocus).hasClass('autosubmit'))) {
|
discard = true;
|
||||||
this.icinga.logger.debug('Not changing content for ', containerId, ' form has focus');
|
} else {
|
||||||
|
content = changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (discard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
/**
|
/**
|
||||||
* Raise or lower current log level
|
* Raise or lower current log level
|
||||||
*
|
*
|
||||||
* Messages blow this threshold will be silently discarded
|
* Messages below this threshold will be silently discarded
|
||||||
*/
|
*/
|
||||||
setLevel: function (level) {
|
setLevel: function (level) {
|
||||||
if ('undefined' !== typeof this.numericLevel(level)) {
|
if ('undefined' !== typeof this.numericLevel(level)) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user