react-diagrams
react-diagrams copied to clipboard
Keep track if canvas has changes to conditionally render a save button?
I'd like to conditionally render a Save button at the bottom of the screen only once there have been changes to the canvas, i.e. (nodes moved/added/deleted, links changed etc.).
Sounds simple enough in theory, but I'm having a lot of trouble finding one place to check for changes without writing a lot of complicated logic.
I've tried checking for when a 'nodesUpdated' or 'linksUpdated' event fires and dispatching an action to signal changes to the canvas then, but that's causing some weird behavior with the canvas.
Normally I'd just save something in the local state and compare. I can see that there's a StateMachine
listener in the StateMachine file, but not quite sure how to utilize this. I can use this.engine.getStateMachine().getCurrentState()
to get my current State, but how to I find/compare it with my previousState?
Has anyone done something similar? I feel like I must be missing something obvious!
Hey!
Hmm, there's two approaches I can think of.
-
Very simple, but expensive: serialize the diagram whenever it is saved and store that on a
lastSaved
state as a string (JSON.stringify(model.serialize)
). Do the same thing every X seconds, but instead of saving on a state, compare it to the last state. This is expensive because serializing the whole diagram is not a cheap operation... and has the downside of the delay that will happen between the user doing something and the save button enabling. -
More complex, but efficient: start with the state
changesSinceLastSave
with valuefalse
. Listen to a list of events that you want to consider "changes", and whenever one of them happens, just setchangesSinceLastSave
totrue
. Whenever the user saves the diagram, set this tofalse
again. This is more complex, because as far as I know there's no proper documentation on which events are emitted and when... so my suggestion would be to change thefireEvent
on theBaseObserver
class to log every event that is fired and see if an event is fired for every case you need (you can do it directly in yournode_modules
just to analyze). If there's any action that does not fire any event, then you would probably need to implement your custom state and fire them manually. There's an example on how to implement your custom state on the demos. On my project, I've been using something similar to this to implement undo/redo. For instance:
- I have my custom state (
States
) - This custom state has a custom implementation for
DragNewLinkState
, which fires the custom'linkAdded'
event - I call
registerListener
on this event when I initialize the engine (DiagramEngine
) - And then I handle these events (
commandHandlers
)
Using that strategy didn't cause any weird behavior in my case. It has been working fine.
@renato-bohler Thanks for posting this!
Following it to good effect, but any suggestions on how to track position changes? I thought I could add a positionChanged
to the model like below...
this.model.registerListener({
linksUpdated: (e: any) => {
console.log("linksUpdated ", e);
this.setState({changesSinceLastSave: true});
},
nodesUpdated: (e: any) => {
console.log("nodesUpdated ", e);
this.setState({changesSinceLastSave: true});
},
positionChanged: (e: any) => {
console.log("positionChanged !", e);
this.setState({changesSinceLastSave: true});
},
});
... but realizing now that unfortunately a global positionChanged
event isn't broadcast from the DiagramModel if any node is moved.
Any thoughts on how to watch for this across any node in any layer?
Just like the original poster, I'm trying to come up with the simplest way to know when to enable the save button.
@danielmcquillen I'd say you need to create a custom state, just like I mentioned before, but adding a custom implementation for MoveItemsState
, which will fire an event with a name like entitiesMoved
when triggered.
You can then listen to entitiesMoved
instead of positionChanged
.