From 983fafdf9c118da102506427da36df8c6ba4c73d Mon Sep 17 00:00:00 2001 From: jos Date: Tue, 7 Aug 2018 11:03:27 +0200 Subject: [PATCH] Implemented options `onChangeJSON(json)` and `onChangeText(jsonString)` --- HISTORY.md | 7 ++++- docs/api.md | 29 +++++++++++++++---- examples/16_synchronize_editors.html | 34 +++++++++++++---------- examples/react_demo/src/App.js | 6 ++-- examples/react_demo/src/JSONEditorDemo.js | 12 +------- src/js/JSONEditor.js | 26 +++++++++++++++-- src/js/textmode.js | 18 ++++++++++-- src/js/treemode.js | 33 +++++++++++++++++++++- test/test_build.html | 6 ++++ 9 files changed, 131 insertions(+), 40 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 4f4c55c..ea25d2a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,8 +5,13 @@ https://github.com/josdejong/jsoneditor ## not yet released, version 5.20.0 +_Good news: JSONEditor is finally framework friendly and can now be easily +integrated in React, Vue, and Angular!_ + - Implemented new methods `update` and `updateText`, which maintain the state - of the editor (expanded nodes, search, selection). + of the editor (expanded nodes, search, selection). This makes it easy to + integrate in frameworks like React. +- Implemented options `onChangeJSON(json)` and `onChangeText(jsonString)`. ## 2018-08-02, version 5.19.2 diff --git a/docs/api.md b/docs/api.md index 64c4060..7e12532 100644 --- a/docs/api.md +++ b/docs/api.md @@ -48,11 +48,30 @@ Constructs a new JSONEditor. } ``` -- `{function} onChange` +- `{function} onChange()` - Set a callback function triggered when the contents of the JSONEditor change. Called without parameters. Will only be triggered on changes made by the user, not in case of programmatic changes via the functions `set`, `setText`, `update`, or `updateText`. + Set a callback function triggered when the contents of the JSONEditor change. + This callback does not pass the changed contents, use `get()` or `getText()` for that. + Note that `get()` can throw an exception in mode `text` or `code`, when the editor contains invalid JSON. + Will only be triggered on changes made by the user, not in case of programmatic changes via the functions `set`, `setText`, `update`, or `updateText`. + See also callback functions `onChangeJSON(json)` and `onChangeText(jsonString)`. -- `{function} onEditable` +- `{function} onChangeJSON(json)` + + Set a callback function triggered when the contents of the JSONEditor change. + Passes the changed contents as a JSON object. + Only applicable when option `mode` is `tree`, `form`, or `view`. + The callback will only be triggered on changes made by the user, not in case of programmatic changes via the functions `set`, `setText`, `update`, or `updateText`. + See also callback function `onChangeText(jsonString)`. + +- `{function} onChangeText(jsonString)` + + Set a callback function triggered when the contents of the JSONEditor change. + Passes the changed contents as a stringified JSON object. + The callback will only be triggered on changes made by the user, not in case of programmatic changes via the functions `set`, `setText`, `update`, or `updateText`. + See also callback function `onChangeJSON(json)`. + +- `{function} onEditable(node)` Set a callback function to determine whether individual nodes are editable or read-only. Only applicable when option `mode` is `tree`, `text`, or `code`. @@ -60,7 +79,7 @@ Constructs a new JSONEditor. In modes `text` and `code`, the callback is invoked as `editable(node)` where `node` is an empty object (no field, value, or path). In that case the function can return false to make the text or code editor completely read-only. -- `{function} onError` +- `{function} onError(error)` Set a callback function triggered when an error occurs. Invoked with the error as first argument. The callback is only invoked for errors triggered by a users action, like switching from code mode to tree mode or clicking the Format button whilst the editor doesn't contain valid JSON. @@ -459,7 +478,7 @@ See also `JSONEditor.set(json)`. Replace text data when the new data contains changes. In modes `tree`, `form`, and `view`, the state of the editor will be maintained (expanded nodes, search, selection). -See also `JSONEditor.setText(json)`. +See also `JSONEditor.setText(jsonString)`. This method throws an exception when the provided jsonString does not contain valid JSON and the editor is in mode `tree`, `view`, or `form`. diff --git a/examples/16_synchronize_editors.html b/examples/16_synchronize_editors.html index ed34ffc..b3a5fb4 100644 --- a/examples/16_synchronize_editors.html +++ b/examples/16_synchronize_editors.html @@ -23,14 +23,32 @@

- Keep two editors synchronized using onChange and update. + Keep two editors synchronized using onChangeText and updateText. +

+

+ This can be done too with onChangeJSON and update, which can only be used in + modes tree, form (and view).

diff --git a/examples/react_demo/src/App.js b/examples/react_demo/src/App.js index 0f99dae..e9d438a 100644 --- a/examples/react_demo/src/App.js +++ b/examples/react_demo/src/App.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; -import JSONEditorDemo from "./JSONEditorDemo"; +import JSONEditorDemo from './JSONEditorDemo'; import './App.css'; class App extends Component { @@ -27,7 +27,7 @@ class App extends Component {
@@ -41,7 +41,7 @@ class App extends Component {
     );
   }
 
-  onChange = (json) => {
+  onChangeJSON = (json) => {
     this.setState({ json });
   };
 
diff --git a/examples/react_demo/src/JSONEditorDemo.js b/examples/react_demo/src/JSONEditorDemo.js
index 6bbb027..c9be776 100644
--- a/examples/react_demo/src/JSONEditorDemo.js
+++ b/examples/react_demo/src/JSONEditorDemo.js
@@ -9,8 +9,7 @@ export default class JSONEditorDemo extends Component {
   componentDidMount () {
     const options = {
       mode: 'tree',
-      modes: ['tree', 'code'],
-      onChange: this.onChange
+      onChangeJSON: this.props.onChangeJSON
     };
 
     this.jsoneditor = new JSONEditor(this.container, options);
@@ -32,13 +31,4 @@ export default class JSONEditorDemo extends Component {
         
this.container = elem} /> ); } - - onChange = () => { - if (this.props.onChange) { - // note that this.jsoneditor.get() can fail in mode text/code - // when the contents is no valid JSON object. - // So if you need mode text/node, you must use getText() and setText(). - this.props.onChange(this.jsoneditor.get()); - } - } } diff --git a/src/js/JSONEditor.js b/src/js/JSONEditor.js index 0c6f66d..4abb4f2 100644 --- a/src/js/JSONEditor.js +++ b/src/js/JSONEditor.js @@ -20,7 +20,20 @@ var util = require('./util'); * 'tree' (default), 'view', * 'form', 'text', and 'code'. * {function} onChange Callback method, triggered - * on change of contents + * on change of contents. + * Does not pass the contents itself. + * See also `onChangeJSON` and + * `onChangeText`. + * {function} onChangeJSON Callback method, triggered + * in modes on change of contents, + * passing the changed contents + * as JSON. + * Only applicable for modes + * 'tree', 'view', and 'form'. + * {function} onChangeText Callback method, triggered + * in modes on change of contents, + * passing the changed contents + * as stringified JSON. * {function} onError Callback method, triggered * when an error occurs * {Boolean} search Enable search box. @@ -87,6 +100,14 @@ function JSONEditor (container, options, json) { delete options.editable; } + // warn if onChangeJSON is used when mode can be `text` or `code` + if (options.onChangeJSON) { + if (options.mode === 'text' || options.mode === 'code' || + (options.modes && (options.modes.indexOf('text') !== -1 || options.modes.indexOf('code') !== -1))) { + console.warn('Option "onChangeJSON" is not applicable to modes "text" and "code". Use "onChangeText" instead.'); + } + } + // validate options if (options) { Object.keys(options).forEach(function (option) { @@ -125,7 +146,8 @@ JSONEditor.prototype.DEBOUNCE_INTERVAL = 150; JSONEditor.VALID_OPTIONS = [ 'ajv', 'schema', 'schemaRefs','templates', 'ace', 'theme','autocomplete', - 'onChange', 'onEditable', 'onError', 'onModeChange', 'onSelectionChange', 'onTextSelectionChange', + 'onChange', 'onChangeJSON', 'onChangeText', + 'onEditable', 'onError', 'onModeChange', 'onSelectionChange', 'onTextSelectionChange', 'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', 'sortObjectKeys', 'navigationBar', 'statusBar', 'languages', 'language' ]; diff --git a/src/js/textmode.js b/src/js/textmode.js index e2d5be9..fcb26f5 100644 --- a/src/js/textmode.js +++ b/src/js/textmode.js @@ -20,8 +20,12 @@ var DEFAULT_THEME = 'ace/theme/jsoneditor'; * or "code". * {Number} indentation Number of indentation * spaces. 2 by default. - * {function} onChange Callback method - * triggered on change + * {function} onChange Callback method triggered on change. + * Does not pass the changed contents. + * {function} onChangeText Callback method, triggered + * in modes on change of contents, + * passing the changed contents + * as stringified JSON. * {function} onModeChange Callback method * triggered after setMode * {function} onEditable Determine if textarea is readOnly @@ -330,6 +334,16 @@ textmode._onChange = function () { console.error('Error in onChange callback: ', err); } } + + // trigger the onChangeText callback + if (this.options.onChangeText) { + try { + this.options.onChangeText(this.getText()); + } + catch (err) { + console.error('Error in onChangeText callback: ', err); + } + } }; /** diff --git a/src/js/treemode.js b/src/js/treemode.js index b63a65e..191fcf3 100644 --- a/src/js/treemode.js +++ b/src/js/treemode.js @@ -33,7 +33,18 @@ var treemode = {}; * {Boolean} history Enable history (undo/redo). * True by default * {function} onChange Callback method, triggered - * on change of contents + * on change of contents. + * Does not pass the changed contents. + * {function} onChangeJSON Callback method, triggered + * in modes on change of contents, + * passing the changed contents + * as JSON. + * Only applicable for modes + * 'tree', 'view', and 'form'. + * {function} onChangeText Callback method, triggered + * in modes on change of contents, + * passing the changed contents + * as stringified JSON. * {String} name Field name for the root node. * {boolean} escapeUnicode If true, unicode * characters are escaped. @@ -461,6 +472,26 @@ treemode._onChange = function () { console.error('Error in onChange callback: ', err); } } + + // trigger the onChangeJSON callback + if (this.options.onChangeJSON) { + try { + this.options.onChangeJSON(this.get()); + } + catch (err) { + console.error('Error in onChangeJSON callback: ', err); + } + } + + // trigger the onChangeText callback + if (this.options.onChangeText) { + try { + this.options.onChangeText(this.getText()); + } + catch (err) { + console.error('Error in onChangeText callback: ', err); + } + } }; /** diff --git a/test/test_build.html b/test/test_build.html index 728e5af..c8e22b3 100644 --- a/test/test_build.html +++ b/test/test_build.html @@ -51,6 +51,12 @@ onChange: function () { console.log('change'); }, + onChangeJSON: function (json) { + console.log('onChangeJSON', json); + }, + onChangeText: function (text) { + console.log('onChangeText', text); + }, indentation: 4, escapeUnicode: true };