Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for custom History actions #442

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

dhughes-xumak
Copy link
Contributor

Minor change to allow custom History actions to be recorded.

Includes a simple test, but no documentation (other than the History.prototype.add docblock).

@josdejong
Copy link
Owner

Thanks Dave. Can you explain a bit more about the use case behind this PR? If I understand you correctly you want to be able to change the content of the JSON document externally (programmatically), and want to log this as a change in the history (which the user can undo/redo)?

It may be interesting to pick this up in v6, which has JSON-Patch support built-in already which maybe makes this solution superfluous?

@dhughes-xumak
Copy link
Contributor Author

This followed from #438.

When I implemented a new context menu action, I wanted to be able to undo it, but the built-in History actions weren't sufficient. This also allows multiple operations to be grouped into a single history item.

Is History in v6 implemented as a chain of JSON-Patches? Does the v6 solution allow custom patches (as opposed to built-in patches) to be submitted to History?

@josdejong
Copy link
Owner

Ah, that makes sense. In that case, shouldn't we allow defining undo/redo in the context menus directly? Via two new options undo and redo?

The history in v6 is indeed a list of JSON-Patches, and a patch can contain one or multiple actions/changes (like add, replace, move, remove), so there is complete freedom there. JSON Patch is quite brilliant: very simple yet completely flexible.

@dhughes-xumak
Copy link
Contributor Author

The use case that I provided was just one example. I think a use case could exist which would want to be able to be able to keep track of changes in history, with those changes coming from outside of the plugins. I feel that keeping the History more flexible is advantageous.

As an example, the new test makes changes without using a plugin:
https://github.com/josdejong/jsoneditor/pull/442/files#diff-85a6eb0adcc489318be5cc9d507d8dedR69


Yes, it seems that JSON Patch is ideal for use with JSONEditor. I haven't looked much into v6 yet, so you may have already done this, but here's something to consider. In my integration with JSONEditor, I have built a simple engine around the history actions for implementing my context menu plugins' actions:

    var actions = {
        replaceArrayWithChild: {
            'getParams': function (node) {
                // remove the first character of the field name
                var oldName = node.getField();
                var newName = oldName.substring(1);

                // replace the array value of this node with the sole child in the array
                var oldValue = node.getValue();
                var newValue = node.childs[0].getValue();

                return { oldField:oldName, newField:newName, oldValue:oldValue, newValue:newValue, node:node };
            },
            'undo': function (params) {
                params.node.updateField(params.oldField);
                params.node.setValue(params.oldValue);
            },
            'redo': function (params) {
                params.node.updateField(params.newField);
                params.node.setValue(params.newValue);
            }
        }
    };
...
// JSONEditor options
                    contextMenuPlugins: [
                        {
                            ...
                            click: function(node) {
                                performActionOnNode(actions.replaceArrayWithChild, node);
                            }
                        }
                    ]
...
    var performActionOnNode = function(action, node) {
        // the action builds its own params
        var params = action.getParams(node);

        // perform the action
        // note that `redo` should probably be called `do` in this context
        action.redo(params);

        // save the action to history
        node.editor._onAction(action, params);
    }

This encapsulates the action setup in getParams, so the history object can store exactly what it needs to do/undo the action. It uses performActionOnNode as the engine to perform the setup, perform the action, and save it in history.

@josdejong
Copy link
Owner

Please check out v6 then :D I'm curious to hear your feedback.

In v6, you can call a method editor.patch() passing JSON-Patch action(s). This is automatically registered in the history, so a user can undo it. So when we have support for contextMenuPlugins in v6, it should be simply a matter of calling .patch(...) in the click handler of the plugin button.

The following example shows how you can keep two editors in sync with v6: https://github.com/josdejong/jsoneditor/blob/next/examples/09_synchronize_two_editors.html

@dhughes-xumak
Copy link
Contributor Author

dhughes-xumak commented Jul 26, 2017

It seems we independently arrived at very similar solutions. When I find the time, I will upgrade my integration to v6 and see how it works.

@josdejong
Copy link
Owner

👍

@josdejong
Copy link
Owner

Thinking aloud: it may still be a good idea to finish your implementation for v5. v6 is far from finished. I expect there are some things you will have to do quite different, since it's rebuilt from scratch with React. We'll see.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants