Show warning icon when value or field is invalid or duplicate

This commit is contained in:
jos 2019-03-30 20:58:43 +01:00
parent 19a625fa6e
commit 12d908d08a
2 changed files with 95 additions and 38 deletions

View File

@ -268,7 +268,7 @@ Node.prototype.setError = function (error, child) {
* Render the error * Render the error
*/ */
Node.prototype.updateError = function() { Node.prototype.updateError = function() {
var error = this.error; var error = this.fieldError || this.valueError || this.error;
var tdError = this.dom.tdError; var tdError = this.dom.tdError;
if (error && this.dom && this.dom.tr) { if (error && this.dom && this.dom.tr) {
util.addClassName(this.dom.tr, 'jsoneditor-validation-error'); util.addClassName(this.dom.tr, 'jsoneditor-validation-error');
@ -379,7 +379,7 @@ Node.prototype.setField = function(field, fieldEditable) {
*/ */
Node.prototype.getField = function() { Node.prototype.getField = function() {
if (this.field === undefined) { if (this.field === undefined) {
this._getDomField(false); this._getDomField(true);
} }
return this.field; return this.field;
@ -920,23 +920,23 @@ Node.prototype.hideChilds = function(options) {
*/ */
Node.prototype._updateCssClassName = function () { Node.prototype._updateCssClassName = function () {
if(this.dom.field if(this.dom.field
&& this.editor && this.editor
&& this.editor.options && this.editor.options
&& typeof this.editor.options.onClassName ==='function' && typeof this.editor.options.onClassName ==='function'
&& this.dom.tree){ && this.dom.tree){
util.removeAllClassNames(this.dom.tree); util.removeAllClassNames(this.dom.tree);
var addClasses = this.editor.options.onClassName({ path: this.getPath(), field: this.field, value: this.value }) || ""; var addClasses = this.editor.options.onClassName({ path: this.getPath(), field: this.field, value: this.value }) || "";
util.addClassName(this.dom.tree, "jsoneditor-values " + addClasses); util.addClassName(this.dom.tree, "jsoneditor-values " + addClasses);
} }
}; };
Node.prototype.recursivelyUpdateCssClassesOnNodes = function () { Node.prototype.recursivelyUpdateCssClassesOnNodes = function () {
this._updateCssClassName(); this._updateCssClassName();
if (Array.isArray(this.childs)) { if (Array.isArray(this.childs)) {
for (var i = 0; i < this.childs.length; i++) { for (var i = 0; i < this.childs.length; i++) {
this.childs[i].recursivelyUpdateCssClassesOnNodes(); this.childs[i].recursivelyUpdateCssClassesOnNodes();
} }
} }
} }
/** /**
@ -1317,8 +1317,8 @@ Node.select = function(editableDiv) {
*/ */
Node.prototype.blur = function() { Node.prototype.blur = function() {
// retrieve the actual field and value from the DOM. // retrieve the actual field and value from the DOM.
this._getDomValue(false); this._getDomValue();
this._getDomField(false); this._getDomField(true);
}; };
/** /**
@ -1541,11 +1541,11 @@ Node.prototype.deepEqual = function (json) {
/** /**
* Retrieve value from DOM * Retrieve value from DOM
* @param {boolean} [silent] If true (default), no errors will be thrown in
* case of invalid data
* @private * @private
*/ */
Node.prototype._getDomValue = function(silent) { Node.prototype._getDomValue = function() {
this._clearValueError();
if (this.dom.value && this.type != 'array' && this.type != 'object') { if (this.dom.value && this.type != 'array' && this.type != 'object') {
this.valueInnerText = util.getInnerText(this.dom.value); this.valueInnerText = util.getInnerText(this.dom.value);
} }
@ -1567,11 +1567,50 @@ Node.prototype._getDomValue = function(silent) {
} }
} }
catch (err) { catch (err) {
// keep the previous (valid) value // keep the previous value
this._setValueError(translate('cannotParseValueError'));
} }
} }
}; };
/**
* Show a local error in case of invalid value
* @param {string} message
* @private
*/
Node.prototype._setValueError = function (message) {
this.valueError = {
message: message
};
this.updateError();
}
Node.prototype._clearValueError = function () {
if (this.valueError) {
this.valueError = null;
this.updateError();
}
}
/**
* Show a local error in case of invalid or duplicate field
* @param {string} message
* @private
*/
Node.prototype._setFieldError = function (message) {
this.fieldError = {
message: message
};
this.updateError();
}
Node.prototype._clearFieldError = function () {
if (this.fieldError) {
this.fieldError = null;
this.updateError();
}
}
/** /**
* Handle a changed value * Handle a changed value
* @private * @private
@ -1878,11 +1917,13 @@ Node.prototype._updateDomField = function () {
/** /**
* Retrieve field from DOM * Retrieve field from DOM
* @param {boolean} [silent] If true (default), no errors will be thrown in * @param {boolean} [forceUnique] If true, the field name will be changed
* case of invalid data * into a unique name in case it is a duplicate.
* @private * @private
*/ */
Node.prototype._getDomField = function(silent) { Node.prototype._getDomField = function(forceUnique) {
this._clearFieldError();
if (this.dom.field && this.fieldEditable) { if (this.dom.field && this.fieldEditable) {
this.fieldInnerText = util.getInnerText(this.dom.field); this.fieldInnerText = util.getInnerText(this.dom.field);
} }
@ -1900,16 +1941,25 @@ Node.prototype._getDomField = function(silent) {
this._debouncedOnChangeField(); this._debouncedOnChangeField();
} }
else { else {
if (!silent) { if (forceUnique) {
// fix duplicate field: change it into a unique name // fix duplicate field: change it into a unique name
this.field = util.findUniqueName(field, existingFieldNames); field = util.findUniqueName(field, existingFieldNames);
this._debouncedOnChangeField(); if (field !== this.field) {
this.field = field;
// this._debouncedOnChangeField = util.debounce(this._onChangeField.bind(this), Node.prototype.DEBOUNCE_INTERVAL);
// this._onChangeField();
this._debouncedOnChangeField(); //FIXME: don't debounce but resolve right away, and cancel current debounce
}
}
else {
this._setFieldError(translate('duplicateFieldError'));
} }
} }
} }
} }
catch (err) { catch (err) {
// keep the previous (valid) field value // keep the previous field value
this._setFieldError(translate('cannotParseFieldError'));
} }
} }
}; };
@ -2412,6 +2462,7 @@ Node.prototype.setSelected = function (selected, isFirst) {
Node.prototype.updateValue = function (value) { Node.prototype.updateValue = function (value) {
this.value = value; this.value = value;
this.previousValue = value; this.previousValue = value;
this.valueError = undefined;
this.updateDom(); this.updateDom();
}; };
@ -2422,6 +2473,7 @@ Node.prototype.updateValue = function (value) {
Node.prototype.updateField = function (field) { Node.prototype.updateField = function (field) {
this.field = field; this.field = field;
this.previousField = field; this.previousField = field;
this.fieldError = undefined;
this.updateDom(); this.updateDom();
}; };
@ -2495,7 +2547,7 @@ Node.prototype.updateDom = function (options) {
// update field and value // update field and value
this._updateDomField(); this._updateDomField();
this._updateDomValue(); this._updateDomValue();
this._updateCssClassName(); this._updateCssClassName();
// update childs indexes // update childs indexes
@ -2599,7 +2651,7 @@ Node._findSchema = function (schema, schemaRefs, path) {
foundSchema = Node._findSchema(childSchema, schemaRefs, path); foundSchema = Node._findSchema(childSchema, schemaRefs, path);
} }
} }
for (var i = 0; i < path.length && childSchema; i++) { for (var i = 0; i < path.length && childSchema; i++) {
var nextPath = path.slice(i + 1, path.length); var nextPath = path.slice(i + 1, path.length);
var key = path[i]; var key = path[i];
@ -2864,7 +2916,8 @@ Node.prototype.onEvent = function (event) {
switch (type) { switch (type) {
case 'blur': case 'blur':
case 'change': case 'change':
this._getDomValue(true); this._getDomValue();
this._clearValueError();
this._updateDomValue(); this._updateDomValue();
if (this.value) { if (this.value) {
domValue.innerHTML = this._escapeHTML(this.value); domValue.innerHTML = this._escapeHTML(this.value);
@ -2873,7 +2926,7 @@ Node.prototype.onEvent = function (event) {
case 'input': case 'input':
//this._debouncedGetDomValue(true); // TODO //this._debouncedGetDomValue(true); // TODO
this._getDomValue(true); this._getDomValue();
this._updateDomValue(); this._updateDomValue();
break; break;
@ -2895,14 +2948,14 @@ Node.prototype.onEvent = function (event) {
case 'keyup': case 'keyup':
//this._debouncedGetDomValue(true); // TODO //this._debouncedGetDomValue(true); // TODO
this._getDomValue(true); this._getDomValue();
this._updateDomValue(); this._updateDomValue();
break; break;
case 'cut': case 'cut':
case 'paste': case 'paste':
setTimeout(function () { setTimeout(function () {
node._getDomValue(true); node._getDomValue();
node._updateDomValue(); node._updateDomValue();
}, 1); }, 1);
break; break;
@ -2914,14 +2967,6 @@ Node.prototype.onEvent = function (event) {
if (target == domField) { if (target == domField) {
switch (type) { switch (type) {
case 'blur': case 'blur':
this._getDomField(false);
this._updateDomField();
if (this.field) {
domField.innerHTML = this._escapeHTML(this.field);
}
break;
case 'change':
this._getDomField(true); this._getDomField(true);
this._updateDomField(); this._updateDomField();
if (this.field) { if (this.field) {
@ -2930,7 +2975,7 @@ Node.prototype.onEvent = function (event) {
break; break;
case 'input': case 'input':
this._getDomField(true); this._getDomField();
this._updateSchema(); this._updateSchema();
this._updateDomField(); this._updateDomField();
this._updateDomValue(); this._updateDomValue();
@ -2942,14 +2987,14 @@ Node.prototype.onEvent = function (event) {
break; break;
case 'keyup': case 'keyup':
this._getDomField(true); this._getDomField();
this._updateDomField(); this._updateDomField();
break; break;
case 'cut': case 'cut':
case 'paste': case 'paste':
setTimeout(function () { setTimeout(function () {
node._getDomField(true); node._getDomField();
node._updateDomField(); node._updateDomField();
}, 1); }, 1);
break; break;

View File

@ -22,6 +22,9 @@ var _defs = {
duplicateText: 'Duplicate', duplicateText: 'Duplicate',
duplicateTitle: 'Duplicate selected fields (Ctrl+D)', duplicateTitle: 'Duplicate selected fields (Ctrl+D)',
duplicateField: 'Duplicate this field (Ctrl+D)', duplicateField: 'Duplicate this field (Ctrl+D)',
duplicateFieldError: 'Duplicate field name',
cannotParseFieldError: 'Cannot parse field into JSON',
cannotParseValueError: 'Cannot parse value into JSON',
empty: 'empty', empty: 'empty',
expandAll: 'Expand all fields', expandAll: 'Expand all fields',
expandTitle: 'Click to expand/collapse this field (Ctrl+E). \n' + expandTitle: 'Click to expand/collapse this field (Ctrl+E). \n' +
@ -106,6 +109,9 @@ var _defs = {
duplicateText: '复制', duplicateText: '复制',
duplicateTitle: '复制选中字段(Ctrl+D)', duplicateTitle: '复制选中字段(Ctrl+D)',
duplicateField: '复制该字段(Ctrl+D)', duplicateField: '复制该字段(Ctrl+D)',
duplicateFieldError: '重复的字段名称',
cannotParseFieldError: '无法将字段解析为JSON',
cannotParseValueError: '无法将值解析为JSON',
empty: '清空', empty: '清空',
expandAll: '展开所有字段', expandAll: '展开所有字段',
expandTitle: '点击 展开/收缩 该字段(Ctrl+E). \n' + expandTitle: '点击 展开/收缩 该字段(Ctrl+E). \n' +
@ -190,6 +196,9 @@ var _defs = {
duplicateText: 'Duplicar', duplicateText: 'Duplicar',
duplicateTitle: 'Duplicar campos selecionados (Ctrl+D)', duplicateTitle: 'Duplicar campos selecionados (Ctrl+D)',
duplicateField: 'Duplicar este campo (Ctrl+D)', duplicateField: 'Duplicar este campo (Ctrl+D)',
duplicateFieldError: 'Nome do campo duplicado',
cannotParseFieldError: 'Não é possível analisar o campo no JSON',
cannotParseValueError: 'Não é possível analisar o valor em JSON',
empty: 'vazio', empty: 'vazio',
expandAll: 'Expandir todos campos', expandAll: 'Expandir todos campos',
expandTitle: 'Clique para expandir/encolher este campo (Ctrl+E). \n' + expandTitle: 'Clique para expandir/encolher este campo (Ctrl+E). \n' +
@ -286,6 +295,9 @@ var _defs = {
duplicateText: 'Aşağıya kopyala', duplicateText: 'Aşağıya kopyala',
duplicateTitle: 'Seçili alanlardan bir daha oluştur (Ctrl+D)', duplicateTitle: 'Seçili alanlardan bir daha oluştur (Ctrl+D)',
duplicateField: 'Bu alandan bir daha oluştur (Ctrl+D)', duplicateField: 'Bu alandan bir daha oluştur (Ctrl+D)',
duplicateFieldError: 'Duplicate field name',
cannotParseFieldError: 'Alan JSON\'a ayrıştırılamıyor',
cannotParseValueError: 'JSON\'a değer ayrıştırılamıyor',
empty: 'boş', empty: 'boş',
expandAll: 'Tüm alanları aç', expandAll: 'Tüm alanları aç',
expandTitle: 'Bu alanı açmak/kapatmak için tıkla (Ctrl+E). \n' + expandTitle: 'Bu alanı açmak/kapatmak için tıkla (Ctrl+E). \n' +